Skip to content

Commit 963568b

Browse files
committed
feat: IDE features for primitive tuple fields
1 parent 5945709 commit 963568b

25 files changed

+242
-96
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-def/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ pub struct FieldId {
307307

308308
pub type LocalFieldId = Idx<data::adt::FieldData>;
309309

310+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
311+
pub struct TupleId(pub u32);
312+
313+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
314+
pub struct TupleFieldId {
315+
pub tuple: TupleId,
316+
pub index: u32,
317+
}
318+
310319
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
311320
pub struct ConstId(salsa::InternId);
312321
type ConstLoc = AssocItemLoc<Const>;

crates/hir-ty/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ once_cell = "1.17.0"
3232
triomphe.workspace = true
3333
nohash-hasher.workspace = true
3434
typed-arena = "2.0.1"
35+
indexmap.workspace = true
3536

3637
rustc-dependencies.workspace = true
3738

@@ -60,4 +61,4 @@ test-fixture.workspace = true
6061
in-rust-tree = ["rustc-dependencies/in-rust-tree"]
6162

6263
[lints]
63-
workspace = true
64+
workspace = true

crates/hir-ty/src/infer.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ use hir_def::{
4141
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
4242
type_ref::TypeRef,
4343
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
44-
TraitId, TypeAliasId, VariantId,
44+
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
4545
};
4646
use hir_expand::name::{name, Name};
47+
use indexmap::IndexSet;
4748
use la_arena::{ArenaMap, Entry};
4849
use rustc_hash::{FxHashMap, FxHashSet};
4950
use stdx::{always, never};
@@ -403,11 +404,15 @@ pub struct InferenceResult {
403404
/// For each method call expr, records the function it resolves to.
404405
method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>,
405406
/// For each field access expr, records the field it resolves to.
406-
field_resolutions: FxHashMap<ExprId, FieldId>,
407+
field_resolutions: FxHashMap<ExprId, Either<FieldId, TupleFieldId>>,
407408
/// For each struct literal or pattern, records the variant it resolves to.
408409
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
409410
/// For each associated item record what it resolves to
410411
assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Substitution)>,
412+
/// Whenever a tuple field expression access a tuple field, we allocate a tuple id in
413+
/// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
414+
/// that which allows us to resolve a [`TupleFieldId`]s type.
415+
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
411416
pub diagnostics: Vec<InferenceDiagnostic>,
412417
pub type_of_expr: ArenaMap<ExprId, Ty>,
413418
/// For each pattern record the type it resolves to.
@@ -447,7 +452,7 @@ impl InferenceResult {
447452
pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> {
448453
self.method_resolutions.get(&expr).cloned()
449454
}
450-
pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
455+
pub fn field_resolution(&self, expr: ExprId) -> Option<Either<FieldId, TupleFieldId>> {
451456
self.field_resolutions.get(&expr).copied()
452457
}
453458
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
@@ -517,6 +522,8 @@ pub(crate) struct InferenceContext<'a> {
517522
/// The traits in scope, disregarding block modules. This is used for caching purposes.
518523
traits_in_scope: FxHashSet<TraitId>,
519524
pub(crate) result: InferenceResult,
525+
tuple_field_accesses_rev:
526+
IndexSet<Substitution, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>,
520527
/// The return type of the function being inferred, the closure or async block if we're
521528
/// currently within one.
522529
///
@@ -598,6 +605,7 @@ impl<'a> InferenceContext<'a> {
598605
InferenceContext {
599606
result: InferenceResult::default(),
600607
table: unify::InferenceTable::new(db, trait_env),
608+
tuple_field_accesses_rev: Default::default(),
601609
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
602610
resume_yield_tys: None,
603611
return_coercion: None,
@@ -621,7 +629,13 @@ impl<'a> InferenceContext<'a> {
621629
// used this function for another workaround, mention it here. If you really need this function and believe that
622630
// there is no problem in it being `pub(crate)`, remove this comment.
623631
pub(crate) fn resolve_all(self) -> InferenceResult {
624-
let InferenceContext { mut table, mut result, deferred_cast_checks, .. } = self;
632+
let InferenceContext {
633+
mut table,
634+
mut result,
635+
deferred_cast_checks,
636+
tuple_field_accesses_rev,
637+
..
638+
} = self;
625639
// Destructure every single field so whenever new fields are added to `InferenceResult` we
626640
// don't forget to handle them here.
627641
let InferenceResult {
@@ -645,6 +659,7 @@ impl<'a> InferenceContext<'a> {
645659
// to resolve them here.
646660
closure_info: _,
647661
mutated_bindings_in_closure: _,
662+
tuple_field_access_types: _,
648663
} = &mut result;
649664

650665
table.fallback_if_possible();
@@ -720,6 +735,11 @@ impl<'a> InferenceContext<'a> {
720735
for adjustment in pat_adjustments.values_mut().flatten() {
721736
*adjustment = table.resolve_completely(adjustment.clone());
722737
}
738+
result.tuple_field_access_types = tuple_field_accesses_rev
739+
.into_iter()
740+
.enumerate()
741+
.map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst.clone())))
742+
.collect();
723743
result
724744
}
725745

crates/hir-ty/src/infer/closure.rs

+23-16
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ use chalk_ir::{
77
fold::{FallibleTypeFolder, TypeFoldable},
88
AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
99
};
10+
use either::Either;
1011
use hir_def::{
1112
data::adt::VariantData,
1213
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
1314
lang_item::LangItem,
1415
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
15-
DefWithBodyId, FieldId, HasModule, VariantId,
16+
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
1617
};
1718
use hir_expand::name;
1819
use rustc_hash::FxHashMap;
@@ -186,7 +187,7 @@ impl CapturedItem {
186187
result = format!("*{result}");
187188
field_need_paren = true;
188189
}
189-
ProjectionElem::Field(f) => {
190+
ProjectionElem::Field(Either::Left(f)) => {
190191
if field_need_paren {
191192
result = format!("({result})");
192193
}
@@ -207,7 +208,15 @@ impl CapturedItem {
207208
result = format!("{result}.{field}");
208209
field_need_paren = false;
209210
}
210-
&ProjectionElem::TupleOrClosureField(field) => {
211+
ProjectionElem::Field(Either::Right(f)) => {
212+
let field = f.index;
213+
if field_need_paren {
214+
result = format!("({result})");
215+
}
216+
result = format!("{result}.{field}");
217+
field_need_paren = false;
218+
}
219+
&ProjectionElem::ClosureField(field) => {
211220
if field_need_paren {
212221
result = format!("({result})");
213222
}
@@ -329,15 +338,10 @@ impl InferenceContext<'_> {
329338
}
330339
}
331340
}
332-
Expr::Field { expr, name } => {
341+
Expr::Field { expr, name: _ } => {
333342
let mut place = self.place_of_expr(*expr)?;
334-
if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
335-
let index = name.as_tuple_index()?;
336-
place.projections.push(ProjectionElem::TupleOrClosureField(index))
337-
} else {
338-
let field = self.result.field_resolution(tgt_expr)?;
339-
place.projections.push(ProjectionElem::Field(field));
340-
}
343+
let field = self.result.field_resolution(tgt_expr)?;
344+
place.projections.push(ProjectionElem::Field(field));
341345
return Some(place);
342346
}
343347
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
@@ -825,7 +829,10 @@ impl InferenceContext<'_> {
825829
let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
826830
for (arg, i) in it {
827831
let mut p = place.clone();
828-
p.projections.push(ProjectionElem::TupleOrClosureField(i));
832+
p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
833+
tuple: TupleId(!0), // dummy this, as its unused anyways
834+
index: i as u32,
835+
})));
829836
self.consume_with_pat(p, *arg);
830837
}
831838
}
@@ -850,10 +857,10 @@ impl InferenceContext<'_> {
850857
continue;
851858
};
852859
let mut p = place.clone();
853-
p.projections.push(ProjectionElem::Field(FieldId {
860+
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
854861
parent: variant.into(),
855862
local_id,
856-
}));
863+
})));
857864
self.consume_with_pat(p, arg);
858865
}
859866
}
@@ -894,10 +901,10 @@ impl InferenceContext<'_> {
894901
al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
895902
for (arg, (i, _)) in it {
896903
let mut p = place.clone();
897-
p.projections.push(ProjectionElem::Field(FieldId {
904+
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
898905
parent: variant.into(),
899906
local_id: i,
900-
}));
907+
})));
901908
self.consume_with_pat(p, *arg);
902909
}
903910
}

crates/hir-ty/src/infer/expr.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ use std::{
66
};
77

88
use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
9+
use either::Either;
910
use hir_def::{
1011
generics::TypeOrConstParamData,
1112
hir::{
1213
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
1314
},
1415
lang_item::{LangItem, LangItemTarget},
1516
path::{GenericArg, GenericArgs},
16-
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
17+
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
1718
};
1819
use hir_expand::name::{name, Name};
1920
use stdx::always;
@@ -1406,7 +1407,7 @@ impl InferenceContext<'_> {
14061407
&mut self,
14071408
receiver_ty: &Ty,
14081409
name: &Name,
1409-
) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
1410+
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
14101411
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
14111412
let mut private_field = None;
14121413
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
@@ -1418,7 +1419,20 @@ impl InferenceContext<'_> {
14181419
.get(idx)
14191420
.map(|a| a.assert_ty_ref(Interner))
14201421
.cloned()
1421-
.map(|ty| (None, ty))
1422+
.map(|ty| {
1423+
(
1424+
Either::Right(TupleFieldId {
1425+
tuple: TupleId(
1426+
self.tuple_field_accesses_rev
1427+
.insert_full(substs.clone())
1428+
.0
1429+
as u32,
1430+
),
1431+
index: idx as u32,
1432+
}),
1433+
ty,
1434+
)
1435+
})
14221436
});
14231437
}
14241438
TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
@@ -1444,7 +1458,7 @@ impl InferenceContext<'_> {
14441458
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
14451459
.clone()
14461460
.substitute(Interner, &parameters);
1447-
Some((Some(field_id), ty))
1461+
Some((Either::Left(field_id), ty))
14481462
});
14491463

14501464
Some(match res {
@@ -1464,7 +1478,7 @@ impl InferenceContext<'_> {
14641478
let ty = self.insert_type_vars(ty);
14651479
let ty = self.normalize_associated_types_in(ty);
14661480

1467-
(ty, Some(field_id), adjustments, false)
1481+
(ty, Either::Left(field_id), adjustments, false)
14681482
}
14691483
})
14701484
}
@@ -1487,11 +1501,9 @@ impl InferenceContext<'_> {
14871501
match self.lookup_field(&receiver_ty, name) {
14881502
Some((ty, field_id, adjustments, is_public)) => {
14891503
self.write_expr_adj(receiver, adjustments);
1490-
if let Some(field_id) = field_id {
1491-
self.result.field_resolutions.insert(tgt_expr, field_id);
1492-
}
1504+
self.result.field_resolutions.insert(tgt_expr, field_id);
14931505
if !is_public {
1494-
if let Some(field) = field_id {
1506+
if let Either::Left(field) = field_id {
14951507
// FIXME: Merge this diagnostic into UnresolvedField?
14961508
self.result
14971509
.diagnostics
@@ -1581,9 +1593,7 @@ impl InferenceContext<'_> {
15811593
{
15821594
Some((ty, field_id, adjustments, _public)) => {
15831595
self.write_expr_adj(receiver, adjustments);
1584-
if let Some(field_id) = field_id {
1585-
self.result.field_resolutions.insert(tgt_expr, field_id);
1586-
}
1596+
self.result.field_resolutions.insert(tgt_expr, field_id);
15871597
Some(ty)
15881598
}
15891599
None => None,

crates/hir-ty/src/mir.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use crate::{
1414
};
1515
use base_db::CrateId;
1616
use chalk_ir::Mutability;
17+
use either::Either;
1718
use hir_def::{
1819
hir::{BindingId, Expr, ExprId, Ordering, PatId},
19-
DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
20+
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
2021
};
2122
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
2223

@@ -124,9 +125,9 @@ impl Operand {
124125
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
125126
pub enum ProjectionElem<V, T> {
126127
Deref,
127-
Field(FieldId),
128+
Field(Either<FieldId, TupleFieldId>),
128129
// FIXME: get rid of this, and use FieldId for tuples and closures
129-
TupleOrClosureField(usize),
130+
ClosureField(usize),
130131
Index(V),
131132
ConstantIndex { offset: u64, from_end: bool },
132133
Subslice { from: u64, to: u64 },
@@ -161,7 +162,7 @@ impl<V, T> ProjectionElem<V, T> {
161162
return TyKind::Error.intern(Interner);
162163
}
163164
},
164-
ProjectionElem::Field(f) => match &base.kind(Interner) {
165+
ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) {
165166
TyKind::Adt(_, subst) => {
166167
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
167168
}
@@ -170,19 +171,25 @@ impl<V, T> ProjectionElem<V, T> {
170171
return TyKind::Error.intern(Interner);
171172
}
172173
},
173-
ProjectionElem::TupleOrClosureField(f) => match &base.kind(Interner) {
174+
ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) {
174175
TyKind::Tuple(_, subst) => subst
175176
.as_slice(Interner)
176-
.get(*f)
177+
.get(f.index as usize)
177178
.map(|x| x.assert_ty_ref(Interner))
178179
.cloned()
179180
.unwrap_or_else(|| {
180181
never!("Out of bound tuple field");
181182
TyKind::Error.intern(Interner)
182183
}),
184+
_ => {
185+
never!("Only tuple has tuple field");
186+
return TyKind::Error.intern(Interner);
187+
}
188+
},
189+
ProjectionElem::ClosureField(f) => match &base.kind(Interner) {
183190
TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
184191
_ => {
185-
never!("Only tuple or closure has tuple or closure field");
192+
never!("Only closure has closure field");
186193
return TyKind::Error.intern(Interner);
187194
}
188195
},

crates/hir-ty/src/mir/borrowck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
205205
| ProjectionElem::ConstantIndex { .. }
206206
| ProjectionElem::Subslice { .. }
207207
| ProjectionElem::Field(_)
208-
| ProjectionElem::TupleOrClosureField(_)
208+
| ProjectionElem::ClosureField(_)
209209
| ProjectionElem::Index(_) => {
210210
is_part_of = true;
211211
}

0 commit comments

Comments
 (0)