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

@@ -1650,8 +1650,7 @@ AbstractionPattern::getFunctionParamFlags(unsigned index) const {
}
bool
AbstractionPattern::isFunctionParamAddressable(TypeConverter &TC,
unsigned index) const {
AbstractionPattern::isFunctionParamAddressable(unsigned index) const {
switch (getKind()) {
case Kind::Invalid:
case Kind::Tuple:
@@ -1661,7 +1660,7 @@ AbstractionPattern::isFunctionParamAddressable(TypeConverter &TC,
case Kind::OpaqueDerivativeFunction:
// If the function abstraction pattern is completely opaque, assume we
// may need to preserve the address for dependencies.
return true;
return false;
case Kind::ClangType:
case Kind::ObjCCompletionHandlerArgumentsType:
@@ -1685,29 +1684,47 @@ AbstractionPattern::isFunctionParamAddressable(TypeConverter &TC,
auto fnTy = cast<AnyFunctionType>(getType());
// The parameter might directly be marked addressable.
if (fnTy.getParams()[index].getParameterFlags().isAddressable()) {
return true;
}
return fnTy.getParams()[index].getParameterFlags().isAddressable();
}
}
llvm_unreachable("bad kind");
}
ArrayRef<LifetimeDependenceInfo>
AbstractionPattern::getLifetimeDependencies() const {
switch (getKind()) {
case Kind::Invalid:
case Kind::Tuple:
llvm_unreachable("not any kind of function!");
case Kind::Opaque:
case Kind::OpaqueFunction:
case Kind::OpaqueDerivativeFunction:
// If the function abstraction pattern is completely opaque, assume we
// may need to preserve the address for dependencies.
return {};
case Kind::ClangType:
case Kind::ObjCCompletionHandlerArgumentsType:
case Kind::CurriedObjCMethodType:
case Kind::PartialCurriedObjCMethodType:
case Kind::ObjCMethodType:
case Kind::CFunctionAsMethodType:
case Kind::CurriedCFunctionAsMethodType:
case Kind::PartialCurriedCFunctionAsMethodType:
case Kind::CXXMethodType:
case Kind::CurriedCXXMethodType:
case Kind::PartialCurriedCXXMethodType:
case Kind::Type:
case Kind::Discard: {
auto type = getType();
// The parameter could be of a type that is addressable for dependencies,
// in which case it becomes addressable when a return has a scoped
// dependency on it.
for (auto &dep : fnTy->getLifetimeDependencies()) {
auto scoped = dep.getScopeIndices();
if (!scoped) {
continue;
}
if (scoped->contains(index)) {
auto paramTy = getFunctionParamType(index);
return TC.getTypeLowering(paramTy, paramTy.getType(),
TypeExpansionContext::minimal())
.getRecursiveProperties().isAddressableForDependencies();
}
if (type->isTypeParameter() || type->is<ArchetypeType>()) {
return {};
}
return false;
auto fnTy = cast<AnyFunctionType>(getType());
return fnTy->getExtInfo().getLifetimeDependencies();
}
}
llvm_unreachable("bad kind");