[Runtime] Check function types against suppressible protocols

Form a set of suppressed protocols for a function type based on
the extended flags (where future compilers can start recording
suppressible protocols) and the existing "noescape" bit. Compare
that against the "ignored" suppressible protocol requirements, as we
do for other types.

This involves a behavior change if any client has managed to evade the
static checking for noescape function types, but it's unlikely that
existing code has done so (and it was unsafe anyway).
This commit is contained in:
Doug Gregor
2024-03-21 23:01:24 -07:00
parent 5b020068c5
commit 11774e5d17
6 changed files with 155 additions and 3 deletions

View File

@@ -1673,7 +1673,43 @@ checkSuppressibleRequirementsStructural(const Metadata *type,
}
case MetadataKind::Function: {
// FIXME: Implement me
auto functionMetadata = cast<FunctionTypeMetadata>(type);
// Determine the set of protocols that are suppressed by the function
// type.
SuppressibleProtocolSet suppressed;
if (functionMetadata->hasExtendedFlags()) {
suppressed = functionMetadata->getExtendedFlags()
.getSuppressedProtocols();
}
// Map the existing "noescape" bit as a suppressed protocol, when
// appropriate.
switch (functionMetadata->getConvention()) {
case FunctionMetadataConvention::Swift:
// Swift function types can be non-escaping, so honor the bit.
if (!functionMetadata->isEscaping())
suppressed.insert(SuppressibleProtocolKind::Escapable);
break;
case FunctionMetadataConvention::Block:
// Objective-C block types don't encode non-escaping-ness in metadata,
// so we assume that they are always escaping.
break;
case FunctionMetadataConvention::Thin:
case FunctionMetadataConvention::CFunctionPointer:
// Thin and C function pointers have no captures, so whether they
// escape is irrelevant.
break;
}
auto missing = suppressed - ignored;
if (!missing.empty()) {
return TYPE_LOOKUP_ERROR_FMT(
"function type missing suppressible protocols %x", missing.rawBits());
}
return std::nullopt;
}