Skip to content

Commit 6f95203

Browse files
committed
Sema: introduce SuppressedAssociatedTypesWithDefaults
This is similar to SuppressedAssociatedTypes, but infers default requirements when primary associated types of protocols are suppressed. This defaulting for the primary associated types happens in extensions of the protocol, along with generic parameters, whenever a source-written requirement states a conformance requirement for the protocol. Thus, the current scheme for this defaulting is a simplistic, driven by source-written requirements, rather than facts that are inferred while building generic signatures. Defaults are not expanded for infinitely many associated types. rdar://135168163
1 parent 761502f commit 6f95203

23 files changed

+978
-74
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5811,6 +5811,11 @@ class ProtocolDecl final : public NominalTypeDecl {
58115811
/// instead.
58125812
ArrayRef<StructuralRequirement> getStructuralRequirements() const;
58135813

5814+
/// Retrieves the original inverse requirements written in source.
5815+
///
5816+
/// The structural requirements already have had these applied to them.
5817+
ArrayRef<InverseRequirement> getInverseRequirements() const;
5818+
58145819
/// Retrieve same-type requirements implied by protocol typealiases with the
58155820
/// same name as associated types, and diagnose cases that are better expressed
58165821
/// via a 'where' clause.

include/swift/AST/Requirement.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,16 @@ struct InverseRequirement {
274274

275275
/// Appends additional requirements corresponding to defaults for the given
276276
/// generic parameters.
277+
/// \param gps the generic parameters for which start expanding defaults.
278+
/// \param existingReqs The source-written requirements prior to expansion.
279+
/// \param result The vector to append new default requirements into.
280+
/// \param expandedGPs The subjects for which defaults were expanded,
281+
/// which may be different and more Type's than 'gps'
277282
static void expandDefaults(ASTContext &ctx,
278283
ArrayRef<Type> gps,
279-
SmallVectorImpl<StructuralRequirement> &result);
284+
ArrayRef<StructuralRequirement> existingReqs,
285+
SmallVectorImpl<StructuralRequirement> &result,
286+
SmallVectorImpl<Type> &expandedGPs);
280287

281288
void print(raw_ostream &os, const PrintOptions &opts, bool forInherited=false) const;
282289
};

include/swift/AST/TypeCheckRequests.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,18 +561,19 @@ class IsDynamicRequest :
561561
void cacheResult(bool value) const;
562562
};
563563

564-
class StructuralRequirementsRequest :
565-
public SimpleRequest<StructuralRequirementsRequest,
566-
ArrayRef<StructuralRequirement>(ProtocolDecl *),
567-
RequestFlags::Cached> {
564+
class StructuralRequirementsRequest
565+
: public SimpleRequest<StructuralRequirementsRequest,
566+
std::pair<ArrayRef<StructuralRequirement>,
567+
ArrayRef<InverseRequirement>>(ProtocolDecl *),
568+
RequestFlags::Cached> {
568569
public:
569570
using SimpleRequest::SimpleRequest;
570571

571572
private:
572573
friend SimpleRequest;
573574

574575
// Evaluation.
575-
ArrayRef<StructuralRequirement>
576+
std::pair<ArrayRef<StructuralRequirement>, ArrayRef<InverseRequirement>>
576577
evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
577578

578579
public:

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ SWIFT_REQUEST(TypeChecker, RequirementRequest,
301301
Requirement(WhereClauseOwner, unsigned, TypeResolutionStage),
302302
Cached, HasNearestLocation)
303303
SWIFT_REQUEST(TypeChecker, StructuralRequirementsRequest,
304-
ArrayRef<StructuralRequirement>(ProtocolDecl *), Cached,
304+
(std::pair<ArrayRef<StructuralRequirement>, ArrayRef<InverseRequirement>>(ProtocolDecl *)), Cached,
305305
HasNearestLocation)
306306
SWIFT_REQUEST(TypeChecker, TypeAliasRequirementsRequest,
307307
ArrayRef<Requirement>(ProtocolDecl *), Cached,

include/swift/Basic/Features.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,13 @@ EXPERIMENTAL_FEATURE(Embedded, true)
438438
/// Enables importing the Volatile module
439439
EXPERIMENTAL_FEATURE(Volatile, true)
440440

441-
// Enables ~Copyable and ~Escapable annotations on associatedtype declarations.
441+
// LEGACY: Enables ~Copyable and ~Escapable annotations on associatedtype declarations.
442442
EXPERIMENTAL_FEATURE(SuppressedAssociatedTypes, true)
443443

444+
// Enables ~Copyable and ~Escapable annotations on associatedtype declarations,
445+
// and infers defaults for them.
446+
EXPERIMENTAL_FEATURE(SuppressedAssociatedTypesWithDefaults, true)
447+
444448
/// Allow destructuring stored `let` bindings in structs.
445449
EXPERIMENTAL_FEATURE(StructLetDestructuring, true)
446450

lib/AST/Decl.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7472,7 +7472,14 @@ ArrayRef<StructuralRequirement>
74727472
ProtocolDecl::getStructuralRequirements() const {
74737473
return evaluateOrDefault(
74747474
getASTContext().evaluator,
7475-
StructuralRequirementsRequest{const_cast<ProtocolDecl *>(this)}, {});
7475+
StructuralRequirementsRequest{const_cast<ProtocolDecl *>(this)}, {}).first;
7476+
}
7477+
7478+
ArrayRef<InverseRequirement>
7479+
ProtocolDecl::getInverseRequirements() const {
7480+
return evaluateOrDefault(
7481+
getASTContext().evaluator,
7482+
StructuralRequirementsRequest{const_cast<ProtocolDecl *>(this)}, {}).second;
74767483
}
74777484

74787485
ArrayRef<Requirement>

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ UNINTERESTING_FEATURE(RawLayout)
121121
UNINTERESTING_FEATURE(Embedded)
122122
UNINTERESTING_FEATURE(Volatile)
123123
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
124+
UNINTERESTING_FEATURE(SuppressedAssociatedTypesWithDefaults)
124125
UNINTERESTING_FEATURE(StructLetDestructuring)
125126
UNINTERESTING_FEATURE(MacrosOnImports)
126127
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)

lib/AST/Requirement.cpp

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/AST/Module.h"
2424
#include "swift/AST/Types.h"
2525
#include "swift/Basic/Assertions.h"
26+
#include "RequirementMachine/RequirementLowering.h"
2627

2728
using namespace swift;
2829

@@ -364,10 +365,51 @@ InvertibleProtocolKind InverseRequirement::getKind() const {
364365
return *getInvertibleProtocolKind(*(protocol->getKnownProtocolKind()));
365366
}
366367

368+
/// Do these two ArrayRefs alias any of the same memory?
369+
template<typename T>
370+
bool arrayrefs_overlap(ArrayRef<T> A, ArrayRef<T> B) {
371+
if (A.empty() || B.empty())
372+
return false;
373+
374+
const T *ABegin = A.data();
375+
const T *AEnd = ABegin + A.size();
376+
const T *BBegin = B.data();
377+
const T *BEnd = BBegin + B.size();
378+
379+
return ABegin < BEnd && BBegin < AEnd;
380+
}
381+
367382
void InverseRequirement::expandDefaults(
368383
ASTContext &ctx,
369384
ArrayRef<Type> gps,
370-
SmallVectorImpl<StructuralRequirement> &result) {
385+
ArrayRef<StructuralRequirement> existingReqs,
386+
SmallVectorImpl<StructuralRequirement> &result,
387+
SmallVectorImpl<Type> &expandedGPs) {
388+
// If there are no subjects, there's nothing to expand.
389+
if (gps.empty())
390+
return;
391+
392+
// Vectors can reallocate, so we mustn't be looking at an ArrayRef pointing
393+
// into the same span of memory that we're also mutating!
394+
ASSERT(!arrayrefs_overlap(existingReqs, {result.data(), result.size()}) &&
395+
"requirements are aliasing!");
396+
ASSERT(!arrayrefs_overlap(gps, {expandedGPs.data(), expandedGPs.size()}) &&
397+
"types are aliasing!");
398+
399+
auto expandFor = [&](Type gp) {
400+
expandedGPs.push_back(gp);
401+
for (auto ip : InvertibleProtocolSet::allKnown()) {
402+
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
403+
404+
result.push_back({{RequirementKind::Conformance, gp,
405+
proto->getDeclaredInterfaceType()},
406+
SourceLoc()});
407+
}
408+
};
409+
410+
// Used for further expansion of defaults for dependent type members of gps.
411+
// Contains the root generic parameters of the ones we were asked to expand.
412+
llvm::SmallSetVector<CanType, 8> seenRoots;
371413
for (auto gp : gps) {
372414
// Value generics never have inverses (or the positive thereof).
373415
if (auto gpTy = gp->getAs<GenericTypeParamType>()) {
@@ -376,11 +418,57 @@ void InverseRequirement::expandDefaults(
376418
}
377419
}
378420

379-
for (auto ip : InvertibleProtocolSet::allKnown()) {
380-
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
381-
result.push_back({{RequirementKind::Conformance, gp,
382-
proto->getDeclaredInterfaceType()},
383-
SourceLoc()});
421+
// Each generic parameter is inferred to have a conformance requirement
422+
// to all invertible protocols, regardless of what other requirements exist.
423+
// We later cancel them out in applyInverses.
424+
expandFor(gp);
425+
seenRoots.insert(gp->getDependentMemberRoot()->getCanonicalType());
426+
}
427+
428+
// Look for structural requirements stating type parameter G conforms to P.
429+
// If P has a primary associatedtype P.A, infer default requirements for G.A
430+
// For example, given protocol,
431+
//
432+
// protocol P<A>: ~Copyable { associatedtype A: ~Copyable }
433+
//
434+
// For an initial gp [T] and structural requirements [T: P, T.A: P],
435+
// we proceed with one pass over the original structural requirements:
436+
//
437+
// 1. Expand new requirement 'T: Copyable' (already done earlier)
438+
// 2. Because of requirement 'T: P', infer requirement [T.A: Copyable]
439+
// 4. Because of requirement 'T.A: P', infer requirement [T.A.A: Copyable]
440+
// 5. Expansion stops, as no other structural requirements are relevant.
441+
// Because Copyable & Escapable don't have associated types, we're done.
442+
if (ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypesWithDefaults)) {
443+
// Help avoid duplicate expansions of the same member type.
444+
llvm::SmallSetVector<CanType, 8> dmtsExpanded;
445+
446+
for (auto const& sreq : existingReqs) {
447+
auto &req = sreq.req;
448+
if (req.getKind() != RequirementKind::Conformance)
449+
continue;
450+
451+
// Is this subject rooted in one we did expand defaults for?
452+
auto subject = req.getFirstType();
453+
auto subjectRoot = subject->getDependentMemberRoot()->getCanonicalType();
454+
if (!seenRoots.contains(subjectRoot))
455+
continue;
456+
457+
// Given a structural requirement `Subject: P`,
458+
// for each primary associated type A of P, expand defaults for Subject.A
459+
auto *proto = req.getProtocolDecl();
460+
for (auto *ATD : proto->getPrimaryAssociatedTypes()) {
461+
auto dmt = DependentMemberType::get(subject, ATD);
462+
auto cleanDMT =
463+
rewriting::stripBoundDependentMemberTypes(dmt)->getCanonicalType();
464+
465+
// Did we already expand for the same DMT?
466+
if (dmtsExpanded.contains(cleanDMT))
467+
continue;
468+
469+
expandFor(dmt);
470+
dmtsExpanded.insert(cleanDMT);
471+
}
384472
}
385473
}
386474
}

lib/AST/RequirementMachine/ApplyInverses.cpp

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ void swift::rewriting::applyInverses(
5454
}
5555

5656
const bool allowInverseOnAssocType =
57-
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes);
57+
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes) ||
58+
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypesWithDefaults);
5859

5960
llvm::DenseMap<CanType, CanType> representativeGPs;
6061

6162
// Start with an identity mapping.
6263
for (auto gp : gps) {
63-
auto canGP = gp->getCanonicalType();
64+
auto canGP = stripBoundDependentMemberTypes(gp)->getCanonicalType();
6465
representativeGPs.insert({canGP, canGP});
6566
}
6667
bool hadSameTypeConstraintInScope = false;
@@ -95,8 +96,12 @@ void swift::rewriting::applyInverses(
9596
// If one end of the same-type requirement is in scope, and the other is
9697
// a concrete type or out-of-scope generic parameter, then the other
9798
// parameter is also effectively out of scope.
98-
auto firstTy = explicitReqt.req.getFirstType()->getCanonicalType();
99-
auto secondTy = explicitReqt.req.getSecondType()->getCanonicalType();
99+
auto firstTy =
100+
stripBoundDependentMemberTypes(explicitReqt.req.getFirstType())
101+
->getCanonicalType();
102+
auto secondTy =
103+
stripBoundDependentMemberTypes(explicitReqt.req.getSecondType())
104+
->getCanonicalType();
100105
if (!representativeGPs.count(firstTy)
101106
&& !representativeGPs.count(secondTy)) {
102107
// Same type constraint doesn't involve any in-scope generic parameters.
@@ -118,23 +123,27 @@ void swift::rewriting::applyInverses(
118123
typeOutOfScope = firstTy;
119124
} else {
120125
// Otherwise, both ends of the same-type constraint are in scope.
121-
// Fold the lexicographically-greater parameter with the lesser.
122-
auto firstGP = cast<GenericTypeParamType>(firstTy);
123-
auto secondGP = cast<GenericTypeParamType>(secondTy);
124-
125-
if (firstGP == secondGP) {
126+
127+
// Choose one parameter to be the representative for the other, using
128+
// a notion of "order" where we prefer the lexicographically smaller
129+
// type to be the representative for the larger one.
130+
const int sign = compareDependentTypes(firstTy, secondTy);
131+
if (sign == 0) {
126132
// `T == T` has no effect.
127133
continue;
128134
}
129-
130-
if (firstGP->getDepth() > secondGP->getDepth()
131-
|| (firstGP->getDepth() == secondGP->getDepth()
132-
&& firstGP->getIndex() > secondGP->getIndex())) {
133-
std::swap(firstGP, secondGP);
135+
136+
CanType smaller, larger;
137+
if (sign < 0) {
138+
smaller = firstTy;
139+
larger = secondTy;
140+
} else {
141+
smaller = secondTy;
142+
larger = firstTy;
134143
}
135144

136145
hadSameTypeConstraintInScope = true;
137-
representativeGPs.insert_or_assign(secondGP, representativeGPFor(firstGP));
146+
representativeGPs.insert_or_assign(larger, representativeGPFor(smaller));
138147
continue;
139148
}
140149

@@ -164,7 +173,8 @@ void swift::rewriting::applyInverses(
164173
// Summarize the inverses and diagnose ones that are incorrect.
165174
llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
166175
for (auto inverse : inverseList) {
167-
auto canSubject = inverse.subject->getCanonicalType();
176+
auto canSubject =
177+
stripBoundDependentMemberTypes(inverse.subject)->getCanonicalType();
168178

169179
// Inverses on associated types are experimental.
170180
if (!allowInverseOnAssocType && canSubject->is<DependentMemberType>()) {
@@ -244,7 +254,7 @@ void swift::rewriting::applyInverses(
244254
}
245255

246256
// See if this subject is in-scope.
247-
auto subject = req.getFirstType()->getCanonicalType();
257+
auto subject = stripBoundDependentMemberTypes(req.getFirstType())->getCanonicalType();
248258
auto representative = representativeGPs.find(subject);
249259
if (representative == representativeGPs.end()) {
250260
return false;

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ using namespace rewriting;
156156
/// Strip associated types from types used as keys to erase differences between
157157
/// resolved types coming from the parent generic signature and unresolved types
158158
/// coming from user-written requirements.
159-
static Type stripBoundDependentMemberTypes(Type t) {
159+
Type swift::rewriting::stripBoundDependentMemberTypes(Type t) {
160160
if (auto *depMemTy = t->getAs<DependentMemberType>()) {
161161
return DependentMemberType::get(
162162
stripBoundDependentMemberTypes(depMemTy->getBase()),

0 commit comments

Comments
 (0)