Merge pull request #19062 from paldepind/rust-ti-1

Rust: Improve handling of trait bounds
This commit is contained in:
Simon Friis Vindum
2025-03-20 14:38:03 +01:00
committed by GitHub
5 changed files with 950 additions and 705 deletions

View File

@@ -81,6 +81,7 @@ abstract private class StructOrEnumType extends Type {
)
}
/** Gets all of the fully parametric `impl` blocks that target this type. */
final override ImplMention getABaseTypeMention() {
this.asItemNode() = result.resolveSelfTy() and
result.isFullyParametric()
@@ -153,6 +154,7 @@ class TraitType extends Type, TTrait {
result = trait.getTypeBoundList().getABound().getTypeRepr()
}
/** Gets any of the trait bounds of this trait. */
override TypeMention getABaseTypeMention() { result = this.getABoundMention() }
override string toString() { result = trait.toString() }
@@ -308,11 +310,19 @@ class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter {
TypeParam getTypeParam() { result = typeParam }
override Function getMethod(string name) { result = typeParam.(ItemNode).getASuccessor(name) }
override Function getMethod(string name) {
// NOTE: If the type parameter has trait bounds, then this finds methods
// on the bounding traits.
result = typeParam.(ItemNode).getASuccessor(name)
}
override string toString() { result = typeParam.toString() }
override Location getLocation() { result = typeParam.getLocation() }
final override TypeMention getABaseTypeMention() {
result = typeParam.getTypeBoundList().getABound().getTypeRepr()
}
}
/** An implicit reference type parameter. */

View File

@@ -234,6 +234,10 @@ private Type inferImplicitSelfType(SelfParam self, TypePath path) {
)
}
/**
* Gets any of the types mentioned in `path` that corresponds to the type
* parameter `tp`.
*/
private TypeMention getExplicitTypeArgMention(Path path, TypeParam tp) {
exists(int i |
result = path.getPart().getGenericArgList().getTypeArg(pragma[only_bind_into](i)) and

View File

@@ -36,14 +36,14 @@ mod field_access {
let y = GenericThing { a: S };
println!("{:?}", x.a);
// The type of the field `a` can only be infered from the concrete type
// The type of the field `a` can only be inferred from the concrete type
// in the struct declaration.
let x = OptionS {
a: MyOption::MyNone(),
};
println!("{:?}", x.a);
// The type of the field `a` can only be infered from the type argument
// The type of the field `a` can only be inferred from the type argument
let x = GenericThing::<MyOption<S>> {
a: MyOption::MyNone(),
};
@@ -191,6 +191,68 @@ mod method_non_parametric_trait_impl {
}
}
mod type_parameter_bounds {
use std::fmt::Debug;
#[derive(Debug)]
struct S1;
#[derive(Debug)]
struct S2;
// Two traits with the same method name.
trait FirstTrait<FT> {
fn method(self) -> FT;
}
trait SecondTrait<ST> {
fn method(self) -> ST;
}
fn call_first_trait_per_bound<I: Debug, T: SecondTrait<I>>(x: T) {
// The type parameter bound determines which method this call is resolved to.
let s1 = x.method();
println!("{:?}", s1);
}
fn call_second_trait_per_bound<I: Debug, T: SecondTrait<I>>(x: T) {
// The type parameter bound determines which method this call is resolved to.
let s2 = x.method();
println!("{:?}", s2);
}
fn trait_bound_with_type<T: FirstTrait<S1>>(x: T) {
let s = x.method();
println!("{:?}", s);
}
fn trait_per_bound_with_type<T: FirstTrait<S1>>(x: T) {
let s = x.method();
println!("{:?}", s);
}
trait Pair<P1, P2> {
fn fst(self) -> P1;
fn snd(self) -> P2;
}
fn call_trait_per_bound_with_type_1<T: Pair<S1, S2>>(x: T, y: T) {
// The type in the type parameter bound determines the return type.
let s1 = x.fst();
let s2 = y.snd();
println!("{:?}, {:?}", s1, s2);
}
fn call_trait_per_bound_with_type_2<T2: Debug, T: Pair<S1, T2>>(x: T, y: T) {
// The type in the type parameter bound determines the return type.
let s1 = x.fst();
let s2 = y.snd();
println!("{:?}, {:?}", s1, s2);
}
}
mod function_trait_bounds {
#[derive(Debug)]
struct MyThing<A> {
@@ -443,6 +505,49 @@ mod function_trait_bounds_2 {
}
}
mod type_aliases {
#[derive(Debug)]
enum PairOption<Fst, Snd> {
PairNone(),
PairFst(Fst),
PairSnd(Snd),
PairBoth(Fst, Snd),
}
#[derive(Debug)]
struct S1;
#[derive(Debug)]
struct S2;
#[derive(Debug)]
struct S3;
// Non-generic type alias that fully applies the generic type
type MyPair = PairOption<S1, S2>;
// Generic type alias that partially applies the generic type
type AnotherPair<Thr> = PairOption<S2, Thr>;
pub fn f() {
// Type can be inferred from the constructor
let p1: MyPair = PairOption::PairBoth(S1, S2);
println!("{:?}", p1);
// Type can be only inferred from the type alias
let p2: MyPair = PairOption::PairNone(); // types for `Fst` and `Snd` missing
println!("{:?}", p2);
// First type from alias, second from constructor
let p3: AnotherPair<_> = PairOption::PairSnd(S3); // type for `Fst` missing
println!("{:?}", p3);
// First type from alias definition, second from argument to alias
let p3: AnotherPair<S3> = PairOption::PairNone(); // type for `Snd` missing, spurious `S3` for `Fst`
println!("{:?}", p3);
}
}
mod option_methods {
#[derive(Debug)]
enum MyOption<T> {

View File

@@ -254,6 +254,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
module Make2<InputSig2 Input2> {
private import Input2
/** Gets the type at the empty path of `tm`. */
pragma[nomagic]
private Type resolveTypeMentionRoot(TypeMention tm) {
result = tm.resolveTypeAt(TypePath::nil())
@@ -275,7 +276,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*
* class Mid<T3> : Base<C<T3>> { }
*
* class Sub<T4> : Mid<C<T4>> { }
* class Sub<T4> : Mid<C<T4>> { } // Sub<T4> extends Base<C<C<T4>>
* ```
*
* - `T3` is mentioned at `0.0` for immediate base type mention `Base<C<T3>>`
@@ -334,9 +335,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
/**
* Holds if `baseMention` is a (transitive) base type mention of `sub`, and
* non-type-parameter `t` is mentioned (implicitly) at `path` inside
* `baseMention`. For example, in
* Holds if `baseMention` is a (transitive) base type mention of `sub`,
* and `t`, which is not a type parameter of `sub`, is mentioned
* (implicitly) at `path` inside `baseMention`. For example, in
*
* ```csharp
* class C<T1> { }
@@ -345,7 +346,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*
* class Mid<T3> : Base<C<T3>> { }
*
* class Sub<T4> : Mid<C<T4>> { }
* class Sub<T4> : Mid<C<T4>> { } // Sub<T4> extends Base<C<C<T4>>
* ```
*
* - ``C`1`` is mentioned at `0` for immediate base type mention `Base<C<T3>>`
@@ -359,7 +360,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
predicate baseTypeMentionHasNonTypeParameterAt(
Type sub, TypeMention baseMention, TypePath path, Type t
) {
not t instanceof TypeParameter and
not t = sub.getATypeParameter() and
exists(TypeMention immediateBaseMention |
pragma[only_bind_into](immediateBaseMention) =
getABaseTypeMention(pragma[only_bind_into](sub))