Skip to content

Commit 8610e7a

Browse files
authored
Merge pull request #9275 from DougGregor/type-witness-inference-validation
2 parents 8dc3a2b + 3dbfa97 commit 8610e7a

File tree

12 files changed

+225
-77
lines changed

12 files changed

+225
-77
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,15 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
145145

146146
/// Retrieve the type witness for the given associated type.
147147
Type getTypeWitness(AssociatedTypeDecl *assocType,
148-
LazyResolver *resolver) const;
148+
LazyResolver *resolver,
149+
SubstOptions options = None) const;
149150

150151
/// Retrieve the type witness and type decl (if one exists)
151152
/// for the given associated type.
152153
std::pair<Type, TypeDecl *>
153154
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
154-
LazyResolver *resolver) const;
155+
LazyResolver *resolver,
156+
SubstOptions options = None) const;
155157

156158
/// Apply the given function object to each type witness within this
157159
/// protocol conformance.
@@ -399,7 +401,8 @@ class NormalProtocolConformance : public ProtocolConformance,
399401
/// for the given associated type.
400402
std::pair<Type, TypeDecl *>
401403
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
402-
LazyResolver *resolver) const;
404+
LazyResolver *resolver,
405+
SubstOptions options = None) const;
403406

404407
/// Determine whether the protocol conformance has a type witness for the
405408
/// given associated type.
@@ -568,7 +571,8 @@ class SpecializedProtocolConformance : public ProtocolConformance,
568571
/// for the given associated type.
569572
std::pair<Type, TypeDecl *>
570573
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
571-
LazyResolver *resolver) const;
574+
LazyResolver *resolver,
575+
SubstOptions options = None) const;
572576

573577
/// Given that the requirement signature of the protocol directly states
574578
/// that the given dependent type must conform to the given protocol,
@@ -662,8 +666,10 @@ class InheritedProtocolConformance : public ProtocolConformance,
662666
/// for the given associated type.
663667
std::pair<Type, TypeDecl *>
664668
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
665-
LazyResolver *resolver) const {
666-
return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver);
669+
LazyResolver *resolver,
670+
SubstOptions options = None) const {
671+
return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver,
672+
options);
667673
}
668674

669675
/// Given that the requirement signature of the protocol directly states

include/swift/AST/Type.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,26 @@ enum class SubstFlags {
165165
};
166166

167167
/// Options for performing substitutions into a type.
168-
typedef OptionSet<SubstFlags> SubstOptions;
168+
struct SubstOptions : public OptionSet<SubstFlags> {
169+
typedef std::function<Type(const NormalProtocolConformance *,
170+
AssociatedTypeDecl *)>
171+
GetTentativeTypeWitness;
172+
173+
/// Function that retrieves a tentative type witness for a protocol
174+
/// conformance with the state \c CheckingTypeWitnesses.
175+
GetTentativeTypeWitness getTentativeTypeWitness;
176+
177+
SubstOptions(llvm::NoneType) : OptionSet(None) { }
178+
179+
SubstOptions(SubstFlags flags) : OptionSet(flags) { }
180+
181+
SubstOptions(OptionSet<SubstFlags> options) : OptionSet(options) { }
182+
183+
SubstOptions(OptionSet<SubstFlags> options,
184+
GetTentativeTypeWitness getTentativeTypeWitness)
185+
: OptionSet(options),
186+
getTentativeTypeWitness(std::move(getTentativeTypeWitness)) { }
187+
};
169188

170189
inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) {
171190
return SubstOptions(lhs) | rhs;

lib/AST/ProtocolConformance.cpp

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,16 @@ ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
215215

216216
std::pair<Type, TypeDecl *>
217217
ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
218-
LazyResolver *resolver) const {
219-
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl, (assocType, resolver))
218+
LazyResolver *resolver,
219+
SubstOptions options) const {
220+
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl,
221+
(assocType, resolver, options))
220222
}
221223

222224
Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
223-
LazyResolver *resolver) const {
224-
return getTypeWitnessAndDecl(assocType, resolver).first;
225+
LazyResolver *resolver,
226+
SubstOptions options) const {
227+
return getTypeWitnessAndDecl(assocType, resolver, options).first;
225228
}
226229

227230
ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement,
@@ -472,22 +475,39 @@ static bool resolveKnownTypeWitness(NormalProtocolConformance *conformance,
472475

473476
std::pair<Type, TypeDecl *>
474477
NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
475-
LazyResolver *resolver) const {
478+
LazyResolver *resolver,
479+
SubstOptions options) const {
476480
if (Resolver)
477481
resolveLazyInfo();
478482

483+
// Check whether we already have a type witness.
479484
auto known = TypeWitnesses.find(assocType);
480-
if (known == TypeWitnesses.end()) {
481-
PrettyStackTraceRequirement trace("resolving", this, assocType);
482-
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
483-
assocType)) {
484-
assert(resolver && "Unable to resolve type witness");
485-
resolver->resolveTypeWitness(this, assocType);
485+
if (known != TypeWitnesses.end())
486+
return known->second;
487+
488+
// If this conformance is in a state where it is inferring type witnesses,
489+
// check tentative witnesses.
490+
if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) {
491+
// If there is a tentative-type-witness function, use it.
492+
if (options.getTentativeTypeWitness) {
493+
if (Type witnessType = options.getTentativeTypeWitness(this, assocType))
494+
return { witnessType, nullptr };
486495
}
487-
known = TypeWitnesses.find(assocType);
488-
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
496+
497+
// Otherwise, we fail; this is the only case in which we can retturn a
498+
// null type.
499+
return { Type(), nullptr };
489500
}
490501

502+
// Otherwise, resolve the type witness.
503+
PrettyStackTraceRequirement trace("resolving", this, assocType);
504+
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
505+
assocType)) {
506+
assert(resolver && "Unable to resolve type witness");
507+
resolver->resolveTypeWitness(this, assocType);
508+
}
509+
known = TypeWitnesses.find(assocType);
510+
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
491511
return known->second;
492512
}
493513

@@ -656,7 +676,8 @@ bool SpecializedProtocolConformance::hasTypeWitness(
656676
std::pair<Type, TypeDecl *>
657677
SpecializedProtocolConformance::getTypeWitnessAndDecl(
658678
AssociatedTypeDecl *assocType,
659-
LazyResolver *resolver) const {
679+
LazyResolver *resolver,
680+
SubstOptions options) const {
660681
// If we've already created this type witness, return it.
661682
auto known = TypeWitnesses.find(assocType);
662683
if (known != TypeWitnesses.end()) {
@@ -669,26 +690,41 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
669690
auto substitutionMap =
670691
genericSig->getSubstitutionMap(GenericSubstitutions);
671692

693+
// Local function to determine whether we will end up
694+
auto normal = GenericConformance->getRootNormalConformance();
695+
auto isTentativeWitness = [&] {
696+
if (normal->getState() != ProtocolConformanceState::CheckingTypeWitnesses)
697+
return false;
698+
699+
return !normal->hasTypeWitness(assocType, nullptr);
700+
};
701+
672702
auto genericWitnessAndDecl
673-
= GenericConformance->getTypeWitnessAndDecl(assocType, resolver);
703+
= GenericConformance->getTypeWitnessAndDecl(assocType, resolver, options);
704+
705+
auto genericWitness = genericWitnessAndDecl.first;
706+
if (!genericWitness)
707+
return { Type(), nullptr };
674708

675-
auto &genericWitness = genericWitnessAndDecl.first;
676709
auto *typeDecl = genericWitnessAndDecl.second;
677710

678711
// Apply the substitution we computed above
679712
auto specializedType
680-
= genericWitness.subst(substitutionMap);
681-
if (!specializedType)
682-
specializedType = ErrorType::get(genericWitness);
713+
= genericWitness.subst(substitutionMap, options);
714+
if (!specializedType) {
715+
if (isTentativeWitness())
716+
return { Type(), nullptr };
683717

684-
// If the type witness was unchanged, just copy it directly.
685-
if (specializedType.getPointer() == genericWitness.getPointer()) {
686-
TypeWitnesses[assocType] = genericWitnessAndDecl;
687-
return TypeWitnesses[assocType];
718+
specializedType = ErrorType::get(genericWitness);
688719
}
689720

690-
TypeWitnesses[assocType] = std::make_pair(specializedType, typeDecl);
691-
return TypeWitnesses[assocType];
721+
// If we aren't in a case where we used the tentative type witness
722+
// information, cache the result.
723+
auto specializedWitnessAndDecl = std::make_pair(specializedType, typeDecl);
724+
if (!isTentativeWitness() && !specializedType->hasError())
725+
TypeWitnesses[assocType] = specializedWitnessAndDecl;
726+
727+
return specializedWitnessAndDecl;
692728
}
693729

694730
ProtocolConformanceRef

lib/AST/Type.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2909,17 +2909,11 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances,
29092909
if (!conformance) return failed();
29102910
if (!conformance->isConcrete()) return failed();
29112911

2912-
// If we have an unsatisfied type witness while we're checking the
2913-
// conformances we're supposed to skip this conformance's unsatisfied type
2914-
// witnesses, and we have an unsatisfied type witness, return
2915-
// "missing".
2916-
if (conformance->getConcrete()->getRootNormalConformance()->getState()
2917-
== ProtocolConformanceState::CheckingTypeWitnesses &&
2918-
!conformance->getConcrete()->hasTypeWitness(assocType, nullptr))
2919-
return failed();
2920-
2912+
// Retrieve the type witness.
29212913
auto witness =
2922-
conformance->getConcrete()->getTypeWitness(assocType, resolver);
2914+
conformance->getConcrete()->getTypeWitness(assocType, resolver, options);
2915+
if (!witness)
2916+
return failed();
29232917

29242918
// This is a hacky feature allowing code completion to migrate to
29252919
// using Type::subst() without changing output.

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,11 +1246,12 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
12461246
LookupConformanceFn conformances,
12471247
UnsatisfiedDependency *unsatisfiedDependency,
12481248
ConformanceCheckOptions conformanceOptions,
1249-
GenericRequirementsCheckListener *listener) {
1249+
GenericRequirementsCheckListener *listener,
1250+
SubstOptions options) {
12501251
bool valid = true;
12511252

12521253
for (const auto &rawReq : genericSig->getRequirements()) {
1253-
auto req = rawReq.subst(substitutions, conformances);
1254+
auto req = rawReq.subst(substitutions, conformances, options);
12541255
if (!req) {
12551256
// Another requirement will fail later; just continue.
12561257
valid = false;
@@ -1286,6 +1287,7 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
12861287
switch (status) {
12871288
case RequirementCheckResult::UnsatisfiedDependency:
12881289
case RequirementCheckResult::Failure:
1290+
case RequirementCheckResult::SubstitutionFailure:
12891291
// pass it on up.
12901292
return status;
12911293
case RequirementCheckResult::Success:
@@ -1308,28 +1310,32 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13081310
case RequirementKind::Superclass:
13091311
// Superclass requirements.
13101312
if (!isSubtypeOf(firstType, secondType, dc)) {
1311-
// FIXME: Poor source-location information.
1312-
diagnose(loc, diag::type_does_not_inherit, owner, firstType,
1313-
secondType);
1314-
1315-
diagnose(noteLoc, diag::type_does_not_inherit_requirement, rawFirstType,
1316-
rawSecondType,
1317-
genericSig->gatherGenericParamBindingsText(
1318-
{rawFirstType, rawSecondType}, substitutions));
1313+
if (loc.isValid()) {
1314+
// FIXME: Poor source-location information.
1315+
diagnose(loc, diag::type_does_not_inherit, owner, firstType,
1316+
secondType);
1317+
1318+
diagnose(noteLoc, diag::type_does_not_inherit_requirement,
1319+
rawFirstType, rawSecondType,
1320+
genericSig->gatherGenericParamBindingsText(
1321+
{rawFirstType, rawSecondType}, substitutions));
1322+
}
13191323

13201324
return RequirementCheckResult::Failure;
13211325
}
13221326
continue;
13231327

13241328
case RequirementKind::SameType:
13251329
if (!firstType->isEqual(secondType)) {
1326-
// FIXME: Better location info for both diagnostics.
1327-
diagnose(loc, diag::types_not_equal, owner, firstType, secondType);
1328-
1329-
diagnose(noteLoc, diag::types_not_equal_requirement, rawFirstType,
1330-
rawSecondType,
1331-
genericSig->gatherGenericParamBindingsText(
1332-
{rawFirstType, rawSecondType}, substitutions));
1330+
if (loc.isValid()) {
1331+
// FIXME: Better location info for both diagnostics.
1332+
diagnose(loc, diag::types_not_equal, owner, firstType, secondType);
1333+
1334+
diagnose(noteLoc, diag::types_not_equal_requirement, rawFirstType,
1335+
rawSecondType,
1336+
genericSig->gatherGenericParamBindingsText(
1337+
{rawFirstType, rawSecondType}, substitutions));
1338+
}
13331339

13341340
return RequirementCheckResult::Failure;
13351341
}
@@ -1339,5 +1345,5 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13391345

13401346
if (valid)
13411347
return RequirementCheckResult::Success;
1342-
return RequirementCheckResult::Failure;
1348+
return RequirementCheckResult::SubstitutionFailure;
13431349
}

0 commit comments

Comments
 (0)