Skip to content

Commit 2e7158b

Browse files
Refactor path lowering
And add a new diagnostic for non-`Fn` parenthesized generic args. Path lowering started to look like a mess, with each function carrying additional parameters for the diagnostic callback (since paths can occur both in type and in expression/pattern position, and their diagnostic handling is different) and the segment index, for the diagnostics report. So I refactored it from stateless functions on `TyLoweringContext` into stateful struct, `PathLoweringContext`, that tracks the process of lowering a path from resolution til assoc types selection.
1 parent 66599f1 commit 2e7158b

File tree

18 files changed

+1220
-1132
lines changed

18 files changed

+1220
-1132
lines changed

src/tools/rust-analyzer/crates/hir-def/src/data.rs

+4
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ bitflags::bitflags! {
250250
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3;
251251
const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4;
252252
const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5;
253+
const RUSTC_PAREN_SUGAR = 1 << 6;
253254
}
254255
}
255256

@@ -294,6 +295,9 @@ impl TraitData {
294295
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
295296
flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
296297
}
298+
if attrs.by_key(&sym::rustc_paren_sugar).exists() {
299+
flags |= TraitFlags::RUSTC_PAREN_SUGAR;
300+
}
297301

298302
let mut skip_array_during_method_dispatch =
299303
attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();

src/tools/rust-analyzer/crates/hir-def/src/path.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,7 @@ impl Path {
173173
segments: path.mod_path().segments(),
174174
generic_args: Some(path.generic_args()),
175175
},
176-
Path::LangItem(_, seg) => PathSegments {
177-
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
178-
generic_args: None,
179-
},
176+
Path::LangItem(_, seg) => PathSegments { segments: seg.as_slice(), generic_args: None },
180177
}
181178
}
182179

@@ -240,6 +237,11 @@ pub struct PathSegment<'a> {
240237
pub args_and_bindings: Option<&'a GenericArgs>,
241238
}
242239

240+
impl PathSegment<'_> {
241+
pub const MISSING: PathSegment<'static> =
242+
PathSegment { name: &Name::missing(), args_and_bindings: None };
243+
}
244+
243245
#[derive(Debug, Clone, Copy)]
244246
pub struct PathSegments<'a> {
245247
segments: &'a [Name],

src/tools/rust-analyzer/crates/hir-def/src/resolver.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,9 @@ impl Resolver {
327327
| LangItemTarget::ImplDef(_)
328328
| LangItemTarget::Static(_) => return None,
329329
};
330+
// Remaining segments start from 0 because lang paths have no segments other than the remaining.
330331
return Some((
331-
ResolveValueResult::Partial(type_ns, 1, None),
332+
ResolveValueResult::Partial(type_ns, 0, None),
332333
ResolvePathResultPrefixInfo::default(),
333334
));
334335
}

src/tools/rust-analyzer/crates/hir-expand/src/name.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ impl Name {
142142
/// Ideally, we want a `gensym` semantics for missing names -- each missing
143143
/// name is equal only to itself. It's not clear how to implement this in
144144
/// salsa though, so we punt on that bit for a moment.
145-
pub fn missing() -> Name {
146-
Name { symbol: sym::MISSING_NAME.clone(), ctx: () }
145+
pub const fn missing() -> Name {
146+
Name { symbol: sym::consts::MISSING_NAME, ctx: () }
147147
}
148148

149149
/// Returns true if this is a fake name for things missing in the source code. See

src/tools/rust-analyzer/crates/hir-ty/src/infer.rs

+19-33
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
pub(crate) mod cast;
1717
pub(crate) mod closure;
1818
mod coerce;
19-
mod diagnostics;
19+
pub(crate) mod diagnostics;
2020
mod expr;
2121
mod mutability;
2222
mod pat;
@@ -1480,21 +1480,22 @@ impl<'a> InferenceContext<'a> {
14801480
&self.diagnostics,
14811481
InferenceTyDiagnosticSource::Body,
14821482
);
1483+
let mut path_ctx = ctx.at_path(path, node);
14831484
let (resolution, unresolved) = if value_ns {
1484-
let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
1485+
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
14851486
return (self.err_ty(), None);
14861487
};
14871488
match res {
14881489
ResolveValueResult::ValueNs(value, _) => match value {
14891490
ValueNs::EnumVariantId(var) => {
1490-
let substs = ctx.substs_from_path(path, var.into(), true);
1491+
let substs = path_ctx.substs_from_path(var.into(), true);
14911492
drop(ctx);
14921493
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
14931494
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
14941495
return (ty, Some(var.into()));
14951496
}
14961497
ValueNs::StructId(strukt) => {
1497-
let substs = ctx.substs_from_path(path, strukt.into(), true);
1498+
let substs = path_ctx.substs_from_path(strukt.into(), true);
14981499
drop(ctx);
14991500
let ty = self.db.ty(strukt.into());
15001501
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1509,7 +1510,7 @@ impl<'a> InferenceContext<'a> {
15091510
ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
15101511
}
15111512
} else {
1512-
match ctx.resolve_path_in_type_ns(path, node) {
1513+
match path_ctx.resolve_path_in_type_ns() {
15131514
Some((it, idx)) => (it, idx),
15141515
None => return (self.err_ty(), None),
15151516
}
@@ -1520,21 +1521,21 @@ impl<'a> InferenceContext<'a> {
15201521
};
15211522
return match resolution {
15221523
TypeNs::AdtId(AdtId::StructId(strukt)) => {
1523-
let substs = ctx.substs_from_path(path, strukt.into(), true);
1524+
let substs = path_ctx.substs_from_path(strukt.into(), true);
15241525
drop(ctx);
15251526
let ty = self.db.ty(strukt.into());
15261527
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
15271528
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
15281529
}
15291530
TypeNs::AdtId(AdtId::UnionId(u)) => {
1530-
let substs = ctx.substs_from_path(path, u.into(), true);
1531+
let substs = path_ctx.substs_from_path(u.into(), true);
15311532
drop(ctx);
15321533
let ty = self.db.ty(u.into());
15331534
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
15341535
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
15351536
}
15361537
TypeNs::EnumVariantId(var) => {
1537-
let substs = ctx.substs_from_path(path, var.into(), true);
1538+
let substs = path_ctx.substs_from_path(var.into(), true);
15381539
drop(ctx);
15391540
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
15401541
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1545,31 +1546,32 @@ impl<'a> InferenceContext<'a> {
15451546
let substs = generics.placeholder_subst(self.db);
15461547
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
15471548

1548-
let Some(mut remaining_idx) = unresolved else {
1549+
let Some(remaining_idx) = unresolved else {
15491550
drop(ctx);
15501551
return self.resolve_variant_on_alias(ty, None, mod_path);
15511552
};
15521553

15531554
let mut remaining_segments = path.segments().skip(remaining_idx);
15541555

1556+
if remaining_segments.len() >= 2 {
1557+
path_ctx.ignore_last_segment();
1558+
}
1559+
15551560
// We need to try resolving unresolved segments one by one because each may resolve
15561561
// to a projection, which `TyLoweringContext` cannot handle on its own.
15571562
let mut tried_resolving_once = false;
1558-
while !remaining_segments.is_empty() {
1559-
let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
1560-
let current_segment = remaining_segments.take(1);
1561-
1563+
while let Some(current_segment) = remaining_segments.first() {
15621564
// If we can resolve to an enum variant, it takes priority over associated type
15631565
// of the same name.
15641566
if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
15651567
let enum_data = self.db.enum_data(id);
1566-
let name = current_segment.first().unwrap().name;
1567-
if let Some(variant) = enum_data.variant(name) {
1568+
if let Some(variant) = enum_data.variant(current_segment.name) {
15681569
return if remaining_segments.len() == 1 {
15691570
(ty, Some(variant.into()))
15701571
} else {
15711572
// We still have unresolved paths, but enum variants never have
15721573
// associated types!
1574+
// FIXME: Report an error.
15731575
(self.err_ty(), None)
15741576
};
15751577
}
@@ -1578,23 +1580,13 @@ impl<'a> InferenceContext<'a> {
15781580
if tried_resolving_once {
15791581
// FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()`
15801582
// will need to be updated to err at the correct segment.
1581-
//
1582-
// We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()`
1583-
// will be incorrect, and that can mess up error reporting.
15841583
break;
15851584
}
15861585

15871586
// `lower_partly_resolved_path()` returns `None` as type namespace unless
15881587
// `remaining_segments` is empty, which is never the case here. We don't know
15891588
// which namespace the new `ty` is in until normalized anyway.
1590-
(ty, _) = ctx.lower_partly_resolved_path(
1591-
node,
1592-
resolution,
1593-
resolved_segment,
1594-
current_segment,
1595-
(remaining_idx - 1) as u32,
1596-
false,
1597-
);
1589+
(ty, _) = path_ctx.lower_partly_resolved_path(resolution, false);
15981590
tried_resolving_once = true;
15991591

16001592
ty = self.table.insert_type_vars(ty);
@@ -1604,8 +1596,6 @@ impl<'a> InferenceContext<'a> {
16041596
return (self.err_ty(), None);
16051597
}
16061598

1607-
// FIXME(inherent_associated_types): update `resolution` based on `ty` here.
1608-
remaining_idx += 1;
16091599
remaining_segments = remaining_segments.skip(1);
16101600
}
16111601
drop(ctx);
@@ -1621,11 +1611,7 @@ impl<'a> InferenceContext<'a> {
16211611
(ty, variant)
16221612
}
16231613
TypeNs::TypeAliasId(it) => {
1624-
let resolved_seg = match unresolved {
1625-
None => path.segments().last().unwrap(),
1626-
Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
1627-
};
1628-
let substs = ctx.substs_from_path_segment(resolved_seg, it.into(), true, None);
1614+
let substs = path_ctx.substs_from_path_segment(it.into(), true, None);
16291615
drop(ctx);
16301616
let ty = self.db.ty(it.into());
16311617
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));

src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs

+28-48
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@
55
use std::cell::RefCell;
66
use std::ops::{Deref, DerefMut};
77

8-
use hir_def::expr_store::HygieneId;
9-
use hir_def::hir::ExprOrPatId;
10-
use hir_def::path::{Path, PathSegment, PathSegments};
11-
use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
12-
use hir_def::type_ref::TypesMap;
13-
use hir_def::TypeOwnerId;
8+
use either::Either;
9+
use hir_def::{hir::ExprOrPatId, path::Path, resolver::Resolver, type_ref::TypesMap, TypeOwnerId};
1410

15-
use crate::db::HirDatabase;
1611
use crate::{
17-
InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
12+
db::HirDatabase,
13+
lower::path::{PathDiagnosticCallback, PathLoweringContext},
14+
InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
1815
};
1916

2017
// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
@@ -44,13 +41,19 @@ impl Diagnostics {
4441
}
4542
}
4643

44+
pub(crate) struct PathDiagnosticCallbackData<'a> {
45+
node: ExprOrPatId,
46+
diagnostics: &'a Diagnostics,
47+
}
48+
4749
pub(super) struct InferenceTyLoweringContext<'a> {
4850
ctx: TyLoweringContext<'a>,
4951
diagnostics: &'a Diagnostics,
5052
source: InferenceTyDiagnosticSource,
5153
}
5254

5355
impl<'a> InferenceTyLoweringContext<'a> {
56+
#[inline]
5457
pub(super) fn new(
5558
db: &'a dyn HirDatabase,
5659
resolver: &'a Resolver,
@@ -62,65 +65,42 @@ impl<'a> InferenceTyLoweringContext<'a> {
6265
Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
6366
}
6467

65-
pub(super) fn resolve_path_in_type_ns(
66-
&mut self,
67-
path: &Path,
68-
node: ExprOrPatId,
69-
) -> Option<(TypeNs, Option<usize>)> {
70-
let diagnostics = self.diagnostics;
71-
self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
72-
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
73-
})
74-
}
75-
76-
pub(super) fn resolve_path_in_value_ns(
77-
&mut self,
78-
path: &Path,
79-
node: ExprOrPatId,
80-
hygiene_id: HygieneId,
81-
) -> Option<ResolveValueResult> {
82-
let diagnostics = self.diagnostics;
83-
self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
84-
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
85-
})
86-
}
87-
88-
pub(super) fn lower_partly_resolved_path(
89-
&mut self,
68+
#[inline]
69+
pub(super) fn at_path<'b>(
70+
&'b mut self,
71+
path: &'b Path,
9072
node: ExprOrPatId,
91-
resolution: TypeNs,
92-
resolved_segment: PathSegment<'_>,
93-
remaining_segments: PathSegments<'_>,
94-
resolved_segment_idx: u32,
95-
infer_args: bool,
96-
) -> (Ty, Option<TypeNs>) {
97-
let diagnostics = self.diagnostics;
98-
self.ctx.lower_partly_resolved_path(
99-
resolution,
100-
resolved_segment,
101-
remaining_segments,
102-
resolved_segment_idx,
103-
infer_args,
104-
&mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }),
105-
)
73+
) -> PathLoweringContext<'b, 'a> {
74+
let on_diagnostic = PathDiagnosticCallback {
75+
data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
76+
callback: |data, _, diag| {
77+
let data = data.as_ref().right().unwrap();
78+
data.diagnostics
79+
.push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
80+
},
81+
};
82+
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
10683
}
10784
}
10885

10986
impl<'a> Deref for InferenceTyLoweringContext<'a> {
11087
type Target = TyLoweringContext<'a>;
11188

89+
#[inline]
11290
fn deref(&self) -> &Self::Target {
11391
&self.ctx
11492
}
11593
}
11694

11795
impl DerefMut for InferenceTyLoweringContext<'_> {
96+
#[inline]
11897
fn deref_mut(&mut self) -> &mut Self::Target {
11998
&mut self.ctx
12099
}
121100
}
122101

123102
impl Drop for InferenceTyLoweringContext<'_> {
103+
#[inline]
124104
fn drop(&mut self) {
125105
self.diagnostics
126106
.push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));

src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -564,9 +564,17 @@ impl InferenceContext<'_> {
564564
| Pat::Range { .. }
565565
| Pat::Slice { .. } => true,
566566
Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
567-
Pat::Path(p) => {
568-
let v = self.resolve_value_path_inner(p, pat.into());
569-
v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
567+
Pat::Path(path) => {
568+
// A const is a reference pattern, but other value ns things aren't (see #16131). We don't need more than
569+
// the hir-def resolver for this, because if there are segments left, this can only be an (associated) const.
570+
//
571+
// Do not use `TyLoweringContext`'s resolution, we want to ignore errors here (they'll be reported elsewhere).
572+
let resolution = self.resolver.resolve_path_in_value_ns_fully(
573+
self.db.upcast(),
574+
path,
575+
body.pat_path_hygiene(pat),
576+
);
577+
resolution.is_some_and(|it| !matches!(it, hir_def::resolver::ValueNs::ConstId(_)))
570578
}
571579
Pat::ConstBlock(..) => false,
572580
Pat::Lit(expr) => !matches!(

0 commit comments

Comments
 (0)