Code completion enhancement request

I was delighted to discover a certain behavior of code-completion which I'll detail below. However, I tried to push it one step further and found that code-completion stopped working - I'll also detail this step that breaks the camel's back. What I'd like to know is the likelihood that this will work in the future, and of course if a specific timeline is known that would be great.


Here are two essentially trivial enums:

enum AIdentifier {
    case a
}

enum BIdentifier {
    case b
}

They are so simple because their only purpose will be to serve as distinct types to allow for easy switching between function overloads.

Here are the corresponding types which our enums are serving as identifiers for:

struct A {
    static func foo (arg: Bool) -> A {
        A()
    }

    static func bar (arg: Int) -> A {
        A()
    }
}

struct B {
    static var baz: B {
        B()
    }
}

The last piece of the setup is the previously-alluded-to overloaded function:

func C (_ identifier: AIdentifier, _ a: ()->A) { }
func C (_ identifier: BIdentifier, _ b: ()->B) { }

The thing that I was initially delighted to find is that, given this setup, if I type:

C(.)

then the code-completion menu offers both .a and .b. As soon as I pick one of the two identifiers, the proper overload is now known which means that the type of the second parameter is known, which allows for even more code-completion magic. If I type . inside the braces of the closure:

C(.a) {
    .
}

then code-completion very nicely shows me my two options, which are .foo(arg:) and .bar(arg:):

C(.a) {
    .foo(arg: true)
}

So far so good. The discoverability that this allows for is absolutely amazing. What I really want though is for .foo(arg: true) to bring with it an associated type which can propagate onto further usage. I want .foo(arg:) to have a different associated type than .bar(arg:). Here are the associated types for each method:

struct X { }
struct Y { }

I'll re-define A to be generic, and change .foo(arg:) and .bar(arg:) to return values specialized with different generic parameters:

struct A <T> {
    static func foo (arg: Bool) -> A<X> {
        A<X>()
    }
    
    static func bar (arg: Int) -> A<Y> {
        A<Y>()
    }
}

Now that A<T> is generic I have to adjust the overload of C(_:_:) that is associated with it. I'll also add a return type which will better illustrate the use case that I'm pursuing here:

func C <T> (_ identifier: AIdentifier, _ a: ()->A<T>) -> D<T> { }

where D is defined as:

struct D <T> {
    func sink (_ handler: @escaping (T)->()) { }
}

The first step of code-completion still works with this setup - if I type C(.) I am still offered my two choices, .a and .b. However, after choosing .a and moving on to the value inside the closure, code-completion breaks down. Now that the first overload of C(_:_:) is generic, the two static functions on the A type are no longer offered by code-completion, despite the fact that they satisfy the type constraints of the context. This code still compiles, but has to be typed out by memory because it is not offered by code-completion:

C(.a) {
    .foo(bar: true)
}
    .sink { value in
        print(value)
    }

The advantage of this code is of course that the value argument in the sink closure is known to be of type X, and that if I simply change .foo(arg: true) to .bar(arg: 2) then suddenly the value argument is of type Y. That's pretty huge for me. However, I think that the code-completion offered by the less complex type inference demands currently outweighs the value of the type-safety for me. The ideal would of course be to have the best of both worlds. Does anyone have any thoughts on this? I'm open to estimations of timeline, conjectures as to whether this can or will ever be possible, or alternative architectural solutions. Thanks!

5 Likes

Thank you for the report!

I think this is the minimal test case to demonstrate the issues:

struct X { }

struct A<T> {
  static var foo: A<X> { A<X>() }
}

func C<T>(_ a: ()->A<T>) { }

C {
  ./*HERE*/
}

I will look into it. In the meantime, could you file a bug report to JIRA (https://bugs.swift.org/) ?

1 Like

Thank you! I filed the bug report: [SR-13271] Code-completion fails in specific context · Issue #55711 · apple/swift · GitHub.

Perhaps this is a naive question - I'm somewhat out of touch with release schedules - do you think there's any chance that if the fix turns out to be simple it would be included in Swift 5.3? I ask because at this very moment I'm deciding how to architect my code, and if there's a decent chance that I'll retroactively gain code-completion by the fall then I'll go with the fancy type-inference approach, but if it'll be much longer than that then perhaps I would reconsider.

Thanks for the bug report. Unfortunately, I cannot promise anything at this point though..

I have investigated this and probably I can prepare the patch to fix this issue in this week. But still, I cannot promise it would be included in 5.3.

Wow, thanks for investigating so quickly! I hope it goes smoothly. If you succeed in fixing it then it doesn't matter as much to me when it ships - I just didn't want to paint myself into a corner that lacks code-completion.

I know very little about the compiler implementation but it interest me a lot, especially how things like code-completion are implemented, so I'd love to look at the pull request if/when it's ready. No pressure though.

Godspeed :pray:

Merged [CodeCompletion] Use TypeContextInfo to get expected return types by rintaro · Pull Request #33080 · apple/swift · GitHub

We decided not to include this to 5.3 at this point, sorry! :pray: Since the change is not trivial, it's a little too risky.

1 Like

That's alright, I understand. Assuming the fix doesn't cause any problems, should I expect it to be released eventually?

It will ship with whatever the next official Swift version is. If you want to try it out now, you can download the next available development snapshot from swift.org, which should contain this fix.

1 Like

Great news, thank you! Thank you @rintaro for fixing this!

1 Like