swiftlang / swift

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

[SR-14859] Unexpected behavior and compiler crash related to closure args #57206

Open jepers opened 3 years ago

jepers commented 3 years ago
Previous ID SR-14859
Radar rdar://problem/80093482
Original Reporter @jepers
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: 37ce43a6293a5b7db0413eb7094bf10f

Issue Description:

As discussed here:
https://forums.swift.org/t/an-odd-error-escaping-closure-captures-mutating-self/50118/10

this compiles:

func test(_ a: () -> Void) { // Compiles, no need for it to be @escaping
    let x = a
    x()
}

while this doesn't (only added the type of x explicitly):

func test(_ a: () -> Void) {  // Error, which says `a` must be @escaping
    let x: () -> Void = a 
    x()
}

And this crashes the compiler (default toolchain of Xcode 12.5):

func test(_ a: () -> Void) {
  let b = { print("b") }
  let x = Bool.random() ? a : b
  x()
}

As noted here, the above example compiles and runs if the parameter type is annotated with `@escaping` (even though it is not actually escaping).

typesanitizer commented 3 years ago

Thanks for the minimal examples, I see the same behavior on main. The third one triggers an assertion with main.

Assertion failed: (!NodePtr->isKnownSentinel()), function operator*, file ilist_iterator.h, line 138.
<snip>
8  swift-frontend           0x000000010f5a3c5d fixupClosureLifetimes(swift::SILFunction&,
bool&, bool&) + 9741
9  swift-frontend           0x000000010f5a15db (anonymous namespace)::ClosureLifetimeFixup::run() + 43
41412bb9-c79a-4f5a-8a7f-383cb40aa74b commented 3 years ago
func test(_ a: () -> Void) {
    let x = a
    x()
}

This should be invalid. In 5.0.3, it's correctly diagnosed:

./main.swift:6:13: error: non-escaping parameter 'callback' may only be called
    let x = callback
            ^
./main.swift:5:11: note: parameter 'callback' is implicitly non-escaping
func test(_ callback: () -> Void) {
          ^
                      @escaping 
41412bb9-c79a-4f5a-8a7f-383cb40aa74b commented 3 years ago

@swift-ci create

eeckstein commented 3 years ago

@rintaro This is expected behavior: `let x = a` assigns a non-escaping closure to a local variable, which gets the same type as the argument: a non-escaping closure. Here, the 5.0.3 compiler was too restrictive.

The problem with `let x: () -> Void = a` is that the explicitly specified type means: an escaping closure. Therefore in this case an error is issued. Though I don't know if it's possible to explicitly specify non-escaping closure types for local variables. And yes, this is confusing.