Merge pull request #21188 from paldepind/rust/self-path-assoc

Rust: Implement type inference for associated types for concrete types
This commit is contained in:
Simon Friis Vindum
2026-02-02 13:50:43 +01:00
committed by GitHub
10 changed files with 2055 additions and 1460 deletions

View File

@@ -110,18 +110,15 @@ pragma[nomagic]
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
item = result.getImmediateParent() and
name = result.getName() and
// Associated items in `impl` and `trait` blocks are handled elsewhere
not (item instanceof ImplOrTraitItemNode and result instanceof AssocItem) and
// type parameters are only available inside the declaring item
if result instanceof TypeParam
then kind.isInternal()
else
// associated items must always be qualified, also within the declaring
// item (using `Self`)
if item instanceof ImplOrTraitItemNode and result instanceof AssocItem
then kind.isExternal()
else
if result.isPublic()
then kind.isBoth()
else kind.isInternal()
if result.isPublic()
then kind.isBoth()
else kind.isInternal()
}
private module UseOption = Option<Use>;
@@ -327,30 +324,25 @@ abstract class ItemNode extends Locatable {
)
)
or
// a trait has access to the associated items of its supertraits
this =
any(TraitItemNodeImpl trait |
result = trait.resolveABoundCand().getASuccessor(name, kind, useOpt) and
kind.isExternalOrBoth() and
result instanceof AssocItemNode and
not trait.hasAssocItem(name)
)
result = trait.getAssocItem(name)
or
// a trait has access to the associated items of its supertraits
not trait.hasAssocItem(name) and
result = trait.resolveABoundCand().getASuccessor(name).(AssocItemNode)
) and
kind.isExternal() and
useOpt.isNone()
or
// items made available by an implementation where `this` is the implementing type
typeImplEdge(this, _, name, kind, result, useOpt)
typeImplEdge(this, _, name, result) and
kind.isExternal() and
useOpt.isNone()
or
// trait items with default implementations made available in an implementation
exists(ImplItemNodeImpl impl, TraitItemNode trait |
this = impl and
trait = impl.resolveTraitTyCand() and
result = trait.getASuccessor(name, kind, useOpt) and
// do not inherit default implementations from super traits; those are inherited by
// their `impl` blocks
result = trait.getAssocItem(name) and
result.(AssocItemNode).hasImplementation() and
kind.isExternalOrBoth() and
not impl.hasAssocItem(name)
)
implEdge(this, name, result) and
kind.isExternal() and
useOpt.isNone()
or
// type parameters have access to the associated items of its bounds
result =
@@ -413,14 +405,8 @@ abstract class ItemNode extends Locatable {
this instanceof SourceFile and
builtin(name, result)
or
exists(ImplOrTraitItemNode i |
name = "Self" and
this = i.getAnItemInSelfScope()
|
result = i.(Trait)
or
result = i.(ImplItemNodeImpl).resolveSelfTyCand()
)
name = "Self" and
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
or
name = "crate" and
this = result.(CrateItemNode).getASourceFile()
@@ -755,7 +741,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
}
/** Gets an associated item belonging to this trait or `impl` block. */
abstract AssocItemNode getAnAssocItem();
AssocItemNode getAnAssocItem() { result = this.getADescendant() }
/** Gets the associated item named `name` belonging to this trait or `impl` block. */
pragma[nomagic]
@@ -807,12 +793,12 @@ final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) }
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
override string getName() { result = "(impl)" }
override Namespace getNamespace() {
result.isType() // can be referenced with `Self`
// `impl` blocks are referred to using `Self` paths which can appear both as
// types and as values (when the implementing type is a tuple-like struct).
result.isType() or result.isValue()
}
override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) }
@@ -985,6 +971,18 @@ private class ImplItemNodeImpl extends ImplItemNode {
}
TraitItemNodeImpl resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
/**
* Gets the associated item named `name` in this impl block or the default
* inherited from the trait being implemented.
*/
AssocItemNode getAssocItemOrDefault(string name) {
result = this.getAssocItem(name)
or
not this.hasAssocItem(name) and
result = this.resolveTraitTyCand().getAssocItem(name) and
result.hasImplementation()
}
}
private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNode instanceof Struct
@@ -1020,8 +1018,6 @@ final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof T
ItemNode resolveABound() { result = this.resolveBound(_) }
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
override string getName() { result = Trait.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
@@ -1790,7 +1786,17 @@ private module DollarCrateResolution {
pragma[nomagic]
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
result = unqualifiedPathLookup(path, ns, _)
exists(ItemNode res |
res = unqualifiedPathLookup(path, ns, _) and
if
// `Self` paths that are not used as qualifiers (for instance `Self` in
// `fn(..) -> Self`) should resolve to the type being implemented.
not any(PathExt parent).getQualifier() = path and
isUnqualifiedSelfPath(path) and
res instanceof ImplItemNode
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
else result = res
)
or
DollarCrateResolution::resolveDollarCrate(path, result) and
ns = result.getNamespace()
@@ -1852,35 +1858,12 @@ private predicate checkQualifiedVisibility(
not i instanceof TypeParam
}
pragma[nomagic]
private predicate isImplSelfQualifiedPath(
ImplItemNode impl, PathExt qualifier, PathExt path, string name
) {
qualifier = impl.getASelfPath() and
qualifier = path.getQualifier() and
name = path.getText()
}
private ItemNode resolveImplSelfQualified(PathExt qualifier, PathExt path, Namespace ns) {
exists(ImplItemNode impl, string name |
isImplSelfQualifiedPath(impl, qualifier, path, name) and
result = impl.getAssocItem(name) and
ns = result.getNamespace()
)
}
/**
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
* qualifier of `path` and `qualifier` resolves to `q`, if any.
*/
pragma[nomagic]
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
// Special case for `Self::Assoc`; this always refers to the associated
// item in the enclosing `impl` block, if available.
q = resolvePathCandQualifier(qualifier, path, _) and
result = resolveImplSelfQualified(qualifier, path, ns)
or
not exists(resolveImplSelfQualified(qualifier, path, ns)) and
exists(string name, SuccessorKind kind, UseOption useOpt |
q = resolvePathCandQualifier(qualifier, path, name) and
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1940,6 +1923,37 @@ private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNod
name = macro.getName()
}
/**
* Holds if a `Self` path inside `impl` might refer to a function named `name`
* from another impl block.
*/
pragma[nomagic]
private predicate relevantSelfFunctionName(ImplItemNodeImpl impl, string name) {
any(Path path | path.getQualifier() = impl.getASelfPath()).getText() = name and
not impl.hasAssocItem(name)
}
/**
* Holds if `impl` has a `node` available externally at `name`.
*
* Since `Self` in an impl block resolves to the impl block, this corresponds to
* the items that should be available on `Self` within the `impl` block.
*/
private predicate implEdge(ImplItemNodeImpl impl, string name, ItemNode node) {
node = impl.getAssocItemOrDefault(name)
or
// Associated types from the implemented trait are available on `Self`.
not impl.hasAssocItem(name) and
node = impl.resolveTraitTyCand().getASuccessor(name).(TypeAliasItemNode)
or
// Items available on the implementing type are available on `Self`. We only
// add these edges when they are relevant. If a type has `n` impl blocks with
// `m` functions each, we would otherwise end up always constructing something
// proportional to `O(n * m)`.
relevantSelfFunctionName(impl, name) and
node = impl.resolveSelfTyCand().getASuccessor(name)
}
/**
* Holds if item `i` contains a `mod` or `extern crate` definition that
* makes the macro `macro` named `name` available using a `#[macro_use]`
@@ -2009,9 +2023,10 @@ private ItemNode resolvePathCand(PathExt path) {
/** Get a trait that should be visible when `path` resolves to `node`, if any. */
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
exists(TypeItemNode type, ImplItemNodeImpl impl |
node = resolvePathCandQualified(_, type, path, _) and
typeImplEdge(type, impl, _, _, node, _) and
exists(TypeItemNode type, ItemNode qual, ImplItemNodeImpl impl |
node = resolvePathCandQualified(_, qual, path, _) and
type = [qual, qual.(ImplItemNodeImpl).resolveSelfTyCand()] and
typeImplEdge(type, impl, _, node) and
result = impl.resolveTraitTyCand()
)
}
@@ -2179,15 +2194,17 @@ private predicate externCrateEdge(
/**
* Holds if `typeItem` is the implementing type of `impl` and the implementation
* makes `assoc` available as `name` at `kind`.
* makes `assoc` available as `name`.
*/
private predicate typeImplEdge(
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind,
AssocItemNode assoc, UseOption useOpt
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, AssocItemNode assoc
) {
assoc = impl.getAssocItemOrDefault(name) and
typeItem = impl.resolveSelfTyCand() and
assoc = impl.getASuccessor(name, kind, useOpt) and
kind.isExternalOrBoth()
// Functions in `impl` blocks are made available on the implementing type
// (e.g., `S::fun` is valid) but associated types are not (e.g., `S::Output`
// is invalid).
not assoc instanceof TypeAlias
}
pragma[nomagic]

View File

@@ -75,7 +75,7 @@ abstract class Type extends TType {
abstract TypeParameter getPositionalTypeParameter(int i);
/** Gets the default type for the `i`th type parameter, if any. */
TypeMention getTypeParameterDefault(int i) { none() }
TypeRepr getTypeParameterDefault(int i) { none() }
/**
* Gets a type parameter of this type.
@@ -129,7 +129,7 @@ class DataType extends Type, TDataType {
result = TTypeParamTypeParameter(typeItem.getGenericParamList().getTypeParam(i))
}
override TypeMention getTypeParameterDefault(int i) {
override TypeRepr getTypeParameterDefault(int i) {
result = typeItem.getGenericParamList().getTypeParam(i).getDefaultType()
}
@@ -189,7 +189,7 @@ class TraitType extends Type, TTrait {
result.(SelfTypeParameter).getTrait() = trait
}
override TypeMention getTypeParameterDefault(int i) {
override TypeRepr getTypeParameterDefault(int i) {
result = trait.getGenericParamList().getTypeParam(i).getDefaultType()
}

View File

@@ -134,8 +134,8 @@ class TypePath = M1::TypePath;
module TypePath = M1::TypePath;
private module Input2 implements InputSig2<TypeMention> {
TypeMention getABaseTypeMention(Type t) { none() }
private module Input2 implements InputSig2<PreTypeMention> {
PreTypeMention getABaseTypeMention(Type t) { none() }
Type getATypeParameterConstraint(TypeParameter tp, TypePath path) {
exists(TypeMention tm | result = tm.getTypeAt(path) |
@@ -158,7 +158,7 @@ private module Input2 implements InputSig2<TypeMention> {
* inference module for more information.
*/
predicate conditionSatisfiesConstraint(
TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive
TypeAbstraction abs, PreTypeMention condition, PreTypeMention constraint, boolean transitive
) {
// `impl` blocks implementing traits
transitive = false and
@@ -208,7 +208,7 @@ private module Input2 implements InputSig2<TypeMention> {
}
}
private module M2 = Make2<TypeMention, Input2>;
private module M2 = Make2<PreTypeMention, Input2>;
import M2
@@ -1960,7 +1960,7 @@ private module MethodResolution {
pragma[nomagic]
predicate hasTypeQualifiedCandidate(ImplItemNode impl) {
exists(getCallExprTypeQualifier(this, _)) and
CallExprImpl::getResolvedFunction(this) = impl.getASuccessor(_)
CallExprImpl::getResolvedFunction(this) = impl.getADescendant()
}
pragma[nomagic]

View File

@@ -10,7 +10,11 @@ private import TypeInference::Consistency as Consistency
import TypeInference::Consistency
query predicate illFormedTypeMention(TypeMention tm) {
Consistency::illFormedTypeMention(tm) and
// NOTE: We do not use `illFormedTypeMention` from the shared library as it is
// instantiated with `PreTypeMention` and we are interested in inconsistencies
// for `TypeMention`.
not exists(tm.getTypeAt(TypePath::nil())) and
exists(tm.getLocation()) and
// avoid overlap with `PathTypeMention`
not tm instanceof PathTypeReprMention and
// known limitation for type mentions that would mention an escaping type parameter
@@ -27,7 +31,8 @@ query predicate illFormedTypeMention(TypeMention tm) {
}
query predicate nonUniqueCertainType(AstNode n, TypePath path) {
Consistency::nonUniqueCertainType(n, path, _)
Consistency::nonUniqueCertainType(n, path, _) and
n.fromSource() // Only include inconsistencies in the source.
}
int getTypeInferenceInconsistencyCounts(string type) {

File diff suppressed because it is too large Load Diff