Skip to content

Conformance to RawRepresentable prevents static members from being initialized #81575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
lufinkey opened this issue May 16, 2025 · 2 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels

Comments

@lufinkey
Copy link

lufinkey commented May 16, 2025

Description

I have a short piece of code here that works perfectly when it doesn't conform to RawRepresentable. However, if it conforms to RawRepresentable, it crashes with EXC_BREAKPOINT when accessing the static member dictionary (when .rawValue is called). Basically seems like the static member is never getting initialized.

I tested this in the swift playground.

Reproduction

enum NormalEnum: RawRepresentable {
	case blue
	
	private static let mapping: [Self: String] = [
		.blue: "helloworld"
	]
	
	var rawValue: String {
		return Self.mapping[self] ?? "what" // fallback here just to be sure it isn't crashing from nil unwrap
	}
	
	init(rawValue: String) {
		if rawValue == "helloworld" {
			self = .blue
		} else {
			fatalError("wrong rawValue")
		}
	}
}

print("blue = \(NormalEnum.blue.rawValue)")

Expected behavior

The program should print blue = helloworld and not crash

Environment

swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3)
Target: arm64-apple-macosx15.0

Additional information

No response

@lufinkey lufinkey added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels labels May 16, 2025
@lufinkey
Copy link
Author

I just checked, and this also happens in a normal NSApplication. I added the print line in my applicationDidFinishLaunching function to test it.

@tbkka
Copy link
Contributor

tbkka commented May 16, 2025

If you run this under LLDB and look at the backtrace, you'll see something interesting:

  * frame #0: 0x00000001825a8c28 libdispatch.dylib`_dispatch_once_wait.cold.1 + 200
    frame #1: 0x0000000182575c48 libdispatch.dylib`_dispatch_once_wait + 60
    frame #2: 0x0000000100000d68 enum_raw`NormalEnum.mapping.unsafeMutableAddressor at enum_raw.swift:6:21
    frame #3: 0x0000000100000ab4 enum_raw`NormalEnum.rawValue.getter() at enum_raw.swift:11:15
    frame #5: 0x0000000195c08f30 libswiftCore.dylib`Swift.RawRepresentable< where τ_0_0: Swift.Hashable, τ_0_0.Swift.RawRepresentable.RawValue: Swift.Hashable>.hash(into: inout Swift.Hasher) -> () + 212
    frame #7: 0x0000000195bd3b1c libswiftCore.dylib`Swift.RawRepresentable< where τ_0_0: Swift.Hashable, τ_0_0.Swift.RawRepresentable.RawValue: Swift.Hashable>._rawHashValue(seed: Swift.Int) -> Swift.Int + 160
    frame #9: 0x0000000195c475ac libswiftCore.dylib`Swift.__RawDictionaryStorage.find<τ_0_0 where τ_0_0: Swift.Hashable>(τ_0_0) -> (bucket: Swift._HashTable.Bucket, found: Swift.Bool) + 60
    frame #10: 0x0000000195bd408c libswiftCore.dylib`Swift.Dictionary.init(dictionaryLiteral: (τ_0_0, τ_0_1)...) -> Swift.Dictionary<τ_0_0, τ_0_1> + 772
    frame #11: 0x0000000100000d08 enum_raw`one-time initialization function for mapping at enum_raw.swift:6:47
    frame #12: 0x000000018258da48 libdispatch.dylib`_dispatch_client_callout + 16
    frame #13: 0x0000000182576a60 libdispatch.dylib`_dispatch_once_callout + 32
    frame #14: 0x0000000100000d68 enum_raw`NormalEnum.mapping.unsafeMutableAddressor at enum_raw.swift:6:21
    frame #15: 0x0000000100000ab4 enum_raw`NormalEnum.rawValue.getter() at enum_raw.swift:11:15
    frame #16: 0x0000000100000990 enum_raw`main at enum_raw.swift:23:33
    frame #17: 0x0000000182379b5c dyld`start + 6904

Reading from the bottom, you see that someone calls rawValue.getter() to get the raw value, which triggers the construction of the static property. But the static property needs to hash the value .blue in order to build the dictionary, which requires getting the raw value. This crashes because it's impossible: You can't have the dictionary depend on the raw value if the raw value calculation requires a dictionary.

There are a few ways you might rework this to use the same basic idea but without the recursive deadlock.

But no, this does not appear to be a bug in Swift.

@tbkka tbkka closed this as not planned Won't fix, can't repro, duplicate, stale May 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels
Projects
None yet
Development

No branches or pull requests

2 participants