Skip to content

Rust: Support non-universal impl blocks #19372

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

paldepind
Copy link
Contributor

@paldepind paldepind commented Apr 24, 2025

Adds type inference support for non-universal impl blocks. By "non-universal" we mean impl blocks that target generic types but which are not valid for all instantiations of the generic type.

For instance

impl<T> Option<Option<T>> {
  fn flatten(self) -> Option<T> { ... }
}

where the flatten method is only valid for some Option instantiations.

Non-universal impl block affect both method resolution and trait implementations as the method/trait implementation can be valid only for some instantiations of a type. A Foo<i64> might have a different set of methods/traits than a Foo<String>. Finding the right method/trait implementation is the crux of the matter. The tests have examples of this.

I've tried to document the new additions in this PR with QLdoc, but here are some additional high-level comments on the changes:

  • As mentioned above, with non-universal impl blocks it is no longer enough to know the root of a type to determine which traits it implements and which methods it supports. This affects a bunch of things.

    • The getMethod and getABaseTypeMention member predicate on Type is removed, as a Type is now not enough to determine these things.
    • The new conditionSatisfiesConstraint takes a TypeMention as its second parameter as a Type is not enough.
  • In this PR I've split subtype handling (inferring type parameters through supertypes) from constraint handling (inferring type parameters from type parameter interface constraints (in C#)/trait bounds (in Rust). The former is now the sole job of the AccessBaseType sub-module and the later is done in the new AccessConstraint sub-module. There's also a predicate for each in the module signature: getABaseTypeMention and conditionSatisfiesConstraint.

    This has both pros and cons:

    • PRO: Languages (like Rust) that don't have subtyping can leave getABaseTypeMention as none() and avoid any computation related to subtyping.
    • PRO: Constraints are now more complicated and I don't think any languages have subtyping that is similarly complicated. So subtyping is supported in a simpler and potentially more efficient way.
    • CON: It leads to more code within the shared library compared to handling these two things uniformly as before.
    • CON: In languages where these things are identical, i.e., C# where the question "does T implement the interface I" is equal to asking "is T a subtype of I" doing it as one thing could be more performant.

    All in all I'm not sure which approach is best, but when I made this change performance improved quite a bit (but that was before making some other optimizations) so that's why I ended up with this. Another approach (in follow up work) could be to 1/ merge the two things back again, 2/ add a getVariance predicate for access positions than can be covariant or invariant, 3/ only do subtyping for covariant positions, 4/ make all positions invariant for Rust. That should give equal performance for Rust, cut down on the duplicated code and hopefully the optimization with countConstraintImplementations would mean that subtyping for languages like C# wouldn't regress in performance (but we'd have no way to measure that).

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Apr 24, 2025
@paldepind paldepind force-pushed the rust-ti-implementing-type-method branch from c66a71a to a9afbb3 Compare April 28, 2025 13:15
@paldepind paldepind force-pushed the rust-ti-implementing-type-method branch from a9afbb3 to 76baa34 Compare April 28, 2025 13:35
@paldepind paldepind force-pushed the rust-ti-implementing-type-method branch from 76baa34 to 02115f6 Compare April 29, 2025 09:18
@paldepind paldepind marked this pull request as ready for review April 29, 2025 12:54
@paldepind paldepind requested a review from a team as a code owner April 29, 2025 12:54
@paldepind paldepind requested a review from hvitved April 29, 2025 12:54
@paldepind paldepind added the no-change-note-required This PR does not need a change note label Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-change-note-required This PR does not need a change note Rust Pull requests that update Rust code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant