Skip to content

Commit 0151d5e

Browse files
committed
Lower hir::ConstArgKind::Struct to a ValTree
1 parent 2765b31 commit 0151d5e

File tree

10 files changed

+324
-6
lines changed

10 files changed

+324
-6
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,15 +2263,117 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
22632263
)
22642264
.unwrap_or_else(|guar| Const::new_error(tcx, guar))
22652265
}
2266-
hir::ConstArgKind::Struct(..) => {
2267-
span_bug!(const_arg.span(), "lowering `{:?}` is not yet implemented", const_arg)
2266+
hir::ConstArgKind::Struct(qpath, inits) => {
2267+
self.lower_struct_expr_const_arg(hir_id, qpath, inits, const_arg.span())
22682268
}
22692269
hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
22702270
hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
22712271
hir::ConstArgKind::Error(_, e) => ty::Const::new_error(tcx, e),
22722272
}
22732273
}
22742274

2275+
fn lower_struct_expr_const_arg(
2276+
&self,
2277+
hir_id: HirId,
2278+
qpath: hir::QPath<'tcx>,
2279+
inits: &'tcx [&'tcx hir::ConstArgExprField<'tcx>],
2280+
span: Span,
2281+
) -> Const<'tcx> {
2282+
let tcx = self.tcx();
2283+
2284+
let non_adt_or_variant_res = || {
2285+
let e = tcx.dcx().span_err(span, "struct expression with invalid base path");
2286+
ty::Const::new_error(tcx, e)
2287+
};
2288+
2289+
let (ty, variant_did) = match qpath {
2290+
hir::QPath::Resolved(maybe_qself, path) => {
2291+
debug!(?maybe_qself, ?path);
2292+
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
2293+
let ty =
2294+
self.lower_resolved_ty_path(opt_self_ty, path, hir_id, PermitVariants::Yes);
2295+
let variant_did = match path.res {
2296+
Res::Def(DefKind::Variant | DefKind::Struct, did) => did,
2297+
_ => return non_adt_or_variant_res(),
2298+
};
2299+
2300+
(ty, variant_did)
2301+
}
2302+
hir::QPath::TypeRelative(hir_self_ty, segment) => {
2303+
debug!(?hir_self_ty, ?segment);
2304+
let self_ty = self.lower_ty(hir_self_ty);
2305+
let opt_res = self.lower_type_relative_ty_path(
2306+
self_ty,
2307+
hir_self_ty,
2308+
segment,
2309+
hir_id,
2310+
span,
2311+
PermitVariants::Yes,
2312+
);
2313+
2314+
let (ty, _, res_def_id) = match opt_res {
2315+
Ok(r @ (_, DefKind::Variant | DefKind::Struct, _)) => r,
2316+
Ok(_) => return non_adt_or_variant_res(),
2317+
Err(e) => return ty::Const::new_error(tcx, e),
2318+
};
2319+
2320+
(ty, res_def_id)
2321+
}
2322+
};
2323+
2324+
let ty::Adt(adt_def, adt_args) = ty.kind() else { unreachable!() };
2325+
2326+
let variant_def = adt_def.variant_with_id(variant_did);
2327+
let variant_idx = adt_def.variant_index_with_id(variant_did).as_u32();
2328+
2329+
let fields = variant_def
2330+
.fields
2331+
.iter()
2332+
.map(|field_def| {
2333+
// FIXME(mgca): we aren't really handling privacy or stability at all but we should
2334+
let mut init_expr =
2335+
inits.iter().filter(|init_expr| init_expr.field.name == field_def.name);
2336+
2337+
match init_expr.next() {
2338+
Some(expr) => {
2339+
if let Some(expr) = init_expr.next() {
2340+
let e = tcx.dcx().span_err(
2341+
expr.span,
2342+
format!(
2343+
"struct expression with multiple initialisers for `{}`",
2344+
field_def.name,
2345+
),
2346+
);
2347+
return ty::Const::new_error(tcx, e);
2348+
}
2349+
2350+
self.lower_const_arg(expr.expr, FeedConstTy::Param(field_def.did, adt_args))
2351+
}
2352+
None => {
2353+
let e = tcx.dcx().span_err(
2354+
span,
2355+
format!(
2356+
"struct expression with missing field initialiser for `{}`",
2357+
field_def.name
2358+
),
2359+
);
2360+
ty::Const::new_error(tcx, e)
2361+
}
2362+
}
2363+
})
2364+
.collect::<Vec<_>>();
2365+
2366+
let opt_discr_const = if adt_def.is_enum() {
2367+
let valtree = ty::ValTree::from_scalar_int(tcx, variant_idx.into());
2368+
Some(ty::Const::new_value(tcx, valtree, tcx.types.u32))
2369+
} else {
2370+
None
2371+
};
2372+
2373+
let valtree = ty::ValTree::from_branches(tcx, opt_discr_const.into_iter().chain(fields));
2374+
ty::Const::new_value(tcx, valtree, ty)
2375+
}
2376+
22752377
/// Lower a [resolved][hir::QPath::Resolved] path to a (type-level) constant.
22762378
fn lower_resolved_const_path(
22772379
&self,

compiler/rustc_resolve/src/def_collector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
374374
});
375375
return;
376376
}
377-
377+
378378
self.with_direct_const_arg(false, |this| {
379379
let parent =
380380
this.create_def(constant.id, None, DefKind::AnonConst, constant.value.span);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#![feature(min_generic_const_args, adt_const_params)]
2+
#![expect(incomplete_features)]
3+
4+
#[derive(Eq, PartialEq, std::marker::ConstParamTy)]
5+
enum Option<T> {
6+
Some(T),
7+
None,
8+
}
9+
use Option::Some;
10+
11+
fn foo<const N: Option<u32>>() {}
12+
13+
trait Trait {
14+
#[type_const]
15+
const ASSOC: usize;
16+
}
17+
18+
fn bar<T: Trait, const N: u32>() {
19+
// the initializer of `_0` is a `N` which is a legal const argument
20+
// so this is ok.
21+
foo::<{ Some::<u32> { 0: N } }>();
22+
23+
// this is allowed as mgca supports uses of assoc consts in the
24+
// type system. ie `<T as Trait>::ASSOC` is a legal const argument
25+
foo::<{ Some::<u32> { 0: <T as Trait>::ASSOC } }>();
26+
27+
// this on the other hand is not allowed as `N + 1` is not a legal
28+
// const argument
29+
foo::<{ Some::<u32> { 0: N + 1 } }>();
30+
//~^ ERROR: complex const arguments must be placed inside of a `const` block
31+
32+
// this also is not allowed as generic parameters cannot be used
33+
// in anon const const args
34+
foo::<{ Some::<u32> { 0: const { N + 1 } } }>();
35+
//~^ ERROR: generic parameters may not be used in const operations
36+
//~| ERROR: generic parameters may not be used in const operations
37+
}
38+
39+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: complex const arguments must be placed inside of a `const` block
2+
--> $DIR/adt_expr_arg_simple.rs:29:30
3+
|
4+
LL | foo::<{ Some::<u32> { 0: N + 1 } }>();
5+
| ^^^^^
6+
7+
error: generic parameters may not be used in const operations
8+
--> $DIR/adt_expr_arg_simple.rs:34:38
9+
|
10+
LL | foo::<{ Some::<u32> { 0: const { N + 1 } } }>();
11+
| ^
12+
13+
error: generic parameters may not be used in const operations
14+
--> $DIR/adt_expr_arg_simple.rs:34:38
15+
|
16+
LL | foo::<{ Some::<u32> { 0: const { N + 1 } } }>();
17+
| ^
18+
|
19+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
20+
21+
error: aborting due to 3 previous errors
22+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![feature(min_generic_const_args, adt_const_params)]
2+
#![expect(incomplete_features)]
3+
#![crate_type = "lib"]
4+
5+
#[derive(Eq, PartialEq, std::marker::ConstParamTy)]
6+
struct Foo<T> { field: T }
7+
8+
fn NonStruct() {}
9+
10+
fn accepts<const N: Foo<u8>>() {}
11+
12+
fn bar() {
13+
accepts::<{ Foo::<u8> { }}>();
14+
//~^ ERROR: struct expression with missing field initialiser for `field`
15+
accepts::<{ Foo::<u8> { field: const { 1 }, field: const { 2} }}>();
16+
//~^ ERROR: struct expression with multiple initialisers for `field`
17+
accepts::<{ Fooo::<u8> { field: const { 1 } }}>();
18+
//~^ ERROR: cannot find struct, variant or union type `Fooo` in this scope
19+
//~| ERROR: struct expression with invalid base path
20+
accepts::<{ NonStruct { }}>();
21+
//~^ ERROR: expected struct, variant or union type, found function `NonStruct`
22+
//~| ERROR: struct expression with invalid base path
23+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0422]: cannot find struct, variant or union type `Fooo` in this scope
2+
--> $DIR/adt_expr_erroneuous_inits.rs:17:17
3+
|
4+
LL | struct Foo<T> { field: T }
5+
| ------------- similarly named struct `Foo` defined here
6+
...
7+
LL | accepts::<{ Fooo::<u8> { field: const { 1 } }}>();
8+
| ^^^^
9+
|
10+
help: a struct with a similar name exists
11+
|
12+
LL - accepts::<{ Fooo::<u8> { field: const { 1 } }}>();
13+
LL + accepts::<{ Foo::<u8> { field: const { 1 } }}>();
14+
|
15+
16+
error[E0574]: expected struct, variant or union type, found function `NonStruct`
17+
--> $DIR/adt_expr_erroneuous_inits.rs:20:17
18+
|
19+
LL | accepts::<{ NonStruct { }}>();
20+
| ^^^^^^^^^ not a struct, variant or union type
21+
22+
error: struct expression with missing field initialiser for `field`
23+
--> $DIR/adt_expr_erroneuous_inits.rs:13:17
24+
|
25+
LL | accepts::<{ Foo::<u8> { }}>();
26+
| ^^^^^^^^^
27+
28+
error: struct expression with multiple initialisers for `field`
29+
--> $DIR/adt_expr_erroneuous_inits.rs:15:49
30+
|
31+
LL | accepts::<{ Foo::<u8> { field: const { 1 }, field: const { 2} }}>();
32+
| ^^^^^^^^^^^^^^^^^
33+
34+
error: struct expression with invalid base path
35+
--> $DIR/adt_expr_erroneuous_inits.rs:17:17
36+
|
37+
LL | accepts::<{ Fooo::<u8> { field: const { 1 } }}>();
38+
| ^^^^^^^^^^
39+
40+
error: struct expression with invalid base path
41+
--> $DIR/adt_expr_erroneuous_inits.rs:20:17
42+
|
43+
LL | accepts::<{ NonStruct { }}>();
44+
| ^^^^^^^^^
45+
46+
error: aborting due to 6 previous errors
47+
48+
Some errors have detailed explanations: E0422, E0574.
49+
For more information about an error, try `rustc --explain E0422`.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ check-pass
2+
// This should error
3+
4+
#![feature(min_generic_const_args, adt_const_params)]
5+
#![expect(incomplete_features)]
6+
7+
#[derive(Eq, PartialEq, std::marker::ConstParamTy)]
8+
struct Foo<T> { field: T }
9+
10+
fn accepts<const N: Foo<u8>>() {}
11+
12+
fn bar<const N: bool>() {
13+
// `N` is not of type `u8` but we don't actually check this anywhere yet
14+
accepts::<{ Foo::<u8> { field: N }}>();
15+
}
16+
17+
fn main() {}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//@ check-pass
2+
3+
#![feature(
4+
generic_const_items,
5+
min_generic_const_args,
6+
adt_const_params,
7+
generic_const_parameter_types,
8+
unsized_const_params,
9+
)]
10+
#![expect(incomplete_features)]
11+
12+
use std::marker::{PhantomData, ConstParamTy, ConstParamTy_};
13+
14+
#[derive(PartialEq, Eq, ConstParamTy)]
15+
struct Foo<T> {
16+
field: T,
17+
}
18+
19+
#[type_const]
20+
const WRAP<T: ConstParamTy_, const N: T>: Foo<T> = { Foo::<T> {
21+
field: N,
22+
} };
23+
24+
fn main() {
25+
// What we're trying to accomplish here is winding up with an equality relation
26+
// between two `ty::Const` that looks something like:
27+
//
28+
// ```
29+
// Foo<u8> { field: const { 1 + 2 } }
30+
// eq
31+
// Foo<u8> { field: ?x }
32+
// ```
33+
//
34+
// Note that the `field: _` here means a const argument `_` not a wildcard pattern.
35+
// This tests that we are able to infer `?x=3` even though the first `ty::Const`
36+
// may be a fully evaluated constant, and the latter is not fully evaluatable due
37+
// to inference variables.
38+
let _: PC<_, { WRAP::<u8, const { 1 + 1 }> }>
39+
= PC::<_, { Foo::<u8> { field: _ }}>;
40+
}
41+
42+
// "PhantomConst" helper equivalent to "PhantomData" used for testing equalities
43+
// of arbitrarily typed const arguments.
44+
struct PC<T: ConstParamTy_, const N: T> { _0: PhantomData<T> }
45+
const PC<T: ConstParamTy_, const N: T>: PC<T, N> = PC { _0: PhantomData::<T> };

tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
//@ check-pass
2-
31
// We allow for literals to implicitly be anon consts still regardless
42
// of whether a const block is placed around them or not
3+
//
4+
// However, we don't allow so for const arguments in field init positions.
5+
// This is just harder to implement so we did not do so.
6+
57

6-
#![feature(min_generic_const_args, associated_const_equality)]
8+
#![feature(min_generic_const_args, adt_const_params, associated_const_equality)]
79
#![expect(incomplete_features)]
810

911
trait Trait {
@@ -19,4 +21,15 @@ type ArrLen = [(); 1];
1921
struct Foo<const N: isize>;
2022
type NormalArg = (Foo<1>, Foo<-1>);
2123

24+
#[derive(Eq, PartialEq, std::marker::ConstParamTy)]
25+
struct ADT { field: u8 }
26+
27+
fn struct_expr() {
28+
fn takes_n<const N: ADT>() {}
29+
30+
takes_n::<{ ADT { field: 1 } }>();
31+
//~^ ERROR: complex const arguments must be placed inside of a `const` block
32+
takes_n::<{ ADT { field: const { 1 } } }>();
33+
}
34+
2235
fn main() {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: complex const arguments must be placed inside of a `const` block
2+
--> $DIR/explicit_anon_consts_literals_hack.rs:30:30
3+
|
4+
LL | takes_n::<{ ADT { field: 1 } }>();
5+
| ^
6+
7+
error: aborting due to 1 previous error
8+

0 commit comments

Comments
 (0)