Skip to content

Commit dbb973a

Browse files
committed
[AST] Allow tentative type witnesses to be plumbed through SubstOptions.
Extend SubstOptions, which controls how substitution is performed, to allow the caller to subst() to provide a callback function that may provide a type witness for a normal protocol conformance that is undergoing type witness inference. In effect, it's allowing us to provide tentative bindings for type witnesses so we can see the effects of substitution.
1 parent 204f6f2 commit dbb973a

File tree

4 files changed

+97
-42
lines changed

4 files changed

+97
-42
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.

0 commit comments

Comments
 (0)