[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.
This commit is contained in:
Doug Gregor
2017-05-03 22:38:53 -07:00
parent 204f6f2317
commit dbb973aab4
4 changed files with 98 additions and 43 deletions

View File

@@ -145,13 +145,15 @@ public:
/// Retrieve the type witness for the given associated type. /// Retrieve the type witness for the given associated type.
Type getTypeWitness(AssociatedTypeDecl *assocType, Type getTypeWitness(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const; LazyResolver *resolver,
SubstOptions options = None) const;
/// Retrieve the type witness and type decl (if one exists) /// Retrieve the type witness and type decl (if one exists)
/// for the given associated type. /// for the given associated type.
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const; LazyResolver *resolver,
SubstOptions options = None) const;
/// Apply the given function object to each type witness within this /// Apply the given function object to each type witness within this
/// protocol conformance. /// protocol conformance.
@@ -399,7 +401,8 @@ public:
/// for the given associated type. /// for the given associated type.
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const; LazyResolver *resolver,
SubstOptions options = None) const;
/// Determine whether the protocol conformance has a type witness for the /// Determine whether the protocol conformance has a type witness for the
/// given associated type. /// given associated type.
@@ -568,7 +571,8 @@ public:
/// for the given associated type. /// for the given associated type.
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const; LazyResolver *resolver,
SubstOptions options = None) const;
/// Given that the requirement signature of the protocol directly states /// Given that the requirement signature of the protocol directly states
/// that the given dependent type must conform to the given protocol, /// that the given dependent type must conform to the given protocol,
@@ -662,8 +666,10 @@ public:
/// for the given associated type. /// for the given associated type.
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const { LazyResolver *resolver,
return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver); SubstOptions options = None) const {
return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver,
options);
} }
/// Given that the requirement signature of the protocol directly states /// Given that the requirement signature of the protocol directly states

View File

@@ -165,7 +165,26 @@ enum class SubstFlags {
}; };
/// Options for performing substitutions into a type. /// Options for performing substitutions into a type.
typedef OptionSet<SubstFlags> SubstOptions; struct SubstOptions : public OptionSet<SubstFlags> {
typedef std::function<Type(const NormalProtocolConformance *,
AssociatedTypeDecl *)>
GetTentativeTypeWitness;
/// Function that retrieves a tentative type witness for a protocol
/// conformance with the state \c CheckingTypeWitnesses.
GetTentativeTypeWitness getTentativeTypeWitness;
SubstOptions(llvm::NoneType) : OptionSet(None) { }
SubstOptions(SubstFlags flags) : OptionSet(flags) { }
SubstOptions(OptionSet<SubstFlags> options) : OptionSet(options) { }
SubstOptions(OptionSet<SubstFlags> options,
GetTentativeTypeWitness getTentativeTypeWitness)
: OptionSet(options),
getTentativeTypeWitness(std::move(getTentativeTypeWitness)) { }
};
inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) { inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) {
return SubstOptions(lhs) | rhs; return SubstOptions(lhs) | rhs;

View File

@@ -215,13 +215,16 @@ ProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, ProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const { LazyResolver *resolver,
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl, (assocType, resolver)) SubstOptions options) const {
CONFORMANCE_SUBCLASS_DISPATCH(getTypeWitnessAndDecl,
(assocType, resolver, options))
} }
Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType, Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const { LazyResolver *resolver,
return getTypeWitnessAndDecl(assocType, resolver).first; SubstOptions options) const {
return getTypeWitnessAndDecl(assocType, resolver, options).first;
} }
ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement, ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement,
@@ -472,22 +475,39 @@ static bool resolveKnownTypeWitness(NormalProtocolConformance *conformance,
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType, NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const { LazyResolver *resolver,
SubstOptions options) const {
if (Resolver) if (Resolver)
resolveLazyInfo(); resolveLazyInfo();
// Check whether we already have a type witness.
auto known = TypeWitnesses.find(assocType); auto known = TypeWitnesses.find(assocType);
if (known == TypeWitnesses.end()) { if (known != TypeWitnesses.end())
PrettyStackTraceRequirement trace("resolving", this, assocType); return known->second;
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
assocType)) { // If this conformance is in a state where it is inferring type witnesses,
assert(resolver && "Unable to resolve type witness"); // check tentative witnesses.
resolver->resolveTypeWitness(this, assocType); if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) {
// If there is a tentative-type-witness function, use it.
if (options.getTentativeTypeWitness) {
if (Type witnessType = options.getTentativeTypeWitness(this, assocType))
return { witnessType, nullptr };
} }
known = TypeWitnesses.find(assocType);
assert(known != TypeWitnesses.end() && "Didn't resolve witness?"); // Otherwise, we fail; this is the only case in which we can retturn a
// null type.
return { Type(), nullptr };
} }
// Otherwise, resolve the type witness.
PrettyStackTraceRequirement trace("resolving", this, assocType);
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
assocType)) {
assert(resolver && "Unable to resolve type witness");
resolver->resolveTypeWitness(this, assocType);
}
known = TypeWitnesses.find(assocType);
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
return known->second; return known->second;
} }
@@ -656,7 +676,8 @@ bool SpecializedProtocolConformance::hasTypeWitness(
std::pair<Type, TypeDecl *> std::pair<Type, TypeDecl *>
SpecializedProtocolConformance::getTypeWitnessAndDecl( SpecializedProtocolConformance::getTypeWitnessAndDecl(
AssociatedTypeDecl *assocType, AssociatedTypeDecl *assocType,
LazyResolver *resolver) const { LazyResolver *resolver,
SubstOptions options) const {
// If we've already created this type witness, return it. // If we've already created this type witness, return it.
auto known = TypeWitnesses.find(assocType); auto known = TypeWitnesses.find(assocType);
if (known != TypeWitnesses.end()) { if (known != TypeWitnesses.end()) {
@@ -669,26 +690,41 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
auto substitutionMap = auto substitutionMap =
genericSig->getSubstitutionMap(GenericSubstitutions); genericSig->getSubstitutionMap(GenericSubstitutions);
auto genericWitnessAndDecl // Local function to determine whether we will end up
= GenericConformance->getTypeWitnessAndDecl(assocType, resolver); auto normal = GenericConformance->getRootNormalConformance();
auto isTentativeWitness = [&] {
if (normal->getState() != ProtocolConformanceState::CheckingTypeWitnesses)
return false;
return !normal->hasTypeWitness(assocType, nullptr);
};
auto genericWitnessAndDecl
= GenericConformance->getTypeWitnessAndDecl(assocType, resolver, options);
auto genericWitness = genericWitnessAndDecl.first;
if (!genericWitness)
return { Type(), nullptr };
auto &genericWitness = genericWitnessAndDecl.first;
auto *typeDecl = genericWitnessAndDecl.second; auto *typeDecl = genericWitnessAndDecl.second;
// Apply the substitution we computed above // Apply the substitution we computed above
auto specializedType auto specializedType
= genericWitness.subst(substitutionMap); = genericWitness.subst(substitutionMap, options);
if (!specializedType) if (!specializedType) {
specializedType = ErrorType::get(genericWitness); if (isTentativeWitness())
return { Type(), nullptr };
// If the type witness was unchanged, just copy it directly. specializedType = ErrorType::get(genericWitness);
if (specializedType.getPointer() == genericWitness.getPointer()) {
TypeWitnesses[assocType] = genericWitnessAndDecl;
return TypeWitnesses[assocType];
} }
TypeWitnesses[assocType] = std::make_pair(specializedType, typeDecl); // If we aren't in a case where we used the tentative type witness
return TypeWitnesses[assocType]; // information, cache the result.
auto specializedWitnessAndDecl = std::make_pair(specializedType, typeDecl);
if (!isTentativeWitness() && !specializedType->hasError())
TypeWitnesses[assocType] = specializedWitnessAndDecl;
return specializedWitnessAndDecl;
} }
ProtocolConformanceRef ProtocolConformanceRef

View File

@@ -2909,17 +2909,11 @@ static Type getMemberForBaseType(LookupConformanceFn lookupConformances,
if (!conformance) return failed(); if (!conformance) return failed();
if (!conformance->isConcrete()) return failed(); if (!conformance->isConcrete()) return failed();
// If we have an unsatisfied type witness while we're checking the // Retrieve the type witness.
// conformances we're supposed to skip this conformance's unsatisfied type
// witnesses, and we have an unsatisfied type witness, return
// "missing".
if (conformance->getConcrete()->getRootNormalConformance()->getState()
== ProtocolConformanceState::CheckingTypeWitnesses &&
!conformance->getConcrete()->hasTypeWitness(assocType, nullptr))
return failed();
auto witness = auto witness =
conformance->getConcrete()->getTypeWitness(assocType, resolver); conformance->getConcrete()->getTypeWitness(assocType, resolver, options);
if (!witness)
return failed();
// This is a hacky feature allowing code completion to migrate to // This is a hacky feature allowing code completion to migrate to
// using Type::subst() without changing output. // using Type::subst() without changing output.