Skip to content

Commit 7575e06

Browse files
committed
Fix anon const def-creation when macros are involved
Ever since rust-lang#125915, some `ast::AnonConst`s turn into `hir::ConstArgKind::Path`s, which don't have associated `DefId`s. To deal with the fact that we don't have resolution information in `DefCollector`, we decided to implement a process where if the anon const *appeared* to be trivial (i.e., `N` or `{ N }`), we would avoid creating a def for it in `DefCollector`. If later, in AST lowering, we realized it turned out to be a unit struct literal, or we were lowering it to something that didn't use `hir::ConstArg`, we'd create its def there. However, let's say we have a macro `m!()` that expands to a reference to a free constant `FOO`. If we use `m!()` in the body of an anon const (e.g., `Foo<{ m!() }>`), then in def collection, it appears to be a nontrivial anon const and we create a def. But the macro expands to something that looks like a trivial const arg, but is not, so in AST lowering we "fix" the mistake we assumed def collection made and create a def for it. This causes a duplicate definition ICE. The long-term fix for this is to delay the creation of defs for all expression-like nodes until AST lowering (see rust-lang#128844 for an incomplete attempt at this). This would avoid issues like this one that are caused by hacky workarounds. However, doing this uncovers a pre-existing bug with opaque types that is quite involved to fix (see rust-lang#129023). In the meantime, this PR fixes the bug by delaying def creation for anon consts whose bodies are macro invocations until after we expand the macro and know what is inside it. This is accomplished by adding information to create the anon const's def to the data in `Resolver.invocation_parents`.
1 parent d2b5aa6 commit 7575e06

10 files changed

+224
-74
lines changed

compiler/rustc_ast/src/ast.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -1188,14 +1188,7 @@ impl Expr {
11881188
///
11891189
/// Does not ensure that the path resolves to a const param, the caller should check this.
11901190
pub fn is_potential_trivial_const_arg(&self) -> bool {
1191-
let this = if let ExprKind::Block(block, None) = &self.kind
1192-
&& let [stmt] = block.stmts.as_slice()
1193-
&& let StmtKind::Expr(expr) = &stmt.kind
1194-
{
1195-
expr
1196-
} else {
1197-
self
1198-
};
1191+
let this = self.maybe_unwrap_block();
11991192

12001193
if let ExprKind::Path(None, path) = &this.kind
12011194
&& path.is_potential_trivial_const_arg()
@@ -1206,6 +1199,17 @@ impl Expr {
12061199
}
12071200
}
12081201

1202+
pub fn maybe_unwrap_block(&self) -> &Expr {
1203+
if let ExprKind::Block(block, None) = &self.kind
1204+
&& let [stmt] = block.stmts.as_slice()
1205+
&& let StmtKind::Expr(expr) = &stmt.kind
1206+
{
1207+
expr
1208+
} else {
1209+
self
1210+
}
1211+
}
1212+
12091213
pub fn to_bound(&self) -> Option<GenericBound> {
12101214
match &self.kind {
12111215
ExprKind::Path(None, path) => Some(GenericBound::Trait(

compiler/rustc_resolve/src/def_collector.rs

+92-46
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,37 @@ use rustc_span::symbol::{kw, sym, Symbol};
1111
use rustc_span::Span;
1212
use tracing::debug;
1313

14-
use crate::{ImplTraitContext, Resolver};
14+
use crate::{ImplTraitContext, InvocationParent, PendingAnonConstInfo, Resolver};
1515

1616
pub(crate) fn collect_definitions(
1717
resolver: &mut Resolver<'_, '_>,
1818
fragment: &AstFragment,
1919
expansion: LocalExpnId,
2020
) {
21-
let (parent_def, impl_trait_context, in_attr) = resolver.invocation_parents[&expansion];
22-
let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr };
21+
let InvocationParent { parent_def, pending_anon_const_info, impl_trait_context, in_attr } =
22+
resolver.invocation_parents[&expansion];
23+
let mut visitor = DefCollector {
24+
resolver,
25+
parent_def,
26+
pending_anon_const_info,
27+
expansion,
28+
impl_trait_context,
29+
in_attr,
30+
};
2331
fragment.visit_with(&mut visitor);
2432
}
2533

2634
/// Creates `DefId`s for nodes in the AST.
2735
struct DefCollector<'a, 'b, 'tcx> {
2836
resolver: &'a mut Resolver<'b, 'tcx>,
2937
parent_def: LocalDefId,
38+
/// If we have an anon const that consists of a macro invocation, e.g. `Foo<{ m!() }>`,
39+
/// we need to wait until we know what the macro expands to before we create the def for
40+
/// the anon const. That's because we lower some anon consts into `hir::ConstArgKind::Path`,
41+
/// which don't have defs.
42+
///
43+
/// See `Self::visit_anon_const()`.
44+
pending_anon_const_info: Option<PendingAnonConstInfo>,
3045
impl_trait_context: ImplTraitContext,
3146
in_attr: bool,
3247
expansion: LocalExpnId,
@@ -110,10 +125,16 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
110125

111126
fn visit_macro_invoc(&mut self, id: NodeId) {
112127
let id = id.placeholder_to_expn_id();
113-
let old_parent = self
114-
.resolver
115-
.invocation_parents
116-
.insert(id, (self.parent_def, self.impl_trait_context, self.in_attr));
128+
let pending_anon_const_info = self.pending_anon_const_info.take();
129+
let old_parent = self.resolver.invocation_parents.insert(
130+
id,
131+
InvocationParent {
132+
parent_def: self.parent_def,
133+
pending_anon_const_info,
134+
impl_trait_context: self.impl_trait_context,
135+
in_attr: self.in_attr,
136+
},
137+
);
117138
assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
118139
}
119140
}
@@ -320,7 +341,13 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
320341
// def. we'll then create a def later in ast lowering in this case. the parent of nested
321342
// items will be messed up, but that's ok because there can't be any if we're just looking
322343
// for bare idents.
323-
if constant.value.is_potential_trivial_const_arg() {
344+
345+
if matches!(constant.value.maybe_unwrap_block().kind, ExprKind::MacCall(..)) {
346+
// See self.pending_anon_const_info for explanation
347+
self.pending_anon_const_info =
348+
Some(PendingAnonConstInfo { id: constant.id, span: constant.value.span });
349+
visit::walk_anon_const(self, constant)
350+
} else if constant.value.is_potential_trivial_const_arg() {
324351
visit::walk_anon_const(self, constant)
325352
} else {
326353
let def =
@@ -330,48 +357,67 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
330357
}
331358

332359
fn visit_expr(&mut self, expr: &'a Expr) {
333-
let parent_def = match expr.kind {
334-
ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
335-
ExprKind::Closure(ref closure) => {
336-
// Async closures desugar to closures inside of closures, so
337-
// we must create two defs.
338-
let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
339-
match closure.coroutine_kind {
340-
Some(coroutine_kind) => {
341-
self.with_parent(closure_def, |this| {
342-
let coroutine_def = this.create_def(
343-
coroutine_kind.closure_id(),
344-
kw::Empty,
345-
DefKind::Closure,
346-
expr.span,
347-
);
348-
this.with_parent(coroutine_def, |this| visit::walk_expr(this, expr));
349-
});
350-
return;
360+
if matches!(expr.kind, ExprKind::MacCall(..)) {
361+
return self.visit_macro_invoc(expr.id);
362+
}
363+
364+
let grandparent_def = if let Some(pending_anon) = self.pending_anon_const_info.take() {
365+
// See self.pending_anon_const_info for explanation
366+
if !expr.is_potential_trivial_const_arg() {
367+
self.create_def(pending_anon.id, kw::Empty, DefKind::AnonConst, pending_anon.span)
368+
} else {
369+
self.parent_def
370+
}
371+
} else {
372+
self.parent_def
373+
};
374+
375+
self.with_parent(grandparent_def, |this| {
376+
let parent_def = match expr.kind {
377+
ExprKind::Closure(ref closure) => {
378+
// Async closures desugar to closures inside of closures, so
379+
// we must create two defs.
380+
let closure_def =
381+
this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
382+
match closure.coroutine_kind {
383+
Some(coroutine_kind) => {
384+
this.with_parent(closure_def, |this| {
385+
let coroutine_def = this.create_def(
386+
coroutine_kind.closure_id(),
387+
kw::Empty,
388+
DefKind::Closure,
389+
expr.span,
390+
);
391+
this.with_parent(coroutine_def, |this| {
392+
visit::walk_expr(this, expr)
393+
});
394+
});
395+
return;
396+
}
397+
None => closure_def,
351398
}
352-
None => closure_def,
353399
}
354-
}
355-
ExprKind::Gen(_, _, _, _) => {
356-
self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
357-
}
358-
ExprKind::ConstBlock(ref constant) => {
359-
for attr in &expr.attrs {
360-
visit::walk_attribute(self, attr);
400+
ExprKind::Gen(_, _, _, _) => {
401+
this.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
361402
}
362-
let def = self.create_def(
363-
constant.id,
364-
kw::Empty,
365-
DefKind::InlineConst,
366-
constant.value.span,
367-
);
368-
self.with_parent(def, |this| visit::walk_anon_const(this, constant));
369-
return;
370-
}
371-
_ => self.parent_def,
372-
};
403+
ExprKind::ConstBlock(ref constant) => {
404+
for attr in &expr.attrs {
405+
visit::walk_attribute(this, attr);
406+
}
407+
let def = this.create_def(
408+
constant.id,
409+
kw::Empty,
410+
DefKind::InlineConst,
411+
constant.value.span,
412+
);
413+
this.with_parent(def, |this| visit::walk_anon_const(this, constant));
414+
return;
415+
}
416+
_ => this.parent_def,
417+
};
373418

374-
self.with_parent(parent_def, |this| visit::walk_expr(this, expr));
419+
this.with_parent(parent_def, |this| visit::walk_expr(this, expr))
420+
})
375421
}
376422

377423
fn visit_ty(&mut self, ty: &'a Ty) {

compiler/rustc_resolve/src/lib.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,29 @@ impl<'a> ParentScope<'a> {
170170
}
171171
}
172172

173+
#[derive(Copy, Debug, Clone)]
174+
struct InvocationParent {
175+
parent_def: LocalDefId,
176+
pending_anon_const_info: Option<PendingAnonConstInfo>,
177+
impl_trait_context: ImplTraitContext,
178+
in_attr: bool,
179+
}
180+
181+
impl InvocationParent {
182+
const ROOT: Self = Self {
183+
parent_def: CRATE_DEF_ID,
184+
pending_anon_const_info: None,
185+
impl_trait_context: ImplTraitContext::Existential,
186+
in_attr: false,
187+
};
188+
}
189+
190+
#[derive(Copy, Debug, Clone)]
191+
struct PendingAnonConstInfo {
192+
id: NodeId,
193+
span: Span,
194+
}
195+
173196
#[derive(Copy, Debug, Clone)]
174197
enum ImplTraitContext {
175198
Existential,
@@ -1143,7 +1166,7 @@ pub struct Resolver<'a, 'tcx> {
11431166
/// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId`
11441167
/// we know what parent node that fragment should be attached to thanks to this table,
11451168
/// and how the `impl Trait` fragments were introduced.
1146-
invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext, bool /*in_attr*/)>,
1169+
invocation_parents: FxHashMap<LocalExpnId, InvocationParent>,
11471170

11481171
/// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
11491172
/// FIXME: Replace with a more general AST map (together with some other fields).
@@ -1380,8 +1403,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
13801403
node_id_to_def_id.insert(CRATE_NODE_ID, crate_feed);
13811404

13821405
let mut invocation_parents = FxHashMap::default();
1383-
invocation_parents
1384-
.insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential, false));
1406+
invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT);
13851407

13861408
let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = tcx
13871409
.sess

compiler/rustc_resolve/src/macros.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ use crate::errors::{
4242
use crate::imports::Import;
4343
use crate::Namespace::*;
4444
use crate::{
45-
BindingKey, BuiltinMacroState, DeriveData, Determinacy, Finalize, MacroData, ModuleKind,
46-
ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, ResolutionError,
47-
Resolver, ScopeSet, Segment, ToNameBinding, Used,
45+
BindingKey, BuiltinMacroState, DeriveData, Determinacy, Finalize, InvocationParent, MacroData,
46+
ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult,
47+
ResolutionError, Resolver, ScopeSet, Segment, ToNameBinding, Used,
4848
};
4949

5050
type Res = def::Res<NodeId>;
@@ -183,7 +183,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
183183
}
184184

185185
fn invocation_parent(&self, id: LocalExpnId) -> LocalDefId {
186-
self.invocation_parents[&id].0
186+
self.invocation_parents[&id].parent_def
187187
}
188188

189189
fn resolve_dollar_crates(&mut self) {
@@ -303,12 +303,12 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
303303
.invocation_parents
304304
.get(&invoc_id)
305305
.or_else(|| self.invocation_parents.get(&eager_expansion_root))
306-
.filter(|&&(mod_def_id, _, in_attr)| {
306+
.filter(|&&InvocationParent { parent_def: mod_def_id, in_attr, .. }| {
307307
in_attr
308308
&& invoc.fragment_kind == AstFragmentKind::Expr
309309
&& self.tcx.def_kind(mod_def_id) == DefKind::Mod
310310
})
311-
.map(|&(mod_def_id, ..)| mod_def_id);
311+
.map(|&InvocationParent { parent_def: mod_def_id, .. }| mod_def_id);
312312
let (ext, res) = self.smart_resolve_macro_path(
313313
path,
314314
kind,
@@ -951,7 +951,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
951951
let node_id = self
952952
.invocation_parents
953953
.get(&parent_scope.expansion)
954-
.map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[id.0]);
954+
.map_or(ast::CRATE_NODE_ID, |parent| {
955+
self.def_id_to_node_id[parent.parent_def]
956+
});
955957
self.lint_buffer.buffer_lint(
956958
LEGACY_DERIVE_HELPERS,
957959
node_id,

tests/crashes/128016.rs

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ check-pass
2+
3+
// This is a regression test for #128016.
4+
5+
macro_rules! len_inner {
6+
() => {
7+
BAR
8+
};
9+
}
10+
11+
macro_rules! len {
12+
() => {
13+
len_inner!()
14+
};
15+
}
16+
17+
const BAR: usize = 0;
18+
19+
fn main() {
20+
let val: [bool; len!()] = [];
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ check-pass
2+
3+
macro_rules! len {
4+
($x:ident) => {
5+
$x
6+
};
7+
}
8+
9+
fn bar<const N: usize>() {
10+
let val: [bool; len!(N)] = [true; N];
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// This is a regression test for #128016.
2+
3+
macro_rules! len {
4+
() => {
5+
target
6+
//~^ ERROR cannot find value `target`
7+
};
8+
}
9+
10+
fn main() {
11+
let val: [str; len!()] = [];
12+
//~^ ERROR the size for values
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0425]: cannot find value `target` in this scope
2+
--> $DIR/trivial-const-arg-macro-res-error.rs:5:9
3+
|
4+
LL | target
5+
| ^^^^^^ not found in this scope
6+
...
7+
LL | let val: [str; len!()] = [];
8+
| ------ in this macro invocation
9+
|
10+
= note: this error originates in the macro `len` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error[E0277]: the size for values of type `str` cannot be known at compilation time
13+
--> $DIR/trivial-const-arg-macro-res-error.rs:11:14
14+
|
15+
LL | let val: [str; len!()] = [];
16+
| ^^^^^^^^^^^^^ doesn't have a size known at compile-time
17+
|
18+
= help: the trait `Sized` is not implemented for `str`
19+
= note: slice and array elements must have `Sized` type
20+
21+
error: aborting due to 2 previous errors
22+
23+
Some errors have detailed explanations: E0277, E0425.
24+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)