Skip to content

embedded: support default methods in existentials and improve embedded error reporting #80884

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

Merged
merged 18 commits into from
Apr 18, 2025

Conversation

eeckstein
Copy link
Contributor

The first part of this PR is to support default methods in existentials.
For example:

protocol P: AnyObject {
  func foo()
}
extension P {
  func foo() {}
}
class C: P {}

let e: any P = C()

Such default methods are SILGen'd with a generic self argument. Therefore we need to specialize such witness methods, even if the conforming type is not generic.

The second part of the PR is a big improvements of embedded diagnostic:

  1. move embedded diagnostics out of the PerformanceDiagnostics pass. It was completely separated from the other logic in this pass, anyway.
  2. rewrite it in swift
  3. fix several bugs, that means: missed diagnostics, which led to IRGen crashes
  • look at all methods in witness tables, including base protocols and associated conformances
  • visit all functions in the call tree, including generic functions with class bound generic arguments
  • handle all instructions, e.g. concurrency builtins
  1. improve error messages by adding meaningful call-site information. For example:
  • if the error is in a specialized function, report where the generic function is originally specialized with concrete types
  • if the error is in a protocol witness method, report where the existential is created

Beside these two big parts, the PR also contains some refactoring and infrastructure improvements. For details see the commit messages.

rdar://145855851

@eeckstein eeckstein requested review from kubamracek and removed request for xedin, jckarter, xymus, rjmccall and hborla April 17, 2025 14:38
@eeckstein
Copy link
Contributor Author

@swift-ci smoke test

@eeckstein eeckstein force-pushed the embedded-error-message2 branch from 3dcd68c to f870b2b Compare April 17, 2025 18:28
@eeckstein
Copy link
Contributor Author

@swift-ci smoke test

Store specialize witness tables in a separate lookup table in the module. This allows that for a normal conformance there can exist the original _and_ a specialized witness table.
Also, add a boolean property `isSpecialized` to `WitnessTable` which indicates whether the witness table is specialized or not.
…aliases in some cases

Need to canonicalize the replacement type. Otherwise it could be generic if it is a typealias inside a generic type, e.g.
```
  struct S<T> {
    typealias I = Int
  }
```
* move it from the SIL to the AST module (where it belongs)
* change the signature of `diagnose` from `diagnose(location, .some_error)` to `diagnose(.some_error, at: location)`
* add an overload to allow passing a `SIL.Location` directly to `diagnose`
* add a `Diagnostic : Error` utility struct which allows throwing a `Diagnostic`
* in `Decl`: `var parent: Decl?`
* in `ProtocolDecl`: `var requiresClass: Bool`
… function

Add an initializer which takes the `hasLoweredAddresses` directly and add `Context.moduleHasLoweredAddresses`
* make `var sourceLoc` return nil if the location doesn't have a valid line number
* add `var decl`
…dSubstitutions`

If the method is a default witness methods (`selfType` != nil) it has generic self type.
In this case the generic self parameter is at depth 0 and the actual generic parameters of the substitution map are at depth + 1, e.g:
```
    @convention(witness_method: P) <τ_0_0><τ_1_0 where τ_0_0 : GenClass<τ_1_0>.T>
                                      ^      ^
                                   self      params of substitution map at depth + 1
```
…existentials

For example:
```
protocol P: AnyObject {
  func foo()
}
extension P {
  func foo() {}
}
class C: P {}

let e: any P = C()
```

Such default methods are SILGen'd with a generic self argument. Therefore we need to specialize such witness methods, even if the conforming type is not generic.

rdar://145855851
1. move embedded diagnostics out of the PerformanceDiagnostics pass. It was completely separated from the other logic in this pass, anyway.
2. rewrite it in swift
3. fix several bugs, that means: missed diagnostics, which led to IRGen crashes
  * look at all methods in witness tables, including base protocols and associated conformances
  * visit all functions in the call tree, including generic functions with class bound generic arguments
  * handle all instructions, e.g. concurrency builtins
4. improve error messages by adding meaningful call-site information. For example:
  * if the error is in a specialized function, report where the generic function is originally specialized with concrete types
  * if the error is in a protocol witness method, report where the existential is created
@eeckstein eeckstein force-pushed the embedded-error-message2 branch from f870b2b to 6c31eb0 Compare April 18, 2025 04:58
@eeckstein
Copy link
Contributor Author

@swift-ci smoke test

@eeckstein
Copy link
Contributor Author

@swift-ci smoke test linux

1 similar comment
@eeckstein
Copy link
Contributor Author

@swift-ci smoke test linux

@kubamracek
Copy link
Contributor

LGTM and the approach makes sense (specializing the wtable of even non-generic classes). Thanks, Erik!

@eeckstein eeckstein enabled auto-merge April 18, 2025 19:14
@eeckstein eeckstein merged commit ad82261 into swiftlang:main Apr 18, 2025
3 checks passed
@eeckstein eeckstein deleted the embedded-error-message2 branch April 19, 2025 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants