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(
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(
354 def: ty::WithOptConstParam<LocalDefId>,
355 ) -> Result<Option<ty::Const<'_>>, 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) },