Auto merge of #150603 - Kivooeo:tuple-struct, r=BoxyUwU

MGCA: Support for tuple constructors

r? BoxyUwU

part of https://github.com/rust-lang/rust/issues/132980

fixes rust-lang/rust#136379
fixes rust-lang/rust#138132

i tried to keep implementation very minimal and it's very similar to how structs was implemented with small adjustments

this does not make const constructor like None works, just something like Some(n)

todo:
* ~~tests~~
* write a better description (not sure if needed)
* add more comments and FIXMEs from structs code
This commit is contained in:
bors 2026-01-05 01:45:18 +00:00
commit 6885bdf1af
24 changed files with 537 additions and 28 deletions

View file

@ -2396,6 +2396,35 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
match &expr.kind {
ExprKind::Call(func, args) if let ExprKind::Path(qself, path) = &func.kind => {
let qpath = self.lower_qpath(
func.id,
qself,
path,
ParamMode::Explicit,
AllowReturnTypeNotation::No,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
);
let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| {
let const_arg = if let ExprKind::ConstBlock(anon_const) = &arg.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_direct(anon_const)
} else {
self.lower_expr_to_const_arg_direct(arg)
};
&*self.arena.alloc(const_arg)
}));
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::TupleCall(qpath, lowered_args),
}
}
ExprKind::Path(qself, path) => {
let qpath = self.lower_qpath(
expr.id,
@ -2460,7 +2489,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&& let StmtKind::Expr(expr) = &stmt.kind
&& matches!(
expr.kind,
ExprKind::Block(..) | ExprKind::Path(..) | ExprKind::Struct(..)
ExprKind::Block(..)
| ExprKind::Path(..)
| ExprKind::Struct(..)
| ExprKind::Call(..)
)
{
return self.lower_expr_to_const_arg_direct(expr);

View file

@ -499,6 +499,7 @@ impl<'hir, Unambig> ConstArg<'hir, Unambig> {
match self.kind {
ConstArgKind::Struct(path, _) => path.span(),
ConstArgKind::Path(path) => path.span(),
ConstArgKind::TupleCall(path, _) => path.span(),
ConstArgKind::Anon(anon) => anon.span,
ConstArgKind::Error(span, _) => span,
ConstArgKind::Infer(span, _) => span,
@ -519,6 +520,8 @@ pub enum ConstArgKind<'hir, Unambig = ()> {
Anon(&'hir AnonConst),
/// Represents construction of struct/struct variants
Struct(QPath<'hir>, &'hir [&'hir ConstArgExprField<'hir>]),
/// Tuple constructor variant
TupleCall(QPath<'hir>, &'hir [&'hir ConstArg<'hir>]),
/// Error const
Error(Span, ErrorGuaranteed),
/// This variant is not always used to represent inference consts, sometimes

View file

@ -1090,6 +1090,13 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>(
V::Result::output()
}
ConstArgKind::TupleCall(qpath, args) => {
try_visit!(visitor.visit_qpath(qpath, *hir_id, qpath.span()));
for arg in *args {
try_visit!(visitor.visit_const_arg_unambig(*arg));
}
V::Result::output()
}
ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()),
ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
ConstArgKind::Error(_, _) => V::Result::output(), // errors and spans are not important

View file

@ -314,6 +314,7 @@ pub enum PermitVariants {
enum TypeRelativePath<'tcx> {
AssocItem(DefId, GenericArgsRef<'tcx>),
Variant { adt: Ty<'tcx>, variant_did: DefId },
Ctor { ctor_def_id: DefId, args: GenericArgsRef<'tcx> },
}
/// New-typed boolean indicating whether explicit late-bound lifetimes
@ -1375,6 +1376,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let adt = self.check_param_uses_if_mcg(adt, span, false);
Ok((adt, DefKind::Variant, variant_did))
}
TypeRelativePath::Ctor { .. } => {
let e = tcx.dcx().span_err(span, "expected type, found tuple constructor");
Err(e)
}
}
}
@ -1410,6 +1415,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ct = self.check_param_uses_if_mcg(ct, span, false);
Ok(ct)
}
TypeRelativePath::Ctor { ctor_def_id, args } => {
return Ok(ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, ctor_def_id, args)));
}
// FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
// not just const ctors
TypeRelativePath::Variant { .. } => {
@ -1441,6 +1449,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.iter()
.find(|vd| tcx.hygienic_eq(segment.ident, vd.ident(tcx), adt_def.did()));
if let Some(variant_def) = variant_def {
// FIXME(mgca): do we want constructor resolutions to take priority over
// other possible resolutions?
if matches!(mode, LowerTypeRelativePathMode::Const)
&& let Some((CtorKind::Fn, ctor_def_id)) = variant_def.ctor
{
tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
let _ = self.prohibit_generic_args(
slice::from_ref(segment).iter(),
GenericsArgsErrExtend::EnumVariant {
qself: hir_self_ty,
assoc_segment: segment,
adt_def,
},
);
let ty::Adt(_, enum_args) = self_ty.kind() else { unreachable!() };
return Ok(TypeRelativePath::Ctor { ctor_def_id, args: enum_args });
}
if let PermitVariants::Yes = mode.permit_variants() {
tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
let _ = self.prohibit_generic_args(
@ -2349,12 +2374,106 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
hir::ConstArgKind::Struct(qpath, inits) => {
self.lower_const_arg_struct(hir_id, qpath, inits, const_arg.span())
}
hir::ConstArgKind::TupleCall(qpath, args) => {
self.lower_const_arg_tuple_call(hir_id, qpath, args, const_arg.span())
}
hir::ConstArgKind::Anon(anon) => self.lower_const_arg_anon(anon),
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
hir::ConstArgKind::Error(_, e) => ty::Const::new_error(tcx, e),
}
}
fn lower_const_arg_tuple_call(
&self,
hir_id: HirId,
qpath: hir::QPath<'tcx>,
args: &'tcx [&'tcx hir::ConstArg<'tcx>],
span: Span,
) -> Const<'tcx> {
let tcx = self.tcx();
let non_adt_or_variant_res = || {
let e = tcx.dcx().span_err(span, "tuple constructor with invalid base path");
ty::Const::new_error(tcx, e)
};
let ctor_const = match qpath {
hir::QPath::Resolved(maybe_qself, path) => {
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
self.lower_resolved_const_path(opt_self_ty, path, hir_id)
}
hir::QPath::TypeRelative(hir_self_ty, segment) => {
let self_ty = self.lower_ty(hir_self_ty);
match self.lower_type_relative_const_path(
self_ty,
hir_self_ty,
segment,
hir_id,
span,
) {
Ok(c) => c,
Err(_) => return non_adt_or_variant_res(),
}
}
};
let Some(value) = ctor_const.try_to_value() else {
return non_adt_or_variant_res();
};
let (adt_def, adt_args, variant_did) = match value.ty.kind() {
ty::FnDef(def_id, fn_args)
if let DefKind::Ctor(CtorOf::Variant, _) = tcx.def_kind(*def_id) =>
{
let parent_did = tcx.parent(*def_id);
let enum_did = tcx.parent(parent_did);
(tcx.adt_def(enum_did), fn_args, parent_did)
}
ty::FnDef(def_id, fn_args)
if let DefKind::Ctor(CtorOf::Struct, _) = tcx.def_kind(*def_id) =>
{
let parent_did = tcx.parent(*def_id);
(tcx.adt_def(parent_did), fn_args, parent_did)
}
_ => return non_adt_or_variant_res(),
};
let variant_def = adt_def.variant_with_id(variant_did);
let variant_idx = adt_def.variant_index_with_id(variant_did).as_u32();
if args.len() != variant_def.fields.len() {
let e = tcx.dcx().span_err(
span,
format!(
"tuple constructor has {} arguments but {} were provided",
variant_def.fields.len(),
args.len()
),
);
return ty::Const::new_error(tcx, e);
}
let fields = variant_def
.fields
.iter()
.zip(args)
.map(|(field_def, arg)| {
self.lower_const_arg(arg, FeedConstTy::Param(field_def.did, adt_args))
})
.collect::<Vec<_>>();
let opt_discr_const = if adt_def.is_enum() {
let valtree = ty::ValTree::from_scalar_int(tcx, variant_idx.into());
Some(ty::Const::new_value(tcx, valtree, tcx.types.u32))
} else {
None
};
let valtree = ty::ValTree::from_branches(tcx, opt_discr_const.into_iter().chain(fields));
let adt_ty = Ty::new_adt(tcx, adt_def, adt_args);
ty::Const::new_value(tcx, valtree, adt_ty)
}
fn lower_const_arg_struct(
&self,
hir_id: HirId,
@ -2486,6 +2605,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let args = self.lower_generic_args_of_path_segment(span, did, segment);
ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args))
}
Res::Def(DefKind::Ctor(_, CtorKind::Fn), did) => {
assert_eq!(opt_self_ty, None);
let [leading_segments @ .., segment] = path.segments else { bug!() };
let _ = self
.prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None);
let parent_did = tcx.parent(did);
let generics_did = if let DefKind::Ctor(CtorOf::Variant, _) = tcx.def_kind(did) {
tcx.parent(parent_did)
} else {
parent_did
};
let args = self.lower_generic_args_of_path_segment(span, generics_did, segment);
ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args))
}
Res::Def(DefKind::AssocConst, did) => {
let trait_segment = if let [modules @ .., trait_, _item] = path.segments {
let _ = self.prohibit_generic_args(modules.iter(), GenericsArgsErrExtend::None);
@ -2521,9 +2654,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
DefKind::Mod
| DefKind::Enum
| DefKind::Variant
| DefKind::Ctor(CtorOf::Variant, CtorKind::Fn)
| DefKind::Struct
| DefKind::Ctor(CtorOf::Struct, CtorKind::Fn)
| DefKind::OpaqueTy
| DefKind::TyAlias
| DefKind::TraitAlias

View file

@ -1139,6 +1139,7 @@ impl<'a> State<'a> {
match &const_arg.kind {
// FIXME(mgca): proper printing for struct exprs
ConstArgKind::Struct(..) => self.word("/* STRUCT EXPR */"),
ConstArgKind::TupleCall(..) => self.word("/* TUPLE CALL */"),
ConstArgKind::Path(qpath) => self.print_qpath(qpath, true),
ConstArgKind::Anon(anon) => self.print_anon_const(anon),
ConstArgKind::Error(_, _) => self.word("/*ERROR*/"),

View file

@ -1441,6 +1441,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// Skip encoding defs for these as they should not have had a `DefId` created
hir::ConstArgKind::Error(..)
| hir::ConstArgKind::Struct(..)
| hir::ConstArgKind::TupleCall(..)
| hir::ConstArgKind::Path(..)
| hir::ConstArgKind::Infer(..) => true,
hir::ConstArgKind::Anon(..) => false,

View file

@ -419,7 +419,7 @@ 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(_) => return visit::walk_expr(self, expr),
ExprKind::Struct(_) | ExprKind::Call(..) => 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

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

View file

@ -320,6 +320,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.body(field!(anon_const.body));
},
ConstArgKind::Struct(..) => chain!(self, "let ConstArgKind::Struct(..) = {const_arg}.kind"),
ConstArgKind::TupleCall(..) => chain!(self, "let ConstArgKind::TupleCall(..) = {const_arg}.kind"),
ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"),
ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"),
}

View file

@ -1140,7 +1140,7 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx
ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value),
ConstItemRhs::TypeConst(const_arg) => match const_arg.kind {
ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value),
ConstArgKind::Struct(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => {
ConstArgKind::Struct(..) | ConstArgKind::TupleCall(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => {
None
},
},

View file

@ -671,11 +671,19 @@ impl HirEqInterExpr<'_, '_, '_> {
.iter()
.zip(*inits_b)
.all(|(init_a, init_b)| self.eq_const_arg(init_a.expr, init_b.expr))
},
}
(ConstArgKind::TupleCall(path_a, args_a), ConstArgKind::TupleCall(path_b, args_b)) => {
self.eq_qpath(path_a, path_b)
&& args_a
.iter()
.zip(*args_b)
.all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b))
}
// Use explicit match for now since ConstArg is undergoing flux.
(
ConstArgKind::Path(..)
| ConstArgKind::Anon(..)
| ConstArgKind::TupleCall(..)
| ConstArgKind::Infer(..)
| ConstArgKind::Struct(..)
| ConstArgKind::Error(..),
@ -1546,6 +1554,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_const_arg(init.expr);
}
},
ConstArgKind::TupleCall(path, args) => {
self.hash_qpath(path);
for arg in *args {
self.hash_const_arg(arg);
}
},
ConstArgKind::Infer(..) | ConstArgKind::Error(..) => {},
}
}

View file

@ -1,11 +0,0 @@
//@ known-bug: #136379
#![feature(min_generic_const_args)]
pub struct S();
impl S {
pub fn f() -> [u8; S] {
[]
}
}
pub fn main() {}

View file

@ -1,10 +0,0 @@
//@ known-bug: #138132
#![feature(min_generic_const_args)]
struct b(Box<[u8; c]>);
impl b {
fn d(self) {
self.0.e()
}
}
struct c<'a>(&'a u8);
fn main() {}

View file

@ -0,0 +1,52 @@
//@ run-pass
#![feature(min_generic_const_args, adt_const_params)]
#![expect(incomplete_features)]
#![allow(dead_code)]
use std::marker::ConstParamTy;
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
struct Point(u32, u32);
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
enum MyEnum<T> {
Variant(T),
Other,
}
trait Trait {
#[type_const]
const ASSOC: u32;
}
fn with_point<const P: Point>() -> Point {
P
}
fn with_enum<const E: MyEnum<u32>>() -> MyEnum<u32> {
E
}
fn test<T: Trait, const N: u32>() {
with_point::<{ Point(<T as Trait>::ASSOC, N) }>();
}
fn test_basic<const N: u32>() {
with_point::<{ Point(N, N) }>();
with_point::<{ Point(const { 5 }, const { 10 }) }>();
with_enum::<{ MyEnum::Variant::<u32>(N) }>();
with_enum::<{ MyEnum::Variant::<u32>(const { 42 }) }>();
with_enum::<{ <MyEnum<u32>>::Variant(N) }>();
}
fn main() {
test_basic::<5>();
let p = with_point::<{ Point(const { 1 }, const { 2 }) }>();
assert_eq!(p, Point(1, 2));
let e = with_enum::<{ MyEnum::Variant::<u32>(const { 10 }) }>();
assert_eq!(e, MyEnum::Variant(10));
}

View file

@ -0,0 +1,19 @@
#![feature(min_generic_const_args, adt_const_params)]
#![expect(incomplete_features)]
use std::marker::ConstParamTy;
#[derive(Eq, PartialEq, ConstParamTy)]
struct Point(u32, u32);
fn with_point<const P: Point>() {}
fn test<const N: u32>() {
with_point::<{ Point(N + 1, N) }>();
//~^ ERROR complex const arguments must be placed inside of a `const` block
with_point::<{ Point(const { N + 1 }, N) }>();
//~^ ERROR generic parameters may not be used in const operations
}
fn main() {}

View file

@ -0,0 +1,14 @@
error: complex const arguments must be placed inside of a `const` block
--> $DIR/tuple_ctor_complex_args.rs:12:26
|
LL | with_point::<{ Point(N + 1, N) }>();
| ^^^^^
error: generic parameters may not be used in const operations
--> $DIR/tuple_ctor_complex_args.rs:15:34
|
LL | with_point::<{ Point(const { N + 1 }, N) }>();
| ^
error: aborting due to 2 previous errors

View file

@ -0,0 +1,46 @@
#![feature(min_generic_const_args, adt_const_params)]
#![expect(incomplete_features)]
use std::marker::ConstParamTy;
#[derive(Eq, PartialEq, ConstParamTy)]
struct Point(u32, u32);
#[derive(Eq, PartialEq, ConstParamTy)]
enum MyEnum<T> {
Variant(T),
Unit,
}
const CONST_ITEM: u32 = 42;
fn accepts_point<const P: Point>() {}
fn accepts_enum<const E: MyEnum<u32>>() {}
fn non_ctor() {}
fn test_errors<const N: usize>() {
accepts_point::<{ Point(N) }>();
//~^ ERROR tuple constructor has 2 arguments but 1 were provided
accepts_point::<{ Point(N, N, N) }>();
//~^ ERROR tuple constructor has 2 arguments but 3 were provided
accepts_point::<{ UnresolvedIdent(N, N) }>();
//~^ ERROR cannot find function, tuple struct or tuple variant `UnresolvedIdent` in this scope
//~| ERROR tuple constructor with invalid base path
accepts_point::<{ non_ctor(N, N) }>();
//~^ ERROR tuple constructor with invalid base path
accepts_point::<{ CONST_ITEM(N, N) }>();
//~^ ERROR tuple constructor with invalid base path
accepts_point::<{ Point }>();
//~^ ERROR the constant `Point` is not of type `Point`
accepts_enum::<{ MyEnum::Variant::<u32> }>();
//~^ ERROR the constant `MyEnum::<u32>::Variant` is not of type `MyEnum<u32>`
}
fn main() {}

View file

@ -0,0 +1,68 @@
error[E0425]: cannot find function, tuple struct or tuple variant `UnresolvedIdent` in this scope
--> $DIR/tuple_ctor_erroneous.rs:29:23
|
LL | accepts_point::<{ UnresolvedIdent(N, N) }>();
| ^^^^^^^^^^^^^^^ not found in this scope
|
help: you might be missing a const parameter
|
LL | fn test_errors<const N: usize, const UnresolvedIdent: /* Type */>() {
| +++++++++++++++++++++++++++++++++++
error: tuple constructor has 2 arguments but 1 were provided
--> $DIR/tuple_ctor_erroneous.rs:23:23
|
LL | accepts_point::<{ Point(N) }>();
| ^^^^^
error: tuple constructor has 2 arguments but 3 were provided
--> $DIR/tuple_ctor_erroneous.rs:26:23
|
LL | accepts_point::<{ Point(N, N, N) }>();
| ^^^^^
error: tuple constructor with invalid base path
--> $DIR/tuple_ctor_erroneous.rs:29:23
|
LL | accepts_point::<{ UnresolvedIdent(N, N) }>();
| ^^^^^^^^^^^^^^^
error: tuple constructor with invalid base path
--> $DIR/tuple_ctor_erroneous.rs:33:23
|
LL | accepts_point::<{ non_ctor(N, N) }>();
| ^^^^^^^^
error: tuple constructor with invalid base path
--> $DIR/tuple_ctor_erroneous.rs:36:23
|
LL | accepts_point::<{ CONST_ITEM(N, N) }>();
| ^^^^^^^^^^
error: the constant `Point` is not of type `Point`
--> $DIR/tuple_ctor_erroneous.rs:39:23
|
LL | accepts_point::<{ Point }>();
| ^^^^^ expected `Point`, found struct constructor
|
note: required by a const generic parameter in `accepts_point`
--> $DIR/tuple_ctor_erroneous.rs:17:18
|
LL | fn accepts_point<const P: Point>() {}
| ^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_point`
error: the constant `MyEnum::<u32>::Variant` is not of type `MyEnum<u32>`
--> $DIR/tuple_ctor_erroneous.rs:42:22
|
LL | accepts_enum::<{ MyEnum::Variant::<u32> }>();
| ^^^^^^^^^^^^^^^^^^^^^^ expected `MyEnum<u32>`, found enum constructor
|
note: required by a const generic parameter in `accepts_enum`
--> $DIR/tuple_ctor_erroneous.rs:18:17
|
LL | fn accepts_enum<const E: MyEnum<u32>>() {}
| ^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `accepts_enum`
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0425`.

View file

@ -0,0 +1,16 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/136379>
#![feature(min_generic_const_args)]
#![expect(incomplete_features)]
pub struct S();
impl S {
pub fn f() -> [u8; S] {
//~^ ERROR the constant `S` is not of type `usize`
[]
//~^ ERROR mismatched types [E0308]
}
}
pub fn main() {}

View file

@ -0,0 +1,23 @@
error: the constant `S` is not of type `usize`
--> $DIR/tuple_ctor_in_array_len.rs:9:19
|
LL | pub fn f() -> [u8; S] {
| ^^^^^^^ expected `usize`, found struct constructor
|
= note: the length of array `[u8; S]` must be type `usize`
error[E0308]: mismatched types
--> $DIR/tuple_ctor_in_array_len.rs:11:9
|
LL | pub fn f() -> [u8; S] {
| ------- expected `[u8; S]` because of return type
LL |
LL | []
| ^^ expected an array with a size of S, found one with a size of 0
|
= note: expected array `[u8; S]`
found array `[_; 0]`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,38 @@
//@ run-pass
#![feature(min_generic_const_args, adt_const_params)]
#![expect(incomplete_features)]
use std::marker::ConstParamTy;
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
struct Inner(u32);
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
struct Outer(Inner);
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
enum Container<T> {
Wrap(T),
}
fn with_outer<const O: Outer>() -> Outer {
O
}
fn with_container<const C: Container<Inner>>() -> Container<Inner> {
C
}
fn test<const N: u32>() {
with_outer::<{ Outer(Inner(N)) }>();
with_outer::<{ Outer(Inner(const { 42 })) }>();
with_container::<{ Container::Wrap::<Inner>(Inner(N)) }>();
}
fn main() {
test::<5>();
let o = with_outer::<{ Outer(Inner(const { 10 })) }>();
assert_eq!(o, Outer(Inner(10)));
}

View file

@ -0,0 +1,26 @@
//@ run-pass
#![feature(min_generic_const_args, adt_const_params)]
#![expect(incomplete_features)]
use std::marker::ConstParamTy;
#[derive(Debug, Eq, PartialEq, ConstParamTy)]
enum Option<T> {
Some(T),
}
fn with_option<const O: Option<u32>>() -> Option<u32> {
O
}
fn test<const N: u32>() {
with_option::<{ <Option<u32>>::Some(N) }>();
with_option::<{ <Option<u32>>::Some(const { 42 }) }>();
}
fn main() {
test::<5>();
let o = with_option::<{ <Option<u32>>::Some(const { 10 }) }>();
assert_eq!(o, Option::Some(10));
}

View file

@ -0,0 +1,14 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/138132>
#![feature(min_generic_const_args)]
#![expect(incomplete_features)]
struct B(Box<[u8; C]>);
//~^ ERROR missing generics for struct `C`
impl B {
fn d(self) {
self.0.e()
}
}
struct C<'a>(&'a u8);
fn main() {}

View file

@ -0,0 +1,19 @@
error[E0107]: missing generics for struct `C`
--> $DIR/type_as_const_in_array_len.rs:6:19
|
LL | struct B(Box<[u8; C]>);
| ^ expected 1 lifetime argument
|
note: struct defined here, with 1 lifetime parameter: `'a`
--> $DIR/type_as_const_in_array_len.rs:13:8
|
LL | struct C<'a>(&'a u8);
| ^ --
help: add missing lifetime argument
|
LL | struct B(Box<[u8; C<'a>]>);
| ++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0107`.