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-12276] bad diagnostics, suggests to make a function that returns something return Void instead #54704

Closed
weissi opened this issue Feb 26, 2020 · 6 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself type checker Area → compiler: Semantic analysis

Comments

@weissi
Copy link
Member

weissi commented Feb 26, 2020

Previous ID SR-12276
Radar rdar://problem/59802835
Original Reporter @weissi
Type Bug
Status Resolved
Resolution Won't Do

Attachment: Download

Environment
$ jw-docker-swift-master swift -version
docker.io/swiftlang/swift:nightly-master-bionic
Swift version 5.2-dev (LLVM 4cd92ac2f9, Swift 79716efbee)
Target: x86_64-unknown-linux-gnu
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, TypeChecker
Assignee None
Priority Medium

md5: e2c939f054f2050f8284756ea08b3794

Issue Description:

I have the following code (full SwiftPM package attached):

import NIO

struct Thing {}


    func bar(eventLoop: EventLoop, process: @escaping (Thing) -> EventLoopFuture<Void>) -> EventLoopFuture<Void> {

        func doIt(thingsLeft: ArraySlice<Thing>) -> EventLoopFuture<Void> {
            var thingsLeft = thingsLeft
            guard let first = thingsLeft.popFirst() else {
                return eventLoop.makeSucceededFuture(())
            }

            return process(first).flatMap { [thingsLeft] in
                return eventLoop.scheduleTask(in: .milliseconds(100)) { () -> EventLoopFuture<Void> in
                    return doIt(thingsLeft: thingsLeft)
                }.futureResult /* <=== actual problem, needs a .flatMap { $0 }
            }
        }

        return doIt(thingsLeft: [][...])
    }

what Swift suggests to do is

/private/tmp/test_package_cwcErB/Sources/TestApp/main.swift:16:79: error: declared closure result 'EventLoopFuture<Void>' is incompatible with contextual type 'Void'
                return eventLoop.scheduleTask(in: .milliseconds(100)) { () -> EventLoopFuture<Void> in
                                                                              ^~~~~~~~~~~~~~~~~~~~~
                                                                              Void

which really doesn't help much.

Repro that doesn't depend on NIO:

enum Time {
    case milliseconds(Int)
}
struct Thing {}
struct Scheduled<T> {
    let futureResult: EventLoopFuture<T>
}
struct EventLoop {
    func makeSucceededFuture<T>(_ v: T) -> EventLoopFuture<T> {
        fatalError()
    }
    func scheduleTask<T>(in t: Time, _ body: @escaping () -> T) -> Scheduled<T> {
        fatalError()
    }
}
struct EventLoopFuture<T> {
    func flatMap<V>(_ body: @escaping (T) -> EventLoopFuture<V>) -> EventLoopFuture<V> {
        fatalError()
    }
}


func bar(eventLoop: EventLoop, process: @escaping (Thing) -> EventLoopFuture<Void>) -> EventLoopFuture<Void> {
    func doIt(thingsLeft: ArraySlice<Thing>) -> EventLoopFuture<Void> {
        var thingsLeft = thingsLeft
        guard let first = thingsLeft.popFirst() else {
            return eventLoop.makeSucceededFuture(())
        }

        return process(first).flatMap { [thingsLeft] in
            return eventLoop.scheduleTask(in: .milliseconds(100)) { () -> EventLoopFuture<Void> in
                return doIt(thingsLeft: thingsLeft)
            }.futureResult
        }
    }
    return doIt(thingsLeft: [][...])
}
@weissi
Copy link
Member Author

weissi commented Feb 26, 2020

@swift-ci create

@xedin
Copy link
Member

xedin commented Feb 27, 2020

@weissi Unfortunately I don't think that solver would be able to figure out that something is missing. Current diagnostic seems reasonable based on the code "as written" because contextual type is considered to be always correct, which means it's going to be propagated down that's why we produce diagnostic aboid `Void` vs. `EventLoopFuture<Void>` for the closure result.

@weissi
Copy link
Member Author

weissi commented Feb 27, 2020

@xedin hmm, but shouldn't a manually added type signature overrule the contextual information? I specifically added () -> EventLoopFuture<Void> into the code to get a better message (which totally didn't work) 🙂

@xedin
Copy link
Member

xedin commented Feb 27, 2020

@weissi It doesn't actually because contextual type applies only to the closure context, result type effectively becomes contextual for the "body" expression. That's why diagnostic talks about `Void` (from `return` contextual type) an `EventLoopFuture<Void>` (result of the closure), everything else inferred types correctly, expect that and only mismatch...

@weissi
Copy link
Member Author

weissi commented Feb 27, 2020

I see

@xedin
Copy link
Member

xedin commented Feb 27, 2020

I'm going to resolve this issue as `Won't Do` since we already do as much as possible to get this issue diagnosed.

@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 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

2 participants