Skip to content

Commit 2abea11

Browse files
committed
Add section on type-relative name-resolution
1 parent 3eb2b8c commit 2abea11

1 file changed

Lines changed: 195 additions & 2 deletions

File tree

src/names/name-resolution.md

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,201 @@ r[names.resolution.primary]
558558
559559
r[names.resolution.type-relative]
560560
## Type-relative resolution
561-
> [!NOTE]
562-
> This is a placeholder for future expansion about type-dependent resolution.
561+
562+
r[names.resolution.type-relative.intro]
563+
_Type-relative resolution_ is the process of tying paths that depend on type
564+
information to the declarations of those entities once that type information is
565+
available in the compiler. This stage of name resolution exclusively applies to
566+
a subset of [Qualified Paths].
567+
568+
Specifically, Qualified Paths from:
569+
570+
- Types
571+
- Expressions
572+
- Patterns
573+
- Structs & Tuple Structs
574+
- these include enums variant structs
575+
576+
Type relative paths fall into the following two categories
577+
578+
- Associated Items
579+
- Variants
580+
581+
`Trait::assoc_item`, `<Type as Trait>::assoc_item` and `Enum::Variant` are
582+
resolved during late resolution. This works because the resolver tracks an
583+
associated `Module` for enums and traits where it can directly lookup these
584+
candidates purely based on information within the enum/trait's original
585+
definition.
586+
587+
`Type::assoc_item`, `<Type>::assoc_item`, `<Enum>::Variant` and
588+
`EnumTyAlias::Variant` are resolved during type checking. These cannot be
589+
resolved earlier because the type checker needs to resolve the trait an
590+
associated item comes from or the definition an alias points to.
591+
592+
r[names.resolution.type-relative.stages]
593+
These type relative paths are resolved separately in item definitions and in function bodies.
594+
595+
r[names.resolution.type-relative.stages.items]
596+
Items:
597+
598+
Type::AssocTy
599+
600+
```rust
601+
// ty path
602+
fn foo<T: Trait>() -> T::AssocTy { .. } // AssocTy can come from a trait and it could be ambiguous with an enum variant
603+
// or an inherent associated type (on unstable)
604+
```
605+
606+
r[names.resolution.type-relative.stages.bodies]
607+
fn bodies:
608+
609+
r[names.resolution.type-relative.stages.bodies.path-expressions]
610+
Path expressions
611+
612+
r[names.resolution.type-relative.stages.bodies.path-expressions.enum-variant-via-alias]
613+
enum variant via alias
614+
615+
```rust
616+
enum Enum {
617+
Variant,
618+
}
619+
type Alias = Enum;
620+
fn main() {
621+
match Enum::Variant {
622+
Alias::Variant => {}
623+
}
624+
}
625+
```
626+
627+
Fully qualified paths for enums are also resolved as type relative names. This
628+
is mainly because the implementation is simpler this way not because this
629+
information would not otherwise be available during primary resolution.
630+
631+
```rust
632+
<Enum>::Variant
633+
```
634+
635+
r[names.resolution.type-relative.stages.bodies.path-expressions.associated-const]
636+
associated consts
637+
638+
```rust
639+
struct Foo;
640+
641+
impl Foo {
642+
const C: u8 = 8;
643+
}
644+
645+
fn eight() -> u8 {
646+
Foo::C
647+
}
648+
```
649+
650+
When enum variants share the same name as associated types or consts, the enum variant candidate is given preference during resolution
651+
652+
653+
r[names.resolution.type-relative.stages.bodies.call-expressions]
654+
fully qualified call expressions:
655+
656+
```rust
657+
Type::assoc_func();
658+
```
659+
660+
- Call expressions
661+
- inherent associated functions
662+
- trait associated functions
663+
- methods via fqcs
664+
- No autoderefs are performed
665+
- static methods are included
666+
- lookup is done based on the type each implementation is for
667+
- what does this actually mean?
668+
669+
r[names.resolution.type-relative.stages.bodies.call-expressions.differences-from-method-resolution]
670+
671+
r[names.resolution.type-relative.stages.bodies.call-expressions.candidate-selection]
672+
673+
"lookup is done based on the type each implementation is for"
674+
similar to method resolution, we look at all impls applicable
675+
for Type. Where "all impls applicable" is a mess, consider
676+
the following
677+
678+
```rust
679+
struct Foo;
680+
impl Foo {
681+
fn inherent_func() {}
682+
}
683+
684+
trait Trait: Sized {
685+
fn trait_method() -> Self {
686+
todo!();
687+
}
688+
}
689+
impl Trait for Foo {}
690+
691+
fn main() {
692+
//<_>::inherent_func(); // ERROR: unable to get the assoc item
693+
//<_>::trait_method(); // ERROR: chose trait method, unable to infer the self type
694+
let _: Foo = <_>::trait_method(); // OK
695+
}
696+
```
697+
698+
- we only consider trait items for <_> but also consider - and prefer - inherent impls if the self type is not an infer var
699+
- There would be no way to refer to inherent associated items otherwise, we lack a disambiguation syntax in their favor
700+
701+
```rust
702+
struct Foo;
703+
impl Foo {
704+
fn overlap(x: Foo) {
705+
println!("inherent");
706+
}
707+
}
708+
709+
trait Trait: Sized {
710+
fn overlap(x: Self) {
711+
println!("trait");
712+
}
713+
}
714+
impl Trait for Foo {}
715+
716+
fn main() {
717+
<_>::overlap(Foo); // trait
718+
<Foo>::overlap(Foo); // inherent
719+
}
720+
```
721+
722+
- as types and traits are in the same namespace, their disambiguation is only relevant for inherent methods of trait objects without dyn i think
723+
- candidate preference is a mess, the trait candidate for dyn Trait gets treated as an inherent candidate instead of a trait candidate wrt to candidate preference
724+
725+
```rust
726+
trait Trait {
727+
fn assoc(&self) {
728+
println!("trait");
729+
}
730+
}
731+
impl Trait for i32 {}
732+
impl dyn Trait {
733+
fn assoc(&self) {
734+
println!("inherent");
735+
}
736+
}
737+
738+
fn main() {
739+
let x: &dyn Trait = &1;
740+
Trait::assoc(x); // trait
741+
<Trait>::assoc(x); // ambiguous
742+
<Trait as Trait>::assoc(x); // trait
743+
x.assoc(); // ambiguous
744+
}
745+
```
746+
747+
struct literals?
748+
749+
https://github.com/rust-lang/rust/blob/main/compiler/rustc_hir_typeck/src/method/mod.rs#L494
750+
https://github.com/rust-lang/rust/blob/main/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs#L295-L299
751+
https://github.com/rust-lang/rust/blob/main/compiler/rustc_resolve/src/late.rs#L475-L490
752+
https://github.com/rust-lang/rust/blob/main/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs#L1371
753+
- If you chase down where `QPath::TypeRelative` gets constructed you primarily
754+
end up at the callers of `lower_qpath`
755+
- lower_pat_mut for PatKinds TupleStruct, Path, and Struct
563756

564757
[AST]: glossary.ast
565758
[Builtin attributes]: ./preludes.md#r-names.preludes.lang

0 commit comments

Comments
 (0)