Skip to content

feat: IDE features for primitive tuple fields #16279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions crates/hir-def/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,15 @@ pub struct FieldId {

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

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TupleId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TupleFieldId {
pub tuple: TupleId,
pub index: u32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId);
type ConstLoc = AssocItemLoc<Const>;
Expand Down
3 changes: 2 additions & 1 deletion crates/hir-ty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ once_cell = "1.17.0"
triomphe.workspace = true
nohash-hasher.workspace = true
typed-arena = "2.0.1"
indexmap.workspace = true

rustc-dependencies.workspace = true

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

[lints]
workspace = true
workspace = true
28 changes: 24 additions & 4 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ use hir_def::{
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
TraitId, TypeAliasId, VariantId,
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
use indexmap::IndexSet;
use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never};
Expand Down Expand Up @@ -403,11 +404,15 @@ pub struct InferenceResult {
/// For each method call expr, records the function it resolves to.
method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>,
/// For each field access expr, records the field it resolves to.
field_resolutions: FxHashMap<ExprId, FieldId>,
field_resolutions: FxHashMap<ExprId, Either<FieldId, TupleFieldId>>,
/// For each struct literal or pattern, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to
assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Substitution)>,
/// Whenever a tuple field expression access a tuple field, we allocate a tuple id in
/// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
/// that which allows us to resolve a [`TupleFieldId`]s type.
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to.
Expand Down Expand Up @@ -447,7 +452,7 @@ impl InferenceResult {
pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> {
self.method_resolutions.get(&expr).cloned()
}
pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
pub fn field_resolution(&self, expr: ExprId) -> Option<Either<FieldId, TupleFieldId>> {
self.field_resolutions.get(&expr).copied()
}
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
Expand Down Expand Up @@ -517,6 +522,8 @@ pub(crate) struct InferenceContext<'a> {
/// The traits in scope, disregarding block modules. This is used for caching purposes.
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
tuple_field_accesses_rev:
IndexSet<Substitution, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>,
/// The return type of the function being inferred, the closure or async block if we're
/// currently within one.
///
Expand Down Expand Up @@ -598,6 +605,7 @@ impl<'a> InferenceContext<'a> {
InferenceContext {
result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env),
tuple_field_accesses_rev: Default::default(),
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
resume_yield_tys: None,
return_coercion: None,
Expand All @@ -621,7 +629,13 @@ impl<'a> InferenceContext<'a> {
// used this function for another workaround, mention it here. If you really need this function and believe that
// there is no problem in it being `pub(crate)`, remove this comment.
pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, deferred_cast_checks, .. } = self;
let InferenceContext {
mut table,
mut result,
deferred_cast_checks,
tuple_field_accesses_rev,
..
} = self;
// Destructure every single field so whenever new fields are added to `InferenceResult` we
// don't forget to handle them here.
let InferenceResult {
Expand All @@ -645,6 +659,7 @@ impl<'a> InferenceContext<'a> {
// to resolve them here.
closure_info: _,
mutated_bindings_in_closure: _,
tuple_field_access_types: _,
} = &mut result;

table.fallback_if_possible();
Expand Down Expand Up @@ -720,6 +735,11 @@ impl<'a> InferenceContext<'a> {
for adjustment in pat_adjustments.values_mut().flatten() {
*adjustment = table.resolve_completely(adjustment.clone());
}
result.tuple_field_access_types = tuple_field_accesses_rev
.into_iter()
.enumerate()
.map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst.clone())))
.collect();
result
}

Expand Down
39 changes: 23 additions & 16 deletions crates/hir-ty/src/infer/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use chalk_ir::{
fold::{FallibleTypeFolder, TypeFoldable},
AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
};
use either::Either;
use hir_def::{
data::adt::VariantData,
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, FieldId, HasModule, VariantId,
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
};
use hir_expand::name;
use rustc_hash::FxHashMap;
Expand Down Expand Up @@ -186,7 +187,7 @@ impl CapturedItem {
result = format!("*{result}");
field_need_paren = true;
}
ProjectionElem::Field(f) => {
ProjectionElem::Field(Either::Left(f)) => {
if field_need_paren {
result = format!("({result})");
}
Expand All @@ -207,7 +208,15 @@ impl CapturedItem {
result = format!("{result}.{field}");
field_need_paren = false;
}
&ProjectionElem::TupleOrClosureField(field) => {
ProjectionElem::Field(Either::Right(f)) => {
let field = f.index;
if field_need_paren {
result = format!("({result})");
}
result = format!("{result}.{field}");
field_need_paren = false;
}
&ProjectionElem::ClosureField(field) => {
if field_need_paren {
result = format!("({result})");
}
Expand Down Expand Up @@ -329,15 +338,10 @@ impl InferenceContext<'_> {
}
}
}
Expr::Field { expr, name } => {
Expr::Field { expr, name: _ } => {
let mut place = self.place_of_expr(*expr)?;
if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
let index = name.as_tuple_index()?;
place.projections.push(ProjectionElem::TupleOrClosureField(index))
} else {
let field = self.result.field_resolution(tgt_expr)?;
place.projections.push(ProjectionElem::Field(field));
}
let field = self.result.field_resolution(tgt_expr)?;
place.projections.push(ProjectionElem::Field(field));
return Some(place);
}
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
Expand Down Expand Up @@ -825,7 +829,10 @@ impl InferenceContext<'_> {
let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
for (arg, i) in it {
let mut p = place.clone();
p.projections.push(ProjectionElem::TupleOrClosureField(i));
p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
tuple: TupleId(!0), // dummy this, as its unused anyways
index: i as u32,
})));
self.consume_with_pat(p, *arg);
}
}
Expand All @@ -850,10 +857,10 @@ impl InferenceContext<'_> {
continue;
};
let mut p = place.clone();
p.projections.push(ProjectionElem::Field(FieldId {
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
parent: variant.into(),
local_id,
}));
})));
self.consume_with_pat(p, arg);
}
}
Expand Down Expand Up @@ -894,10 +901,10 @@ impl InferenceContext<'_> {
al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
for (arg, (i, _)) in it {
let mut p = place.clone();
p.projections.push(ProjectionElem::Field(FieldId {
p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
parent: variant.into(),
local_id: i,
}));
})));
self.consume_with_pat(p, *arg);
}
}
Expand Down
34 changes: 22 additions & 12 deletions crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use std::{
};

use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
use either::Either;
use hir_def::{
generics::TypeOrConstParamData,
hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs},
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
};
use hir_expand::name::{name, Name};
use stdx::always;
Expand Down Expand Up @@ -1406,7 +1407,7 @@ impl InferenceContext<'_> {
&mut self,
receiver_ty: &Ty,
name: &Name,
) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
Expand All @@ -1418,7 +1419,20 @@ impl InferenceContext<'_> {
.get(idx)
.map(|a| a.assert_ty_ref(Interner))
.cloned()
.map(|ty| (None, ty))
.map(|ty| {
(
Either::Right(TupleFieldId {
tuple: TupleId(
self.tuple_field_accesses_rev
.insert_full(substs.clone())
.0
as u32,
),
index: idx as u32,
}),
ty,
)
})
});
}
TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
Expand All @@ -1444,7 +1458,7 @@ impl InferenceContext<'_> {
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
.clone()
.substitute(Interner, &parameters);
Some((Some(field_id), ty))
Some((Either::Left(field_id), ty))
});

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

(ty, Some(field_id), adjustments, false)
(ty, Either::Left(field_id), adjustments, false)
}
})
}
Expand All @@ -1487,11 +1501,9 @@ impl InferenceContext<'_> {
match self.lookup_field(&receiver_ty, name) {
Some((ty, field_id, adjustments, is_public)) => {
self.write_expr_adj(receiver, adjustments);
if let Some(field_id) = field_id {
self.result.field_resolutions.insert(tgt_expr, field_id);
}
self.result.field_resolutions.insert(tgt_expr, field_id);
if !is_public {
if let Some(field) = field_id {
if let Either::Left(field) = field_id {
// FIXME: Merge this diagnostic into UnresolvedField?
self.result
.diagnostics
Expand Down Expand Up @@ -1581,9 +1593,7 @@ impl InferenceContext<'_> {
{
Some((ty, field_id, adjustments, _public)) => {
self.write_expr_adj(receiver, adjustments);
if let Some(field_id) = field_id {
self.result.field_resolutions.insert(tgt_expr, field_id);
}
self.result.field_resolutions.insert(tgt_expr, field_id);
Some(ty)
}
None => None,
Expand Down
21 changes: 14 additions & 7 deletions crates/hir-ty/src/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ use crate::{
};
use base_db::CrateId;
use chalk_ir::Mutability;
use either::Either;
use hir_def::{
hir::{BindingId, Expr, ExprId, Ordering, PatId},
DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};

Expand Down Expand Up @@ -124,9 +125,9 @@ impl Operand {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectionElem<V, T> {
Deref,
Field(FieldId),
Field(Either<FieldId, TupleFieldId>),
// FIXME: get rid of this, and use FieldId for tuples and closures
TupleOrClosureField(usize),
ClosureField(usize),
Index(V),
ConstantIndex { offset: u64, from_end: bool },
Subslice { from: u64, to: u64 },
Expand Down Expand Up @@ -161,7 +162,7 @@ impl<V, T> ProjectionElem<V, T> {
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::Field(f) => match &base.kind(Interner) {
ProjectionElem::Field(Either::Left(f)) => match &base.kind(Interner) {
TyKind::Adt(_, subst) => {
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
}
Expand All @@ -170,19 +171,25 @@ impl<V, T> ProjectionElem<V, T> {
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::TupleOrClosureField(f) => match &base.kind(Interner) {
ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) {
TyKind::Tuple(_, subst) => subst
.as_slice(Interner)
.get(*f)
.get(f.index as usize)
.map(|x| x.assert_ty_ref(Interner))
.cloned()
.unwrap_or_else(|| {
never!("Out of bound tuple field");
TyKind::Error.intern(Interner)
}),
_ => {
never!("Only tuple has tuple field");
return TyKind::Error.intern(Interner);
}
},
ProjectionElem::ClosureField(f) => match &base.kind(Interner) {
TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
_ => {
never!("Only tuple or closure has tuple or closure field");
never!("Only closure has closure field");
return TyKind::Error.intern(Interner);
}
},
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-ty/src/mir/borrowck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_)
| ProjectionElem::TupleOrClosureField(_)
| ProjectionElem::ClosureField(_)
| ProjectionElem::Index(_) => {
is_part_of = true;
}
Expand Down
Loading