[Typechecker/OpenedExistentials] NFC: Extract some checks from canOpenExistentialCallArgument into a separate function

Some of the logic of `canOpenExistentialCallArgument` has to be
shared with constraint optimizer which does it's own validation
and most importantly - doesn't open overload choice type.

In order to share the logic between the solver and the optimizer,
let's extract checking that determines whether it's possible to
open existential argument into `canOpenExistentialAt`.
This commit is contained in:
Pavel Yaskevich
2025-08-14 13:23:57 -07:00
parent 030564b9db
commit 90224bf927
2 changed files with 31 additions and 15 deletions

View File

@@ -677,16 +677,6 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
if (!typeVar || !genericParam)
return std::nullopt;
// Only allow opening the innermost generic parameters.
auto genericContext = callee->getAsGenericContext();
if (!genericContext || !genericContext->isGeneric())
return std::nullopt;
auto genericSig = callee->getInnermostDeclContext()
->getGenericSignatureOfContext().getCanonicalSignature();
if (genericParam->getDepth() < genericSig->getMaxDepth())
return std::nullopt;
// The binding could be an existential metatype. Get the instance type for
// conformance checks and to build an opened existential signature. If the
// instance type is not an existential type, i.e., the metatype is nested,
@@ -695,6 +685,28 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
if (!existentialTy->isExistentialType())
return std::nullopt;
if (!canOpenExistentialAt(callee, paramIdx, genericParam, existentialTy))
return std::nullopt;
return std::pair(typeVar, bindingTy);
}
bool swift::canOpenExistentialAt(ValueDecl *callee, unsigned paramIdx,
GenericTypeParamType *genericParam,
Type existentialTy) {
ASSERT(existentialTy->isExistentialType());
// Only allow opening the innermost generic parameters.
auto genericContext = callee->getAsGenericContext();
if (!genericContext || !genericContext->isGeneric())
return false;
auto genericSig = callee->getInnermostDeclContext()
->getGenericSignatureOfContext()
.getCanonicalSignature();
if (genericParam->getDepth() < genericSig->getMaxDepth())
return false;
auto &ctx = callee->getASTContext();
// If the existential argument conforms to all of protocol requirements on
@@ -715,7 +727,7 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
}
if (!containsNonSelfConformance)
return std::nullopt;
return false;
}
auto existentialSig = ctx.getOpenedExistentialSignature(existentialTy);
@@ -726,10 +738,7 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
callee, existentialSig.OpenedSig, genericParam,
existentialSig.SelfType->castTo<GenericTypeParamType>(),
/*skipParamIdx=*/paramIdx);
if (referenceInfo.hasNonCovariantRef())
return std::nullopt;
return std::pair(typeVar, bindingTy);
return !referenceInfo.hasNonCovariantRef();
}
/// For each occurrence of a type **type** in `refTy` that satisfies

View File

@@ -150,6 +150,13 @@ std::optional<std::pair<TypeVariableType *, Type>>
canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
Type paramTy, Type argTy);
/// A limited form of the check performed by \c canOpenExistentialCallArgument
/// that assumes that a declaration where parameter came from, the parameter
/// itself, and the types involved have been validated already.
bool canOpenExistentialAt(ValueDecl *callee, unsigned paramIdx,
GenericTypeParamType *genericParam,
Type existentialTy);
/// Given a type that includes an existential type that has been opened to
/// the given type variable, replace the opened type variable and its member
/// types with their upper bounds.