mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #69950 from slavapestov/clean-up-open-existentials
Cleanups and fixes for opened existentials
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user