Experimental support for implicitly opening existential arguments.

When calling a generic function with an argument of existential type,
implicitly "open" the existential type into a concrete archetype, which
can then be bound to the generic type. This extends the implicit
opening that is performed when accessing a member of an existential
type from the "self" parameter to all parameters. For example:

    func unsafeFirst<C: Collection>(_ c: C) -> C.Element { c.first! }

    func g(c: any Collection) {
      unsafeFirst(c)   // currently an error
                       // with this change, succeeds and produces an 'Any'
    }

This avoids many common sources of errors of the form

    protocol 'P' as a type cannot conform to the protocol itself

which come from calling generic functions with an existential, and
allows another way "out" if one has an existention and needs to treat
it generically.

This feature is behind a frontend flag
`-enable-experimental-opened-existential-types`.
This commit is contained in:
Doug Gregor
2022-02-18 09:06:16 -08:00
parent b3b8fa50c3
commit 1e1b3427c3
8 changed files with 209 additions and 58 deletions

View File

@@ -573,6 +573,15 @@ ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
}
std::pair<Type, OpenedArchetypeType *> ConstraintSystem::openExistentialType(
Type type, ConstraintLocator *locator) {
OpenedArchetypeType *opened = nullptr;
Type result = type->openAnyExistentialType(opened);
assert(OpenedExistentialTypes.count(locator) == 0);
OpenedExistentialTypes.insert({locator, opened});
return {result, opened};
}
/// Extend the given depth map by adding depths for all of the subexpressions
/// of the given expression.
static void extendDepthMap(
@@ -1847,6 +1856,40 @@ static Type typeEraseCovariantExistentialSelfReferences(Type refTy,
return transformFn(refTy, TypePosition::Covariant);
}
Type constraints::typeEraseOpenedExistentialReference(
Type type, Type existentialBaseType, TypeVariableType *openedTypeVar) {
Type selfGP = GenericTypeParamType::get(false, 0, 0, type->getASTContext());
// First, temporarily reconstitute the 'Self' generic parameter.
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeVariable())
return Type(t);
if (isa<TypeVariableType>(t) && t->isEqual(openedTypeVar))
return selfGP;
// Recurse.
return None;
});
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
type = typeEraseCovariantExistentialSelfReferences(type, existentialBaseType);
// Finally, swap the 'Self'-corresponding type variable back in.
return type.transformRec([&](TypeBase *t) -> Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeParameter())
return Type(t);
if (isa<GenericTypeParamType>(t) && t->isEqual(selfGP))
return Type(openedTypeVar);
// Recurse.
return None;
});
}
std::pair<Type, Type>
ConstraintSystem::getTypeOfMemberReference(
Type baseTy, ValueDecl *value, DeclContext *useDC,
@@ -2119,35 +2162,8 @@ ConstraintSystem::getTypeOfMemberReference(
type->hasTypeVariable()) {
const auto selfGP = cast<GenericTypeParamType>(
outerDC->getSelfInterfaceType()->getCanonicalType());
// First, temporarily reconstitute the 'Self' generic parameter.
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeVariable())
return Type(t);
if (isa<TypeVariableType>(t) && t->isEqual(replacements.lookup(selfGP)))
return selfGP;
// Recurse.
return None;
});
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
type = typeEraseCovariantExistentialSelfReferences(type, baseObjTy);
// Finally, swap the 'Self'-corresponding type variable back in.
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
// Don't recurse into children unless we have to.
if (!type->hasTypeParameter())
return Type(t);
if (isa<GenericTypeParamType>(t) && t->isEqual(selfGP))
return Type(replacements.lookup(selfGP));
// Recurse.
return None;
});
auto openedTypeVar = replacements.lookup(selfGP);
type = typeEraseOpenedExistentialReference(type, baseObjTy, openedTypeVar);
}
// Construct an idealized parameter type of the initializer associated