Skip to content

Commit 8f6a2df

Browse files
committed
AssociatedTypeInference: Draft up TypeWitnessSystem
1 parent cb01397 commit 8f6a2df

File tree

2 files changed

+334
-0
lines changed

2 files changed

+334
-0
lines changed

lib/Sema/TypeCheckProtocol.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,101 @@ class ConformanceChecker : public WitnessChecker {
929929
llvm::function_ref<bool(AbstractFunctionDecl *)>predicate);
930930
};
931931

932+
/// A system for recording and probing the intergrity of a type witness solution
933+
/// for a set of unresolved associated type declarations.
934+
///
935+
/// Right now can reason only about abstract type witnesses, i.e., same-type
936+
/// constraints, default type definitions, and bindings to generic parameters.
937+
class TypeWitnessSystem final {
938+
/// Equivalence classes are used on demand to express equivalences between
939+
/// witness candidates and reflect changes to resolved types across their
940+
/// members.
941+
struct EquivalenceClass final {
942+
/// The resolved type for witness candidates belonging to this equivalence
943+
/// class. The resolved type may be a type parameter, but cannot directly
944+
/// pertain to a name variable in the system; instead, witness candidates
945+
/// that should resolve to the same type share an equivalence class.
946+
Type ResolvedTy;
947+
948+
EquivalenceClass(const EquivalenceClass &) = delete;
949+
EquivalenceClass(EquivalenceClass &&) = delete;
950+
EquivalenceClass &operator=(const EquivalenceClass &) = delete;
951+
EquivalenceClass &operator=(EquivalenceClass &&) = delete;
952+
};
953+
954+
/// A type witness candidate for a name variable.
955+
struct TypeWitnessCandidate final {
956+
/// The defaulted associated type declaration correlating with this
957+
/// candidate, if present.
958+
const AssociatedTypeDecl *DefaultedAssocType;
959+
960+
/// The equivalence class of this candidate.
961+
EquivalenceClass *EquivClass;
962+
};
963+
964+
/// The set of equivalence classes in the system.
965+
llvm::SmallPtrSet<EquivalenceClass *, 4> EquivalenceClasses;
966+
967+
/// The mapping from name variables (the names of unresolved associated
968+
/// type declarations) to their corresponding type witness candidates.
969+
llvm::SmallDenseMap<Identifier, TypeWitnessCandidate, 4> TypeWitnesses;
970+
971+
public:
972+
TypeWitnessSystem(ArrayRef<AssociatedTypeDecl *> assocTypes);
973+
~TypeWitnessSystem();
974+
975+
TypeWitnessSystem(const TypeWitnessSystem &) = delete;
976+
TypeWitnessSystem(TypeWitnessSystem &&) = delete;
977+
TypeWitnessSystem &operator=(const TypeWitnessSystem &) = delete;
978+
TypeWitnessSystem &operator=(TypeWitnessSystem &&) = delete;
979+
980+
/// Get the resolved type witness for the associated type with the given name.
981+
Type getResolvedTypeWitness(Identifier name) const;
982+
bool hasResolvedTypeWitness(Identifier name) const;
983+
984+
/// Get the defaulted associated type relating to the resolved type witness
985+
/// for the associated type with the given name, if present.
986+
const AssociatedTypeDecl *getDefaultedAssocType(Identifier name) const;
987+
988+
/// Record a type witness for the given associated type name.
989+
///
990+
/// \note This need not lead to the resolution of a type witness, e.g.
991+
/// an associated type may be defaulted to another.
992+
void addTypeWitness(Identifier name, Type type);
993+
994+
/// Record a default type witness.
995+
///
996+
/// \param defaultedAssocType The specific associated type declaration that
997+
/// defines the given default type.
998+
///
999+
/// \note This need not lead to the resolution of a type witness.
1000+
void addDefaultTypeWitness(Type type,
1001+
const AssociatedTypeDecl *defaultedAssocType);
1002+
1003+
/// Record the given same-type requirement, if regarded of interest to
1004+
/// the system.
1005+
///
1006+
/// \note This need not lead to the resolution of a type witness.
1007+
void addSameTypeRequirement(const Requirement &req);
1008+
1009+
void dump(llvm::raw_ostream &out,
1010+
const NormalProtocolConformance *conformance) const;
1011+
1012+
private:
1013+
/// Form an equivalence between the given name variables.
1014+
void addEquivalence(Identifier name1, Identifier name2);
1015+
1016+
/// Merge \p equivClass2 into \p equivClass1.
1017+
///
1018+
/// \note This will delete \p equivClass2 after migrating its members to
1019+
/// \p equivClass1.
1020+
void mergeEquivalenceClasses(EquivalenceClass *equivClass1,
1021+
const EquivalenceClass *equivClass2);
1022+
1023+
/// Determine whether \p ty1 is a better resolved type than \p ty2.
1024+
static bool isBetterResolvedType(Type ty1, Type ty2);
1025+
};
1026+
9321027
/// Captures the state needed to infer associated types.
9331028
class AssociatedTypeInference {
9341029
/// The type checker we'll need to validate declarations etc.

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,245 @@ auto AssociatedTypeInference::solve(ConformanceChecker &checker)
21072107
return None;
21082108
}
21092109

2110+
TypeWitnessSystem::TypeWitnessSystem(
2111+
ArrayRef<AssociatedTypeDecl *> assocTypes) {
2112+
for (auto *assocType : assocTypes) {
2113+
this->TypeWitnesses.try_emplace(assocType->getName());
2114+
}
2115+
}
2116+
2117+
TypeWitnessSystem::~TypeWitnessSystem() {
2118+
for (auto *equivClass : this->EquivalenceClasses) {
2119+
delete equivClass;
2120+
}
2121+
}
2122+
2123+
bool TypeWitnessSystem::hasResolvedTypeWitness(Identifier name) const {
2124+
return (bool)getResolvedTypeWitness(name);
2125+
}
2126+
2127+
Type TypeWitnessSystem::getResolvedTypeWitness(Identifier name) const {
2128+
assert(this->TypeWitnesses.count(name));
2129+
2130+
if (auto *equivClass = this->TypeWitnesses.lookup(name).EquivClass) {
2131+
return equivClass->ResolvedTy;
2132+
}
2133+
2134+
return Type();
2135+
}
2136+
2137+
const AssociatedTypeDecl *
2138+
TypeWitnessSystem::getDefaultedAssocType(Identifier name) const {
2139+
assert(this->TypeWitnesses.count(name));
2140+
2141+
return this->TypeWitnesses.lookup(name).DefaultedAssocType;
2142+
}
2143+
2144+
void TypeWitnessSystem::addTypeWitness(Identifier name, Type type) {
2145+
assert(this->TypeWitnesses.count(name));
2146+
2147+
if (const auto *depTy = type->getAs<DependentMemberType>()) {
2148+
// If the type corresponds to a name variable in the system, form an
2149+
// equivalence between variables.
2150+
if (depTy->getBase()->is<GenericTypeParamType>()) {
2151+
if (this->TypeWitnesses.count(depTy->getName())) {
2152+
return addEquivalence(name, depTy->getName());
2153+
}
2154+
} else {
2155+
while (depTy->getBase()->is<DependentMemberType>()) {
2156+
depTy = depTy->getBase()->castTo<DependentMemberType>();
2157+
}
2158+
2159+
// Equivalences of the form 'Self.X == Self.X.*' do not contribute
2160+
// to solving the system, so just ignore them.
2161+
if (name == depTy->getName()) {
2162+
return;
2163+
}
2164+
}
2165+
}
2166+
2167+
auto &tyWitness = this->TypeWitnesses[name];
2168+
2169+
// Assume that the type resolves the type witness.
2170+
//
2171+
// If we already have a resolved type, keep going only if the new one is
2172+
// better.
2173+
if (tyWitness.EquivClass && tyWitness.EquivClass->ResolvedTy) {
2174+
if (!isBetterResolvedType(type, tyWitness.EquivClass->ResolvedTy)) {
2175+
return;
2176+
}
2177+
}
2178+
2179+
// If we can find an existing equivalence class for this type, use it.
2180+
for (auto *const equivClass : this->EquivalenceClasses) {
2181+
if (equivClass->ResolvedTy && equivClass->ResolvedTy->isEqual(type)) {
2182+
if (tyWitness.EquivClass) {
2183+
mergeEquivalenceClasses(equivClass, tyWitness.EquivClass);
2184+
} else {
2185+
tyWitness.EquivClass = equivClass;
2186+
}
2187+
2188+
return;
2189+
}
2190+
}
2191+
2192+
if (tyWitness.EquivClass) {
2193+
tyWitness.EquivClass->ResolvedTy = type;
2194+
} else {
2195+
auto *equivClass = new EquivalenceClass{type};
2196+
this->EquivalenceClasses.insert(equivClass);
2197+
2198+
tyWitness.EquivClass = equivClass;
2199+
}
2200+
}
2201+
2202+
void TypeWitnessSystem::addDefaultTypeWitness(
2203+
Type type, const AssociatedTypeDecl *defaultedAssocType) {
2204+
const auto name = defaultedAssocType->getName();
2205+
assert(this->TypeWitnesses.count(name));
2206+
2207+
auto &tyWitness = this->TypeWitnesses[name];
2208+
assert(!hasResolvedTypeWitness(name) && "already resolved a type witness");
2209+
assert(!tyWitness.DefaultedAssocType &&
2210+
"already recorded a default type witness");
2211+
2212+
// Set the defaulted associated type.
2213+
tyWitness.DefaultedAssocType = defaultedAssocType;
2214+
2215+
// Record the type witness.
2216+
addTypeWitness(name, type);
2217+
}
2218+
2219+
void TypeWitnessSystem::addSameTypeRequirement(const Requirement &req) {
2220+
assert(req.getKind() == RequirementKind::SameType);
2221+
2222+
auto *const depTy1 = req.getFirstType()->getAs<DependentMemberType>();
2223+
auto *const depTy2 = req.getSecondType()->getAs<DependentMemberType>();
2224+
2225+
// Equivalences other than 'Self.X == ...' (or '... == Self.X'), where
2226+
// 'X' is a name variable in this system, do not contribute to solving
2227+
// the system.
2228+
if (depTy1 && depTy1->getBase()->is<GenericTypeParamType>() &&
2229+
this->TypeWitnesses.count(depTy1->getName())) {
2230+
addTypeWitness(depTy1->getName(), req.getSecondType());
2231+
} else if (depTy2 && depTy2->getBase()->is<GenericTypeParamType>() &&
2232+
this->TypeWitnesses.count(depTy2->getName())) {
2233+
addTypeWitness(depTy2->getName(), req.getFirstType());
2234+
}
2235+
}
2236+
2237+
void TypeWitnessSystem::dump(
2238+
llvm::raw_ostream &out,
2239+
const NormalProtocolConformance *conformance) const {
2240+
llvm::SmallVector<Identifier, 4> sortedNames;
2241+
sortedNames.reserve(this->TypeWitnesses.size());
2242+
2243+
for (const auto &pair : this->TypeWitnesses) {
2244+
sortedNames.push_back(pair.first);
2245+
}
2246+
2247+
// Deterministic ordering.
2248+
llvm::array_pod_sort(sortedNames.begin(), sortedNames.end(),
2249+
[](const Identifier *lhs, const Identifier *rhs) -> int {
2250+
return lhs->compare(*rhs);
2251+
});
2252+
2253+
out << "Abstract type witness system for conformance"
2254+
<< " of " << conformance->getType() << " to "
2255+
<< conformance->getProtocol()->getName() << ": {\n";
2256+
2257+
for (const auto &name : sortedNames) {
2258+
out.indent(2) << name << " => ";
2259+
2260+
const auto *eqClass = this->TypeWitnesses.lookup(name).EquivClass;
2261+
if (eqClass) {
2262+
if (eqClass->ResolvedTy) {
2263+
out << eqClass->ResolvedTy;
2264+
} else {
2265+
out << "(unresolved)";
2266+
}
2267+
} else {
2268+
out << "(unresolved)";
2269+
}
2270+
2271+
if (eqClass) {
2272+
out << ", " << eqClass;
2273+
}
2274+
out << "\n";
2275+
}
2276+
out << "}\n";
2277+
}
2278+
2279+
void TypeWitnessSystem::addEquivalence(Identifier name1, Identifier name2) {
2280+
assert(this->TypeWitnesses.count(name1));
2281+
assert(this->TypeWitnesses.count(name2));
2282+
2283+
if (name1 == name2) {
2284+
return;
2285+
}
2286+
2287+
auto &tyWitness1 = this->TypeWitnesses[name1];
2288+
auto &tyWitness2 = this->TypeWitnesses[name2];
2289+
2290+
// If both candidates are associated with existing equivalence classes,
2291+
// merge them.
2292+
if (tyWitness1.EquivClass && tyWitness2.EquivClass) {
2293+
mergeEquivalenceClasses(tyWitness1.EquivClass, tyWitness2.EquivClass);
2294+
return;
2295+
}
2296+
2297+
if (tyWitness1.EquivClass) {
2298+
tyWitness2.EquivClass = tyWitness1.EquivClass;
2299+
} else if (tyWitness2.EquivClass) {
2300+
tyWitness1.EquivClass = tyWitness2.EquivClass;
2301+
} else {
2302+
// Neither has an associated equivalence class.
2303+
auto *equivClass = new EquivalenceClass{nullptr};
2304+
this->EquivalenceClasses.insert(equivClass);
2305+
2306+
tyWitness1.EquivClass = equivClass;
2307+
tyWitness2.EquivClass = equivClass;
2308+
}
2309+
}
2310+
2311+
void TypeWitnessSystem::mergeEquivalenceClasses(
2312+
EquivalenceClass *equivClass1, const EquivalenceClass *equivClass2) {
2313+
assert(equivClass1 && equivClass2);
2314+
if (equivClass1 == equivClass2) {
2315+
return;
2316+
}
2317+
2318+
// Merge the second resolved type into the first.
2319+
if (equivClass1->ResolvedTy && equivClass2->ResolvedTy) {
2320+
if (isBetterResolvedType(equivClass2->ResolvedTy,
2321+
equivClass1->ResolvedTy)) {
2322+
equivClass1->ResolvedTy = equivClass2->ResolvedTy;
2323+
}
2324+
} else if (equivClass2->ResolvedTy) {
2325+
equivClass1->ResolvedTy = equivClass2->ResolvedTy;
2326+
}
2327+
2328+
// Migrate members of the second equivalence class to the first.
2329+
for (auto &pair : this->TypeWitnesses) {
2330+
if (pair.second.EquivClass == equivClass2) {
2331+
pair.second.EquivClass = equivClass1;
2332+
}
2333+
}
2334+
2335+
// Finally, dispose of the second equivalence class.
2336+
this->EquivalenceClasses.erase(const_cast<EquivalenceClass *>(equivClass2));
2337+
delete equivClass2;
2338+
}
2339+
2340+
bool TypeWitnessSystem::isBetterResolvedType(Type ty1, Type ty2) {
2341+
assert(ty1 && ty2);
2342+
assert(
2343+
(ty1->isTypeParameter() || ty2->isTypeParameter() || ty1->isEqual(ty2)) &&
2344+
"Ambigious concrete resolved type");
2345+
2346+
return !ty1->isTypeParameter() && ty2->isTypeParameter();
2347+
}
2348+
21102349
void ConformanceChecker::resolveTypeWitnesses() {
21112350
// Attempt to infer associated type witnesses.
21122351
AssociatedTypeInference inference(getASTContext(), Conformance);

0 commit comments

Comments
 (0)