Skip to content

Commit 1867b38

Browse files
committed
Sema: Fix problems with checkTypeWitness()
Our tentative witness might have weird concrete DependentMemberTypes in it. In that case, perform a more relaxed check that can succeed as long as the witness is not completely invalid. This problem was exposed by existing test cases starting to fail once implicit Copyable requirements were introduced, because these concrete DependentMemberTypes do not conform to protocols. This now postpones the check until all witnesses have been fully substituted. This also fixes a long-standing bug with superclass requirements on associated types. Fixes #48770, #51772, rdar://95729075.
1 parent a0480b6 commit 1867b38

9 files changed

+290
-132
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,11 +2905,6 @@ NOTE(default_associated_type_unsatisfied_superclass,none,
29052905
"default type %0 for associated type %1 (from protocol %2) "
29062906
"does not inherit from %3",
29072907
(Type, const AssociatedTypeDecl *, Type, Type))
2908-
NOTE(default_associated_type_tuple,none,
2909-
"default type %0 for associated type %1 (from protocol %2) "
2910-
"is unsuitable for tuple conformance; the associated type requirement "
2911-
"must be fulfilled by a type alias with underlying type %3",
2912-
(Type, const AssociatedTypeDecl *, Type, Type))
29132908
ERROR(associated_type_access,none,
29142909
"associated type in "
29152910
"%select{a private|a fileprivate|an internal|a package|a public|%error}0 protocol "
@@ -2945,11 +2940,6 @@ NOTE(associated_type_deduction_unsatisfied_superclass,none,
29452940
"candidate would match and infer %0 = %1 if %1 "
29462941
"inherited from %2",
29472942
(const AssociatedTypeDecl *, Type, Type))
2948-
NOTE(associated_type_deduction_tuple,none,
2949-
"cannot infer %0 = %1 in tuple conformance because "
2950-
"the associated type requirement must be fulfilled by a type alias with "
2951-
"underlying type %2",
2952-
(const AssociatedTypeDecl *, Type, Type))
29532943
NOTE(associated_type_witness_conform_impossible,none,
29542944
"candidate can not infer %0 = %1 because %1 "
29552945
"is not a nominal type and so can't conform to %2",

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 68 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,7 @@ class CheckTypeWitnessResult {
8787

8888
/// Type witness does not satisfy a layout requirement on
8989
/// the associated type.
90-
Layout,
91-
92-
/// Type witness of a tuple conformance does not have the form
93-
/// (repeat (each Element).A).
94-
Tuple
90+
Layout
9591
} kind;
9692

9793
private:
@@ -125,25 +121,36 @@ class CheckTypeWitnessResult {
125121
return CheckTypeWitnessResult(Layout, reqt);
126122
}
127123

128-
static CheckTypeWitnessResult forTuple(Type reqt) {
129-
return CheckTypeWitnessResult(Tuple, reqt);
130-
}
131-
132124
Kind getKind() const { return kind; }
133125
Type getRequirement() const { return reqt; }
134126

135127
explicit operator bool() const { return kind != Success; }
136128
};
137129

130+
/// Checks a potential witness for an associated type A against the "local"
131+
/// requirements of the type parameter Self.[P]A. We call this to check
132+
/// type witnesses found by name lookup, as well as candidate witnesses during
133+
/// inference.
134+
///
135+
/// This does not completely check the witness; we check the entire requirement
136+
/// signature at the end. However, rejecting witnesses that are definitely
137+
/// invalid here can cut down the search space.
138138
static CheckTypeWitnessResult
139139
checkTypeWitness(Type type, AssociatedTypeDecl *assocType,
140140
const NormalProtocolConformance *Conf,
141141
SubstOptions options) {
142+
assert(!type->hasTypeParameter());
143+
142144
auto &ctx = assocType->getASTContext();
143145

144146
if (type->hasError())
145147
return CheckTypeWitnessResult::forError();
146148

149+
// If the type witness is equivalent to some other unresolved witness,
150+
// we cannot check anything, so accept the witness.
151+
if (type->is<DependentMemberType>())
152+
return CheckTypeWitnessResult::forSuccess();
153+
147154
const auto proto = Conf->getProtocol();
148155
const auto dc = Conf->getDeclContext();
149156
const auto sig = proto->getGenericSignature();
@@ -167,53 +174,43 @@ checkTypeWitness(Type type, AssociatedTypeDecl *assocType,
167174
const auto depTy = DependentMemberType::get(proto->getSelfInterfaceType(),
168175
assocType);
169176

170-
Type contextType = type->hasTypeParameter() ? dc->mapTypeIntoContext(type)
171-
: type;
172-
173177
if (auto superclass = sig->getSuperclassBound(depTy)) {
174-
if (superclass->hasTypeParameter()) {
175-
// Replace type parameters with other known or tentative type witnesses.
176-
superclass = superclass.subst(
177-
[&](SubstitutableType *type) {
178-
if (type->isEqual(proto->getSelfInterfaceType()))
179-
return Conf->getType();
180-
181-
return Type();
182-
},
183-
LookUpConformanceInModule(dc->getParentModule()), options);
184-
185-
if (superclass->hasTypeParameter())
186-
superclass = dc->mapTypeIntoContext(superclass);
178+
// We only check that the type's superclass declaration is correct.
179+
// If the superclass bound is generic, we may not have resolved all of
180+
// the type witnesses that appear in generic arguments yet, and doing so
181+
// here might run into a request cycle.
182+
auto superclassDecl = superclass->getClassOrBoundGenericClass();
183+
assert(superclassDecl);
184+
185+
// Fish a class declaration out of the type witness.
186+
auto classDecl = type->getClassOrBoundGenericClass();
187+
if (!classDecl) {
188+
if (auto archetype = type->getAs<ArchetypeType>()) {
189+
if (auto superclassType = archetype->getSuperclass())
190+
classDecl = superclassType->getClassOrBoundGenericClass();
191+
}
187192
}
188-
if (!superclass->isExactSuperclassOf(contextType))
193+
194+
if (!classDecl || !superclassDecl->isSuperclassOf(classDecl))
189195
return CheckTypeWitnessResult::forSuperclass(superclass);
190196
}
191197

192198
auto *module = dc->getParentModule();
193199

194-
// Check protocol conformances.
200+
// Check protocol conformances. We don't check conditional requirements here.
195201
for (const auto reqProto : sig->getRequiredProtocols(depTy)) {
196202
if (module->lookupConformance(
197-
contextType, reqProto,
203+
type, reqProto,
198204
/*allowMissing=*/reqProto->isSpecificProtocol(
199205
KnownProtocolKind::Sendable))
200206
.isInvalid())
201207
return CheckTypeWitnessResult::forConformance(reqProto);
202208
}
203209

210+
// We can completely check an AnyObject layout constraint.
204211
if (sig->requiresClass(depTy) &&
205-
!contextType->satisfiesClassConstraint()) {
206-
return CheckTypeWitnessResult::forLayout(
207-
module->getASTContext().getAnyObjectType());
208-
}
209-
210-
// Tuple conformances can only witness associated types by projecting them
211-
// element-wise.
212-
if (isa<BuiltinTupleDecl>(dc->getSelfNominalTypeDecl())) {
213-
auto expectedTy = getTupleConformanceTypeWitness(dc, assocType);
214-
if (!expectedTy->isEqual(type)) {
215-
return CheckTypeWitnessResult::forTuple(expectedTy);
216-
}
212+
!type->satisfiesClassConstraint()) {
213+
return CheckTypeWitnessResult::forLayout(ctx.getAnyObjectType());
217214
}
218215

219216
// Success!
@@ -222,10 +219,21 @@ checkTypeWitness(Type type, AssociatedTypeDecl *assocType,
222219

223220
}
224221

222+
static bool containsConcreteDependentMemberType(Type ty) {
223+
return ty.findIf([](Type t) -> bool {
224+
if (auto *dmt = t->getAs<DependentMemberType>())
225+
return !dmt->isTypeParameter();
226+
227+
return false;
228+
});
229+
}
230+
225231
static void recordTypeWitness(NormalProtocolConformance *conformance,
226232
AssociatedTypeDecl *assocType,
227233
Type type,
228234
TypeDecl *typeDecl) {
235+
assert(!containsConcreteDependentMemberType(type));
236+
229237
// If we already recoded this type witness, there's nothing to do.
230238
if (conformance->hasTypeWitness(assocType)) {
231239
assert(conformance->getTypeWitnessUncached(assocType)
@@ -473,9 +481,12 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
473481
if (!viableTypes.insert(memberType->getCanonicalType()).second)
474482
continue;
475483

484+
auto memberTypeInContext = dc->mapTypeIntoContext(memberType);
485+
476486
// Check this type against the protocol requirements.
477487
if (auto checkResult =
478-
checkTypeWitness(memberType, assocType, conformance, llvm::None)) {
488+
checkTypeWitness(memberTypeInContext, assocType,
489+
conformance, llvm::None)) {
479490
nonViable.push_back({typeDecl, checkResult});
480491
} else {
481492
viable.push_back({typeDecl, memberType, nullptr});
@@ -551,14 +562,6 @@ static ResolveWitnessResult resolveTypeWitnessViaLookup(
551562
candidate.first->getDeclaredInterfaceType(),
552563
candidate.second.getRequirement());
553564
break;
554-
555-
case CheckTypeWitnessResult::Tuple:
556-
diags.diagnose(
557-
candidate.first,
558-
diag::protocol_type_witness_tuple,
559-
candidate.first->getDeclaredInterfaceType(),
560-
candidate.second.getRequirement());
561-
break;
562565
}
563566
}
564567
});
@@ -1353,7 +1356,6 @@ enum class InferenceCandidateKind {
13531356

13541357
static InferenceCandidateKind checkInferenceCandidate(
13551358
std::pair<AssociatedTypeDecl *, Type> *result,
1356-
bool *canInferFromOtherAssociatedType,
13571359
NormalProtocolConformance *conformance,
13581360
ValueDecl *witness) {
13591361
auto isTautological = [&](Type t) -> bool {
@@ -1418,7 +1420,6 @@ static InferenceCandidateKind checkInferenceCandidate(
14181420
return otherDMT;
14191421
return t;
14201422
});
1421-
*canInferFromOtherAssociatedType = true;
14221423
LLVM_DEBUG(llvm::dbgs() << "++ we can same-type to:\n";
14231424
result->second->dump(llvm::dbgs()));
14241425
return InferenceCandidateKind::Good;
@@ -1543,11 +1544,7 @@ AssociatedTypeInference::getPotentialTypeWitnessesFromRequirement(
15431544
// Filter out circular possibilities, e.g. that
15441545
// AssocType == S.AssocType or
15451546
// AssocType == Foo<S.AssocType>.
1546-
bool canInferFromOtherAssociatedType = false;
1547-
1548-
switch (checkInferenceCandidate(&result,
1549-
&canInferFromOtherAssociatedType,
1550-
conformance, witness)) {
1547+
switch (checkInferenceCandidate(&result, conformance, witness)) {
15511548
case InferenceCandidateKind::Good:
15521549
// Continued below.
15531550
break;
@@ -1588,23 +1585,19 @@ AssociatedTypeInference::getPotentialTypeWitnessesFromRequirement(
15881585
}
15891586
}
15901587

1591-
// If we same-typed to another unresolved associated type, we won't
1592-
// be able to check conformances yet.
1593-
if (!canInferFromOtherAssociatedType) {
1594-
// Check that the type witness meets the
1595-
// requirements on the associated type.
1596-
if (auto failed =
1597-
checkTypeWitness(result.second, result.first, conformance,
1598-
llvm::None)) {
1599-
witnessResult.NonViable.push_back(
1600-
std::make_tuple(result.first,result.second,failed));
1601-
LLVM_DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n");
1602-
1603-
// By adding an element to NonViable we ensure the witness is rejected
1604-
// below, so we continue to consider other bindings to generate better
1605-
// diagnostics later.
1606-
REJECT;
1607-
}
1588+
// Check that the type witness meets the requirements on the
1589+
// associated type.
1590+
if (auto failed =
1591+
checkTypeWitness(result.second, result.first, conformance,
1592+
llvm::None)) {
1593+
witnessResult.NonViable.push_back(
1594+
std::make_tuple(result.first,result.second,failed));
1595+
LLVM_DEBUG(llvm::dbgs() << "-- doesn't fulfill requirements\n");
1596+
1597+
// By adding an element to NonViable we ensure the witness is rejected
1598+
// below, so we continue to consider other bindings to generate better
1599+
// diagnostics later.
1600+
REJECT;
16081601
}
16091602

16101603
LLVM_DEBUG(llvm::dbgs() << "++ seems legit\n");
@@ -2286,6 +2279,8 @@ AssociatedTypeInference::computeDerivedTypeWitness(
22862279
if (!result.first)
22872280
return std::make_pair(Type(), nullptr);
22882281

2282+
assert(!containsConcreteDependentMemberType(result.first));
2283+
22892284
// Make sure that the derived type satisfies requirements.
22902285
if (checkTypeWitness(result.first, assocType, conformance, llvm::None)) {
22912286
/// FIXME: Diagnose based on this.
@@ -2649,15 +2644,6 @@ bool AssociatedTypeInference::checkConstrainedExtension(ExtensionDecl *ext) {
26492644
llvm_unreachable("unhandled result");
26502645
}
26512646

2652-
static bool containsConcreteDependentMemberType(Type ty) {
2653-
return ty.findIf([](Type t) -> bool {
2654-
if (auto *dmt = t->getAs<DependentMemberType>())
2655-
return !dmt->isTypeParameter();
2656-
2657-
return false;
2658-
});
2659-
}
2660-
26612647
AssociatedTypeDecl *AssociatedTypeInference::inferAbstractTypeWitnesses(
26622648
ArrayRef<AssociatedTypeDecl *> unresolvedAssocTypes, unsigned reqDepth) {
26632649
if (unresolvedAssocTypes.empty()) {
@@ -3560,16 +3546,6 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
35603546
proto->getDeclaredInterfaceType(),
35613547
failedDefaultedResult.getRequirement());
35623548
break;
3563-
3564-
case CheckTypeWitnessResult::Tuple:
3565-
diags.diagnose(
3566-
failedDefaultedAssocType,
3567-
diag::default_associated_type_tuple,
3568-
failedDefaultedWitness,
3569-
failedDefaultedAssocType,
3570-
proto->getDeclaredInterfaceType(),
3571-
failedDefaultedResult.getRequirement());
3572-
break;
35733549
}
35743550
});
35753551

@@ -3675,14 +3651,6 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
36753651
assocType, failed.TypeWitness,
36763652
failed.Result.getRequirement());
36773653
break;
3678-
3679-
case CheckTypeWitnessResult::Tuple:
3680-
diags.diagnose(
3681-
failed.Witness,
3682-
diag::associated_type_deduction_tuple,
3683-
assocType, failed.TypeWitness,
3684-
failed.Result.getRequirement());
3685-
break;
36863654
}
36873655
}
36883656
});

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3456,7 +3456,7 @@ static bool isNSObjectProtocol(ProtocolDecl *proto) {
34563456
return proto->hasClangNode();
34573457
}
34583458

3459-
Type swift::getTupleConformanceTypeWitness(DeclContext *dc,
3459+
static Type getTupleConformanceTypeWitness(DeclContext *dc,
34603460
AssociatedTypeDecl *assocType) {
34613461
auto genericSig = dc->getGenericSignatureOfContext();
34623462
assert(genericSig.getGenericParams().size() == 1);
@@ -4751,16 +4751,29 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
47514751
return;
47524752
}
47534753

4754-
// Now check that our associated conformances are at least as visible as
4755-
// the conformance itself.
4754+
bool isTupleConformance = isa<BuiltinTupleDecl>(dc->getSelfNominalTypeDecl());
4755+
47564756
auto where = ExportContext::forConformance(dc, proto);
4757-
if (where.isImplicit())
4758-
return;
47594757

47604758
conformance->forEachTypeWitness([&](AssociatedTypeDecl *assocType,
47614759
Type type, TypeDecl *typeDecl) -> bool {
47624760
checkObjCTypeErasedGenerics(conformance, assocType, type, typeDecl);
47634761

4762+
// Tuple conformances can only witness associated types by projecting them
4763+
// element-wise.
4764+
if (isTupleConformance) {
4765+
auto expectedTy = getTupleConformanceTypeWitness(dc, assocType);
4766+
if (!type->hasError() && !expectedTy->isEqual(type)) {
4767+
ctx.addDelayedConformanceDiag(conformance, true,
4768+
[dc, type, typeDecl, expectedTy](NormalProtocolConformance *conformance) {
4769+
dc->getASTContext().Diags.diagnose(
4770+
getLocForDiagnosingWitness(conformance, typeDecl),
4771+
diag::protocol_type_witness_tuple,
4772+
type, expectedTy);
4773+
});
4774+
}
4775+
}
4776+
47644777
if (typeDecl && !typeDecl->isImplicit()) {
47654778
auto requiredAccessScope = evaluateOrDefault(
47664779
ctx.evaluator, ConformanceAccessScopeRequest{dc, proto},
@@ -4808,6 +4821,11 @@ static void ensureRequirementsAreSatisfied(ASTContext &ctx,
48084821
return false;
48094822
});
48104823

4824+
// Now check that our associated conformances are at least as visible as
4825+
// the conformance itself.
4826+
if (where.isImplicit())
4827+
return;
4828+
48114829
conformance->forEachAssociatedConformance(
48124830
[&](Type depTy, ProtocolDecl *proto, unsigned index) {
48134831
auto assocConf = conformance->getAssociatedConformance(depTy, proto);

lib/Sema/TypeCheckProtocol.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,6 @@ void diagnoseConformanceFailure(Type T,
218218
DeclContext *DC,
219219
SourceLoc ComplainLoc);
220220

221-
Type getTupleConformanceTypeWitness(DeclContext *dc,
222-
AssociatedTypeDecl *assocType);
223-
224221
/// Find an associated type declaration that provides a default definition.
225222
AssociatedTypeDecl *findDefaultedAssociatedType(
226223
DeclContext *dc, NominalTypeDecl *adoptee,

0 commit comments

Comments
 (0)