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-9860] Code that compiles in Xcode 10.1/Swift 4.2 results in ambiguous errors in Xcode 10.2-beta/Swift 4.2 #52267

Open
swift-ci opened this issue Feb 4, 2019 · 3 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself regression swift 5.0 type checker Area → compiler: Semantic analysis

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Feb 4, 2019

Previous ID SR-9860
Radar rdar://problem/47787245
Original Reporter jlew (JIRA User)
Type Bug
Environment

XCode 10.2-beta, iOS 12 project in Swift 4.2 compatibility mode.

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, 5.0Regression, TypeChecker
Assignee None
Priority Medium

md5: 0230e8e1cc86273b5d6c26cf93b400dc

Issue Description:

The code below compiles fine in 10.1/4.2 but fails to compile in 10.2/4.2 (or 5). Possibly relates to SR-9785, but the situation is different enough that I thought this might be valuable to have.

NOTE 1 (see code comment below): When converting this same code from Swift 3 to Swift 4 a while back, if I recall correctly, I had to explicitly "tupelize" the callback argument in the function signature so it went from f: (Arg1) -> T to f: ((Arg1)) -> T. It doesn't work either way in the Xcode 10.2.

enum Err: Error {
    case notimpl
}
func resolve<T>(type: T.Type) throws -> T {
    throw Err.notimpl
}
func resolve<T>(f: () -> T) throws -> T {
    throw Err.notimpl
}
// NOTE 1 (see above)
func resolve<T,Arg1>(f: ((Arg1)) -> T) throws -> T {
    return f(try resolve(type:Arg1.self))
}
func resolve<T,Arg1,Arg2>(f: (Arg1,Arg2) -> T) throws -> T {
    return f(try resolve(type:Arg1.self), try resolve(type:Arg2.self))
}
func resolve<T,Arg1,Arg2,Arg3>(f: (Arg1,Arg2,Arg3) -> T) throws -> T {
    return f(try resolve(type:Arg1.self), try resolve(type:Arg2.self), try resolve(type:Arg3.self))
}

func nullary() {}
func unary(x:Int) {}
func binary(x:Int, y:Int) {}
func ternary(x:Int, y:Int, z:Int) {}

func broken() {
    do {
        try resolve(f: nullary)  // Ambiguous
        try resolve(f: unary)
        try resolve(f: binary) // Ambiguous
        try resolve(f: ternary) // Ambiguous
    }catch{
    }
}
@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 4, 2019

Comment by Jeremy Lew (JIRA)

The ambiguity seems to be between the single-argument overload:
func resolve<T,Arg1>(f: (Arg1) -> T)
...and all of the higher-arity overloads. e.g.:

func resolve<T,Arg1,Arg2>(f: (Arg1,Arg2) -> T)

My uninformed opinion is that there must be some kind of "tuple splatting" (perhaps not the correct term) going on which tries to interpret any higher-arity function argument a funcition with a single argument with a higher-arity tuple type.

For example:
func(x,y,z) becomes func((x,y,z)) and therefore matches both the 3-arg type and the 1-arg type, hence the ambiguity. I think it needs to consider the "exact match" case (if one exists) to take priority over the "auto-splatted" case, but perhaps there are other scenarios where it wouldn't be able to make a judgement like that.

@xedin
Copy link
Member

xedin commented Feb 4, 2019

It seems bugs like these are just to haunt us forever... There is indeed splatting going on in the solver which shouldn't be, in `resolve(f: nullary)` for example, solver deduces solutions with `((())) -> ()` and `() -> ()` arguments, where the first one comes from `resolve<T, Arg1>`.

@swift-ci
Copy link
Collaborator Author

swift-ci commented Feb 5, 2019

Comment by Jeremy Lew (JIRA)

Changing the signatures to accommodate for the implicit splat clears this up, at least for this example:

(e.g. f: (Arg1,Arg2) -> T becomes f: ((Arg1,Arg2)) -> T

enum Err: Error {
    case notimpl
}
func resolve<T>(type: T.Type) throws -> T {
    throw Err.notimpl
}

// This overload is no longer needed, nullary matches the unary tuple below
//func resolve<T>(f: () -> T) throws -> T {
//    throw Err.notimpl
//}

// Matches nullary and unary
func resolve<T,Arg1>(f: ((Arg1)) -> T) throws -> T {
    return f(try resolve(type:Arg1.self))
}
func resolve<T,Arg1,Arg2>(f: ((Arg1,Arg2)) -> T) throws -> T {
    return f((try resolve(type:Arg1.self), try resolve(type:Arg2.self)))
}
func resolve<T,Arg1,Arg2,Arg3>(f: ((Arg1,Arg2,Arg3)) -> T) throws -> T {
    return f((try resolve(type:Arg1.self), try resolve(type:Arg2.self), try resolve(type:Arg3.self)))
}

func nullary() {}
func unary(x:Int) {}
func binary(x:Int, y:Int) {}
func ternary(x:Int, y:Int, z:Int) {}

func no_longer_broken() {
    do {
        // All of these compile now.
        try resolve(f: nullary)  
        try resolve(f: unary)
        try resolve(f: binary) 
        try resolve(f: ternary) 
    }catch{
    }
}

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
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 regression swift 5.0 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants