Skip to content

Commit dc47a69

Browse files
committed
Auto merge of #149136 - BoxyUwU:mgca_explicit_anon_consts, r=oli-obk
MGCA: Syntactically distinguish anon const const args r? oli-obk tracking issue: #132980 This PR requires that when `feature(min_generic_const_args)` is enabled, anon const const args are *syntactically* distinguishable from other kinds of args. We use `const { ... }` in const argument position to denote an anon const: ```rust #![feature(min_generic_const_args)] // no longer allowed as `1 + 1` is represented via an anon const and // there is no syntactic marker type Foo = [(); 1 + 1]; // allowed, `const { ... }` indicates an anon const representation type Foo = [(); const { 1 + 1 }]; ``` This restriction is only placed when mgca is enabled. There should be no effect on stable. This restriction is not enforced for unbraced literals which we continue to implicitly wrap in an anon const: `tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs` This restriction allows us to create `DefId`s for anon consts only when actually required. When it is syntactically ambiguous whether a const argument is an anon const or not we are forced to conservatively create a `DefId` for every const argument even if it doesn't wind up needing one. This works fine on stable but under `mgca` we can wind up with anon consts nested inside non-anon-const const arguments resulting in a broken `DefId` tree. See #148838 where an anon const arg inside of a path arg winds up with a parent of a conservatively created `DefId` that doesn't actually correspond to an anon const, resulting in an ICE. With #149114 every field initialiser in a const argument would become a place where there could *possibly* be an anon const. This would also get worse once we support tuple constructors- now every function argument is a place where there could possibly be an anon const. We introduce this restriction to avoid creating massive amounts of unused `DefId`s that make the parent tree significantly more complicated, and to avoid having to paper over this issue in things like `generics_of`. Fixes #148838 It also must be syntactically clear from context whether `'_` means an inference lifetime or an elided lifetime parameter. This restriction will allow us to properly resolve `'_` in const arguments in mgca. This PR doesn't actually fix handle this, but we could do so trivially after this lands.
2 parents fa5eda1 + acc3a0e commit dc47a69

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+779
-252
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,11 @@ impl Path {
141141
/// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
142142
/// be represented without an anon const in the HIR.
143143
///
144-
/// If `allow_mgca_arg` is true (as should be the case in most situations when
145-
/// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
146-
/// because all paths are valid.
147-
///
148-
/// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
144+
/// Returns true iff the path has exactly one segment, and it has no generic args
149145
/// (i.e., it is _potentially_ a const parameter).
150146
#[tracing::instrument(level = "debug", ret)]
151-
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
152-
allow_mgca_arg
153-
|| self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
147+
pub fn is_potential_trivial_const_arg(&self) -> bool {
148+
self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
154149
}
155150
}
156151

@@ -1385,6 +1380,15 @@ pub enum UnsafeSource {
13851380
UserProvided,
13861381
}
13871382

1383+
/// Track whether under `feature(min_generic_const_args)` this anon const
1384+
/// was explicitly disambiguated as an anon const or not through the use of
1385+
/// `const { ... }` syntax.
1386+
#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)]
1387+
pub enum MgcaDisambiguation {
1388+
AnonConst,
1389+
Direct,
1390+
}
1391+
13881392
/// A constant (expression) that's not an item or associated item,
13891393
/// but needs its own `DefId` for type-checking, const-eval, etc.
13901394
/// These are usually found nested inside types (e.g., array lengths)
@@ -1394,6 +1398,7 @@ pub enum UnsafeSource {
13941398
pub struct AnonConst {
13951399
pub id: NodeId,
13961400
pub value: Box<Expr>,
1401+
pub mgca_disambiguation: MgcaDisambiguation,
13971402
}
13981403

13991404
/// An expression.
@@ -1412,26 +1417,20 @@ impl Expr {
14121417
///
14131418
/// This will unwrap at most one block level (curly braces). After that, if the expression
14141419
/// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
1415-
/// See there for more info about `allow_mgca_arg`.
14161420
///
1417-
/// The only additional thing to note is that when `allow_mgca_arg` is false, this function
1418-
/// will only allow paths with no qself, before dispatching to the `Path` function of
1419-
/// the same name.
1421+
/// This function will only allow paths with no qself, before dispatching to the `Path`
1422+
/// function of the same name.
14201423
///
14211424
/// Does not ensure that the path resolves to a const param/item, the caller should check this.
14221425
/// This also does not consider macros, so it's only correct after macro-expansion.
1423-
pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
1426+
pub fn is_potential_trivial_const_arg(&self) -> bool {
14241427
let this = self.maybe_unwrap_block();
1425-
if allow_mgca_arg {
1426-
matches!(this.kind, ExprKind::Path(..))
1428+
if let ExprKind::Path(None, path) = &this.kind
1429+
&& path.is_potential_trivial_const_arg()
1430+
{
1431+
true
14271432
} else {
1428-
if let ExprKind::Path(None, path) = &this.kind
1429-
&& path.is_potential_trivial_const_arg(allow_mgca_arg)
1430-
{
1431-
true
1432-
} else {
1433-
false
1434-
}
1433+
false
14351434
}
14361435
}
14371436

compiler/rustc_ast/src/visit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ macro_rules! common_visitor_and_walkers {
415415
UnsafeBinderCastKind,
416416
BinOpKind,
417417
BlockCheckMode,
418+
MgcaDisambiguation,
418419
BorrowKind,
419420
BoundAsyncness,
420421
BoundConstness,

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
489489
arg
490490
};
491491

492-
let anon_const = AnonConst { id: node_id, value: const_value };
492+
let anon_const = AnonConst {
493+
id: node_id,
494+
value: const_value,
495+
mgca_disambiguation: MgcaDisambiguation::AnonConst,
496+
};
493497
generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const)));
494498
} else {
495499
real_args.push(arg);

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12191219
.and_then(|partial_res| partial_res.full_res())
12201220
{
12211221
if !res.matches_ns(Namespace::TypeNS)
1222-
&& path.is_potential_trivial_const_arg(false)
1222+
&& path.is_potential_trivial_const_arg()
12231223
{
12241224
debug!(
12251225
"lower_generic_arg: Lowering type argument as const argument: {:?}",
@@ -2287,11 +2287,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
22872287
) -> &'hir hir::ConstArg<'hir> {
22882288
let tcx = self.tcx;
22892289

2290-
let ct_kind = if path
2291-
.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
2292-
&& (tcx.features().min_generic_const_args()
2293-
|| matches!(res, Res::Def(DefKind::ConstParam, _)))
2294-
{
2290+
let is_trivial_path = path.is_potential_trivial_const_arg()
2291+
&& matches!(res, Res::Def(DefKind::ConstParam, _));
2292+
let ct_kind = if is_trivial_path || tcx.features().min_generic_const_args() {
22952293
let qpath = self.lower_qpath(
22962294
ty_id,
22972295
&None,
@@ -2370,6 +2368,53 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23702368
}
23712369
}
23722370

2371+
#[instrument(level = "debug", skip(self), ret)]
2372+
fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> {
2373+
let overly_complex_const = |this: &mut Self| {
2374+
let e = this.dcx().struct_span_err(
2375+
expr.span,
2376+
"complex const arguments must be placed inside of a `const` block",
2377+
);
2378+
2379+
ConstArg { hir_id: this.next_id(), kind: hir::ConstArgKind::Error(expr.span, e.emit()) }
2380+
};
2381+
2382+
match &expr.kind {
2383+
ExprKind::Path(qself, path) => {
2384+
let qpath = self.lower_qpath(
2385+
expr.id,
2386+
qself,
2387+
path,
2388+
ParamMode::Explicit,
2389+
AllowReturnTypeNotation::No,
2390+
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
2391+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
2392+
None,
2393+
);
2394+
2395+
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath) }
2396+
}
2397+
ExprKind::Underscore => ConstArg {
2398+
hir_id: self.lower_node_id(expr.id),
2399+
kind: hir::ConstArgKind::Infer(expr.span, ()),
2400+
},
2401+
ExprKind::Block(block, _) => {
2402+
if let [stmt] = block.stmts.as_slice()
2403+
&& let StmtKind::Expr(expr) = &stmt.kind
2404+
&& matches!(
2405+
expr.kind,
2406+
ExprKind::Block(..) | ExprKind::Path(..) | ExprKind::Struct(..)
2407+
)
2408+
{
2409+
return self.lower_expr_to_const_arg_direct(expr);
2410+
}
2411+
2412+
overly_complex_const(self)
2413+
}
2414+
_ => overly_complex_const(self),
2415+
}
2416+
}
2417+
23732418
/// See [`hir::ConstArg`] for when to use this function vs
23742419
/// [`Self::lower_anon_const_to_anon_const`].
23752420
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> {
@@ -2379,6 +2424,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23792424
#[instrument(level = "debug", skip(self))]
23802425
fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
23812426
let tcx = self.tcx;
2427+
2428+
// We cannot change parsing depending on feature gates available,
2429+
// we can only require feature gates to be active as a delayed check.
2430+
// Thus we just parse anon consts generally and make the real decision
2431+
// making in ast lowering.
2432+
// FIXME(min_generic_const_args): revisit once stable
2433+
if tcx.features().min_generic_const_args() {
2434+
return match anon.mgca_disambiguation {
2435+
MgcaDisambiguation::AnonConst => {
2436+
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
2437+
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon) }
2438+
}
2439+
MgcaDisambiguation::Direct => self.lower_expr_to_const_arg_direct(&anon.value),
2440+
};
2441+
}
2442+
23822443
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
23832444
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
23842445
let expr = if let ExprKind::Block(block, _) = &anon.value.kind
@@ -2390,20 +2451,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23902451
} else {
23912452
&anon.value
23922453
};
2454+
23932455
let maybe_res =
23942456
self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
23952457
if let ExprKind::Path(qself, path) = &expr.kind
2396-
&& path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
2397-
&& (tcx.features().min_generic_const_args()
2398-
|| matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
2458+
&& path.is_potential_trivial_const_arg()
2459+
&& matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _)))
23992460
{
24002461
let qpath = self.lower_qpath(
24012462
expr.id,
24022463
qself,
24032464
path,
24042465
ParamMode::Explicit,
24052466
AllowReturnTypeNotation::No,
2406-
// FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
24072467
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
24082468
None,
24092469
);

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
517517
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
518518
gate_all!(postfix_match, "postfix match is experimental");
519519
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
520+
gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental");
520521
gate_all!(global_registration, "global registration is experimental");
521522
gate_all!(return_type_notation, "return type notation is experimental");
522523
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");

compiler/rustc_builtin_macros/src/autodiff.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod llvm_enzyme {
1717
use rustc_ast::{
1818
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode,
1919
FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind,
20-
MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility,
20+
MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility,
2121
};
2222
use rustc_expand::base::{Annotatable, ExtCtxt};
2323
use rustc_span::{Ident, Span, Symbol, sym};
@@ -558,7 +558,11 @@ mod llvm_enzyme {
558558
}
559559
GenericParamKind::Const { .. } => {
560560
let expr = ecx.expr_path(ast::Path::from_ident(p.ident));
561-
let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr };
561+
let anon_const = AnonConst {
562+
id: ast::DUMMY_NODE_ID,
563+
value: expr,
564+
mgca_disambiguation: MgcaDisambiguation::Direct,
565+
};
562566
Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const)))
563567
}
564568
GenericParamKind::Lifetime { .. } => None,
@@ -813,6 +817,7 @@ mod llvm_enzyme {
813817
let anon_const = rustc_ast::AnonConst {
814818
id: ast::DUMMY_NODE_ID,
815819
value: ecx.expr_usize(span, 1 + x.width as usize),
820+
mgca_disambiguation: MgcaDisambiguation::Direct,
816821
};
817822
TyKind::Array(ty.clone(), anon_const)
818823
};
@@ -827,6 +832,7 @@ mod llvm_enzyme {
827832
let anon_const = rustc_ast::AnonConst {
828833
id: ast::DUMMY_NODE_ID,
829834
value: ecx.expr_usize(span, x.width as usize),
835+
mgca_disambiguation: MgcaDisambiguation::Direct,
830836
};
831837
let kind = TyKind::Array(ty.clone(), anon_const);
832838
let ty =

compiler/rustc_builtin_macros/src/pattern_type.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_ast::tokenstream::TokenStream;
2-
use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token};
2+
use rustc_ast::{AnonConst, DUMMY_NODE_ID, MgcaDisambiguation, Ty, TyPat, TyPatKind, ast, token};
33
use rustc_errors::PResult;
44
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
55
use rustc_parse::exp;
@@ -60,8 +60,20 @@ fn ty_pat(kind: TyPatKind, span: Span) -> TyPat {
6060
fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat {
6161
let kind = match pat.kind {
6262
ast::PatKind::Range(start, end, include_end) => TyPatKind::Range(
63-
start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
64-
end.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })),
63+
start.map(|value| {
64+
Box::new(AnonConst {
65+
id: DUMMY_NODE_ID,
66+
value,
67+
mgca_disambiguation: MgcaDisambiguation::Direct,
68+
})
69+
}),
70+
end.map(|value| {
71+
Box::new(AnonConst {
72+
id: DUMMY_NODE_ID,
73+
value,
74+
mgca_disambiguation: MgcaDisambiguation::Direct,
75+
})
76+
}),
6577
include_end,
6678
),
6779
ast::PatKind::Or(variants) => {

compiler/rustc_expand/src/build.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use rustc_ast::token::Delimiter;
22
use rustc_ast::tokenstream::TokenStream;
33
use rustc_ast::util::literal;
44
use rustc_ast::{
5-
self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind,
6-
UnOp, attr, token, tokenstream,
5+
self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind,
6+
MgcaDisambiguation, PatKind, UnOp, attr, token, tokenstream,
77
};
88
use rustc_span::source_map::Spanned;
99
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
@@ -101,6 +101,7 @@ impl<'a> ExtCtxt<'a> {
101101
attrs: AttrVec::new(),
102102
tokens: None,
103103
}),
104+
mgca_disambiguation: MgcaDisambiguation::Direct,
104105
}
105106
}
106107

compiler/rustc_parse/src/parser/asm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_ast::{self as ast, AsmMacro};
1+
use rustc_ast::{self as ast, AsmMacro, MgcaDisambiguation};
22
use rustc_span::{Span, Symbol, kw};
33

44
use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos};
@@ -149,7 +149,7 @@ fn parse_asm_operand<'a>(
149149
let block = p.parse_block()?;
150150
ast::InlineAsmOperand::Label { block }
151151
} else if p.eat_keyword(exp!(Const)) {
152-
let anon_const = p.parse_expr_anon_const()?;
152+
let anon_const = p.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?;
153153
ast::InlineAsmOperand::Const { anon_const }
154154
} else if p.eat_keyword(exp!(Sym)) {
155155
let expr = p.parse_expr()?;

0 commit comments

Comments
 (0)