Skip to content

[5.5][CodeComplete] Show completions from constrained protocol extension #37080

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

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Apr 27, 2021

Cherry-picks #37065


Consider the following example.

protocol FontStyle {}
struct FontStyleOne: FontStyle {}

extension FontStyle where Self == FontStyleOne {
    static var one: FontStyleOne { FontStyleOne() }
}

func foo<T: FontStyle>(x: T) {}
func case1() {
    foo(x: .#^COMPLETE^#)
}

With SE-0299 accepted, we should be suggesting .one.

For that, we need to consider the extension applied when performing the unresolved dot code completion with a primary archetype that conforms to FontStyle.

However, in the following case, which performs an unresolved dot completion on the same base type, we don't want to suggest .one because that would require T == FontStyleOne, which we can’t assume.

func case2<T: FontStyle>(x: T) {
    x.#^COMPLETE_2^#
}

Since the constraint system cannot tell us how it came up with the archetype, we need to apply a heuristic to differentiate between the two cases.

What seems to work fine in most cases, is to determine if T referes to a generic parameter that is visible from the decl context we are completing in (i.e. the decl context we are completing in is a child context of the context that T is declared in). If it is not, then T cannot be the type of a variable we are completing on. Thus, we are in the first case and we should consider all extensions of FontStyle because we can further specialize T by picking a more concrete type.
Otherwise T may be the type of a variable we are completing on and we should be conservative and only suggest those extensions whose requirements are fulfilled by T.

Since this is just a heuristic, there are some corner cases, where we aren’t suggesting constrainted extensions although we should. For example, in the following example the call to testRecursive doesn’t use T and we should thus suggest one. But by the rules described above we detect that T is accessible at the call and thus don’t apply extension whose requirements aren’t satisfied.

func testRecursive<T: FontStyle>(_ style: T) {
  testRecursive(.#^COMPLETE_RECURSIVE_GENERIC^#)
}

Similar completion issues also occurred without SE-0299 in more complicated, generic scenarios.

Consider the following example.

```swift
protocol FontStyle {}
struct FontStyleOne: FontStyle {}

extension FontStyle where Self == FontStyleOne {
    static var one: FontStyleOne { FontStyleOne() }
}

func foo<T: FontStyle>(x: T) {}
func case1() {
    foo(x: .#^COMPLETE^#)
}
```

With SE-0299 accepted, we should be suggesting `.one`.

For that, we need to consider the extension applied when performing the unresolved dot code completion with a primary archetype that conforms to `FontStyle`.

However, in the following case, which performs an unresolved dot completion on the same base type, we don't want to suggest `.one` because that would require `T == FontStyleOne`, which we can’t assume.

```swift
func case2<T: FontStyle>(x: T) {
    x.#^COMPLETE_2^#
}
```

Since the constraint system cannot tell us how it came up with the archetype, we need to apply a heuristic to differentiate between the two cases.

What seems to work fine in most cases, is to determine if `T` referes to a generic parameter that is visible from the decl context we are completing in (i.e. the decl context we are completing in is a child context of the context that `T` is declared in). If it is not, then `T` cannot be the type of a variable we are completing on. Thus, we are in the first case and we should consider all extensions of `FontStyle` because we can further specialize `T` by picking a more concrete type.
Otherwise `T` may be the type of a variable we are completing on and we should be conservative and only suggest those extensions whose requirements are fulfilled by `T`.

Since this is just a heuristic, there are some corner cases, where we aren’t suggesting constrainted extensions although we should. For example, in the following example the call to `testRecursive` doesn’t use `T` and we should thus suggest `one`. But by the rules described above we detect that `T` is accessible at the call and thus don’t apply extension whose requirements aren’t satisfied.

```swift
func testRecursive<T: FontStyle>(_ style: T) {
  testRecursive(.#^COMPLETE_RECURSIVE_GENERIC^#)
}
```

Similar completion issues also occurred without SE-0299 in more complicated, generic scenarios.

Resolves rdar://74958497 and SR-12973
@ahoppen ahoppen added the r5.5 label Apr 27, 2021
@ahoppen ahoppen requested a review from akyrtzi April 27, 2021 06:17
@ahoppen
Copy link
Member Author

ahoppen commented Apr 27, 2021

@swift-ci Please test

@ahoppen
Copy link
Member Author

ahoppen commented Apr 27, 2021

@swift-ci Please test Windows

@ahoppen ahoppen merged commit bebc8b8 into swiftlang:release/5.5 Apr 27, 2021
@ahoppen ahoppen deleted the pr-5.5/complete-from-constrained-extension branch April 27, 2021 18:46
@AnthonyLatsis AnthonyLatsis added 🍒 release cherry pick Flag: Release branch cherry picks swift 5.5 labels Jan 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🍒 release cherry pick Flag: Release branch cherry picks swift 5.5
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants