Merge pull request #69950 from slavapestov/clean-up-open-existentials

Cleanups and fixes for opened existentials
This commit is contained in:
Slava Pestov
2023-11-28 10:45:19 -05:00
committed by GitHub
15 changed files with 285 additions and 684 deletions

View File

@@ -2115,165 +2115,193 @@ static bool isMainDispatchQueueMember(ConstraintLocator *locator) {
}
/// Type-erase occurrences of covariant 'Self'-rooted type parameters to their
/// most specific non-dependent bounds throughout the given type, using
/// \p baseTy as the existential base object type.
/// most specific upper bounds throughout the given type, using \p baseTy as
/// the existential base object type.
///
/// \note If a 'Self'-rooted type parameter is bound to a concrete type, this
/// routine will recurse into the concrete type.
static Type typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
TypePosition outermostPosition) {
TypePosition outermostPosition,
GenericSignature existentialSig,
llvm::function_ref<bool(Type)> containsFn,
llvm::function_ref<bool(Type)> predicateFn,
llvm::function_ref<Type(Type)> projectionFn,
bool force, unsigned metatypeDepth = 0) {
assert(baseTy->isExistentialType());
if (!refTy->hasTypeParameter()) {
if (!containsFn(refTy))
return refTy;
}
const auto existentialSig =
baseTy->getASTContext().getOpenedExistentialSignature(baseTy,
GenericSignature());
return refTy.transformWithPosition(
outermostPosition,
[&](TypeBase *t, TypePosition currPos) -> llvm::Optional<Type> {
if (!containsFn(t)) {
return Type(t);
}
unsigned metatypeDepth = 0;
if (t->is<MetatypeType>()) {
const auto instanceTy = t->getMetatypeInstanceType();
const auto erasedTy =
typeEraseExistentialSelfReferences(
instanceTy, baseTy, currPos,
existentialSig, containsFn, predicateFn, projectionFn,
force, metatypeDepth + 1);
/// Check whether the given type has a reference to the generic parameter
/// that we are erasing.
auto hasErasedGenericParameter = [&](Type type) {
if (!type->hasTypeParameter())
return false;
if (instanceTy.getPointer() == erasedTy.getPointer()) {
return Type(t);
}
return type.findIf([&](Type type) {
if (auto gp = type->getAs<GenericTypeParamType>())
return gp->getDepth() == 0;
return false;
return Type(ExistentialMetatypeType::get(erasedTy));
}
// Opaque types whose substitutions involve this type parameter are
// erased to their upper bound.
if (auto opaque = dyn_cast<OpaqueTypeArchetypeType>(t)) {
for (auto replacementType :
opaque->getSubstitutions().getReplacementTypes()) {
auto erasedReplacementType =
typeEraseExistentialSelfReferences(
replacementType, baseTy, TypePosition::Covariant,
existentialSig, containsFn, predicateFn, projectionFn,
force, metatypeDepth);
if (erasedReplacementType.getPointer() != replacementType.getPointer())
return opaque->getExistentialType();
}
}
if (auto lvalue = dyn_cast<LValueType>(t)) {
auto objTy = lvalue->getObjectType();
auto erasedTy =
typeEraseExistentialSelfReferences(
objTy, baseTy, currPos,
existentialSig, containsFn, predicateFn, projectionFn,
force, metatypeDepth);
if (erasedTy.getPointer() == objTy.getPointer())
return Type(lvalue);
return erasedTy;
}
if (!predicateFn(t)) {
// Recurse.
return llvm::None;
}
auto paramTy = projectionFn(t);
if (!paramTy)
return Type(t);
// This can happen with invalid code.
if (!existentialSig->isValidTypeParameter(paramTy)) {
return Type(t);
}
// If the type parameter is fixed to a concrete type, recurse into it.
if (const auto concreteTy = existentialSig->getConcreteType(paramTy)) {
auto erasedTy = typeEraseExistentialSelfReferences(
concreteTy, baseTy, currPos, existentialSig,
[](Type t) { return t->hasTypeParameter(); },
[](Type t) { return t->isTypeParameter(); },
[](Type t) { return t; },
force, metatypeDepth);
if (erasedTy.getPointer() == concreteTy.getPointer()) {
// Don't return the concrete type if we haven't type-erased
// anything inside it, or else we might inadvertently transform a
// normal metatype into an existential one.
return Type(t);
}
return erasedTy;
}
if (!force) {
switch (currPos) {
case TypePosition::Covariant:
break;
case TypePosition::Contravariant:
case TypePosition::Invariant:
case TypePosition::Shape:
return Type(t);
}
}
Type erasedTy;
// The upper bounds of 'Self' is the existential base type.
if (paramTy->is<GenericTypeParamType>()) {
erasedTy = baseTy;
} else {
erasedTy = existentialSig->getUpperBound(paramTy);
}
if (metatypeDepth) {
if (const auto existential = erasedTy->getAs<ExistentialType>())
return existential->getConstraintType();
}
return erasedTy;
});
};
std::function<Type(Type, TypePosition)> transformFn;
transformFn = [&](Type type, TypePosition initialPos) -> Type {
return type.transformWithPosition(
initialPos,
[&](TypeBase *t, TypePosition currPos) -> llvm::Optional<Type> {
if (!t->hasTypeParameter()) {
return Type(t);
}
if (t->is<MetatypeType>()) {
const auto instanceTy = t->getMetatypeInstanceType();
++metatypeDepth;
const auto erasedTy = transformFn(instanceTy, currPos);
--metatypeDepth;
if (instanceTy.getPointer() == erasedTy.getPointer()) {
return Type(t);
}
return Type(ExistentialMetatypeType::get(erasedTy));
}
// Opaque types whose substitutions involve this type parameter are
// erased to their upper bound.
if (auto opaque = dyn_cast<OpaqueTypeArchetypeType>(t)) {
for (auto replacementType :
opaque->getSubstitutions().getReplacementTypes()) {
if (hasErasedGenericParameter(replacementType)) {
Type interfaceType = opaque->getInterfaceType();
auto genericSig =
opaque->getDecl()->getOpaqueInterfaceGenericSignature();
return genericSig->getNonDependentUpperBounds(interfaceType);
}
}
}
if (!t->isTypeParameter()) {
// Recurse.
return llvm::None;
}
if (t->getRootGenericParam()->getDepth() > 0) {
return Type(t);
}
// If the type parameter is beyond the domain of the existential
// generic signature, ignore it.
if (!existentialSig->isValidTypeParameter(t)) {
return Type(t);
}
// If the type parameter is bound to a concrete type, recurse into it.
if (const auto concreteTy = existentialSig->getConcreteType(t)) {
const auto erasedTy = transformFn(concreteTy, currPos);
if (erasedTy.getPointer() == concreteTy.getPointer()) {
// Don't return the concrete type if we haven't type-erased
// anything inside it, or else we might inadvertently transform a
// normal metatype into an existential one.
return Type(t);
}
return erasedTy;
}
switch (currPos) {
case TypePosition::Covariant:
break;
case TypePosition::Contravariant:
case TypePosition::Invariant:
case TypePosition::Shape:
return Type(t);
}
Type erasedTy;
// The upper bounds of 'Self' is the existential base type.
if (t->is<GenericTypeParamType>()) {
erasedTy = baseTy;
} else {
erasedTy = existentialSig->getNonDependentUpperBounds(t);
}
if (metatypeDepth) {
if (const auto existential = erasedTy->getAs<ExistentialType>())
return existential->getConstraintType();
}
return erasedTy;
});
};
return transformFn(refTy, outermostPosition);
}
Type constraints::typeEraseOpenedExistentialReference(
Type type, Type existentialBaseType, TypeVariableType *openedTypeVar,
TypePosition outermostPosition) {
Type selfGP = GenericTypeParamType::get(false, 0, 0, type->getASTContext());
auto existentialSig =
type->getASTContext().getOpenedExistentialSignature(
existentialBaseType, GenericSignature());
auto selfGP = existentialSig.getGenericParams()[0];
// First, temporarily reconstitute the 'Self' generic parameter.
type = type.transformRec([&](TypeBase *t) -> llvm::Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeVariable())
return Type(t);
return typeEraseExistentialSelfReferences(
type, existentialBaseType, outermostPosition, existentialSig,
/*containsFn=*/[](Type t) {
return t->hasTypeVariable();
},
/*predicateFn=*/[](Type t) {
return t->isTypeVariableOrMember();
},
/*projectionFn=*/[&](Type t) {
bool found = false;
auto result = t.transformRec([&](Type t) -> llvm::Optional<Type> {
if (t.getPointer() == openedTypeVar) {
found = true;
return selfGP;
}
return llvm::None;
});
if (isa<TypeVariableType>(t) && t->isEqual(openedTypeVar))
return selfGP;
if (!found)
return Type();
// Recurse.
return llvm::None;
});
assert(result->isTypeParameter());
return result;
},
/*force=*/false);
}
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
type = typeEraseExistentialSelfReferences(type, existentialBaseType,
outermostPosition);
Type constraints::typeEraseOpenedArchetypesWithRoot(
Type type, const OpenedArchetypeType *root) {
assert(root->isRoot() && "Expected a root archetype");
// Finally, swap the 'Self'-corresponding type variable back in.
return type.transformRec([&](TypeBase *t) -> llvm::Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeParameter())
return Type(t);
auto *env = root->getGenericEnvironment();
auto sig = env->getGenericSignature();
if (isa<GenericTypeParamType>(t) && t->isEqual(selfGP))
return Type(openedTypeVar);
return typeEraseExistentialSelfReferences(
type, root->getExistentialType(), TypePosition::Covariant, sig,
/*containsFn=*/[](Type t) {
return t->hasOpenedExistential();
},
/*predicateFn=*/[](Type t) {
return t->is<OpenedArchetypeType>();
},
/*projectionFn=*/[&](Type t) {
auto *openedTy = t->castTo<OpenedArchetypeType>();
if (openedTy->getGenericEnvironment() == env)
return openedTy->getInterfaceType();
// Recurse.
return llvm::None;
});
return Type();
},
/*force=*/true);
}
Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
@@ -2354,14 +2382,6 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
baseObjTy->isExistentialType() && outerDC->getSelfProtocolDecl() &&
// If there are no type variables, there were no references to 'Self'.
type->hasTypeVariable()) {
auto getResultType = [](Type type) {
if (auto *funcTy = type->getAs<FunctionType>())
return funcTy->getResult();
return type;
};
auto nonErasedResultTy = getResultType(type);
const auto selfGP = cast<GenericTypeParamType>(
outerDC->getSelfInterfaceType()->getCanonicalType());
auto openedTypeVar = replacements.lookup(selfGP);
@@ -2375,19 +2395,6 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
contextualTy =
getContextualType(getParentExpr(anchor), /*forConstraint=*/false);
}
if (!hasFixFor(locator) &&
AddExplicitExistentialCoercion::isRequired(
*this, nonErasedResultTy,
[&](TypeVariableType *typeVar) {
return openedTypeVar == typeVar ? baseObjTy
: llvm::Optional<Type>();
},
locator) &&
!contextualTy) {
recordFix(AddExplicitExistentialCoercion::create(
*this, getResultType(type), locator));
}
}
// Construct an idealized parameter type of the initializer associated
@@ -2632,7 +2639,7 @@ ConstraintSystem::getTypeOfMemberReference(
} else if (baseObjTy->isExistentialType()) {
auto openedArchetype =
OpenedArchetypeType::get(baseObjTy->getCanonicalType(),
useDC->getGenericSignatureOfContext());
GenericSignature());
OpenedExistentialTypes.insert(
{getConstraintLocator(locator), openedArchetype});
baseOpenedTy = openedArchetype;