Type substitution eliminates dependencies with Escapable targets.

When a generic function has potentially Escapable outputs, those outputs
declare lifetime dependencies, which have no effect when substitution
leads to those types becoming `Escapable` in a concrete context.
This means that type substitution should canonically eliminate lifetime
dependencies targeting Escapable parameters or returns, and that
type checking should allow a function value with potentially-Escapable
lifetime dependencies to bind to a function type without those dependencies
when the target of the dependencies is Escapable.

Fixes rdar://147533059.
This commit is contained in:
Joe Groff
2025-04-01 08:01:02 -07:00
parent dccbcf3cd7
commit 6b605f41cb
16 changed files with 398 additions and 56 deletions

View File

@@ -3182,8 +3182,44 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
if (!matchFunctionIsolations(func1, func2, kind, flags, locator))
return getTypeMatchFailure(locator);
// A function with a lifetime dependency in a generic context is equivalent to
// one without that lifetime dependency when the substituted type is
// Escapable.
//
// TODO: There should also be a subtype relationship from less-constrained to
// more-constrained lifetime dependencies.
if (func1->getLifetimeDependencies() != func2->getLifetimeDependencies()) {
return getTypeMatchFailure(locator);
auto escapable = getASTContext().getProtocol(KnownProtocolKind::Escapable)
->getDeclaredType();
for (auto &fromDep : func1->getLifetimeDependencies()) {
auto toDep = func2->getLifetimeDependenceFor(fromDep.getTargetIndex());
if (toDep) {
// If a dependency is present for the same target in both types, then
// the dependency must match.
if (fromDep != *toDep) {
return getTypeMatchFailure(locator);
}
continue;
}
// If the dependency is absent from the destination type, constrain the
// corresponding parameter or result in the source type to be Escapable.
if (fromDep.getTargetIndex() == func1->getParams().size()) {
// Result dependency.
addConstraint(ConstraintKind::ConformsTo,
func1->getResult(),
escapable,
locator);
} else {
// Parameter dependency.
addConstraint(ConstraintKind::ConformsTo,
func1->getParams()[fromDep.getTargetIndex()].getPlainType(),
escapable,
locator);
}
}
}
// To contextual type increase the score to avoid ambiguity when solver can