Skip to content

Commit 12cb3f9

Browse files
authored
Merge pull request #85704 from kavon/suppressed-assoc-types-with-defaults
Sema: introduce SuppressedAssociatedTypesWithDefaults
2 parents 5c2fe42 + ab1d83a commit 12cb3f9

30 files changed

+1112
-137
lines changed

docs/Generics/chapters/basic-operation.tex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,16 @@ \subsection*{The Rewrite Context}
636636
\item \texttt{getProtocolDependencies()} evaluates the \texttt{ProtocolDependenciesRequest}.
637637
\end{itemize}
638638

639+
\IndexSource{protocol inverses request}
640+
\apiref{ProtocolInversesRequest}{class}
641+
A request evaluator request which enumerates the inverse requirements written on the given protocol and its associated types.
642+
643+
\apiref{ProtocolDecl}{class}
644+
See also \SecRef{src:declarations}.
645+
\begin{itemize}
646+
\item \texttt{getInverseRequirements()} evaluates the \texttt{ProtocolInversesRequest}.
647+
\end{itemize}
648+
639649
\IndexSource{requirement machine}
640650
\apiref{rewriting::RequirementMachine}{class}
641651
A list of rewrite rules and a property map. See also \SecRef{src:symbols terms rules} and \SecRef{property map sourceref}. Entry points for initializing a requirement machine, called by the rewrite context and various requests:

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5592,6 +5592,7 @@ class ProtocolDecl final : public NominalTypeDecl {
55925592
friend class StructuralRequirementsRequest;
55935593
friend class TypeAliasRequirementsRequest;
55945594
friend class ProtocolDependenciesRequest;
5595+
friend class ProtocolInversesRequest;
55955596
friend class RequirementSignatureRequest;
55965597
friend class ProtocolRequiresClassRequest;
55975598
friend class ExistentialConformsToSelfRequest;
@@ -5825,6 +5826,11 @@ class ProtocolDecl final : public NominalTypeDecl {
58255826
/// instead.
58265827
ArrayRef<StructuralRequirement> getStructuralRequirements() const;
58275828

5829+
/// Retrieves the original inverse requirements written in source.
5830+
///
5831+
/// The structural requirements already have had these applied to them.
5832+
ArrayRef<InverseRequirement> getInverseRequirements() const;
5833+
58285834
/// Retrieve same-type requirements implied by protocol typealiases with the
58295835
/// same name as associated types, and diagnose cases that are better expressed
58305836
/// 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: 25 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:
@@ -618,6 +619,25 @@ class ProtocolDependenciesRequest :
618619
bool isCached() const { return true; }
619620
};
620621

622+
class ProtocolInversesRequest :
623+
public SimpleRequest<ProtocolInversesRequest,
624+
ArrayRef<InverseRequirement>(ProtocolDecl *),
625+
RequestFlags::Cached> {
626+
public:
627+
using SimpleRequest::SimpleRequest;
628+
629+
private:
630+
friend SimpleRequest;
631+
632+
// Evaluation.
633+
ArrayRef<InverseRequirement>
634+
evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
635+
636+
public:
637+
// Caching.
638+
bool isCached() const { return true; }
639+
};
640+
621641
/// Compute the requirements that describe a protocol.
622642
class RequirementSignatureRequest :
623643
public SimpleRequest<RequirementSignatureRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,17 @@ 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,
308308
HasNearestLocation)
309309
SWIFT_REQUEST(TypeChecker, ProtocolDependenciesRequest,
310310
ArrayRef<ProtocolDecl *>(ProtocolDecl *), Cached,
311311
HasNearestLocation)
312+
SWIFT_REQUEST(TypeChecker, ProtocolInversesRequest,
313+
ArrayRef<InverseRequirement>(ProtocolDecl *), Cached,
314+
HasNearestLocation)
312315
SWIFT_REQUEST(TypeChecker, RequirementSignatureRequest,
313316
RequirementSignature(ProtocolDecl *), SeparatelyCached,
314317
NoLocationInfo)

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+
ProtocolInversesRequest{const_cast<ProtocolDecl *>(this)}, {});
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;

0 commit comments

Comments
 (0)