swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.53k stars 10.35k forks source link

[SR-12108] Array.init in flatMap crash: Unrecognized selector #54544

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-12108
Radar rdar://problem/58999154
Original Reporter Banannzza (JIRA User)
Type Bug

Attachment: Download

Environment XCode 11.3 11.5
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, RunTimeCrash | |Assignee | None | |Priority | Medium | md5: c3a241e803036ffde4a8401eae0b9c38

Issue Description:

The runtime error Unrecognized selector -[__lldb_expr_1.SomeClass count] when trying to call Sequence protocol methods

Reproduce only with Class (otherwise compiler time error)

class SomeClass {}

let value: SomeClass? = SomeClass()
let array: [SomeClass] = value.flatMap(Array.init) ?? []
print(array.count)
theblixguy commented 4 years ago

Hmm, reproduces on 5.2 too. This doesn't crash though:

let array: [SomeClass] = SomeClass?.none.flatMap(Array.init) ?? []
print(array.count)
beccadax commented 4 years ago

@swift-ci create

beccadax commented 4 years ago

-dump-ast shows that Array.init selects an underscored initializer used for array bridging:

(declref_expr type='(Array<SomeClass>.Type) -> (AnyObject) -> Array<SomeClass>' location=<stdin>:4:46 range=[<stdin>:4:46 - line:4:46] decl=Swift.(file).Array extension.init(_immutableCocoaArray:) [with (substitution_map generic_signature=<Element> (substitution Element -> SomeClass))] function_ref=unapplied)

This is a result of our lax rules for matching unlabeled parameters.

Banannzza (JIRA User), I'm not sure which initializer you were hoping Array.init would match, but in any case you're not getting the one you wanted. You should probably write this like value.flatMap { [$0] } ?? [].

swift-ci commented 4 years ago

Comment by Aleksey Ostapenko (JIRA)

brentdax (JIRA User), so is everything ok that the compiler allows me to compile it?

beccadax commented 4 years ago

I wouldn't quite say it's "okay"—it's an undesirable behavior—but it's expected given our language rules. The language allows Array.init to match against any set of argument labels; I and several others consider this to be a mis-feature, but it is a feature designed into the language and we couldn't change it without breaking source compatibility.

It's something we could perhaps fix in Swift 6 (or whatever the next version is that allows breaking changes to the language), but it will require an evolution proposal. Such a proposal has previously been discussed in this thread: https://forums.swift.org/t/require-parameter-names-when-referencing-to-functions/27048

beccadax commented 4 years ago

(Sorry for the chain-reply…)

Thinking about it a little more, we could at least warn when this construct matches an initializer whose first argument label starts with an _. The compiler knows this is a convention for "no user-serviceable parts inside", and warnings don't break source compatibility.

swift-ci commented 4 years ago

Comment by Aleksey Ostapenko (JIRA)

brentdax (JIRA User) Thanks!

theblixguy commented 4 years ago

Would there be a fix-it attached to it? If so, what would it do?

swift-ci commented 4 years ago

Comment by aaron crespo (JIRA)

Thankfully we caught this bug today in Dev before it went out to Prod. Xcode 11.5, Swift 5.2.4.