MGCA: Support tuple expressions as direct const arguments

This commit is contained in:
mu001999 2026-01-04 10:36:36 +08:00
parent 0aced202c2
commit af76a2456d
18 changed files with 177 additions and 53 deletions

View file

@ -2427,6 +2427,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
kind: hir::ConstArgKind::TupleCall(qpath, lowered_args),
}
}
ExprKind::Tup(exprs) => {
let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| {
let expr = if let ExprKind::ConstBlock(anon_const) = &expr.kind {
let def_id = self.local_def_id(anon_const.id);
let def_kind = self.tcx.def_kind(def_id);
assert_eq!(DefKind::AnonConst, def_kind);
self.lower_anon_const_to_const_arg(anon_const)
} else {
self.lower_expr_to_const_arg_direct(&expr)
};
&*self.arena.alloc(expr)
}));
ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Tup(expr.span, exprs) }
}
ExprKind::Path(qself, path) => {
let qpath = self.lower_qpath(
expr.id,
@ -2495,6 +2512,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| ExprKind::Path(..)
| ExprKind::Struct(..)
| ExprKind::Call(..)
| ExprKind::Tup(..)
)
{
return self.lower_expr_to_const_arg_direct(expr);

View file

@ -497,6 +497,7 @@ impl<'hir, Unambig> ConstArg<'hir, Unambig> {
pub fn span(&self) -> Span {
match self.kind {
ConstArgKind::Tup(span, ..) => span,
ConstArgKind::Struct(path, _) => path.span(),
ConstArgKind::Path(path) => path.span(),
ConstArgKind::TupleCall(path, _) => path.span(),
@ -511,6 +512,7 @@ impl<'hir, Unambig> ConstArg<'hir, Unambig> {
#[derive(Clone, Copy, Debug, HashStable_Generic)]
#[repr(u8, C)]
pub enum ConstArgKind<'hir, Unambig = ()> {
Tup(Span, &'hir [&'hir ConstArg<'hir, Unambig>]),
/// **Note:** Currently this is only used for bare const params
/// (`N` where `fn foo<const N: usize>(...)`),
/// not paths to any const (`N` where `const N: usize = ...`).

View file

@ -1081,6 +1081,10 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
let ConstArg { hir_id, kind } = const_arg;
try_visit!(visitor.visit_id(*hir_id));
match kind {
ConstArgKind::Tup(_, exprs) => {
walk_list!(visitor, visit_const_arg, *exprs);
V::Result::output()
}
ConstArgKind::Struct(qpath, field_exprs) => {
try_visit!(visitor.visit_qpath(qpath, *hir_id, qpath.span()));

View file

@ -1493,9 +1493,10 @@ fn const_param_default<'tcx>(
};
let icx = ItemCtxt::new(tcx, def_id);
let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id);
let ct = icx
.lowerer()
.lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id(), identity_args));
let ct = icx.lowerer().lower_const_arg(
default_ct,
FeedConstTy::with_type_of(tcx, def_id.to_def_id(), identity_args),
);
ty::EarlyBinder::bind(ct)
}
@ -1553,7 +1554,7 @@ fn const_of_item<'tcx>(
let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id);
let ct = icx
.lowerer()
.lower_const_arg(ct_arg, FeedConstTy::Param(def_id.to_def_id(), identity_args));
.lower_const_arg(ct_arg, FeedConstTy::with_type_of(tcx, def_id.to_def_id(), identity_args));
if let Err(e) = icx.check_tainted_by_errors()
&& !ct.references_error()
{

View file

@ -259,17 +259,26 @@ impl AssocItemQSelf {
/// Use this enum with `<dyn HirTyLowerer>::lower_const_arg` to instruct it with the
/// desired behavior.
#[derive(Debug, Clone, Copy)]
pub enum FeedConstTy<'a, 'tcx> {
/// Feed the type.
///
pub enum FeedConstTy<'tcx> {
/// Feed the type to the (anno) const arg.
WithTy(Ty<'tcx>),
/// Don't feed the type.
No,
}
impl<'tcx> FeedConstTy<'tcx> {
/// The `DefId` belongs to the const param that we are supplying
/// this (anon) const arg to.
///
/// The list of generic args is used to instantiate the parameters
/// used by the type of the const param specified by `DefId`.
Param(DefId, &'a [ty::GenericArg<'tcx>]),
/// Don't feed the type.
No,
pub fn with_type_of(
tcx: TyCtxt<'tcx>,
def_id: DefId,
generic_args: &[ty::GenericArg<'tcx>],
) -> Self {
Self::WithTy(tcx.type_of(def_id).instantiate(tcx, generic_args))
}
}
#[derive(Debug, Clone, Copy)]
@ -723,7 +732,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// Ambig portions of `ConstArg` are handled in the match arm below
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
FeedConstTy::with_type_of(tcx, param.def_id, preceding_args),
)
.into(),
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
@ -2303,15 +2312,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
pub fn lower_const_arg(
&self,
const_arg: &hir::ConstArg<'tcx>,
feed: FeedConstTy<'_, 'tcx>,
feed: FeedConstTy<'tcx>,
) -> Const<'tcx> {
let tcx = self.tcx();
if let FeedConstTy::Param(param_def_id, args) = feed
if let FeedConstTy::WithTy(anon_const_type) = feed
&& let hir::ConstArgKind::Anon(anon) = &const_arg.kind
{
let anon_const_type = tcx.type_of(param_def_id).instantiate(tcx, args);
// FIXME(generic_const_parameter_types): Ideally we remove these errors below when
// we have the ability to intermix typeck of anon const const args with the parent
// bodies typeck.
@ -2352,14 +2359,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
return ty::Const::new_error(tcx, e);
}
tcx.feed_anon_const_type(
anon.def_id,
ty::EarlyBinder::bind(tcx.type_of(param_def_id).instantiate(tcx, args)),
);
tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(anon_const_type));
}
let hir_id = const_arg.hir_id;
match const_arg.kind {
hir::ConstArgKind::Tup(span, exprs) => self.lower_const_arg_tup(exprs, feed, span),
hir::ConstArgKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
@ -2464,7 +2469,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.iter()
.zip(args)
.map(|(field_def, arg)| {
self.lower_const_arg(arg, FeedConstTy::Param(field_def.did, adt_args))
self.lower_const_arg(arg, FeedConstTy::with_type_of(tcx, field_def.did, adt_args))
})
.collect::<Vec<_>>();
@ -2480,6 +2485,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ty::Const::new_value(tcx, valtree, adt_ty)
}
fn lower_const_arg_tup(
&self,
exprs: &'tcx [&'tcx hir::ConstArg<'tcx>],
feed: FeedConstTy<'tcx>,
span: Span,
) -> Const<'tcx> {
let tcx = self.tcx();
let FeedConstTy::WithTy(ty) = feed else {
return Const::new_error_with_message(tcx, span, "unsupported const tuple");
};
let ty::Tuple(tys) = ty.kind() else {
return Const::new_error_with_message(tcx, span, "const tuple must have a tuple type");
};
let exprs = exprs
.iter()
.zip(tys.iter())
.map(|(expr, ty)| self.lower_const_arg(expr, FeedConstTy::WithTy(ty)))
.collect::<Vec<_>>();
let valtree = ty::ValTree::from_branches(tcx, exprs);
ty::Const::new_value(tcx, valtree, ty)
}
fn lower_const_arg_struct(
&self,
hir_id: HirId,
@ -2558,7 +2589,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
return ty::Const::new_error(tcx, e);
}
self.lower_const_arg(expr.expr, FeedConstTy::Param(field_def.did, adt_args))
self.lower_const_arg(
expr.expr,
FeedConstTy::with_type_of(tcx, field_def.did, adt_args),
)
}
None => {
let e = tcx.dcx().span_err(

View file

@ -301,7 +301,7 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
pub fn lower_const_arg_for_rustdoc<'tcx>(
tcx: TyCtxt<'tcx>,
hir_ct: &hir::ConstArg<'tcx>,
feed: FeedConstTy<'_, 'tcx>,
feed: FeedConstTy<'tcx>,
) -> Const<'tcx> {
let env_def_id = tcx.hir_get_parent_item(hir_ct.hir_id);
collect::ItemCtxt::new(tcx, env_def_id.def_id).lowerer().lower_const_arg(hir_ct, feed)

View file

@ -1141,6 +1141,16 @@ impl<'a> State<'a> {
fn print_const_arg(&mut self, const_arg: &hir::ConstArg<'_>) {
match &const_arg.kind {
ConstArgKind::Tup(_, exprs) => {
self.popen();
self.commasep_cmnt(
Inconsistent,
exprs,
|s, arg| s.print_const_arg(arg),
|arg| arg.span(),
);
self.pclose();
}
ConstArgKind::Struct(qpath, fields) => self.print_const_struct(qpath, fields),
ConstArgKind::TupleCall(qpath, args) => self.print_const_ctor(qpath, args),
ConstArgKind::Path(qpath) => self.print_qpath(qpath, true),

View file

@ -525,7 +525,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn lower_const_arg(
&self,
const_arg: &'tcx hir::ConstArg<'tcx>,
feed: FeedConstTy<'_, 'tcx>,
feed: FeedConstTy<'tcx>,
) -> ty::Const<'tcx> {
let ct = self.lowerer().lower_const_arg(const_arg, feed);
self.register_wf_obligation(
@ -1228,7 +1228,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Ambiguous parts of `ConstArg` are handled in the match arms below
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
FeedConstTy::with_type_of(self.fcx.tcx, param.def_id, preceding_args),
)
.into(),
(&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {

View file

@ -447,7 +447,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// We handle the ambig portions of `ConstArg` in the match arms below
.lower_const_arg(
ct.as_unambig_ct(),
FeedConstTy::Param(param.def_id, preceding_args),
FeedConstTy::with_type_of(self.cfcx.tcx, param.def_id, preceding_args),
)
.into(),
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {

View file

@ -1442,6 +1442,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
hir::ConstArgKind::Error(..)
| hir::ConstArgKind::Struct(..)
| hir::ConstArgKind::TupleCall(..)
| hir::ConstArgKind::Tup(..)
| hir::ConstArgKind::Path(..)
| hir::ConstArgKind::Infer(..) => true,
hir::ConstArgKind::Anon(..) => false,

View file

@ -419,7 +419,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
// Avoid overwriting `const_arg_context` as we may want to treat const blocks
// as being anon consts if we are inside a const argument.
ExprKind::Struct(_) | ExprKind::Call(..) => return visit::walk_expr(self, expr),
ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) => {
return visit::walk_expr(self, expr);
}
// FIXME(mgca): we may want to handle block labels in some manner
ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind {
// FIXME(mgca): this probably means that mac calls that expand

View file

@ -326,6 +326,10 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg<'tcx>) -> ConstantKind
hir::ConstArgKind::TupleCall(..) => {
ConstantKind::Path { path: "/* TUPLE CALL */".to_string().into() }
}
hir::ConstArgKind::Tup(..) => {
// FIXME(mgca): proper printing :3
ConstantKind::Path { path: "/* TUPLE EXPR */".to_string().into() }
}
hir::ConstArgKind::Anon(anon) => ConstantKind::Anonymous { body: anon.body },
hir::ConstArgKind::Infer(..) | hir::ConstArgKind::Error(..) => ConstantKind::Infer,
}
@ -1809,7 +1813,8 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
}
hir::ConstArgKind::Struct(..)
| hir::ConstArgKind::Path(..)
| hir::ConstArgKind::TupleCall(..) => {
| hir::ConstArgKind::TupleCall(..)
| hir::ConstArgKind::Tup(..) => {
let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, FeedConstTy::No);
print_const(cx, ct)
}

View file

@ -1,8 +1,8 @@
error: unconstrained generic constant
--> $DIR/doesnt_unify_evaluatable.rs:9:11
--> $DIR/doesnt_unify_evaluatable.rs:9:13
|
LL | bar::<{ T::ASSOC }>();
| ^^^^^^^^^^^^
| ^^^^^^^^
|
help: try adding a `where` bound
|

View file

@ -0,0 +1,20 @@
#![feature(min_generic_const_args, adt_const_params, unsized_const_params)]
#![expect(incomplete_features)]
trait Trait {
#[type_const]
const ASSOC: usize;
}
fn takes_tuple<const A: (u32, u32)>() {}
fn takes_nested_tuple<const A: (u32, (u32, u32))>() {}
fn generic_caller<T: Trait, const N: u32, const N2: u32>() {
takes_tuple::<{ (N, N + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block
takes_tuple::<{ (N, T::ASSOC + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block
takes_nested_tuple::<{ (N, (N, N + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block
takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>(); //~ ERROR generic parameters may not be used in const operations
}
fn main() {}

View file

@ -0,0 +1,26 @@
error: complex const arguments must be placed inside of a `const` block
--> $DIR/adt_expr_arg_tuple_expr_complex.rs:13:25
|
LL | takes_tuple::<{ (N, N + 1) }>();
| ^^^^^
error: complex const arguments must be placed inside of a `const` block
--> $DIR/adt_expr_arg_tuple_expr_complex.rs:14:25
|
LL | takes_tuple::<{ (N, T::ASSOC + 1) }>();
| ^^^^^^^^^^^^
error: complex const arguments must be placed inside of a `const` block
--> $DIR/adt_expr_arg_tuple_expr_complex.rs:16:36
|
LL | takes_nested_tuple::<{ (N, (N, N + 1)) }>();
| ^^^^^
error: generic parameters may not be used in const operations
--> $DIR/adt_expr_arg_tuple_expr_complex.rs:17:44
|
LL | takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>();
| ^
error: aborting due to 4 previous errors

View file

@ -0,0 +1,22 @@
//@ check-pass
#![feature(min_generic_const_args, adt_const_params, unsized_const_params)]
#![expect(incomplete_features)]
trait Trait {
#[type_const]
const ASSOC: usize;
}
fn takes_tuple<const A: (u32, u32)>() {}
fn takes_nested_tuple<const A: (u32, (u32, u32))>() {}
fn generic_caller<T: Trait, const N: u32, const N2: u32>() {
takes_tuple::<{ (N, N2) }>();
takes_tuple::<{ (N, T::ASSOC) }>();
takes_nested_tuple::<{ (N, (N, N2)) }>();
takes_nested_tuple::<{ (N, (N, T::ASSOC)) }>();
}
fn main() {}

View file

@ -4,7 +4,6 @@ trait Foo<const N: Bar<2>> {
//~^ WARN trait objects without an explicit `dyn` are deprecated
//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
//~| ERROR cycle detected when computing type of `Foo::N`
//~| ERROR cycle detected when computing type of `Foo::N`
fn func() {}
}

View file

@ -13,7 +13,7 @@ LL | trait Foo<const N: dyn Bar<2>> {
| +++
warning: trait objects without an explicit `dyn` are deprecated
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:20
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:10:20
|
LL | trait Bar<const M: Foo<2>> {}
| ^^^^^^
@ -32,7 +32,7 @@ LL | trait Foo<const N: Bar<2>> {
| ^^^^^^^^^^^^^^^
|
note: ...which requires computing type of `Bar::M`...
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:10:11
|
LL | trait Bar<const M: Foo<2>> {}
| ^^^^^^^^^^^^^^^
@ -44,26 +44,6 @@ LL | trait Foo<const N: Bar<2>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error[E0391]: cycle detected when computing type of `Foo::N`
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:3:11
|
LL | trait Foo<const N: Bar<2>> {
| ^^^^^^^^^^^^^^^
|
note: ...which requires computing type of `Bar::M`...
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11
|
LL | trait Bar<const M: Foo<2>> {}
| ^^^^^^^^^^^^^^^
= note: ...which again requires computing type of `Foo::N`, completing the cycle
note: cycle used when checking that `Foo` is well-formed
--> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:3:1
|
LL | trait Foo<const N: Bar<2>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 2 previous errors; 2 warnings emitted
error: aborting due to 1 previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0391`.