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-14201] type checker ambiguity regarding return values. #56579

Closed
weissi opened this issue Feb 12, 2021 · 7 comments
Closed

[SR-14201] type checker ambiguity regarding return values. #56579

weissi opened this issue Feb 12, 2021 · 7 comments
Assignees
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 12, 2021

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

swift main, 9th Feb 2021

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

md5: ec593532406ed584d7e1d52315258d4c

Issue Description:

Consider this program which compiles

struct E: Error {}

struct Future<Value> {
    public func flatMap<NewValue>(_ callback: @escaping (Value) -> Future<NewValue>) -> Future<NewValue> {
        fatalError()
    }

    public func flatMapThrowing<NewValue>(_ callback: @escaping (Value) throws -> NewValue) -> Future<NewValue> {
        fatalError()
    }

    public func then<NewValue>(_ body: @escaping (Value) throws -> NewValue) -> Future<NewValue> {
        return self.flatMapThrowing(body)
    }

    public func then<NewValue>(_ body: @escaping (Value) -> Future<NewValue>) -> Future<NewValue> {
        return self.flatMap(body)
    }

    func returnFuture() -> Future<Int> {
        fatalError()
    }

    func test() -> Future<Void> {
        return self.then { value in
            self.returnFuture()
        }.then { value in
            return self.returnFuture()
        }.then { value in
            if value > 4 {
                throw E()
            } else {
                return 8
            }
        }.then { (value: Int) -> Void in
            return ()
        }
    }
}

if at the very bottom, I change {{ (value: Int) -> Void }} to {{ value -> Void }}, then the compiler can no longer resolve the ambiguity and complains

test.swift:35:11: error: type of expression is ambiguous without more context
        }.then { value -> Void in
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~

but I believe that should work.

The failing program

struct E: Error {}

struct Future<Value> {
    public func flatMap<NewValue>(_ callback: @escaping (Value) -> Future<NewValue>) -> Future<NewValue> {
        fatalError()
    }

    public func flatMapThrowing<NewValue>(_ callback: @escaping (Value) throws -> NewValue) -> Future<NewValue> {
        fatalError()
    }

    public func then<NewValue>(_ body: @escaping (Value) throws -> NewValue) -> Future<NewValue> {
        return self.flatMapThrowing(body)
    }

    public func then<NewValue>(_ body: @escaping (Value) -> Future<NewValue>) -> Future<NewValue> {
        return self.flatMap(body)
    }

    func returnFuture() -> Future<Int> {
        fatalError()
    }

    func test() -> Future<Void> {
        return self.then { value in
            self.returnFuture()
        }.then { value in
            return self.returnFuture()
        }.then { value in
            if value > 4 {
                throw E()
            } else {
                return 8
            }
        }.then { value -> Void in
            return ()
        }
    }
}
@typesanitizer
Copy link

@swift-ci create

@LucianoPAlmeida
Copy link
Collaborator

Interestingly both cases bellow type checks correctly ...

func test() -> Future<Void> {
  return self.then { value in
      self.returnFuture()
  }.then { value in
       return self.returnFuture()
  }.then { value -> Int in
     if value > 4 {
       throw E()
     } else {
        return 8
     }
  }.then { value in
     return ()
  }
}

func test() -> Future<Void> {
   return self.then { value in
      self.returnFuture()
   }.then { value in
      return self.returnFuture()
   }.then { value in
      return 8
   }.then { value in
      return ()
   }
}

so this can maybe indicate that the issue maybe related to how contextual information is propagated from the previous closure in this case...

@xedin
Copy link
Member

xedin commented Feb 17, 2021

Unfortunately this is just a consequence multi-statement closure bodies not participating in the type-check (which was a deliberate decision at the time due to performance), so there is no way for the solver to pick a "right" overload for one-before-last `then` since there is no way to tell what type the body is going to produce unless it's actually type-checker together with enclosing context.

@LucianoPAlmeida
Copy link
Collaborator

@xedin I wonder if we could keep this open/or maybe open another to improve diagnostics?
Swift 5.3 produces a better diagnostics for this case

    func test() -> Future<Void> {
        return self.then { value in
            self.returnFuture()
        }.then { value in
            return self.returnFuture()
        }.then { value in // error: Unable to infer complex closure return type; add explicit type to disambiguate
            if value > 4 {
                throw E()
            } else {
                return 8
            }
        }.then { value -> Void in
            return ()
        }
    }

@weissi
Copy link
Member Author

weissi commented Feb 18, 2021

@xedin I also think we should keep this open. It’s a deficiency of the current algorithm that we decided not to fix right now which is unfortunate but understandable. Would you agree with this?

@xedin
Copy link
Member

xedin commented Feb 18, 2021

Let's just dupe it to one of the open ones in the list https://bugs.swift.org/browse/SR-2033?jql=text%20~%20%22multi-statement%22. SR-2033 IMO is the closest one here since it's the same problem where an incorrect choice gets picked up and incorrectly diagnosed.

@xedin
Copy link
Member

xedin commented Feb 18, 2021

It'd be great to organize these a bit too instead of keeping so many duplicates open :/

@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

4 participants