mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user