]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ty_utils/src/consts.rs
Rollup merge of #99222 - atsuzaki:generic_const_err, r=lcnr
[rust.git] / compiler / rustc_ty_utils / src / consts.rs
1 use rustc_errors::ErrorGuaranteed;
2 use rustc_hir::def::DefKind;
3 use rustc_hir::def_id::LocalDefId;
4 use rustc_index::vec::IndexVec;
5 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
6 use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
7 use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
8 use rustc_middle::{mir, thir};
9 use rustc_span::Span;
10 use rustc_target::abi::VariantIdx;
11
12 use std::iter;
13
14 /// Destructures array, ADT or tuple constants into the constants
15 /// of their fields.
16 pub(crate) fn destructure_const<'tcx>(
17     tcx: TyCtxt<'tcx>,
18     const_: ty::Const<'tcx>,
19 ) -> ty::DestructuredConst<'tcx> {
20     let ty::ConstKind::Value(valtree) = const_.kind() else {
21         bug!("cannot destructure constant {:?}", const_)
22     };
23
24     let branches = match valtree {
25         ty::ValTree::Branch(b) => b,
26         _ => bug!("cannot destructure constant {:?}", const_),
27     };
28
29     let (fields, variant) = match const_.ty().kind() {
30         ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
31             // construct the consts for the elements of the array/slice
32             let field_consts = branches
33                 .iter()
34                 .map(|b| tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }))
35                 .collect::<Vec<_>>();
36             debug!(?field_consts);
37
38             (field_consts, None)
39         }
40         ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"),
41         ty::Adt(def, substs) => {
42             let (variant_idx, branches) = if def.is_enum() {
43                 let (head, rest) = branches.split_first().unwrap();
44                 (VariantIdx::from_u32(head.unwrap_leaf().try_to_u32().unwrap()), rest)
45             } else {
46                 (VariantIdx::from_u32(0), branches)
47             };
48             let fields = &def.variant(variant_idx).fields;
49             let mut field_consts = Vec::with_capacity(fields.len());
50
51             for (field, field_valtree) in iter::zip(fields, branches) {
52                 let field_ty = field.ty(tcx, substs);
53                 let field_const = tcx.mk_const(ty::ConstS {
54                     kind: ty::ConstKind::Value(*field_valtree),
55                     ty: field_ty,
56                 });
57                 field_consts.push(field_const);
58             }
59             debug!(?field_consts);
60
61             (field_consts, Some(variant_idx))
62         }
63         ty::Tuple(elem_tys) => {
64             let fields = iter::zip(*elem_tys, branches)
65                 .map(|(elem_ty, elem_valtree)| {
66                     tcx.mk_const(ty::ConstS {
67                         kind: ty::ConstKind::Value(*elem_valtree),
68                         ty: elem_ty,
69                     })
70                 })
71                 .collect::<Vec<_>>();
72
73             (fields, None)
74         }
75         _ => bug!("cannot destructure constant {:?}", const_),
76     };
77
78     let fields = tcx.arena.alloc_from_iter(fields.into_iter());
79
80     ty::DestructuredConst { variant, fields }
81 }
82
83 pub struct AbstractConstBuilder<'a, 'tcx> {
84     tcx: TyCtxt<'tcx>,
85     body_id: thir::ExprId,
86     body: &'a thir::Thir<'tcx>,
87     /// The current WIP node tree.
88     nodes: IndexVec<NodeId, Node<'tcx>>,
89 }
90
91 impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
92     fn root_span(&self) -> Span {
93         self.body.exprs[self.body_id].span
94     }
95
96     fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
97         let reported = self
98             .tcx
99             .sess
100             .struct_span_err(self.root_span(), "overly complex generic constant")
101             .span_label(span, msg)
102             .help("consider moving this anonymous constant into a `const` function")
103             .emit();
104
105         Err(reported)
106     }
107     fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
108         let reported = self
109             .tcx
110             .sess
111             .struct_span_err(self.root_span(), "overly complex generic constant")
112             .span_label(span, msg)
113             .help("consider moving this anonymous constant into a `const` function")
114             .note("this operation may be supported in the future")
115             .emit();
116
117         Err(reported)
118     }
119
120     #[instrument(skip(tcx, body, body_id), level = "debug")]
121     pub fn new(
122         tcx: TyCtxt<'tcx>,
123         (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
124     ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
125         let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
126
127         struct IsThirPolymorphic<'a, 'tcx> {
128             is_poly: bool,
129             thir: &'a thir::Thir<'tcx>,
130         }
131
132         use crate::rustc_middle::thir::visit::Visitor;
133         use thir::visit;
134
135         impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
136             fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
137                 if expr.ty.has_param_types_or_consts() {
138                     return true;
139                 }
140
141                 match expr.kind {
142                     thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(),
143                     thir::ExprKind::ConstParam { .. } => true,
144                     thir::ExprKind::Repeat { value, count } => {
145                         self.visit_expr(&self.thir()[value]);
146                         count.has_param_types_or_consts()
147                     }
148                     _ => false,
149                 }
150             }
151
152             fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
153                 if pat.ty.has_param_types_or_consts() {
154                     return true;
155                 }
156
157                 match pat.kind.as_ref() {
158                     thir::PatKind::Constant { value } => value.has_param_types_or_consts(),
159                     thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => {
160                         lo.has_param_types_or_consts() || hi.has_param_types_or_consts()
161                     }
162                     _ => false,
163                 }
164             }
165         }
166
167         impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
168             fn thir(&self) -> &'a thir::Thir<'tcx> {
169                 &self.thir
170             }
171
172             #[instrument(skip(self), level = "debug")]
173             fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
174                 self.is_poly |= self.expr_is_poly(expr);
175                 if !self.is_poly {
176                     visit::walk_expr(self, expr)
177                 }
178             }
179
180             #[instrument(skip(self), level = "debug")]
181             fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
182                 self.is_poly |= self.pat_is_poly(pat);
183                 if !self.is_poly {
184                     visit::walk_pat(self, pat);
185                 }
186             }
187         }
188
189         let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
190         visit::walk_expr(&mut is_poly_vis, &body[body_id]);
191         debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
192         if !is_poly_vis.is_poly {
193             return Ok(None);
194         }
195
196         Ok(Some(builder))
197     }
198
199     /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
200     fn check_binop(op: mir::BinOp) -> bool {
201         use mir::BinOp::*;
202         match op {
203             Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
204             | Ne | Ge | Gt => true,
205             Offset => false,
206         }
207     }
208
209     /// While we currently allow all unary operations, we still want to explicitly guard against
210     /// future changes here.
211     fn check_unop(op: mir::UnOp) -> bool {
212         use mir::UnOp::*;
213         match op {
214             Not | Neg => true,
215         }
216     }
217
218     /// Builds the abstract const by walking the thir and bailing out when
219     /// encountering an unsupported operation.
220     pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
221         debug!("AbstractConstBuilder::build: body={:?}", &*self.body);
222         self.recurse_build(self.body_id)?;
223
224         for n in self.nodes.iter() {
225             if let Node::Leaf(ct) = n {
226                 if let ty::ConstKind::Unevaluated(ct) = ct.kind() {
227                     // `AbstractConst`s should not contain any promoteds as they require references which
228                     // are not allowed.
229                     assert_eq!(ct.promoted, None);
230                     assert_eq!(ct, self.tcx.erase_regions(ct));
231                 }
232             }
233         }
234
235         Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
236     }
237
238     fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
239         use thir::ExprKind;
240         let node = &self.body.exprs[node];
241         Ok(match &node.kind {
242             // I dont know if handling of these 3 is correct
243             &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
244             &ExprKind::PlaceTypeAscription { source, .. }
245             | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
246             &ExprKind::Literal { lit, neg} => {
247                 let sp = node.span;
248                 let constant =
249                     match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
250                         Ok(c) => c,
251                         Err(LitToConstError::Reported) => {
252                             self.tcx.const_error(node.ty)
253                         }
254                         Err(LitToConstError::TypeError) => {
255                             bug!("encountered type error in lit_to_const")
256                         }
257                     };
258
259                 self.nodes.push(Node::Leaf(constant))
260             }
261             &ExprKind::NonHirLiteral { lit , user_ty: _} => {
262                 let val = ty::ValTree::from_scalar_int(lit);
263                 self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
264             }
265             &ExprKind::ZstLiteral { user_ty: _ } => {
266                 let val = ty::ValTree::zst();
267                 self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
268             }
269             &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
270                 let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
271
272                 let constant = self.tcx.mk_const(ty::ConstS {
273                                 kind: ty::ConstKind::Unevaluated(uneval),
274                                 ty: node.ty,
275                             });
276
277                 self.nodes.push(Node::Leaf(constant))
278             }
279
280             ExprKind::ConstParam {param, ..} => {
281                 let const_param = self.tcx.mk_const(ty::ConstS {
282                         kind: ty::ConstKind::Param(*param),
283                         ty: node.ty,
284                     });
285                 self.nodes.push(Node::Leaf(const_param))
286             }
287
288             ExprKind::Call { fun, args, .. } => {
289                 let fun = self.recurse_build(*fun)?;
290
291                 let mut new_args = Vec::<NodeId>::with_capacity(args.len());
292                 for &id in args.iter() {
293                     new_args.push(self.recurse_build(id)?);
294                 }
295                 let new_args = self.tcx.arena.alloc_slice(&new_args);
296                 self.nodes.push(Node::FunctionCall(fun, new_args))
297             }
298             &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
299                 let lhs = self.recurse_build(lhs)?;
300                 let rhs = self.recurse_build(rhs)?;
301                 self.nodes.push(Node::Binop(op, lhs, rhs))
302             }
303             &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
304                 let arg = self.recurse_build(arg)?;
305                 self.nodes.push(Node::UnaryOp(op, arg))
306             }
307             // This is necessary so that the following compiles:
308             //
309             // ```
310             // fn foo<const N: usize>(a: [(); N + 1]) {
311             //     bar::<{ N + 1 }>();
312             // }
313             // ```
314             ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
315                 self.recurse_build(*e)?
316             }
317             // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
318             // "coercion cast" i.e. using a coercion or is a no-op.
319             // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
320             &ExprKind::Use { source } => {
321                 let arg = self.recurse_build(source)?;
322                 self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty))
323             }
324             &ExprKind::Cast { source } => {
325                 let arg = self.recurse_build(source)?;
326                 self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
327             }
328             ExprKind::Borrow{ arg, ..} => {
329                 let arg_node = &self.body.exprs[*arg];
330
331                 // Skip reborrows for now until we allow Deref/Borrow/AddressOf
332                 // expressions.
333                 // FIXME(generic_const_exprs): Verify/explain why this is sound
334                 if let ExprKind::Deref { arg } = arg_node.kind {
335                     self.recurse_build(arg)?
336                 } else {
337                     self.maybe_supported_error(
338                         node.span,
339                         "borrowing is not supported in generic constants",
340                     )?
341                 }
342             }
343             // FIXME(generic_const_exprs): We may want to support these.
344             ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
345                 node.span,
346                 "dereferencing or taking the address is not supported in generic constants",
347             )?,
348             ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
349                 node.span,
350                 "array construction is not supported in generic constants",
351             )?,
352             ExprKind::Block { .. } => self.maybe_supported_error(
353                 node.span,
354                 "blocks are not supported in generic constant",
355             )?,
356             ExprKind::NeverToAny { .. } => self.maybe_supported_error(
357                 node.span,
358                 "converting nevers to any is not supported in generic constant",
359             )?,
360             ExprKind::Tuple { .. } => self.maybe_supported_error(
361                 node.span,
362                 "tuple construction is not supported in generic constants",
363             )?,
364             ExprKind::Index { .. } => self.maybe_supported_error(
365                 node.span,
366                 "indexing is not supported in generic constant",
367             )?,
368             ExprKind::Field { .. } => self.maybe_supported_error(
369                 node.span,
370                 "field access is not supported in generic constant",
371             )?,
372             ExprKind::ConstBlock { .. } => self.maybe_supported_error(
373                 node.span,
374                 "const blocks are not supported in generic constant",
375             )?,
376             ExprKind::Adt(_) => self.maybe_supported_error(
377                 node.span,
378                 "struct/enum construction is not supported in generic constants",
379             )?,
380             // dont know if this is correct
381             ExprKind::Pointer { .. } =>
382                 self.error(node.span, "pointer casts are not allowed in generic constants")?,
383             ExprKind::Yield { .. } =>
384                 self.error(node.span, "generator control flow is not allowed in generic constants")?,
385             ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
386                 .error(
387                     node.span,
388                     "loops and loop control flow are not supported in generic constants",
389                 )?,
390             ExprKind::Box { .. } =>
391                 self.error(node.span, "allocations are not allowed in generic constants")?,
392
393             ExprKind::Unary { .. } => unreachable!(),
394             // we handle valid unary/binary ops above
395             ExprKind::Binary { .. } =>
396                 self.error(node.span, "unsupported binary operation in generic constants")?,
397             ExprKind::LogicalOp { .. } =>
398                 self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
399             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
400                 self.error(node.span, "assignment is not supported in generic constants")?
401             }
402             ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
403                 node.span,
404                 "closures and function keywords are not supported in generic constants",
405             )?,
406             // let expressions imply control flow
407             ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
408                 self.error(node.span, "control flow is not supported in generic constants")?,
409             ExprKind::InlineAsm { .. } => {
410                 self.error(node.span, "assembly is not supported in generic constants")?
411             }
412
413             // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
414             ExprKind::VarRef { .. }
415             | ExprKind::UpvarRef { .. }
416             | ExprKind::StaticRef { .. }
417             | ExprKind::ThreadLocalRef(_) => {
418                 self.error(node.span, "unsupported operation in generic constant")?
419             }
420         })
421     }
422 }
423
424 /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
425 pub fn thir_abstract_const<'tcx>(
426     tcx: TyCtxt<'tcx>,
427     def: ty::WithOptConstParam<LocalDefId>,
428 ) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
429     if tcx.features().generic_const_exprs {
430         match tcx.def_kind(def.did) {
431             // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
432             // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
433             // we want to look into them or treat them as opaque projections.
434             //
435             // Right now we do neither of that and simply always fail to unify them.
436             DefKind::AnonConst | DefKind::InlineConst => (),
437             _ => return Ok(None),
438         }
439
440         let body = tcx.thir_body(def)?;
441
442         AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
443             .map(AbstractConstBuilder::build)
444             .transpose()
445     } else {
446         Ok(None)
447     }
448 }
449
450 pub fn provide(providers: &mut ty::query::Providers) {
451     *providers = ty::query::Providers {
452         destructure_const,
453         thir_abstract_const: |tcx, def_id| {
454             let def_id = def_id.expect_local();
455             if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
456                 tcx.thir_abstract_const_of_const_arg(def)
457             } else {
458                 thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
459             }
460         },
461         thir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
462             thir_abstract_const(
463                 tcx,
464                 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
465             )
466         },
467         ..*providers
468     };
469 }