Skip to content

Commit 8b03608

Browse files
authored
Merge pull request #21188 from paldepind/rust/self-path-assoc
Rust: Implement type inference for associated types for concrete types
2 parents 4a04f7b + 0567864 commit 8b03608

File tree

10 files changed

+2011
-1416
lines changed

10 files changed

+2011
-1416
lines changed

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

Lines changed: 91 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,15 @@ pragma[nomagic]
110110
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
111111
item = result.getImmediateParent() and
112112
name = result.getName() and
113+
// Associated items in `impl` and `trait` blocks are handled elsewhere
114+
not (item instanceof ImplOrTraitItemNode and result instanceof AssocItem) and
113115
// type parameters are only available inside the declaring item
114116
if result instanceof TypeParam
115117
then kind.isInternal()
116118
else
117-
// associated items must always be qualified, also within the declaring
118-
// item (using `Self`)
119-
if item instanceof ImplOrTraitItemNode and result instanceof AssocItem
120-
then kind.isExternal()
121-
else
122-
if result.isPublic()
123-
then kind.isBoth()
124-
else kind.isInternal()
119+
if result.isPublic()
120+
then kind.isBoth()
121+
else kind.isInternal()
125122
}
126123

127124
private module UseOption = Option<Use>;
@@ -327,30 +324,25 @@ abstract class ItemNode extends Locatable {
327324
)
328325
)
329326
or
330-
// a trait has access to the associated items of its supertraits
331327
this =
332328
any(TraitItemNodeImpl trait |
333-
result = trait.resolveABoundCand().getASuccessor(name, kind, useOpt) and
334-
kind.isExternalOrBoth() and
335-
result instanceof AssocItemNode and
336-
not trait.hasAssocItem(name)
337-
)
329+
result = trait.getAssocItem(name)
330+
or
331+
// a trait has access to the associated items of its supertraits
332+
not trait.hasAssocItem(name) and
333+
result = trait.resolveABoundCand().getASuccessor(name).(AssocItemNode)
334+
) and
335+
kind.isExternal() and
336+
useOpt.isNone()
338337
or
339338
// items made available by an implementation where `this` is the implementing type
340-
typeImplEdge(this, _, name, kind, result, useOpt)
341-
or
342-
// trait items with default implementations made available in an implementation
343-
exists(ImplItemNodeImpl impl, TraitItemNode trait |
344-
this = impl and
345-
trait = impl.resolveTraitTyCand() and
346-
result = trait.getASuccessor(name, kind, useOpt) and
347-
// do not inherit default implementations from super traits; those are inherited by
348-
// their `impl` blocks
349-
result = trait.getAssocItem(name) and
350-
result.(AssocItemNode).hasImplementation() and
351-
kind.isExternalOrBoth() and
352-
not impl.hasAssocItem(name)
353-
)
339+
typeImplEdge(this, _, name, result) and
340+
kind.isExternal() and
341+
useOpt.isNone()
342+
or
343+
implEdge(this, name, result) and
344+
kind.isExternal() and
345+
useOpt.isNone()
354346
or
355347
// type parameters have access to the associated items of its bounds
356348
result =
@@ -413,14 +405,8 @@ abstract class ItemNode extends Locatable {
413405
this instanceof SourceFile and
414406
builtin(name, result)
415407
or
416-
exists(ImplOrTraitItemNode i |
417-
name = "Self" and
418-
this = i.getAnItemInSelfScope()
419-
|
420-
result = i.(Trait)
421-
or
422-
result = i.(ImplItemNodeImpl).resolveSelfTyCand()
423-
)
408+
name = "Self" and
409+
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
424410
or
425411
name = "crate" and
426412
this = result.(CrateItemNode).getASourceFile()
@@ -755,7 +741,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
755741
}
756742

757743
/** Gets an associated item belonging to this trait or `impl` block. */
758-
abstract AssocItemNode getAnAssocItem();
744+
AssocItemNode getAnAssocItem() { result = this.getADescendant() }
759745

760746
/** Gets the associated item named `name` belonging to this trait or `impl` block. */
761747
pragma[nomagic]
@@ -807,12 +793,12 @@ final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
807793

808794
TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) }
809795

810-
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
811-
812796
override string getName() { result = "(impl)" }
813797

814798
override Namespace getNamespace() {
815-
result.isType() // can be referenced with `Self`
799+
// `impl` blocks are referred to using `Self` paths which can appear both as
800+
// types and as values (when the implementing type is a tuple-like struct).
801+
result.isType() or result.isValue()
816802
}
817803

818804
override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) }
@@ -985,6 +971,18 @@ private class ImplItemNodeImpl extends ImplItemNode {
985971
}
986972

987973
TraitItemNodeImpl resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
974+
975+
/**
976+
* Gets the associated item named `name` in this impl block or the default
977+
* inherited from the trait being implemented.
978+
*/
979+
AssocItemNode getAssocItemOrDefault(string name) {
980+
result = this.getAssocItem(name)
981+
or
982+
not this.hasAssocItem(name) and
983+
result = this.resolveTraitTyCand().getAssocItem(name) and
984+
result.hasImplementation()
985+
}
988986
}
989987

990988
private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNode instanceof Struct
@@ -1020,8 +1018,6 @@ final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof T
10201018

10211019
ItemNode resolveABound() { result = this.resolveBound(_) }
10221020

1023-
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
1024-
10251021
override string getName() { result = Trait.super.getName().getText() }
10261022

10271023
override Namespace getNamespace() { result.isType() }
@@ -1790,7 +1786,17 @@ private module DollarCrateResolution {
17901786

17911787
pragma[nomagic]
17921788
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
1793-
result = unqualifiedPathLookup(path, ns, _)
1789+
exists(ItemNode res |
1790+
res = unqualifiedPathLookup(path, ns, _) and
1791+
if
1792+
// `Self` paths that are not used as qualifiers (for instance `Self` in
1793+
// `fn(..) -> Self`) should resolve to the type being implemented.
1794+
not any(PathExt parent).getQualifier() = path and
1795+
isUnqualifiedSelfPath(path) and
1796+
res instanceof ImplItemNode
1797+
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
1798+
else result = res
1799+
)
17941800
or
17951801
DollarCrateResolution::resolveDollarCrate(path, result) and
17961802
ns = result.getNamespace()
@@ -1852,35 +1858,12 @@ private predicate checkQualifiedVisibility(
18521858
not i instanceof TypeParam
18531859
}
18541860

1855-
pragma[nomagic]
1856-
private predicate isImplSelfQualifiedPath(
1857-
ImplItemNode impl, PathExt qualifier, PathExt path, string name
1858-
) {
1859-
qualifier = impl.getASelfPath() and
1860-
qualifier = path.getQualifier() and
1861-
name = path.getText()
1862-
}
1863-
1864-
private ItemNode resolveImplSelfQualified(PathExt qualifier, PathExt path, Namespace ns) {
1865-
exists(ImplItemNode impl, string name |
1866-
isImplSelfQualifiedPath(impl, qualifier, path, name) and
1867-
result = impl.getAssocItem(name) and
1868-
ns = result.getNamespace()
1869-
)
1870-
}
1871-
18721861
/**
18731862
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
18741863
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18751864
*/
18761865
pragma[nomagic]
18771866
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
1878-
// Special case for `Self::Assoc`; this always refers to the associated
1879-
// item in the enclosing `impl` block, if available.
1880-
q = resolvePathCandQualifier(qualifier, path, _) and
1881-
result = resolveImplSelfQualified(qualifier, path, ns)
1882-
or
1883-
not exists(resolveImplSelfQualified(qualifier, path, ns)) and
18841867
exists(string name, SuccessorKind kind, UseOption useOpt |
18851868
q = resolvePathCandQualifier(qualifier, path, name) and
18861869
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1940,6 +1923,37 @@ private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNod
19401923
name = macro.getName()
19411924
}
19421925

1926+
/**
1927+
* Holds if a `Self` path inside `impl` might refer to a function named `name`
1928+
* from another impl block.
1929+
*/
1930+
pragma[nomagic]
1931+
private predicate relevantSelfFunctionName(ImplItemNodeImpl impl, string name) {
1932+
any(Path path | path.getQualifier() = impl.getASelfPath()).getText() = name and
1933+
not impl.hasAssocItem(name)
1934+
}
1935+
1936+
/**
1937+
* Holds if `impl` has a `node` available externally at `name`.
1938+
*
1939+
* Since `Self` in an impl block resolves to the impl block, this corresponds to
1940+
* the items that should be available on `Self` within the `impl` block.
1941+
*/
1942+
private predicate implEdge(ImplItemNodeImpl impl, string name, ItemNode node) {
1943+
node = impl.getAssocItemOrDefault(name)
1944+
or
1945+
// Associated types from the implemented trait are available on `Self`.
1946+
not impl.hasAssocItem(name) and
1947+
node = impl.resolveTraitTyCand().getASuccessor(name).(TypeAliasItemNode)
1948+
or
1949+
// Items available on the implementing type are available on `Self`. We only
1950+
// add these edges when they are relevant. If a type has `n` impl blocks with
1951+
// `m` functions each, we would otherwise end up always constructing something
1952+
// proportional to `O(n * m)`.
1953+
relevantSelfFunctionName(impl, name) and
1954+
node = impl.resolveSelfTyCand().getASuccessor(name)
1955+
}
1956+
19431957
/**
19441958
* Holds if item `i` contains a `mod` or `extern crate` definition that
19451959
* makes the macro `macro` named `name` available using a `#[macro_use]`
@@ -2009,9 +2023,10 @@ private ItemNode resolvePathCand(PathExt path) {
20092023

20102024
/** Get a trait that should be visible when `path` resolves to `node`, if any. */
20112025
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
2012-
exists(TypeItemNode type, ImplItemNodeImpl impl |
2013-
node = resolvePathCandQualified(_, type, path, _) and
2014-
typeImplEdge(type, impl, _, _, node, _) and
2026+
exists(TypeItemNode type, ItemNode qual, ImplItemNodeImpl impl |
2027+
node = resolvePathCandQualified(_, qual, path, _) and
2028+
type = [qual, qual.(ImplItemNodeImpl).resolveSelfTyCand()] and
2029+
typeImplEdge(type, impl, _, node) and
20152030
result = impl.resolveTraitTyCand()
20162031
)
20172032
}
@@ -2179,15 +2194,17 @@ private predicate externCrateEdge(
21792194

21802195
/**
21812196
* Holds if `typeItem` is the implementing type of `impl` and the implementation
2182-
* makes `assoc` available as `name` at `kind`.
2197+
* makes `assoc` available as `name`.
21832198
*/
21842199
private predicate typeImplEdge(
2185-
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind,
2186-
AssocItemNode assoc, UseOption useOpt
2200+
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, AssocItemNode assoc
21872201
) {
2202+
assoc = impl.getAssocItemOrDefault(name) and
21882203
typeItem = impl.resolveSelfTyCand() and
2189-
assoc = impl.getASuccessor(name, kind, useOpt) and
2190-
kind.isExternalOrBoth()
2204+
// Functions in `impl` blocks are made available on the implementing type
2205+
// (e.g., `S::fun` is valid) but associated types are not (e.g., `S::Output`
2206+
// is invalid).
2207+
not assoc instanceof TypeAlias
21912208
}
21922209

21932210
pragma[nomagic]

rust/ql/lib/codeql/rust/internal/typeinference/Type.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ abstract class Type extends TType {
7575
abstract TypeParameter getPositionalTypeParameter(int i);
7676

7777
/** Gets the default type for the `i`th type parameter, if any. */
78-
TypeMention getTypeParameterDefault(int i) { none() }
78+
TypeRepr getTypeParameterDefault(int i) { none() }
7979

8080
/**
8181
* Gets a type parameter of this type.
@@ -129,7 +129,7 @@ class DataType extends Type, TDataType {
129129
result = TTypeParamTypeParameter(typeItem.getGenericParamList().getTypeParam(i))
130130
}
131131

132-
override TypeMention getTypeParameterDefault(int i) {
132+
override TypeRepr getTypeParameterDefault(int i) {
133133
result = typeItem.getGenericParamList().getTypeParam(i).getDefaultType()
134134
}
135135

@@ -189,7 +189,7 @@ class TraitType extends Type, TTrait {
189189
result.(SelfTypeParameter).getTrait() = trait
190190
}
191191

192-
override TypeMention getTypeParameterDefault(int i) {
192+
override TypeRepr getTypeParameterDefault(int i) {
193193
result = trait.getGenericParamList().getTypeParam(i).getDefaultType()
194194
}
195195

rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ class TypePath = M1::TypePath;
134134

135135
module TypePath = M1::TypePath;
136136

137-
private module Input2 implements InputSig2<TypeMention> {
138-
TypeMention getABaseTypeMention(Type t) { none() }
137+
private module Input2 implements InputSig2<PreTypeMention> {
138+
PreTypeMention getABaseTypeMention(Type t) { none() }
139139

140140
Type getATypeParameterConstraint(TypeParameter tp, TypePath path) {
141141
exists(TypeMention tm | result = tm.getTypeAt(path) |
@@ -158,7 +158,7 @@ private module Input2 implements InputSig2<TypeMention> {
158158
* inference module for more information.
159159
*/
160160
predicate conditionSatisfiesConstraint(
161-
TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive
161+
TypeAbstraction abs, PreTypeMention condition, PreTypeMention constraint, boolean transitive
162162
) {
163163
// `impl` blocks implementing traits
164164
transitive = false and
@@ -208,7 +208,7 @@ private module Input2 implements InputSig2<TypeMention> {
208208
}
209209
}
210210

211-
private module M2 = Make2<TypeMention, Input2>;
211+
private module M2 = Make2<PreTypeMention, Input2>;
212212

213213
import M2
214214

@@ -1960,7 +1960,7 @@ private module MethodResolution {
19601960
pragma[nomagic]
19611961
predicate hasTypeQualifiedCandidate(ImplItemNode impl) {
19621962
exists(getCallExprTypeQualifier(this, _)) and
1963-
CallExprImpl::getResolvedFunction(this) = impl.getASuccessor(_)
1963+
CallExprImpl::getResolvedFunction(this) = impl.getADescendant()
19641964
}
19651965

19661966
pragma[nomagic]

rust/ql/lib/codeql/rust/internal/typeinference/TypeInferenceConsistency.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ private import TypeInference::Consistency as Consistency
1010
import TypeInference::Consistency
1111

1212
query predicate illFormedTypeMention(TypeMention tm) {
13-
Consistency::illFormedTypeMention(tm) and
13+
// NOTE: We do not use `illFormedTypeMention` from the shared library as it is
14+
// instantiated with `PreTypeMention` and we are interested in inconsistencies
15+
// for `TypeMention`.
16+
not exists(tm.getTypeAt(TypePath::nil())) and
17+
exists(tm.getLocation()) and
1418
// avoid overlap with `PathTypeMention`
1519
not tm instanceof PathTypeReprMention and
1620
// known limitation for type mentions that would mention an escaping type parameter
@@ -27,7 +31,8 @@ query predicate illFormedTypeMention(TypeMention tm) {
2731
}
2832

2933
query predicate nonUniqueCertainType(AstNode n, TypePath path) {
30-
Consistency::nonUniqueCertainType(n, path, _)
34+
Consistency::nonUniqueCertainType(n, path, _) and
35+
n.fromSource() // Only include inconsistencies in the source.
3136
}
3237

3338
int getTypeInferenceInconsistencyCounts(string type) {

0 commit comments

Comments
 (0)