Skip to content

Imported enum from NS_CLOSED_ENUM shouldn't allow arbitrary raw value #85701

@an0

Description

@an0

Description

I could understand that

Enumerations imported using the NS_ENUM macro won’t fail when you initialize one with a raw value that does not correspond to an enumeration case. This characteristic facilitates compatibility with C, which allows any value to be stored in an enumeration, including values used internally but not exposed in headers.

However, we should not allow that for NS_CLOSED_ENUM because it is unsafe and causes unexpected crashes.

For example:

typedef NS_ENUM(NSInteger, IntEnum) {
    IntEnumZero,
    IntEnumOne,
};

Since it is imported as non-frozen enum so I have to handle @unknown default anyway.

if let ie = IntEnum(rawValue: 100) {
    switch ie {
    case .zero:
        print("zero")
    case .one:
        print("one")
    @unknown default:
        print("unknown")
    }
}

However, if I define it as NS_CLOSED_ENUM:

typedef NS_CLOSED_ENUM(NSInteger, IntEnum) {
    IntEnumZero,
    IntEnumOne,
};

It should be imported as frozen enum and only those raw values declared in the enum definition should convert successfully to the enum. Unfortunately, that’s not the case and it causes crash:

if let ie = IntEnum(rawValue: 100) {
    switch ie {
    case .zero:
        print("zero")
    case .one: // Thread 1: Fatal error: unexpected enum case 'IntEnum(rawValue: 100)'
        print("one")
    }
}

Reproduction

typedef NS_CLOSED_ENUM(NSInteger, IntEnum) {
    IntEnumZero,
    IntEnumOne,
};
if let ie = IntEnum(rawValue: 100) {
    switch ie {
    case .zero:
        print("zero")
    case .one: // Thread 1: Fatal error: unexpected enum case 'IntEnum(rawValue: 100)'
        print("one")
    }
}

Stack dump

#0	0x0000000193dab0dc in _swift_runtime_on_report ()
#1	0x0000000193e91d08 in _swift_stdlib_reportFatalError ()
#2	0x0000000193ec398c in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, flags: Swift.UInt32) -> Swift.Never ()
#3	0x0000000193e9f378 in _assertionFailure ()
#4	0x0000000193ec4734 in _diagnoseUnexpectedEnumCaseValue ()

Expected behavior

IntEnum(rawValue: 100) returns nil

Environment

swift-driver version: 1.127.14.1 Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1)

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.crashBug: A crash, i.e., an abnormal termination of softwaretriage neededThis issue needs more specific labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions