Skip to content

Commit 1b42ca3

Browse files
committed
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.
1 parent a5f41ce commit 1b42ca3

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,39 @@ swift::swift_getFunctionTypeMetadataGlobalActor(
15651565
return &FunctionTypes.getOrInsert(key).first->Data;
15661566
}
15671567

1568+
extern "C" const EnumDescriptor NOMINAL_TYPE_DESCR_SYM(s5NeverO);
1569+
extern "C" const ProtocolDescriptor PROTOCOL_DESCR_SYM(s5Error);
1570+
1571+
namespace {
1572+
/// Classification for a given thrown error type.
1573+
enum class ThrownErrorClassification {
1574+
/// An arbitrary thrown error.
1575+
Arbitrary,
1576+
/// 'Never', which means a function type is non-throwing.
1577+
Never,
1578+
/// 'any Error', which means the function type uses untyped throws.
1579+
AnyError,
1580+
};
1581+
1582+
/// Classify a thrown error type.
1583+
ThrownErrorClassification classifyThrownError(const Metadata *type) {
1584+
if (auto enumMetadata = dyn_cast<EnumMetadata>(type)) {
1585+
if (enumMetadata->getDescription() == &NOMINAL_TYPE_DESCR_SYM(s5NeverO))
1586+
return ThrownErrorClassification::Never;
1587+
} else if (auto existential = dyn_cast<ExistentialTypeMetadata>(type)) {
1588+
auto protocols = existential->getProtocols();
1589+
if (protocols.size() == 1 &&
1590+
!protocols[0].isObjC() &&
1591+
protocols[0].getSwiftProtocol() == &PROTOCOL_DESCR_SYM(s5Error) &&
1592+
!existential->isClassBounded() &&
1593+
!existential->isObjC())
1594+
return ThrownErrorClassification::AnyError;
1595+
}
1596+
1597+
return ThrownErrorClassification::Arbitrary;
1598+
}
1599+
}
1600+
15681601
const FunctionTypeMetadata *
15691602
swift::swift_getExtendedFunctionTypeMetadata(
15701603
FunctionTypeFlags flags, FunctionMetadataDifferentiabilityKind diffKind,
@@ -1573,6 +1606,31 @@ swift::swift_getExtendedFunctionTypeMetadata(
15731606
ExtendedFunctionTypeFlags extFlags, const Metadata *thrownError) {
15741607
assert(flags.hasExtendedFlags() || extFlags.getIntValue() == 0);
15751608
assert(flags.hasExtendedFlags() || thrownError == nullptr);
1609+
1610+
if (thrownError) {
1611+
// Perform adjustments based on the given thrown error.
1612+
switch (classifyThrownError(thrownError)){
1613+
case ThrownErrorClassification::Arbitrary:
1614+
// Nothing to do.
1615+
break;
1616+
1617+
case ThrownErrorClassification::Never:
1618+
// The thrown error was 'Never', so make this a non-throwing function
1619+
flags = flags.withThrows(false);
1620+
1621+
// Fall through to clear out the error.
1622+
SWIFT_FALLTHROUGH;
1623+
1624+
case ThrownErrorClassification::AnyError:
1625+
// Clear out the thrown error and extended flags.
1626+
thrownError = nullptr;
1627+
extFlags = extFlags.withTypedThrows(false);
1628+
if (extFlags.getIntValue() == 0)
1629+
flags = flags.withExtendedFlags(false);
1630+
break;
1631+
}
1632+
}
1633+
15761634
FunctionCacheEntry::Key key = {
15771635
flags, diffKind, parameters,
15781636
reinterpret_cast<const ParameterFlags *>(parameterFlags), result,

test/Runtime/demangleToMetadata.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,12 +526,26 @@ enum MyBigError: Error {
526526
case epicFail
527527
}
528528

529+
@available(SwiftStdlib 5.11, *)
530+
func getFnTypeWithThrownError<E: Error>(_: E.Type) -> Any.Type {
531+
typealias Fn = (Int) throws(E) -> Void
532+
return Fn.self
533+
}
534+
529535
if #available(SwiftStdlib 5.11, *) {
530536
DemangleToMetadataTests.test("typed throws") {
531537
typealias Fn = (Int) throws(MyBigError) -> Void
532538
expectEqual("ySi4main10MyBigErrorOYKc", _mangledTypeName(Fn.self)!)
533-
print("Looking up the typed throws... \(_typeByName("ySi4main10MyBigErrorOYKc"))")
534539
expectEqual(Fn.self, _typeByName("ySi4main10MyBigErrorOYKc")!)
540+
541+
542+
expectEqual(getFnTypeWithThrownError(MyBigError.self), _typeByName("ySi4main10MyBigErrorOYKc")!)
543+
544+
// throws(any Error) -> throws
545+
expectEqual(getFnTypeWithThrownError((any Error).self), _typeByName("ySiKc")!)
546+
547+
// throws(Never) -> non-throwing
548+
expectEqual(getFnTypeWithThrownError(Never.self), _typeByName("ySic")!)
535549
}
536550
}
537551

0 commit comments

Comments
 (0)