-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Rust: Implement type inference for trait objects/dyn
types
#20084
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
base: main
Are you sure you want to change the base?
Changes from all commits
605c8e2
1294266
f5605c9
b3dc6cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,6 +97,9 @@ private module Input1 implements InputSig1<Location> { | |
id = 2 | ||
or | ||
kind = 1 and | ||
id = idOfTypeParameterAstNode(tp0.(DynTraitTypeParameter).getTypeParam()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this separate from the cases for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, |
||
or | ||
kind = 2 and | ||
exists(AstNode node | id = idOfTypeParameterAstNode(node) | | ||
node = tp0.(TypeParamTypeParameter).getTypeParam() or | ||
node = tp0.(AssociatedTypeTypeParameter).getTypeAlias() or | ||
|
@@ -107,7 +110,7 @@ private module Input1 implements InputSig1<Location> { | |
exists(TupleTypeParameter ttp, int maxArity | | ||
maxArity = max(int i | i = any(TupleType tt).getArity()) and | ||
tp0 = ttp and | ||
kind = 2 and | ||
kind = 3 and | ||
id = ttp.getTupleType().getArity() * maxArity + ttp.getIndex() | ||
) | ||
| | ||
|
@@ -189,6 +192,14 @@ private module Input2 implements InputSig2 { | |
condition = impl and | ||
constraint = impl.getTypeBoundList().getABound().getTypeRepr() | ||
) | ||
or | ||
// a `dyn Trait` type implements `Trait`. See the comment on | ||
// `DynTypeBoundListMention` for further details. | ||
exists(DynTraitTypeRepr object | | ||
abs = object and | ||
condition = object.getTypeBoundList() and | ||
constraint = object.getTrait() | ||
) | ||
} | ||
} | ||
|
||
|
@@ -1715,10 +1726,16 @@ private Function getMethodFromImpl(MethodCall mc) { | |
|
||
bindingset[trait, name] | ||
pragma[inline_late] | ||
private Function getTraitMethod(ImplTraitReturnType trait, string name) { | ||
private Function getImplTraitMethod(ImplTraitReturnType trait, string name) { | ||
result = getMethodSuccessor(trait.getImplTraitTypeRepr(), name) | ||
} | ||
|
||
bindingset[traitObject, name] | ||
pragma[inline_late] | ||
private Function getDynTraitMethod(DynTraitType traitObject, string name) { | ||
result = getMethodSuccessor(traitObject.getTrait(), name) | ||
} | ||
|
||
pragma[nomagic] | ||
private Function resolveMethodCallTarget(MethodCall mc) { | ||
// The method comes from an `impl` block targeting the type of the receiver. | ||
|
@@ -1729,7 +1746,10 @@ private Function resolveMethodCallTarget(MethodCall mc) { | |
result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName()) | ||
or | ||
// The type of the receiver is an `impl Trait` type. | ||
result = getTraitMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName()) | ||
result = getImplTraitMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName()) | ||
or | ||
// The type of the receiver is a trait object `dyn Trait` type. | ||
result = getDynTraitMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName()) | ||
} | ||
|
||
pragma[nomagic] | ||
|
@@ -2073,6 +2093,13 @@ private module Debug { | |
result = resolveCallTarget(c) | ||
} | ||
|
||
predicate debugConditionSatisfiesConstraint( | ||
TypeAbstraction abs, TypeMention condition, TypeMention constraint | ||
) { | ||
abs = getRelevantLocatable() and | ||
Input2::conditionSatisfiesConstraint(abs, condition, constraint) | ||
} | ||
|
||
predicate debugInferImplicitSelfType(SelfParam self, TypePath path, Type t) { | ||
self = getRelevantLocatable() and | ||
t = inferImplicitSelfType(self, path) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
category: minorAnalysis | ||
--- | ||
* Type inference now supports trait objects, i.e., `dyn Trait` types. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Test cases for type inference and method resolution with `dyn` types | ||
|
||
use std::fmt::Debug; | ||
|
||
trait MyTrait1 { | ||
// MyTrait1::m | ||
fn m(&self) -> String; | ||
} | ||
|
||
trait GenericGet<A> { | ||
// GenericGet::get | ||
fn get(&self) -> A; | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
struct MyStruct { | ||
value: i32, | ||
} | ||
|
||
impl MyTrait1 for MyStruct { | ||
// MyStruct1::m | ||
fn m(&self) -> String { | ||
format!("MyTrait1: {}", self.value) // $ fieldof=MyStruct | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
struct GenStruct<A: Clone + Debug> { | ||
value: A, | ||
} | ||
|
||
impl<A: Clone + Debug> GenericGet<A> for GenStruct<A> { | ||
// GenStruct<A>::get | ||
fn get(&self) -> A { | ||
self.value.clone() // $ fieldof=GenStruct target=clone | ||
} | ||
} | ||
|
||
fn get_a<A, G: GenericGet<A> + ?Sized>(a: &G) -> A { | ||
a.get() // $ target=GenericGet::get | ||
} | ||
|
||
fn get_box_trait<A: Clone + Debug + 'static>(a: A) -> Box<dyn GenericGet<A>> { | ||
Box::new(GenStruct { value: a }) // $ target=new | ||
} | ||
|
||
fn test_basic_dyn_trait(obj: &dyn MyTrait1) { | ||
let _result = (*obj).m(); // $ target=deref target=MyTrait1::m type=_result:String | ||
} | ||
|
||
fn test_generic_dyn_trait(obj: &dyn GenericGet<String>) { | ||
let _result1 = (*obj).get(); // $ target=deref target=GenericGet::get type=_result1:String | ||
let _result2 = get_a(obj); // $ target=get_a type=_result2:String | ||
} | ||
|
||
fn test_poly_dyn_trait() { | ||
let obj = get_box_trait(true); // $ target=get_box_trait | ||
let _result = (*obj).get(); // $ target=deref target=GenericGet::get type=_result:bool | ||
} | ||
|
||
pub fn test() { | ||
test_basic_dyn_trait(&MyStruct { value: 42 }); // $ target=test_basic_dyn_trait | ||
test_generic_dyn_trait(&GenStruct { | ||
value: "".to_string(), | ||
}); // $ target=test_generic_dyn_trait | ||
test_poly_dyn_trait(); // $ target=test_poly_dyn_trait | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come the definition of
TDynTraitTypeParameter
(on line 34) uses.getAGenericParam()
, but here we use.getTypeParam(i)
? I was expecting them to match.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I've changed the
getAGenericParam
togetATypeParam
👍. I didn't think much of the difference, but I guess the former can contain stuff that are not type parameters (lifetimes?) so usinggetATypeParam
is probably more correct.