Skip to content
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

[SR-12033] Error or inconsistency in capturing variable by @autoclosure closure #54470

Closed
ktoso opened this issue Jan 15, 2020 · 7 comments
Closed
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@ktoso
Copy link
Member

ktoso commented Jan 15, 2020

Previous ID SR-12033
Radar rdar://problem/72801670
Original Reporter @ktoso
Type Bug
Status Resolved
Resolution Duplicate
Environment

Swift: 5.2-DEVELOPMENT-SNAPSHOT-2020-01-13-a

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee @LucianoPAlmeida
Priority Medium

md5: 400b29754adbf7f534b6f2d46fb3b6f2

duplicates:

  • SR-8625 Swift should warn if a closures is passed to an argument that is an autoclosure

Issue Description:

I bumped into a somewhat confusing compile error recently.
Reproducer below.

The general issue seems to come up when using a closure which is an @autoclosure parameter defined within a generic type.

Most notably it's a bit surprising that t5 works fine, so when the function itself has the generic, but the e examples all fail to compile, though they seem equivalent on what they're achieving.

struct Thing<T> {

    public static func capture(_ thunk: () -> T) -> Thing<T> {
        .init() // does not matter how implemented
    }
    public static func captureA(_ thunk: @autoclosure () -> T) -> Thing<T> {
        .init() // does not matter how implemented
    }
    public static func captureE(_ thunk: @escaping () -> T) -> Thing<T> {
        .init() // does not matter how implemented
    }
    public static func captureAE(_ thunk: @autoclosure @escaping () -> T) -> Thing<T> {
        .init() // does not matter how implemented
    }
}
struct Ok {

    public static func captureGenericFuncA<T>(_ thunk: @autoclosure () -> T) -> Thing<T> {
        .init()
    }

    func compiles() {
        var number = 1

        let t = Thing<Int>.capture {
            number
        } // ok, just a sanity check
        let t2 = Thing<Int>.captureE {
            number
        } // ok, just a sanity check

        let t3 = Thing<Int>.captureA(number) // ok

        let t4 = Self.captureGenericFuncA(number) // ok
        let t5 = Self.captureGenericFuncA { number } // ok, works fine, so why not the `e` ones below:
    }
}
struct Bad { 
    func doesNotCompile() {
        var number = 1

        let e = Thing<Int>.captureA { // equivalent to `t5` above
            number
        }
        // cannot convert value of type '@lvalue Int' to closure result type 'Int'
        //        }
        //        ^

        let e2 = Thing<Int>.captureAE {
            number
        }
        // cannot convert value of type '@lvalue Int' to closure result type 'Int'
        //        }
        //        ^
    }
}

Or is there something I'm missing here?

@ktoso
Copy link
Member Author

ktoso commented Jan 15, 2020

(for the time being simply avoiding @autoclosures at all so I can move forward, though the confusion was real)

@beccadax
Copy link
Contributor

@swift-ci create

@xedin
Copy link
Member

xedin commented Jan 6, 2021

This is just a solver bug related to binding ordering. I think both cases here should report the same diagnostic, just like `captureA` now does:

error: trailing closure passed to parameter of type 'Int' that does not accept a closure
        let e = Thing<Int>.captureA { // equivalent to `t5` above
                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: 'captureA' declared here
    public static func captureA(_ thunk: @autoclosure () -> T) -> Thing<T> {
                       ^

The problem is that there are two bindings for the non-generic case - `Int` (`T` of `Thing` gets inferred early) and default `() -> $T` but in generic case there is only one - `() -> $T` since parameter is not yet resolved. The fix here would be to prevent inferring `() -> $T` for autoclosure arguments.

luciano (JIRA User) Do you want to take a look at this one?

@LucianoPAlmeida
Copy link
Collaborator

Sure @xedin =]

@xedin
Copy link
Member

xedin commented Jan 7, 2021

Great![]( Thank you)

@LucianoPAlmeida
Copy link
Collaborator

Resolved by #35503
Thank you @xedin, I've learned a lot working on this =]

@ktoso can you please verify on the next available snapshot and close? Thanks

@xedin
Copy link
Member

xedin commented Feb 17, 2021

Changes had to be reverted due to source compatibility problems.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
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. compiler The Swift compiler in itself
Projects
None yet
Development

No branches or pull requests

4 participants