Skip to content

Commit 336750e

Browse files
authored
Merge pull request #72495 from slavapestov/fix-protocol-superclass
AST: Remove ProtocolDecl::getSuperclass()
2 parents 54c1e46 + 1a7bdb4 commit 336750e

21 files changed

+194
-159
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44
> This is in reverse chronological order, so newer entries are added to the top.
55
66
## Swift 6.0
7+
8+
* Swift 5.10 missed a semantic check from [SE-0309][]. In type context, a reference to a
9+
protocol `P` that has associated types or `Self` requirements should use
10+
the `any` keyword, but this was not enforced in nested generic argument positions.
11+
This is now an error as required by the proposal:
12+
13+
```swift
14+
protocol P { associatedtype A }
15+
struct Outer<T> { struct Inner<U> { } }
16+
let x = Outer<P>.Inner<P>() // error
17+
```
18+
To correct the error, add `any` where appropriate, for example
19+
`Outer<any P>.Inner<any P>`.
20+
21+
* Swift 5.10 accepted certain invalid opaque return types from [SE-0346][].
22+
If a generic argument of a constrained opaque return type did not
23+
satisfy the requirements on the primary associated type, the generic
24+
argument was silently ignored and type checking would proceed as if it
25+
weren't stated. This now results in a diagnostic:
26+
27+
```swift
28+
protocol P<A> { associatedtype A: Sequence }
29+
struct G<A: Sequence>: P {}
30+
31+
func f() -> some P<Int> { return G<Array<Int>>() } // error
32+
```
33+
34+
The return type above should be written as `some P<Array<Int>>` to match
35+
the return statement. The old broken behavior in this situation can also
36+
be restored, by removing the erroneous constraint and using the more general
37+
upper bound `some P`.
38+
739
* [SE-0408][]:
840
A `for`-`in` loop statement can now accept a pack expansion expression,
941
enabling iteration over the elements of its respective value pack. This form

include/swift/AST/Decl.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
598598
IsComputingSemanticMembers : 1
599599
);
600600

601-
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+8,
601+
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
602602
/// Whether the \c RequiresClass bit is valid.
603603
RequiresClassValid : 1,
604604

@@ -625,6 +625,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
625625
/// Whether we've computed the inherited protocols list yet.
626626
InheritedProtocolsValid : 1,
627627

628+
/// Whether we have computed a requirement signature.
629+
HasRequirementSignature : 1,
630+
628631
/// Whether we have a lazy-loaded requirement signature.
629632
HasLazyRequirementSignature : 1,
630633

@@ -5122,15 +5125,11 @@ class ProtocolDecl final : public NominalTypeDecl {
51225125
/// The superclass decl and a bit to indicate whether the
51235126
/// superclass was computed yet or not.
51245127
llvm::PointerIntPair<ClassDecl *, 1, bool> SuperclassDecl;
5125-
5126-
/// The superclass type and a bit to indicate whether the
5127-
/// superclass was computed yet or not.
5128-
llvm::PointerIntPair<Type, 1, bool> SuperclassType;
51295128
} LazySemanticInfo;
51305129

51315130
/// The generic signature representing exactly the new requirements introduced
51325131
/// by this protocol.
5133-
std::optional<RequirementSignature> RequirementSig;
5132+
RequirementSignature RequirementSig;
51345133

51355134
/// Returns the cached result of \c requiresClass or \c None if it hasn't yet
51365135
/// been computed.
@@ -5217,16 +5216,10 @@ class ProtocolDecl final : public NominalTypeDecl {
52175216
/// Determine whether this protocol has a superclass.
52185217
bool hasSuperclass() const { return (bool)getSuperclassDecl(); }
52195218

5220-
/// Retrieve the superclass of this protocol, or null if there is no superclass.
5221-
Type getSuperclass() const;
5222-
52235219
/// Retrieve the ClassDecl for the superclass of this protocol, or null if there
52245220
/// is no superclass.
52255221
ClassDecl *getSuperclassDecl() const;
52265222

5227-
/// Set the superclass of this protocol.
5228-
void setSuperclass(Type superclass);
5229-
52305223
/// Retrieve the set of AssociatedTypeDecl members of this protocol; this
52315224
/// saves loading the set of members in cases where there's no possibility of
52325225
/// a protocol having nested types (ObjC protocols).
@@ -5445,7 +5438,7 @@ class ProtocolDecl final : public NominalTypeDecl {
54455438

54465439
/// Has the requirement signature been computed yet?
54475440
bool isRequirementSignatureComputed() const {
5448-
return RequirementSig.has_value();
5441+
return Bits.ProtocolDecl.HasRequirementSignature;
54495442
}
54505443

54515444
void setRequirementSignature(RequirementSignature requirementSig);

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class InheritedTypeRequest
113113
/// Request the superclass type for the given class.
114114
class SuperclassTypeRequest
115115
: public SimpleRequest<
116-
SuperclassTypeRequest, Type(NominalTypeDecl *, TypeResolutionStage),
116+
SuperclassTypeRequest, Type(ClassDecl *, TypeResolutionStage),
117117
RequestFlags::SeparatelyCached | RequestFlags::DependencySink> {
118118
public:
119119
using SimpleRequest::SimpleRequest;
@@ -123,7 +123,7 @@ class SuperclassTypeRequest
123123

124124
// Evaluation.
125125
Type
126-
evaluate(Evaluator &evaluator, NominalTypeDecl *classDecl,
126+
evaluate(Evaluator &evaluator, ClassDecl *classDecl,
127127
TypeResolutionStage stage) const;
128128

129129
public:

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7766,7 +7766,9 @@ static void getSyntacticInheritanceClause(const ProtocolDecl *proto,
77667766
llvm::SmallVectorImpl<InheritedEntry> &Results) {
77677767
auto &ctx = proto->getASTContext();
77687768

7769-
if (auto superclassTy = proto->getSuperclass()) {
7769+
auto genericSig = proto->getGenericSignature();
7770+
if (auto superclassTy = genericSig->getSuperclassBound(
7771+
proto->getSelfInterfaceType())) {
77707772
Results.emplace_back(TypeLoc::withoutLoc(superclassTy),
77717773
/*isUnchecked=*/false,
77727774
/*isRetroactive=*/false,

lib/AST/ConformanceLookup.cpp

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
7676
return type;
7777
};
7878

79-
auto lookupSuperclassConformance = [&](ExistentialLayout &layout) {
80-
if (auto superclass = layout.explicitSuperclass) {
79+
auto lookupSuperclassConformance = [&](Type superclass) {
80+
if (superclass) {
8181
if (auto result =
8282
lookupConformance(superclass, protocol, /*allowMissing=*/false)) {
8383
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
@@ -101,7 +101,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
101101
auto layout = type->getExistentialLayout();
102102
if (llvm::all_of(layout.getProtocols(),
103103
[](const auto *P) { return P->isMarkerProtocol(); })) {
104-
if (auto conformance = lookupSuperclassConformance(layout)) {
104+
if (auto conformance = lookupSuperclassConformance(layout.explicitSuperclass)) {
105105
return ProtocolConformanceRef(
106106
ctx.getInheritedConformance(type, conformance.getConcrete()));
107107
}
@@ -113,15 +113,12 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
113113

114114
auto layout = type->getExistentialLayout();
115115

116-
// Due to an IRGen limitation, witness tables cannot be passed from an
117-
// existential to an archetype parameter, so for now we restrict this to
118-
// @objc protocols and marker protocols.
116+
// If the existential contains non-@objc protocols and the protocol we're
117+
// conforming to needs a witness table, the existential must have a
118+
// self-conformance witness table. For now, Swift.Error is the only one.
119119
if (!layout.isObjC() && !protocol->isMarkerProtocol()) {
120120
auto constraint = getConstraintType();
121-
// There's a specific exception for protocols with self-conforming
122-
// witness tables, but the existential has to be *exactly* that type.
123-
// TODO: synthesize witness tables on-demand for protocol compositions
124-
// that can satisfy the requirement.
121+
// The existential has to be *exactly* that type.
125122
if (protocol->requiresSelfConformanceWitnessTable() &&
126123
constraint->is<ProtocolType>() &&
127124
constraint->castTo<ProtocolType>()->getDecl() == protocol)
@@ -130,31 +127,23 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
130127
return ProtocolConformanceRef::forInvalid();
131128
}
132129

133-
// If the existential is class-constrained, the class might conform
134-
// concretely.
135-
if (auto conformance = lookupSuperclassConformance(layout))
136-
return conformance;
137-
138-
// Otherwise, the existential might conform abstractly.
130+
// The existential might conform abstractly.
139131
for (auto protoDecl : layout.getProtocols()) {
140-
141132
// If we found the protocol we're looking for, return an abstract
142133
// conformance to it.
143134
if (protoDecl == protocol)
144135
return ProtocolConformanceRef(ctx.getSelfConformance(protocol));
145136

146-
// If the protocol has a superclass constraint, we might conform
147-
// concretely.
148-
if (auto superclass = protoDecl->getSuperclass()) {
149-
if (auto result = lookupConformance(superclass, protocol))
150-
return result;
151-
}
152-
153137
// Now check refined protocols.
154138
if (protoDecl->inheritsFrom(protocol))
155139
return ProtocolConformanceRef(ctx.getSelfConformance(protocol));
156140
}
157141

142+
// If the existential is class-constrained, the class might conform
143+
// concretely.
144+
if (auto conformance = lookupSuperclassConformance(layout.getSuperclass()))
145+
return conformance;
146+
158147
// We didn't find our protocol in the existential's list; it doesn't
159148
// conform.
160149
return ProtocolConformanceRef::forInvalid();

lib/AST/Decl.cpp

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6436,6 +6436,8 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
64366436
Bits.ProtocolDecl.KnownProtocol = 0;
64376437
Bits.ProtocolDecl.HasAssociatedTypes = 0;
64386438
Bits.ProtocolDecl.HasLazyAssociatedTypes = 0;
6439+
Bits.ProtocolDecl.HasRequirementSignature = 0;
6440+
Bits.ProtocolDecl.HasLazyRequirementSignature = 0;
64396441
Bits.ProtocolDecl.ProtocolRequirementsValid = false;
64406442
setTrailingWhereClause(TrailingWhere);
64416443
}
@@ -6545,29 +6547,12 @@ AssociatedTypeDecl *ProtocolDecl::getAssociatedType(Identifier name) const {
65456547
return nullptr;
65466548
}
65476549

6548-
Type ProtocolDecl::getSuperclass() const {
6549-
ASTContext &ctx = getASTContext();
6550-
return evaluateOrDefault(ctx.evaluator,
6551-
SuperclassTypeRequest{const_cast<ProtocolDecl *>(this),
6552-
TypeResolutionStage::Interface},
6553-
Type());
6554-
}
6555-
65566550
ClassDecl *ProtocolDecl::getSuperclassDecl() const {
65576551
ASTContext &ctx = getASTContext();
65586552
return evaluateOrDefault(ctx.evaluator,
65596553
SuperclassDeclRequest{const_cast<ProtocolDecl *>(this)}, nullptr);
65606554
}
65616555

6562-
void ProtocolDecl::setSuperclass(Type superclass) {
6563-
assert((!superclass || !superclass->hasArchetype())
6564-
&& "superclass must be interface type");
6565-
LazySemanticInfo.SuperclassType.setPointerAndInt(superclass, true);
6566-
LazySemanticInfo.SuperclassDecl.setPointerAndInt(
6567-
superclass ? superclass->getClassOrBoundGenericClass() : nullptr,
6568-
true);
6569-
}
6570-
65716556
bool ProtocolDecl::walkInheritedProtocols(
65726557
llvm::function_ref<TypeWalker::Action(ProtocolDecl *)> fn) const {
65736558
auto self = const_cast<ProtocolDecl *>(this);
@@ -6699,12 +6684,13 @@ bool ProtocolDecl::isComputingRequirementSignature() const {
66996684

67006685
void ProtocolDecl::setRequirementSignature(RequirementSignature requirementSig) {
67016686
RequirementSig = requirementSig;
6687+
Bits.ProtocolDecl.HasRequirementSignature = 1;
67026688
}
67036689

67046690
void
67056691
ProtocolDecl::setLazyRequirementSignature(LazyMemberLoader *lazyLoader,
67066692
uint64_t requirementSignatureData) {
6707-
assert(!RequirementSig && "requirement signature already set");
6693+
assert(!isRequirementSignatureComputed() && "requirement signature already set");
67086694

67096695
auto contextData = static_cast<LazyProtocolData *>(
67106696
getASTContext().getOrCreateLazyContextData(this, lazyLoader));

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3219,16 +3219,8 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator,
32193219
// Protocols may get their superclass bound from a `where Self : Superclass`
32203220
// clause.
32213221
if (auto *proto = dyn_cast<ProtocolDecl>(subject)) {
3222-
// If the protocol came from a serialized module, compute the superclass via
3223-
// its generic signature.
3224-
if (proto->wasDeserialized()) {
3225-
auto superTy = proto->getGenericSignature()
3226-
->getSuperclassBound(proto->getSelfInterfaceType());
3227-
if (superTy)
3228-
return superTy->getClassOrBoundGenericClass();
3229-
}
3222+
assert(!proto->wasDeserialized());
32303223

3231-
// Otherwise check the where clause.
32323224
auto selfBounds = getSelfBoundsFromWhereClause(proto);
32333225
for (auto inheritedNominal : selfBounds.decls)
32343226
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal))

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -624,19 +624,26 @@ bool ConcreteContraction::performConcreteContraction(
624624

625625
auto superclassTy = *found->second.begin();
626626

627-
for (const auto *proto : pair.second) {
628-
if (auto otherSuperclassTy = proto->getSuperclass()) {
629-
if (Debug) {
630-
llvm::dbgs() << "@ Subject type of superclass requirement "
631-
<< subjectType << " : " << superclassTy
632-
<< " conforms to "<< proto->getName()
633-
<< " which has a superclass bound "
634-
<< otherSuperclassTy << "\n";
635-
}
636-
637-
if (superclassTy->isEqual(otherSuperclassTy)) {
638-
Superclasses.erase(subjectType);
639-
break;
627+
for (auto *proto : pair.second) {
628+
auto *module = proto->getParentModule();
629+
if (module->lookupConformance(superclassTy, proto)) {
630+
auto genericSig = proto->getGenericSignature();
631+
// FIXME: If we end up here while building the requirement
632+
// signature of `proto`, we will hit a request cycle.
633+
if (auto otherSuperclassTy = genericSig->getSuperclassBound(
634+
proto->getSelfInterfaceType())) {
635+
if (Debug) {
636+
llvm::dbgs() << "@ Subject type of superclass requirement "
637+
<< subjectType << " : " << superclassTy
638+
<< " conforms to "<< proto->getName()
639+
<< " which has a superclass bound "
640+
<< otherSuperclassTy << "\n";
641+
}
642+
643+
if (superclassTy->isEqual(otherSuperclassTy)) {
644+
Superclasses.erase(subjectType);
645+
break;
646+
}
640647
}
641648
}
642649
}

lib/AST/Type.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -407,14 +407,9 @@ Type ExistentialLayout::getSuperclass() const {
407407
return explicitSuperclass;
408408

409409
for (auto protoDecl : getProtocols()) {
410-
// If we have a generic signature, check there, because it
411-
// will pick up superclass constraints from protocols that we
412-
// refine as well.
413-
if (auto genericSig = protoDecl->getGenericSignature()) {
414-
if (auto superclass = genericSig->getSuperclassBound(
415-
protoDecl->getSelfInterfaceType()))
416-
return superclass;
417-
} else if (auto superclass = protoDecl->getSuperclass())
410+
auto genericSig = protoDecl->getGenericSignature();
411+
if (auto superclass = genericSig->getSuperclassBound(
412+
protoDecl->getSelfInterfaceType()))
418413
return superclass;
419414
}
420415

lib/AST/TypeCheckRequests.cpp

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -143,27 +143,16 @@ bool SuperclassTypeRequest::isCached() const {
143143
}
144144

145145
std::optional<Type> SuperclassTypeRequest::getCachedResult() const {
146-
auto nominalDecl = std::get<0>(getStorage());
147-
148-
if (auto *classDecl = dyn_cast<ClassDecl>(nominalDecl))
149-
if (classDecl->LazySemanticInfo.SuperclassType.getInt())
150-
return classDecl->LazySemanticInfo.SuperclassType.getPointer();
151-
152-
if (auto *protocolDecl = dyn_cast<ProtocolDecl>(nominalDecl))
153-
if (protocolDecl->LazySemanticInfo.SuperclassType.getInt())
154-
return protocolDecl->LazySemanticInfo.SuperclassType.getPointer();
146+
auto classDecl = std::get<0>(getStorage());
147+
if (classDecl->LazySemanticInfo.SuperclassType.getInt())
148+
return classDecl->LazySemanticInfo.SuperclassType.getPointer();
155149

156150
return std::nullopt;
157151
}
158152

159153
void SuperclassTypeRequest::cacheResult(Type value) const {
160-
auto nominalDecl = std::get<0>(getStorage());
161-
162-
if (auto *classDecl = dyn_cast<ClassDecl>(nominalDecl))
163-
classDecl->LazySemanticInfo.SuperclassType.setPointerAndInt(value, true);
164-
165-
if (auto *protocolDecl = dyn_cast<ProtocolDecl>(nominalDecl))
166-
protocolDecl->LazySemanticInfo.SuperclassType.setPointerAndInt(value, true);
154+
auto classDecl = std::get<0>(getStorage());
155+
classDecl->LazySemanticInfo.SuperclassType.setPointerAndInt(value, true);
167156
}
168157

169158
void SuperclassTypeRequest::writeDependencySink(
@@ -341,7 +330,7 @@ std::optional<RequirementSignature>
341330
RequirementSignatureRequest::getCachedResult() const {
342331
auto proto = std::get<0>(getStorage());
343332
if (proto->isRequirementSignatureComputed())
344-
return *proto->RequirementSig;
333+
return proto->RequirementSig;
345334

346335
return std::nullopt;
347336
}

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4118,14 +4118,6 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
41184118
}
41194119

41204120
for (auto *protoDecl : layout.getProtocols()) {
4121-
if (auto superclass = protoDecl->getSuperclass()) {
4122-
auto subKind = std::min(ConstraintKind::Subtype, kind);
4123-
auto result = matchTypes(type1, superclass, subKind,
4124-
subflags, locator);
4125-
if (result.isFailure())
4126-
return result;
4127-
}
4128-
41294121
switch (simplifyConformsToConstraint(type1, protoDecl, kind, locator,
41304122
subflags)) {
41314123
case SolutionKind::Solved:

0 commit comments

Comments
 (0)