Handle any Error and Never thrown error types during runtime uniquing.

When forming runtime metadata for a function type that has either `any
Error` or `Never` as the specified thrown error type, drop the thrown
error type and normalize down to (untyped) `throws` or non-throwing,
as appropriate.
This commit is contained in:
Doug Gregor
2023-10-29 19:29:09 -07:00
parent a5f41ce266
commit 1b42ca3673
2 changed files with 73 additions and 1 deletions

View File

@@ -1565,6 +1565,39 @@ swift::swift_getFunctionTypeMetadataGlobalActor(
return &FunctionTypes.getOrInsert(key).first->Data;
}
extern "C" const EnumDescriptor NOMINAL_TYPE_DESCR_SYM(s5NeverO);
extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
namespace {
/// Classification for a given thrown error type.
enum class ThrownErrorClassification {
/// An arbitrary thrown error.
Arbitrary,
/// 'Never', which means a function type is non-throwing.
Never,
/// 'any Error', which means the function type uses untyped throws.
AnyError,
};
/// Classify a thrown error type.
ThrownErrorClassification classifyThrownError(const Metadata *type) {
if (auto enumMetadata = dyn_cast<EnumMetadata>(type)) {
if (enumMetadata->getDescription() == &NOMINAL_TYPE_DESCR_SYM(s5NeverO))
return ThrownErrorClassification::Never;
} else if (auto existential = dyn_cast<ExistentialTypeMetadata>(type)) {
auto protocols = existential->getProtocols();
if (protocols.size() == 1 &&
!protocols[0].isObjC() &&
protocols[0].getSwiftProtocol() == &PROTOCOL_DESCR_SYM(s5Error) &&
!existential->isClassBounded() &&
!existential->isObjC())
return ThrownErrorClassification::AnyError;
}
return ThrownErrorClassification::Arbitrary;
}
}
const FunctionTypeMetadata *
swift::swift_getExtendedFunctionTypeMetadata(
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
@@ -1573,6 +1606,31 @@ swift::swift_getExtendedFunctionTypeMetadata(
ExtendedFunctionTypeFlags extFlags, const Metadata *thrownError) {
assert(flags.hasExtendedFlags() || extFlags.getIntValue() == 0);
assert(flags.hasExtendedFlags() || thrownError == nullptr);
if (thrownError) {
// Perform adjustments based on the given thrown error.
switch (classifyThrownError(thrownError)){
case ThrownErrorClassification::Arbitrary:
// Nothing to do.
break;
case ThrownErrorClassification::Never:
// The thrown error was 'Never', so make this a non-throwing function
flags = flags.withThrows(false);
// Fall through to clear out the error.
SWIFT_FALLTHROUGH;
case ThrownErrorClassification::AnyError:
// Clear out the thrown error and extended flags.
thrownError = nullptr;
extFlags = extFlags.withTypedThrows(false);
if (extFlags.getIntValue() == 0)
flags = flags.withExtendedFlags(false);
break;
}
}
FunctionCacheEntry::Key key = {
flags, diffKind, parameters,
reinterpret_cast<const ParameterFlags *>(parameterFlags), result,

View File

@@ -526,12 +526,26 @@ enum MyBigError: Error {
case epicFail
}
@available(SwiftStdlib 5.11, *)
func getFnTypeWithThrownError<E: Error>(_: E.Type) -> Any.Type {
typealias Fn = (Int) throws(E) -> Void
return Fn.self
}
if #available(SwiftStdlib 5.11, *) {
DemangleToMetadataTests.test("typed throws") {
typealias Fn = (Int) throws(MyBigError) -> Void
expectEqual("ySi4main10MyBigErrorOYKc", _mangledTypeName(Fn.self)!)
print("Looking up the typed throws... \(_typeByName("ySi4main10MyBigErrorOYKc"))")
expectEqual(Fn.self, _typeByName("ySi4main10MyBigErrorOYKc")!)
expectEqual(getFnTypeWithThrownError(MyBigError.self), _typeByName("ySi4main10MyBigErrorOYKc")!)
// throws(any Error) -> throws
expectEqual(getFnTypeWithThrownError((any Error).self), _typeByName("ySiKc")!)
// throws(Never) -> non-throwing
expectEqual(getFnTypeWithThrownError(Never.self), _typeByName("ySic")!)
}
}