Rust: Add type inference test for non-universal impl blocks

This commit is contained in:
Simon Friis Vindum
2025-04-28 13:28:33 +02:00
parent 1770f568a2
commit 22407cad44

View File

@@ -130,7 +130,7 @@ mod method_non_parametric_impl {
println!("{:?}", y.a); // $ fieldof=MyThing
println!("{:?}", x.m1()); // $ MISSING: method=MyThing<S1>::m1
println!("{:?}", y.m1().a); // $ MISSING: method=MyThing<S2>::m1, field=MyThing
println!("{:?}", y.m1().a); // $ MISSING: method=MyThing<S2>::m1 fieldof=MyThing
let x = MyThing { a: S1 };
let y = MyThing { a: S2 };
@@ -141,15 +141,23 @@ mod method_non_parametric_impl {
}
mod method_non_parametric_trait_impl {
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
struct MyThing<A> {
a: A,
}
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
struct MyPair<P1, P2> {
p1: P1,
p2: P2,
}
#[derive(Debug, Clone, Copy)]
struct S1;
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
struct S2;
#[derive(Debug, Clone, Copy, Default)]
struct S3;
trait MyTrait<A> {
fn m1(self) -> A;
@@ -162,6 +170,13 @@ mod method_non_parametric_trait_impl {
}
}
trait MyProduct<A, B> {
// MyProduct::fst
fn fst(self) -> A;
// MyProduct::snd
fn snd(self) -> B;
}
fn call_trait_m1<T1, T2: MyTrait<T1>>(x: T2) -> T1 {
x.m1() // $ method=m1
}
@@ -180,18 +195,170 @@ mod method_non_parametric_trait_impl {
}
}
// Implementation where the type parameter `TD` only occurs in the
// implemented trait and not the implementing type.
impl<TD> MyTrait<TD> for MyThing<S3>
where
TD: Default,
{
// MyThing<S3>::m1
fn m1(self) -> TD {
TD::default()
}
}
impl<I> MyTrait<I> for MyPair<I, S1> {
// MyTrait<I>::m1
fn m1(self) -> I {
self.p1 // $ fieldof=MyPair
}
}
impl MyTrait<S3> for MyPair<S1, S2> {
// MyTrait<S3>::m1
fn m1(self) -> S3 {
S3
}
}
impl<TT> MyTrait<TT> for MyPair<MyThing<TT>, S3> {
// MyTrait<TT>::m1
fn m1(self) -> TT {
let alpha = self.p1; // $ fieldof=MyPair
alpha.a // $ fieldof=MyThing
}
}
// This implementation only applies if the two type parameters are equal.
impl<A> MyProduct<A, A> for MyPair<A, A> {
// MyPair<A,A>::fst
fn fst(self) -> A {
self.p1 // $ fieldof=MyPair
}
// MyPair<A,A>::snd
fn snd(self) -> A {
self.p2 // $ fieldof=MyPair
}
}
// This implementation swaps the type parameters.
impl MyProduct<S1, S2> for MyPair<S2, S1> {
// MyPair<S2,S1>::fst
fn fst(self) -> S1 {
self.p2 // $ fieldof=MyPair
}
// MyPair<S2,S1>::snd
fn snd(self) -> S2 {
self.p1 // $ fieldof=MyPair
}
}
fn get_fst<V1, V2, P: MyProduct<V1, V2>>(p: P) -> V1 {
p.fst() // $ method=MyProduct::fst
}
fn get_snd<V1, V2, P: MyProduct<V1, V2>>(p: P) -> V2 {
p.snd() // $ method=MyProduct::snd
}
fn get_snd_fst<V0, V1, V2, P: MyProduct<V1, V2>>(p: MyPair<V0, P>) -> V1 {
p.p2.fst() // $ fieldof=MyPair method=MyProduct::fst
}
trait ConvertTo<T> {
// ConvertTo::convert_to
fn convert_to(self) -> T;
}
impl<T: MyTrait<S1>> ConvertTo<S1> for T {
// T::convert_to
fn convert_to(self) -> S1 {
self.m1() // $ method=m1
}
}
fn convert_to<TS, T: ConvertTo<TS>>(thing: T) -> TS {
thing.convert_to() // $ method=ConvertTo::convert_to
}
fn type_bound_type_parameter_impl<TP: MyTrait<S1>>(thing: TP) -> S1 {
// The trait bound on `TP` makes the implementation of `ConvertTo` valid
thing.convert_to() // $ MISSING: method=T::convert_to
}
pub fn f() {
let x = MyThing { a: S1 };
let y = MyThing { a: S2 };
let thing_s1 = MyThing { a: S1 };
let thing_s2 = MyThing { a: S2 };
let thing_s3 = MyThing { a: S3 };
println!("{:?}", x.m1()); // $ MISSING: method=MyThing<S1>::m1
println!("{:?}", y.m1().a); // $ MISSING: method=MyThing<S2>::m1, field=MyThing
// Tests for method resolution
let x = MyThing { a: S1 };
let y = MyThing { a: S2 };
println!("{:?}", thing_s1.m1()); // $ MISSING: method=MyThing<S1>::m1
println!("{:?}", thing_s2.m1().a); // $ MISSING: method=MyThing<S2>::m1 fieldof=MyThing
let s3: S3 = thing_s3.m1(); // $ MISSING: method=MyThing<S3>::m1
println!("{:?}", s3);
println!("{:?}", call_trait_m1(x)); // MISSING: type=call_trait_m1(...):S1
println!("{:?}", call_trait_m1(y).a); // MISSING: field=MyThing
let p1 = MyPair { p1: S1, p2: S1 };
println!("{:?}", p1.m1()); // $ MISSING: method=MyTrait<I>::m1
let p2 = MyPair { p1: S1, p2: S2 };
println!("{:?}", p2.m1()); // $ MISSING: method=MyTrait<S3>::m1
let p3 = MyPair {
p1: MyThing { a: S1 },
p2: S3,
};
println!("{:?}", p3.m1()); // $ MISSING: method=MyTrait<TT>::m1
// These calls go to the first implementation of `MyProduct` for `MyPair`
let a = MyPair { p1: S1, p2: S1 };
let x = a.fst(); // $ method=MyPair<A,A>::fst
println!("{:?}", x);
let y = a.snd(); // $ method=MyPair<A,A>::snd
println!("{:?}", y);
// These calls go to the last implementation of `MyProduct` for
// `MyPair`. The first implementation does not apply as the type
// parameters of the implementation enforce that the two generics must
// be equal.
let b = MyPair { p1: S2, p2: S1 };
let x = b.fst(); // $ MISSING: method=MyPair<S2,S1>::fst SPURIOUS: method=MyPair<A,A>::fst
println!("{:?}", x);
let y = b.snd(); // $ MISSING: method=MyPair<S2,S1>::snd SPURIOUS: method=MyPair<A,A>::snd
println!("{:?}", y);
// Tests for inference of type parameters based on trait implementations.
let x = call_trait_m1(thing_s1); // $ MISSING: type=x:S1
println!("{:?}", x);
let y = call_trait_m1(thing_s2); // $ MISSING: type=y:MyThing type=y.A:S2
println!("{:?}", y.a); // $ MISSING: fieldof=MyThing
// First implementation
let a = MyPair { p1: S1, p2: S1 };
let x = get_fst(a); // $ type=x:S1
println!("{:?}", x);
let y = get_snd(a); // $ type=y:S1
println!("{:?}", y);
// Second implementation
let b = MyPair { p1: S2, p2: S1 };
let x = get_fst(b); // $ type=x:S1 SPURIOUS: type=x:S2
println!("{:?}", x);
let y = get_snd(b); // $ type=y:S2 SPURIOUS: type=y:S1
println!("{:?}", y);
let c = MyPair {
p1: S3,
p2: MyPair { p1: S2, p2: S1 },
};
let x = get_snd_fst(c); // $ type=x:S1 SPURIOUS: type=x:S2
let thing = MyThing { a: S1 };
let i = thing.convert_to(); // $ MISSING: type=i:S1 MISSING: method=T::convert_to
let j = convert_to(thing); // $ MISSING: type=j:S1
}
}
@@ -219,13 +386,13 @@ mod type_parameter_bounds {
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(); // $ method=SecondTrait::method
println!("{:?}", s1);
println!("{:?}", s1); // $ type=s1:I
}
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(); // $ method=SecondTrait::method
println!("{:?}", s2);
println!("{:?}", s2); // $ type=s2:I
}
fn trait_bound_with_type<T: FirstTrait<S1>>(x: T) {
@@ -235,7 +402,7 @@ mod type_parameter_bounds {
fn trait_per_bound_with_type<T: FirstTrait<S1>>(x: T) {
let s = x.method(); // $ method=FirstTrait::method
println!("{:?}", s);
println!("{:?}", s); // $ type=s:S1
}
trait Pair<P1, P2> {
@@ -323,8 +490,10 @@ mod function_trait_bounds {
a: MyThing { a: S2 },
};
println!("{:?}", call_trait_thing_m1(x3));
println!("{:?}", call_trait_thing_m1(y3));
let a = call_trait_thing_m1(x3); // $ type=a:S1
println!("{:?}", a);
let b = call_trait_thing_m1(y3); // $ type=b:S2
println!("{:?}", b);
}
}
@@ -584,14 +753,14 @@ mod method_supertraits {
let x = MyThing { a: S1 };
let y = MyThing { a: S2 };
println!("{:?}", x.m2()); // $ method=m2
println!("{:?}", y.m2()); // $ method=m2
println!("{:?}", x.m2()); // $ method=m2 type=x.m2():S1
println!("{:?}", y.m2()); // $ method=m2 type=y.m2():S2
let x = MyThing2 { a: S1 };
let y = MyThing2 { a: S2 };
println!("{:?}", x.m3()); // $ method=m3
println!("{:?}", y.m3()); // $ method=m3
println!("{:?}", x.m3()); // $ method=m3 type=x.m3():S1
println!("{:?}", y.m3()); // $ method=m3 type=y.m3():S2
}
}
@@ -767,7 +936,7 @@ mod option_methods {
println!("{:?}", x4);
let x5 = MyOption::MySome(MyOption::<S>::MyNone());
println!("{:?}", x5.flatten()); // MISSING: method=flatten
println!("{:?}", x5.flatten()); // $ MISSING: method=flatten
let x6 = MyOption::MySome(MyOption::<S>::MyNone());
println!("{:?}", MyOption::<MyOption<S>>::flatten(x6));