Skip to content

Commit 2ebb123

Browse files
committed
AssociatedTypeInference: Properly substitute into tentative type witnesses in inferAbstractTypeWitnesses()
A regular type.subst() is not enough, because tentative type witnesses may contain type parameters, and we have to substitute them as well, recursively, e.g A := G<B>, B := G<C>, C := Never. Avoiding subst() here also eliminates a use case of the getSubstOptionsWithCurrentTypeWitnesses() hack.
1 parent 98567e8 commit 2ebb123

File tree

2 files changed

+359
-27
lines changed

2 files changed

+359
-27
lines changed

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 117 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,7 @@ void AssociatedTypeInference::collectAbstractTypeWitnesses(
886886
if (auto genericSig = dc->getGenericSignatureOfContext()) {
887887
for (auto *gp : genericSig.getInnermostGenericParams()) {
888888
if (gp->getName() == assocType->getName()) {
889-
system.addTypeWitness(assocType->getName(),
890-
dc->mapTypeIntoContext(gp));
889+
system.addTypeWitness(assocType->getName(), gp);
891890
}
892891
}
893892
}
@@ -1153,39 +1152,130 @@ AssociatedTypeDecl *AssociatedTypeInference::inferAbstractTypeWitnesses(
11531152

11541153
// Check each abstract type witness against the generic requirements on the
11551154
// corresponding associated type.
1155+
//
1156+
// FIXME: Consider checking non-dependent type witnesses first. Checking in
1157+
// default order can lead to the creation and exposure of malformed types in
1158+
// diagnostics. For example, we would diagnose that 'G<Never>' (!) does not
1159+
// conform to 'Sequence' in the below.
1160+
//
1161+
// struct G<S: Sequence> {}
1162+
// protocol P {
1163+
// associatedtype A: Sequence = G<B>
1164+
// associatedtype B: Sequence = Never
1165+
// }
11561166
const auto substOptions = getSubstOptionsWithCurrentTypeWitnesses();
11571167
for (auto *const assocType : unresolvedAssocTypes) {
11581168
Type type = system.getResolvedTypeWitness(assocType->getName());
1169+
// Replace type parameters with other known or tentative type witnesses.
11591170
if (type->hasTypeParameter()) {
1160-
// Replace type parameters with other known or tentative type witnesses.
1161-
type = type.subst(
1162-
[&](SubstitutableType *type) {
1163-
if (type->isEqual(proto->getSelfInterfaceType()))
1164-
return adoptee;
1171+
// FIXME: We should find a better way to detect and reason about these
1172+
// cyclic solutions so that we can spot them earlier and express them in
1173+
// diagnostics.
1174+
llvm::SmallPtrSet<AssociatedTypeDecl *, 4> circularityCheck;
1175+
circularityCheck.insert(assocType);
1176+
1177+
std::function<Type(Type)> substCurrentTypeWitnesses;
1178+
substCurrentTypeWitnesses = [&](Type ty) -> Type {
1179+
if (auto *gp = ty->getAs<GenericTypeParamType>()) {
1180+
if (isa<ProtocolDecl>(gp->getDecl()->getDeclContext()->getAsDecl())) {
1181+
return adoptee;
1182+
}
11651183

1166-
return Type();
1167-
},
1168-
LookUpConformanceInModule(dc->getParentModule()), substOptions);
1184+
return ty;
1185+
}
11691186

1170-
// If the substitution produced an error, give up.
1171-
if (type->hasError())
1172-
return assocType;
1187+
auto *const dmt = ty->getAs<DependentMemberType>();
1188+
if (!dmt) {
1189+
return ty;
1190+
}
11731191

1174-
// FIXME: We should find a better way to detect and reason about these
1175-
// cyclic solutions.
1176-
// If mapping into context yields an error, or we still have a type
1177-
// parameter despite not having a generic environment, then a type
1178-
// parameter was sent to a tentative type witness that itself is a type
1179-
// parameter, and the solution is cyclic, e.g { A := B.A, B := A.B };
1180-
// bail out in these cases.
1181-
if (dc->isGenericContext()) {
1182-
type = dc->mapTypeIntoContext(type);
1183-
1184-
if (type->hasError())
1185-
return assocType;
1186-
} else if (type->hasTypeParameter()) {
1192+
const auto substBase =
1193+
dmt->getBase().transform(substCurrentTypeWitnesses);
1194+
if (!substBase) {
1195+
return nullptr;
1196+
}
1197+
1198+
// If the transformed base has the same nominal as the adoptee, we may
1199+
// need to look up a tentative type witness. Otherwise, just substitute
1200+
// the base.
1201+
if (substBase->getAnyNominal() != adoptee->getAnyNominal()) {
1202+
return dmt->substBaseType(dc->getParentModule(), substBase);
1203+
}
1204+
1205+
auto *assocTy = dmt->getAssocType();
1206+
assert(
1207+
assocTy &&
1208+
"found structural DependentMemberType in tentative type witness");
1209+
1210+
// Intercept recursive solutions.
1211+
if (!circularityCheck.insert(assocTy).second) {
1212+
return nullptr;
1213+
}
1214+
SWIFT_DEFER { circularityCheck.erase(dmt->getAssocType()); };
1215+
1216+
if (assocTy->getProtocol() == proto) {
1217+
// We have the associated type we need.
1218+
} else if (proto->inheritsFrom(assocTy->getProtocol())) {
1219+
// See if there is an associated type with the same name in our
1220+
// protocol. If there isn't, keep the original associated type:
1221+
// we'll be falling back to a base substitution.
1222+
if (auto *decl = proto->getAssociatedType(assocTy->getName())) {
1223+
assocTy = decl;
1224+
}
1225+
}
1226+
1227+
// Find the type witness for this associated type.
1228+
Type tyWitness;
1229+
if (assocTy->getProtocol() == proto && typeWitnesses.count(assocTy)) {
1230+
tyWitness = typeWitnesses.begin(assocTy)->first;
1231+
1232+
// A tentative type witness may contain a 'Self'-rooted type
1233+
// parameter,
1234+
// FIXME: or a weird concrete-type-rooted dependent member type
1235+
// coming from inference via a value witness. Make sure we sort these
1236+
// out so that we don't break any subst() invariants.
1237+
if (tyWitness->hasTypeParameter() ||
1238+
tyWitness->hasDependentMember()) {
1239+
tyWitness = tyWitness.transform(substCurrentTypeWitnesses);
1240+
}
1241+
1242+
if (tyWitness) {
1243+
// HACK: Those inferred via value witnesses are eagerly mapped into
1244+
// context. For now, do the same for abstract type witnesses and
1245+
// handle archetypes.
1246+
if (tyWitness->hasArchetype()) {
1247+
tyWitness = tyWitness->mapTypeOutOfContext();
1248+
}
1249+
1250+
// If the transformed base is specialized, apply substitutions.
1251+
if (tyWitness->hasTypeParameter()) {
1252+
const auto conf = dc->getParentModule()->lookupConformance(
1253+
substBase, assocTy->getProtocol(), /*allowMissing=*/true);
1254+
if (auto *specialized = dyn_cast<SpecializedProtocolConformance>(
1255+
conf.getConcrete())) {
1256+
tyWitness = tyWitness.subst(specialized->getSubstitutionMap());
1257+
}
1258+
}
1259+
}
1260+
} else {
1261+
// The associated type has a recorded type witness, or comes from a
1262+
// different, possibly unrelated protocol; fall back to a base
1263+
// substitution to find the type witness.
1264+
tyWitness =
1265+
DependentMemberType::get(proto->getSelfInterfaceType(), assocTy)
1266+
->substBaseType(dc->getParentModule(), substBase);
1267+
}
1268+
1269+
return tyWitness;
1270+
};
1271+
1272+
type = type.transform(substCurrentTypeWitnesses);
1273+
1274+
// If substitution failed, give up.
1275+
if (!type || type->hasError())
11871276
return assocType;
1188-
}
1277+
1278+
type = dc->mapTypeIntoContext(type);
11891279
}
11901280

11911281
if (const auto failed =

0 commit comments

Comments
 (0)