Skip to content

Commit e5f1238

Browse files
authored
Merge pull request #20707 from paldepind/rust/ti-self-trait
Rust: Improve handling of `Self` type parameter
2 parents 51a577a + ce8cffc commit e5f1238

File tree

4 files changed

+3945
-3803
lines changed

4 files changed

+3945
-3803
lines changed

rust/ql/lib/codeql/rust/internal/TypeMention.qll

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,33 +143,73 @@ class NonAliasPathTypeMention extends PathTypeMention {
143143
)
144144
}
145145

146-
private TypeMention getPositionalTypeArgument0(int i) {
146+
/**
147+
* Gets the positional type argument at index `i` that occurs in this path, if
148+
* any.
149+
*/
150+
private TypeMention getPathPositionalTypeArgument(int i) {
147151
result = this.getSegment().getGenericArgList().getTypeArg(i)
148152
or
149153
// `Option::<i32>::Some` is valid in addition to `Option::Some::<i32>`
150154
resolvePath(this) instanceof Variant and
151155
result = this.getQualifier().getSegment().getGenericArgList().getTypeArg(i)
152156
}
153157

154-
private TypeMention getPositionalTypeArgument(int i) {
155-
result = this.getPositionalTypeArgument0(i)
158+
/**
159+
* Gets the type mention that instantiates the implicit `Self` type parameter
160+
* for this path, if it occurs in the position of a trait bound.
161+
*/
162+
private TypeMention getSelfTraitBoundArg() {
163+
exists(ImplItemNode impl | this = impl.getTraitPath() and result = impl.(Impl).getSelfTy())
156164
or
165+
exists(Trait subTrait |
166+
this = subTrait.getATypeBound().getTypeRepr().(PathTypeRepr).getPath() and
167+
result.(SelfTypeParameterMention).getTrait() = subTrait
168+
)
169+
or
170+
exists(TypeParamItemNode tp | this = tp.getABoundPath() and result = tp)
171+
}
172+
173+
private Type getDefaultPositionalTypeArgument(int i, TypePath path) {
157174
// If a type argument is not given in the path, then we use the default for
158175
// the type parameter if one exists for the type.
159-
not exists(this.getPositionalTypeArgument0(i)) and
160-
result = this.resolveRootType().getTypeParameterDefault(i) and
176+
not exists(this.getPathPositionalTypeArgument(i)) and
161177
// Defaults only apply to type mentions in type annotations
162-
this = any(PathTypeRepr ptp).getPath().getQualifier*()
178+
this = any(PathTypeRepr ptp).getPath().getQualifier*() and
179+
exists(Type ty, TypePath prefix |
180+
ty = this.resolveRootType().getTypeParameterDefault(i).resolveTypeAt(prefix) and
181+
if not ty = TSelfTypeParameter(resolved)
182+
then result = ty and path = prefix
183+
else
184+
// When a default contains an implicit `Self` type parameter, it should
185+
// be substituted for the type that implements the trait.
186+
exists(TypePath suffix |
187+
path = prefix.append(suffix) and
188+
result = this.getSelfTraitBoundArg().resolveTypeAt(suffix)
189+
)
190+
)
163191
}
164192

165-
/** Gets the type mention in this path for the type parameter `tp`, if any. */
166-
pragma[nomagic]
167-
private TypeMention getTypeMentionForTypeParameter(TypeParameter tp) {
193+
private Type getPositionalTypeArgument(int i, TypePath path) {
194+
result = this.getPathPositionalTypeArgument(i).resolveTypeAt(path)
195+
or
196+
result = this.getDefaultPositionalTypeArgument(i, path)
197+
}
198+
199+
/**
200+
* Gets the type for this path for the type parameter `tp` at `path`, when the
201+
* type parameter does not correspond directly to a type mention.
202+
*/
203+
private Type getTypeForTypeParameterAt(TypeParameter tp, TypePath path) {
168204
exists(int i |
169-
result = this.getPositionalTypeArgument(pragma[only_bind_into](i)) and
205+
result = this.getPositionalTypeArgument(pragma[only_bind_into](i), path) and
170206
tp = this.resolveRootType().getPositionalTypeParameter(pragma[only_bind_into](i))
171207
)
172-
or
208+
}
209+
210+
/** Gets the type mention in this path for the type parameter `tp`, if any. */
211+
pragma[nomagic]
212+
private TypeMention getTypeMentionForTypeParameter(TypeParameter tp) {
173213
exists(TypeAlias alias |
174214
result = this.getAnAssocTypeArgument(alias) and
175215
tp = TAssociatedTypeTypeParameter(alias)
@@ -237,9 +277,17 @@ class NonAliasPathTypeMention extends PathTypeMention {
237277
typePath.isEmpty() and
238278
result = this.resolveRootType()
239279
or
240-
exists(TypeParameter tp, TypePath suffix |
241-
result = this.getTypeMentionForTypeParameter(tp).resolveTypeAt(suffix) and
242-
typePath = TypePath::cons(tp, suffix)
280+
exists(TypeParameter tp, TypePath suffix | typePath = TypePath::cons(tp, suffix) |
281+
result = this.getTypeForTypeParameterAt(tp, suffix)
282+
or
283+
result = this.getTypeMentionForTypeParameter(tp).resolveTypeAt(suffix)
284+
)
285+
or
286+
// When the path refers to a trait, then the implicit `Self` type parameter
287+
// should be instantiated from the context.
288+
exists(TypePath suffix |
289+
result = this.getSelfTraitBoundArg().resolveTypeAt(suffix) and
290+
typePath = TypePath::cons(TSelfTypeParameter(resolved), suffix)
243291
)
244292
}
245293
}
@@ -296,6 +344,11 @@ class TraitMention extends TypeMention instanceof TraitItemNode {
296344
typePath.isEmpty() and
297345
result = TTrait(this)
298346
or
347+
// The implicit `Self` type parameter occurs at the `Self` type parameter
348+
// position.
349+
typePath = TypePath::singleton(TSelfTypeParameter(this)) and
350+
result = TSelfTypeParameter(this)
351+
or
299352
exists(TypeAlias alias |
300353
alias = super.getAnAssocItem() and
301354
typePath = TypePath::singleton(result) and

rust/ql/test/library-tests/type-inference/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ multipleCallTargets
55
| dereference.rs:184:17:184:30 | ... .foo() |
66
| dereference.rs:186:17:186:25 | S.bar(...) |
77
| dereference.rs:187:17:187:29 | S.bar(...) |
8-
| main.rs:2437:13:2437:31 | ...::from(...) |
9-
| main.rs:2438:13:2438:31 | ...::from(...) |
10-
| main.rs:2439:13:2439:31 | ...::from(...) |
11-
| main.rs:2445:13:2445:31 | ...::from(...) |
12-
| main.rs:2446:13:2446:31 | ...::from(...) |
13-
| main.rs:2447:13:2447:31 | ...::from(...) |
8+
| main.rs:2481:13:2481:31 | ...::from(...) |
9+
| main.rs:2482:13:2482:31 | ...::from(...) |
10+
| main.rs:2483:13:2483:31 | ...::from(...) |
11+
| main.rs:2489:13:2489:31 | ...::from(...) |
12+
| main.rs:2490:13:2490:31 | ...::from(...) |
13+
| main.rs:2491:13:2491:31 | ...::from(...) |

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,50 @@ mod type_parameter_bounds {
652652
}
653653
}
654654

655+
mod trait_default_self_type_parameter {
656+
// A trait with a type parameter that defaults to `Self`.
657+
trait TraitWithSelfTp<A = Option<Self>> {
658+
// TraitWithSelfTp::get_a
659+
fn get_a(&self) -> A;
660+
}
661+
662+
fn get_a<A, T: TraitWithSelfTp<A>>(thing: &T) -> A {
663+
thing.get_a() // $ target=TraitWithSelfTp::get_a
664+
}
665+
666+
// The trait bound on `T` uses the default for `A` which contains `Self`
667+
fn tp_uses_default<S: TraitWithSelfTp>(thing: S) -> i64 {
668+
let _ms = thing.get_a(); // $ target=TraitWithSelfTp::get_a type=_ms:T.S
669+
0
670+
}
671+
672+
// The supertrait uses the default for `A` which contains `Self`
673+
trait SubTraitOfTraitWithSelfTp: TraitWithSelfTp + Sized {}
674+
675+
fn get_a_through_tp<S: SubTraitOfTraitWithSelfTp>(thing: &S) {
676+
// `thing` is a `TraitWithSelfTp` through the trait hierarchy
677+
let _ms = get_a(thing); // $ target=get_a type=_ms:T.S
678+
}
679+
680+
struct MyStruct {
681+
value: i32,
682+
}
683+
684+
// The implementing trait uses the default for `A` which contains `Self`
685+
impl TraitWithSelfTp for MyStruct {
686+
fn get_a(&self) -> Option<Self> {
687+
Some(MyStruct { value: self.value }) // $ fieldof=MyStruct
688+
}
689+
}
690+
691+
impl SubTraitOfTraitWithSelfTp for MyStruct {}
692+
693+
pub fn test() {
694+
let s = MyStruct { value: 0 };
695+
let _ms = get_a(&s); // $ target=get_a type=_ms:T.MyStruct
696+
}
697+
}
698+
655699
mod function_trait_bounds {
656700
#[derive(Debug, Clone, Copy)]
657701
struct MyThing<T> {
@@ -2753,6 +2797,7 @@ fn main() {
27532797
method_impl::g(method_impl::Foo {}, method_impl::Foo {}); // $ target=g
27542798
method_non_parametric_impl::f(); // $ target=f
27552799
method_non_parametric_trait_impl::f(); // $ target=f
2800+
trait_default_self_type_parameter::test(); // $ target=test
27562801
function_trait_bounds::f(); // $ target=f
27572802
associated_type_in_trait::f(); // $ target=f
27582803
generic_enum::f(); // $ target=f

0 commit comments

Comments
 (0)