1 use rustc_errors
::ErrorGuaranteed
;
2 use rustc_hir
::def
::DefKind
;
3 use rustc_hir
::def_id
::LocalDefId
;
4 use rustc_middle
::mir
::interpret
::{LitToConstError, LitToConstInput}
;
5 use rustc_middle
::thir
::visit
;
6 use rustc_middle
::thir
::visit
::Visitor
;
7 use rustc_middle
::ty
::abstract_const
::CastKind
;
8 use rustc_middle
::ty
::{self, Expr, TyCtxt, TypeVisitable}
;
9 use rustc_middle
::{mir, thir}
;
11 use rustc_target
::abi
::VariantIdx
;
15 use crate::errors
::{GenericConstantTooComplex, GenericConstantTooComplexSub}
;
17 /// Destructures array, ADT or tuple constants into the constants
19 pub(crate) fn destructure_const
<'tcx
>(
21 const_
: ty
::Const
<'tcx
>,
22 ) -> ty
::DestructuredConst
<'tcx
> {
23 let ty
::ConstKind
::Value(valtree
) = const_
.kind() else {
24 bug
!("cannot destructure constant {:?}", const_
)
27 let branches
= match valtree
{
28 ty
::ValTree
::Branch(b
) => b
,
29 _
=> bug
!("cannot destructure constant {:?}", const_
),
32 let (fields
, variant
) = match const_
.ty().kind() {
33 ty
::Array(inner_ty
, _
) | ty
::Slice(inner_ty
) => {
34 // construct the consts for the elements of the array/slice
36 branches
.iter().map(|b
| tcx
.mk_const(*b
, *inner_ty
)).collect
::<Vec
<_
>>();
37 debug
!(?field_consts
);
41 ty
::Adt(def
, _
) if def
.variants().is_empty() => bug
!("unreachable"),
42 ty
::Adt(def
, substs
) => {
43 let (variant_idx
, branches
) = if def
.is_enum() {
44 let (head
, rest
) = branches
.split_first().unwrap();
45 (VariantIdx
::from_u32(head
.unwrap_leaf().try_to_u32().unwrap()), rest
)
47 (VariantIdx
::from_u32(0), branches
)
49 let fields
= &def
.variant(variant_idx
).fields
;
50 let mut field_consts
= Vec
::with_capacity(fields
.len());
52 for (field
, field_valtree
) in iter
::zip(fields
, branches
) {
53 let field_ty
= field
.ty(tcx
, substs
);
54 let field_const
= tcx
.mk_const(*field_valtree
, field_ty
);
55 field_consts
.push(field_const
);
57 debug
!(?field_consts
);
59 (field_consts
, Some(variant_idx
))
61 ty
::Tuple(elem_tys
) => {
62 let fields
= iter
::zip(*elem_tys
, branches
)
63 .map(|(elem_ty
, elem_valtree
)| tcx
.mk_const(*elem_valtree
, elem_ty
))
68 _
=> bug
!("cannot destructure constant {:?}", const_
),
71 let fields
= tcx
.arena
.alloc_from_iter(fields
.into_iter());
73 ty
::DestructuredConst { variant, fields }
76 /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
77 fn check_binop(op
: mir
::BinOp
) -> bool
{
80 Add
| Sub
| Mul
| Div
| Rem
| BitXor
| BitAnd
| BitOr
| Shl
| Shr
| Eq
| Lt
| Le
| Ne
86 /// While we currently allow all unary operations, we still want to explicitly guard against
87 /// future changes here.
88 fn check_unop(op
: mir
::UnOp
) -> bool
{
95 fn recurse_build
<'tcx
>(
97 body
: &thir
::Thir
<'tcx
>,
100 ) -> Result
<ty
::Const
<'tcx
>, ErrorGuaranteed
> {
102 let node
= &body
.exprs
[node
];
104 let maybe_supported_error
= |a
| maybe_supported_error(tcx
, a
, root_span
);
105 let error
= |a
| error(tcx
, a
, root_span
);
107 Ok(match &node
.kind
{
108 // I dont know if handling of these 3 is correct
109 &ExprKind
::Scope { value, .. }
=> recurse_build(tcx
, body
, value
, root_span
)?
,
110 &ExprKind
::PlaceTypeAscription { source, .. }
111 | &ExprKind
::ValueTypeAscription { source, .. }
=> {
112 recurse_build(tcx
, body
, source
, root_span
)?
114 &ExprKind
::Literal { lit, neg }
=> {
116 match tcx
.at(sp
).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }
) {
118 Err(LitToConstError
::Reported(guar
)) => {
119 tcx
.const_error_with_guaranteed(node
.ty
, guar
)
121 Err(LitToConstError
::TypeError
) => {
122 bug
!("encountered type error in lit_to_const")
126 &ExprKind
::NonHirLiteral { lit, user_ty: _ }
=> {
127 let val
= ty
::ValTree
::from_scalar_int(lit
);
128 tcx
.mk_const(val
, node
.ty
)
130 &ExprKind
::ZstLiteral { user_ty: _ }
=> {
131 let val
= ty
::ValTree
::zst();
132 tcx
.mk_const(val
, node
.ty
)
134 &ExprKind
::NamedConst { def_id, substs, user_ty: _ }
=> {
135 let uneval
= ty
::UnevaluatedConst
::new(ty
::WithOptConstParam
::unknown(def_id
), substs
);
136 tcx
.mk_const(uneval
, node
.ty
)
138 ExprKind
::ConstParam { param, .. }
=> tcx
.mk_const(*param
, node
.ty
),
140 ExprKind
::Call { fun, args, .. }
=> {
141 let fun
= recurse_build(tcx
, body
, *fun
, root_span
)?
;
143 let mut new_args
= Vec
::<ty
::Const
<'tcx
>>::with_capacity(args
.len());
144 for &id
in args
.iter() {
145 new_args
.push(recurse_build(tcx
, body
, id
, root_span
)?
);
147 let new_args
= tcx
.mk_const_list(new_args
.iter());
148 tcx
.mk_const(Expr
::FunctionCall(fun
, new_args
), node
.ty
)
150 &ExprKind
::Binary { op, lhs, rhs }
if check_binop(op
) => {
151 let lhs
= recurse_build(tcx
, body
, lhs
, root_span
)?
;
152 let rhs
= recurse_build(tcx
, body
, rhs
, root_span
)?
;
153 tcx
.mk_const(Expr
::Binop(op
, lhs
, rhs
), node
.ty
)
155 &ExprKind
::Unary { op, arg }
if check_unop(op
) => {
156 let arg
= recurse_build(tcx
, body
, arg
, root_span
)?
;
157 tcx
.mk_const(Expr
::UnOp(op
, arg
), node
.ty
)
159 // This is necessary so that the following compiles:
162 // fn foo<const N: usize>(a: [(); N + 1]) {
163 // bar::<{ N + 1 }>();
166 ExprKind
::Block { block }
=> {
167 if let thir
::Block { stmts: box [], expr: Some(e), .. }
= &body
.blocks
[*block
] {
168 recurse_build(tcx
, body
, *e
, root_span
)?
170 maybe_supported_error(GenericConstantTooComplexSub
::BlockNotSupported(node
.span
))?
173 // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
174 // "coercion cast" i.e. using a coercion or is a no-op.
175 // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
176 &ExprKind
::Use { source }
=> {
177 let arg
= recurse_build(tcx
, body
, source
, root_span
)?
;
178 tcx
.mk_const(Expr
::Cast(CastKind
::Use
, arg
, node
.ty
), node
.ty
)
180 &ExprKind
::Cast { source }
=> {
181 let arg
= recurse_build(tcx
, body
, source
, root_span
)?
;
182 tcx
.mk_const(Expr
::Cast(CastKind
::As
, arg
, node
.ty
), node
.ty
)
184 ExprKind
::Borrow { arg, .. }
=> {
185 let arg_node
= &body
.exprs
[*arg
];
187 // Skip reborrows for now until we allow Deref/Borrow/AddressOf
189 // FIXME(generic_const_exprs): Verify/explain why this is sound
190 if let ExprKind
::Deref { arg }
= arg_node
.kind
{
191 recurse_build(tcx
, body
, arg
, root_span
)?
193 maybe_supported_error(GenericConstantTooComplexSub
::BorrowNotSupported(node
.span
))?
196 // FIXME(generic_const_exprs): We may want to support these.
197 ExprKind
::AddressOf { .. }
| ExprKind
::Deref { .. }
=> maybe_supported_error(
198 GenericConstantTooComplexSub
::AddressAndDerefNotSupported(node
.span
),
200 ExprKind
::Repeat { .. }
| ExprKind
::Array { .. }
=> {
201 maybe_supported_error(GenericConstantTooComplexSub
::ArrayNotSupported(node
.span
))?
203 ExprKind
::NeverToAny { .. }
=> {
204 maybe_supported_error(GenericConstantTooComplexSub
::NeverToAnyNotSupported(node
.span
))?
206 ExprKind
::Tuple { .. }
=> {
207 maybe_supported_error(GenericConstantTooComplexSub
::TupleNotSupported(node
.span
))?
209 ExprKind
::Index { .. }
=> {
210 maybe_supported_error(GenericConstantTooComplexSub
::IndexNotSupported(node
.span
))?
212 ExprKind
::Field { .. }
=> {
213 maybe_supported_error(GenericConstantTooComplexSub
::FieldNotSupported(node
.span
))?
215 ExprKind
::ConstBlock { .. }
=> {
216 maybe_supported_error(GenericConstantTooComplexSub
::ConstBlockNotSupported(node
.span
))?
218 ExprKind
::Adt(_
) => {
219 maybe_supported_error(GenericConstantTooComplexSub
::AdtNotSupported(node
.span
))?
221 // dont know if this is correct
222 ExprKind
::Pointer { .. }
=> {
223 error(GenericConstantTooComplexSub
::PointerNotSupported(node
.span
))?
225 ExprKind
::Yield { .. }
=> {
226 error(GenericConstantTooComplexSub
::YieldNotSupported(node
.span
))?
228 ExprKind
::Continue { .. }
| ExprKind
::Break { .. }
| ExprKind
::Loop { .. }
=> {
229 error(GenericConstantTooComplexSub
::LoopNotSupported(node
.span
))?
231 ExprKind
::Box { .. }
=> error(GenericConstantTooComplexSub
::BoxNotSupported(node
.span
))?
,
233 ExprKind
::Unary { .. }
=> unreachable
!(),
234 // we handle valid unary/binary ops above
235 ExprKind
::Binary { .. }
=> {
236 error(GenericConstantTooComplexSub
::BinaryNotSupported(node
.span
))?
238 ExprKind
::LogicalOp { .. }
=> {
239 error(GenericConstantTooComplexSub
::LogicalOpNotSupported(node
.span
))?
241 ExprKind
::Assign { .. }
| ExprKind
::AssignOp { .. }
=> {
242 error(GenericConstantTooComplexSub
::AssignNotSupported(node
.span
))?
244 ExprKind
::Closure { .. }
| ExprKind
::Return { .. }
=> {
245 error(GenericConstantTooComplexSub
::ClosureAndReturnNotSupported(node
.span
))?
247 // let expressions imply control flow
248 ExprKind
::Match { .. }
| ExprKind
::If { .. }
| ExprKind
::Let { .. }
=> {
249 error(GenericConstantTooComplexSub
::ControlFlowNotSupported(node
.span
))?
251 ExprKind
::InlineAsm { .. }
=> {
252 error(GenericConstantTooComplexSub
::InlineAsmNotSupported(node
.span
))?
255 // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
256 ExprKind
::VarRef { .. }
257 | ExprKind
::UpvarRef { .. }
258 | ExprKind
::StaticRef { .. }
259 | ExprKind
::ThreadLocalRef(_
) => {
260 error(GenericConstantTooComplexSub
::OperationNotSupported(node
.span
))?
265 struct IsThirPolymorphic
<'a
, 'tcx
> {
267 thir
: &'a thir
::Thir
<'tcx
>,
272 sub
: GenericConstantTooComplexSub
,
274 ) -> Result
<!, ErrorGuaranteed
> {
275 let reported
= tcx
.sess
.emit_err(GenericConstantTooComplex
{
277 maybe_supported
: None
,
284 fn maybe_supported_error
<'tcx
>(
286 sub
: GenericConstantTooComplexSub
,
288 ) -> Result
<!, ErrorGuaranteed
> {
289 let reported
= tcx
.sess
.emit_err(GenericConstantTooComplex
{
291 maybe_supported
: Some(()),
298 impl<'a
, 'tcx
> IsThirPolymorphic
<'a
, 'tcx
> {
299 fn expr_is_poly(&mut self, expr
: &thir
::Expr
<'tcx
>) -> bool
{
300 if expr
.ty
.has_non_region_param() {
305 thir
::ExprKind
::NamedConst { substs, .. }
=> substs
.has_non_region_param(),
306 thir
::ExprKind
::ConstParam { .. }
=> true,
307 thir
::ExprKind
::Repeat { value, count }
=> {
308 self.visit_expr(&self.thir()[value
]);
309 count
.has_non_region_param()
314 fn pat_is_poly(&mut self, pat
: &thir
::Pat
<'tcx
>) -> bool
{
315 if pat
.ty
.has_non_region_param() {
320 thir
::PatKind
::Constant { value }
=> value
.has_non_region_param(),
321 thir
::PatKind
::Range(box thir
::PatRange { lo, hi, .. }
) => {
322 lo
.has_non_region_param() || hi
.has_non_region_param()
329 impl<'a
, 'tcx
> visit
::Visitor
<'a
, 'tcx
> for IsThirPolymorphic
<'a
, 'tcx
> {
330 fn thir(&self) -> &'a thir
::Thir
<'tcx
> {
334 #[instrument(skip(self), level = "debug")]
335 fn visit_expr(&mut self, expr
: &thir
::Expr
<'tcx
>) {
336 self.is_poly
|= self.expr_is_poly(expr
);
338 visit
::walk_expr(self, expr
)
342 #[instrument(skip(self), level = "debug")]
343 fn visit_pat(&mut self, pat
: &thir
::Pat
<'tcx
>) {
344 self.is_poly
|= self.pat_is_poly(pat
);
346 visit
::walk_pat(self, pat
);
351 /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
352 pub fn thir_abstract_const
<'tcx
>(
354 def
: ty
::WithOptConstParam
<LocalDefId
>,
355 ) -> Result
<Option
<ty
::Const
<'tcx
>>, ErrorGuaranteed
> {
356 if tcx
.features().generic_const_exprs
{
357 match tcx
.def_kind(def
.did
) {
358 // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
359 // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
360 // we want to look into them or treat them as opaque projections.
362 // Right now we do neither of that and simply always fail to unify them.
363 DefKind
::AnonConst
| DefKind
::InlineConst
=> (),
364 _
=> return Ok(None
),
367 let body
= tcx
.thir_body(def
)?
;
368 let (body
, body_id
) = (&*body
.0.borrow(), body
.1);
370 let mut is_poly_vis
= IsThirPolymorphic { is_poly: false, thir: body }
;
371 visit
::walk_expr(&mut is_poly_vis
, &body
[body_id
]);
372 if !is_poly_vis
.is_poly
{
376 let root_span
= body
.exprs
[body_id
].span
;
378 Some(recurse_build(tcx
, body
, body_id
, root_span
)).transpose()
384 pub fn provide(providers
: &mut ty
::query
::Providers
) {
385 *providers
= ty
::query
::Providers
{
387 thir_abstract_const
: |tcx
, def_id
| {
388 let def_id
= def_id
.expect_local();
389 if let Some(def
) = ty
::WithOptConstParam
::try_lookup(def_id
, tcx
) {
390 tcx
.thir_abstract_const_of_const_arg(def
)
392 thir_abstract_const(tcx
, ty
::WithOptConstParam
::unknown(def_id
))
395 thir_abstract_const_of_const_arg
: |tcx
, (did
, param_did
)| {
398 ty
::WithOptConstParam { did, const_param_did: Some(param_did) }
,