Skip to content

Commit c486f40

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. Other changes include... Sema: adjust protocol extension signature hack With SuppressedAssociatedTypesWithDefaults, we can have associated types that are missing invertible conformance requirements, so we need to try re-building the signature for the extension of the protocol. Otherwise, we won't infer the new default(s) for those associated types. This previous hack was only trying to shortcut the rebuilding if it saw Self is lacking a conformance requirement. Now it'll check if any inverses were written in source for that protocol.
1 parent 9071d1e commit c486f40

20 files changed

+992
-73
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: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/Basic/Debug.h"
2525
#include "llvm/ADT/Hashing.h"
2626
#include "llvm/ADT/PointerIntPair.h"
27+
#include "llvm/ADT/SmallPtrSet.h"
2728
#include "llvm/Support/ErrorHandling.h"
2829

2930
namespace swift {
@@ -274,9 +275,16 @@ struct InverseRequirement {
274275

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

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

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,10 @@ EXPERIMENTAL_FEATURE(Volatile, true)
441441
// 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, false)
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: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,64 @@ InvertibleProtocolKind InverseRequirement::getKind() const {
364364
return *getInvertibleProtocolKind(*(protocol->getKnownProtocolKind()));
365365
}
366366

367+
/// Do these two ArrayRefs alias any of the same memory?
368+
template<typename T>
369+
bool arrayrefs_overlap(ArrayRef<T> A, ArrayRef<T> B) {
370+
if (A.empty() || B.empty())
371+
return false;
372+
373+
const T *ABegin = A.data();
374+
const T *AEnd = ABegin + A.size();
375+
const T *BBegin = B.data();
376+
const T *BEnd = BBegin + B.size();
377+
378+
return ABegin < BEnd && BBegin < AEnd;
379+
}
380+
381+
static CanType cleanSubject(Type ty) {
382+
if (auto dmt = ty->getAs<DependentMemberType>()) {
383+
auto canBase = cleanSubject(dmt->getBase());
384+
ty = DependentMemberType::get(canBase, dmt->getName());
385+
}
386+
return ty->getCanonicalType();
387+
}
388+
367389
void InverseRequirement::expandDefaults(
368390
ASTContext &ctx,
369391
ArrayRef<Type> gps,
370-
SmallVectorImpl<StructuralRequirement> &result) {
392+
ArrayRef<StructuralRequirement> existingReqs,
393+
SmallVectorImpl<StructuralRequirement> &result,
394+
SmallVectorImpl<Type> &expandedGPs) {
395+
// If there are no subjects, there's nothing to expand.
396+
if (gps.empty())
397+
return;
398+
399+
// Vectors can reallocate, so we mustn't be looking at an ArrayRef pointing
400+
// into the same span of memory that we're also mutating!
401+
ASSERT(!arrayrefs_overlap(existingReqs, {result.data(), result.size()}) &&
402+
"requirements are aliasing!");
403+
ASSERT(!arrayrefs_overlap(gps, {expandedGPs.data(), expandedGPs.size()}) &&
404+
"types are aliasing!");
405+
406+
auto expandFor = [&](Type gp) {
407+
expandedGPs.push_back(gp);
408+
for (auto ip : InvertibleProtocolSet::allKnown()) {
409+
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
410+
411+
result.push_back({{RequirementKind::Conformance, gp,
412+
proto->getDeclaredInterfaceType()},
413+
SourceLoc()});
414+
}
415+
};
416+
417+
// Find the root generic parameter, looking through any DMT's.
418+
auto getRoot = [](Type gp) -> CanType {
419+
return gp->getDependentMemberRoot()->getCanonicalType();
420+
};
421+
422+
// Used for further expansion of defaults for dependent type members of gps.
423+
// Contains the root generic parameters of the ones we were asked to expand.
424+
llvm::SmallSetVector<CanType, 8> seenGPs;
371425
for (auto gp : gps) {
372426
// Value generics never have inverses (or the positive thereof).
373427
if (auto gpTy = gp->getAs<GenericTypeParamType>()) {
@@ -376,11 +430,56 @@ void InverseRequirement::expandDefaults(
376430
}
377431
}
378432

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()});
433+
// Each generic parameter is inferred to have a conformance requirement
434+
// to all invertible protocols, regardless of what other requirements exist.
435+
// We later cancel them out in applyInverses.
436+
expandFor(gp);
437+
seenGPs.insert(getRoot(gp));
438+
}
439+
440+
// Look for structural requirements stating type parameter G conforms to P.
441+
// If P has a primary associatedtype P.A, infer default requirements for G.A
442+
// For example, given protocol,
443+
//
444+
// protocol P<A>: ~Copyable { associatedtype A: ~Copyable }
445+
//
446+
// For an initial gp [T] and structural requirements [T: P, T.A: P],
447+
// we proceed with one pass over the original structural requirements:
448+
//
449+
// 1. Expand new requirement 'T: Copy' (already done earlier)
450+
// 2. Because of original requirement 'T: P', infer requirement [T.A: Copy]
451+
// 4. Because of original requirement 'T.A: P', infer requirement [T.A.A: Copy]
452+
// 5. Expansion stops, as there are no more requirements.
453+
//
454+
// Because Copyable & Escapable don't have associated types, we're done.
455+
if (ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypesWithDefaults)) {
456+
// Help avoid duplicate expansions of the same DMT.
457+
llvm::SmallSetVector<CanType, 8> dmtsExpanded;
458+
459+
for (auto const& sreq : existingReqs) {
460+
auto &req = sreq.req;
461+
if (req.getKind() != RequirementKind::Conformance)
462+
continue;
463+
464+
// Is this subject rooted in one we did expand defaults for?
465+
auto subject = req.getFirstType();
466+
if (!seenGPs.contains(getRoot(subject)))
467+
continue;
468+
469+
// Given a structural requirement `Subject: P`,
470+
// for each primary associated type A of P, expand defaults for Subject.A
471+
auto *proto = req.getProtocolDecl();
472+
for (auto *ATD : proto->getPrimaryAssociatedTypes()) {
473+
auto dmt = DependentMemberType::get(subject, ATD);
474+
auto cleanDMT = cleanSubject(dmt);
475+
476+
// Did we already expand for the same DMT?
477+
if (dmtsExpanded.contains(cleanDMT))
478+
continue;
479+
480+
expandFor(dmt);
481+
dmtsExpanded.insert(cleanDMT);
482+
}
384483
}
385484
}
386485
}

lib/AST/RequirementMachine/ApplyInverses.cpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@
4040
using namespace swift;
4141
using namespace rewriting;
4242

43+
// Wipes-out the AssociatedTypeDecl's from DependentMemberTypes, leaving them
44+
// with just their names. This ensures the DMT's compare equal in applyInverses.
45+
//
46+
// This "cleaning" is needed because InverseRequirement subjects look
47+
// like this, where the DMT is only by-name and not resolved to a particular
48+
// AssociatedTypeDecl:
49+
//
50+
// (dependent_member_type name="Iterator"
51+
// (base=generic_type_param_type depth=0 index=0 param_kind=type))
52+
//
53+
// whereas the ones from 'gps' may have the AssociatedTypeDecl inside them:
54+
//
55+
// (dependent_member_type assoc_type="...Test.Iterator..."
56+
// (base=generic_type_param_type depth=0 index=0 name="Self" param_kind=type))
57+
//
58+
// and they will NOT compare equal, even after being converted to a CanType,
59+
// unless if the AssociatedTypeDecl is replaced with just its name.
60+
static CanType cleanSubject(Type ty) {
61+
if (auto dmt = ty->getAs<DependentMemberType>()) {
62+
auto canBase = cleanSubject(dmt->getBase());
63+
ty = DependentMemberType::get(canBase, dmt->getName());
64+
}
65+
return ty->getCanonicalType();
66+
}
67+
4368
void swift::rewriting::applyInverses(
4469
ASTContext &ctx,
4570
ArrayRef<Type> gps,
@@ -54,13 +79,14 @@ void swift::rewriting::applyInverses(
5479
}
5580

5681
const bool allowInverseOnAssocType =
57-
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes);
82+
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypes) ||
83+
ctx.LangOpts.hasFeature(Feature::SuppressedAssociatedTypesWithDefaults);
5884

5985
llvm::DenseMap<CanType, CanType> representativeGPs;
6086

6187
// Start with an identity mapping.
6288
for (auto gp : gps) {
63-
auto canGP = gp->getCanonicalType();
89+
auto canGP = cleanSubject(gp);
6490
representativeGPs.insert({canGP, canGP});
6591
}
6692
bool hadSameTypeConstraintInScope = false;
@@ -95,8 +121,8 @@ void swift::rewriting::applyInverses(
95121
// If one end of the same-type requirement is in scope, and the other is
96122
// a concrete type or out-of-scope generic parameter, then the other
97123
// parameter is also effectively out of scope.
98-
auto firstTy = explicitReqt.req.getFirstType()->getCanonicalType();
99-
auto secondTy = explicitReqt.req.getSecondType()->getCanonicalType();
124+
auto firstTy = cleanSubject(explicitReqt.req.getFirstType());
125+
auto secondTy = cleanSubject(explicitReqt.req.getSecondType());
100126
if (!representativeGPs.count(firstTy)
101127
&& !representativeGPs.count(secondTy)) {
102128
// Same type constraint doesn't involve any in-scope generic parameters.
@@ -118,23 +144,27 @@ void swift::rewriting::applyInverses(
118144
typeOutOfScope = firstTy;
119145
} else {
120146
// 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) {
147+
148+
// Choose one parameter to be the representative for the other, using
149+
// a notion of "order" where we prefer the lexicographically smaller
150+
// type to be the representative for the larger one.
151+
const int sign = compareDependentTypes(firstTy, secondTy);
152+
if (sign == 0) {
126153
// `T == T` has no effect.
127154
continue;
128155
}
129-
130-
if (firstGP->getDepth() > secondGP->getDepth()
131-
|| (firstGP->getDepth() == secondGP->getDepth()
132-
&& firstGP->getIndex() > secondGP->getIndex())) {
133-
std::swap(firstGP, secondGP);
156+
157+
CanType smaller, larger;
158+
if (sign < 0) {
159+
smaller = firstTy;
160+
larger = secondTy;
161+
} else {
162+
smaller = secondTy;
163+
larger = firstTy;
134164
}
135165

136166
hadSameTypeConstraintInScope = true;
137-
representativeGPs.insert_or_assign(secondGP, representativeGPFor(firstGP));
167+
representativeGPs.insert_or_assign(larger, representativeGPFor(smaller));
138168
continue;
139169
}
140170

@@ -164,7 +194,7 @@ void swift::rewriting::applyInverses(
164194
// Summarize the inverses and diagnose ones that are incorrect.
165195
llvm::DenseMap<CanType, InvertibleProtocolSet> inverses;
166196
for (auto inverse : inverseList) {
167-
auto canSubject = inverse.subject->getCanonicalType();
197+
auto canSubject = cleanSubject(inverse.subject);
168198

169199
// Inverses on associated types are experimental.
170200
if (!allowInverseOnAssocType && canSubject->is<DependentMemberType>()) {
@@ -244,7 +274,7 @@ void swift::rewriting::applyInverses(
244274
}
245275

246276
// See if this subject is in-scope.
247-
auto subject = req.getFirstType()->getCanonicalType();
277+
auto subject = cleanSubject(req.getFirstType());
248278
auto representative = representativeGPs.find(subject);
249279
if (representative == representativeGPs.end()) {
250280
return false;

0 commit comments

Comments
 (0)