1use rustc_errors::ErrorGuaranteed;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::LocalDefId;
4use rustc_middle::query::Providers;
5use rustc_middle::thir::visit;
6use rustc_middle::thir::visit::Visitor;
7use rustc_middle::ty::abstract_const::CastKind;
8use rustc_middle::ty::consts::ConstExt;
9use rustc_middle::ty::{self, Expr, LitToConstInput, TyCtxt, TypeVisitableExt};
10use rustc_middle::{mir, thir};
11use rustc_span::{Span, bug};
12use tracing::instrument;
13
14use crate::diagnostics::{GenericConstantTooComplex, GenericConstantTooComplexSub};
15
16fn check_binop(op: mir::BinOp) -> bool {
18 use mir::BinOp::*;
19 match op {
20 Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul
21 | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Shl
22 | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge | Gt | Cmp => true,
23 Offset => false,
24 }
25}
26
27fn check_unop(op: mir::UnOp) -> bool {
30 use mir::UnOp::*;
31 match op {
32 Not | Neg | PtrMetadata => true,
33 }
34}
35
36fn recurse_build<'tcx>(
37 tcx: TyCtxt<'tcx>,
38 body: &thir::Thir<'tcx>,
39 node: thir::ExprId,
40 root_span: Span,
41) -> Result<ty::Const<'tcx>, ErrorGuaranteed> {
42 use thir::ExprKind;
43 let node = &body.exprs[node];
44
45 let maybe_supported_error = |a| maybe_supported_error(tcx, a, root_span);
46 let error = |a| error(tcx, a, root_span);
47
48 Ok(match &node.kind {
49 &ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?,
51 &ExprKind::PlaceTypeAscription { source, .. }
52 | &ExprKind::ValueTypeAscription { source, .. } => {
53 recurse_build(tcx, body, source, root_span)?
54 }
55 &ExprKind::PlaceUnwrapUnsafeBinder { .. }
56 | &ExprKind::ValueUnwrapUnsafeBinder { .. }
57 | &ExprKind::WrapUnsafeBinder { .. } => {
58 {
::core::panicking::panic_fmt(format_args!("not implemented: {0}",
format_args!("FIXME(unsafe_binders)")));
}unimplemented!("FIXME(unsafe_binders)")
59 }
60 &ExprKind::Literal { lit, neg } => {
61 let sp = node.span;
62 match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: Some(node.ty), neg })
63 {
64 Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty),
65 None => ty::Const::new_misc_error(tcx),
66 }
67 }
68 &ExprKind::NonHirLiteral { lit, user_ty: _ } => {
69 let val = ty::ValTree::from_scalar_int(tcx, lit);
70 ty::Const::new_value(tcx, val, node.ty)
71 }
72 &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty),
73 &ExprKind::NamedConst { def_id, args, user_ty: _ } => {
74 let kind = match tcx.def_kind(def_id) {
75 DefKind::AssocConst => {
76 if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(def_id)) {
77 ty::AliasConstKind::InherentImpl { def_id }
78 } else {
79 ty::AliasConstKind::Projection { def_id }
80 }
81 }
82 DefKind::Const => ty::AliasConstKind::Free { def_id },
83 DefKind::AnonConst => ty::AliasConstKind::Anon { def_id },
84 kind => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected DefKind in THIR ExprKind::NamedConst: {0:?}",
kind), Location::caller())bug!("unexpected DefKind in THIR ExprKind::NamedConst: {kind:?}"),
85 };
86
87 let alias = ty::AliasConst::new(tcx, kind, args);
88 ty::Const::new_alias(tcx, ty::IsRigid::No, alias)
89 }
90 ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param),
91
92 ExprKind::Call { fun, args, .. } => {
93 let fun_ty = body.exprs[*fun].ty;
94 let fun = recurse_build(tcx, body, *fun, root_span)?;
95
96 let mut new_args = Vec::<ty::Const<'tcx>>::with_capacity(args.len());
97 for &id in args.iter() {
98 new_args.push(recurse_build(tcx, body, id, root_span)?);
99 }
100 ty::Const::new_expr(tcx, Expr::new_call(tcx, fun_ty, fun, new_args))
101 }
102 &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
103 let lhs_ty = body.exprs[lhs].ty;
104 let lhs = recurse_build(tcx, body, lhs, root_span)?;
105 let rhs_ty = body.exprs[rhs].ty;
106 let rhs = recurse_build(tcx, body, rhs, root_span)?;
107 ty::Const::new_expr(tcx, Expr::new_binop(tcx, op, lhs_ty, rhs_ty, lhs, rhs))
108 }
109 &ExprKind::Unary { op, arg } if check_unop(op) => {
110 let arg_ty = body.exprs[arg].ty;
111 let arg = recurse_build(tcx, body, arg, root_span)?;
112 ty::Const::new_expr(tcx, Expr::new_unop(tcx, op, arg_ty, arg))
113 }
114 ExprKind::Block { block } => {
122 if let thir::Block { stmts: [], expr: Some(e), .. } = &body.blocks[*block] {
123 recurse_build(tcx, body, *e, root_span)?
124 } else {
125 maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(node.span))?
126 }
127 }
128 &ExprKind::ValueExpr { source } => {
132 let value_ty = body.exprs[source].ty;
133 let value = recurse_build(tcx, body, source, root_span)?;
134 ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::Use, value_ty, value, node.ty))
135 }
136 &ExprKind::Cast { source } => {
137 let value_ty = body.exprs[source].ty;
138 let value = recurse_build(tcx, body, source, root_span)?;
139 ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::As, value_ty, value, node.ty))
140 }
141 ExprKind::Borrow { arg, .. } => {
142 let arg_node = &body.exprs[*arg];
143
144 if let ExprKind::Deref { arg } = arg_node.kind {
148 recurse_build(tcx, body, arg, root_span)?
149 } else {
150 maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(node.span))?
151 }
152 }
153 ExprKind::RawBorrow { .. } | ExprKind::Deref { .. } => maybe_supported_error(
155 GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
156 )?,
157 ExprKind::Repeat { .. } | ExprKind::Array { .. } => {
158 maybe_supported_error(GenericConstantTooComplexSub::ArrayNotSupported(node.span))?
159 }
160 ExprKind::NeverToAny { .. } => {
161 maybe_supported_error(GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span))?
162 }
163 ExprKind::Tuple { .. } => {
164 maybe_supported_error(GenericConstantTooComplexSub::TupleNotSupported(node.span))?
165 }
166 ExprKind::Index { .. } => {
167 maybe_supported_error(GenericConstantTooComplexSub::IndexNotSupported(node.span))?
168 }
169 ExprKind::Field { .. } => {
170 maybe_supported_error(GenericConstantTooComplexSub::FieldNotSupported(node.span))?
171 }
172 ExprKind::ConstBlock { .. } => {
173 maybe_supported_error(GenericConstantTooComplexSub::ConstBlockNotSupported(node.span))?
174 }
175 ExprKind::Adt(_) => {
176 maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?
177 }
178 ExprKind::PointerCoercion { .. } => {
180 error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
181 }
182 ExprKind::Yield { .. } => {
183 error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
184 }
185 ExprKind::Continue { .. }
186 | ExprKind::ConstContinue { .. }
187 | ExprKind::Break { .. }
188 | ExprKind::Loop { .. }
189 | ExprKind::LoopMatch { .. } => {
190 error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
191 }
192 ExprKind::ByUse { .. } => {
193 error(GenericConstantTooComplexSub::ByUseNotSupported(node.span))?
194 }
195 ExprKind::Unary { .. } => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
196 ExprKind::Binary { .. } => {
198 error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))?
199 }
200 ExprKind::LogicalOp { .. } => {
201 error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))?
202 }
203 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
204 error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
205 }
206 ExprKind::Closure { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => {
208 error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
209 }
210 ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
212 error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))?
213 }
214 ExprKind::InlineAsm { .. } => {
215 error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))?
216 }
217
218 ExprKind::VarRef { .. }
220 | ExprKind::UpvarRef { .. }
221 | ExprKind::StaticRef { .. }
222 | ExprKind::ThreadLocalRef(_) => {
223 error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
224 }
225 ExprKind::Reborrow { .. } => {
226 ::core::panicking::panic("not implemented");unimplemented!();
227 }
228 })
229}
230
231struct IsThirPolymorphic<'a, 'tcx> {
232 is_poly: bool,
233 thir: &'a thir::Thir<'tcx>,
234}
235
236fn error(
237 tcx: TyCtxt<'_>,
238 sub: GenericConstantTooComplexSub,
239 root_span: Span,
240) -> Result<!, ErrorGuaranteed> {
241 let reported = tcx.dcx().emit_err(GenericConstantTooComplex {
242 span: root_span,
243 maybe_supported: false,
244 sub,
245 });
246
247 Err(reported)
248}
249
250fn maybe_supported_error(
251 tcx: TyCtxt<'_>,
252 sub: GenericConstantTooComplexSub,
253 root_span: Span,
254) -> Result<!, ErrorGuaranteed> {
255 let reported = tcx.dcx().emit_err(GenericConstantTooComplex {
256 span: root_span,
257 maybe_supported: true,
258 sub,
259 });
260
261 Err(reported)
262}
263
264impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
265 fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
266 if expr.ty.has_non_region_param() {
267 return true;
268 }
269
270 match expr.kind {
271 thir::ExprKind::NamedConst { args, .. } | thir::ExprKind::ConstBlock { args, .. } => {
272 args.has_non_region_param()
273 }
274 thir::ExprKind::ConstParam { .. } => true,
275 thir::ExprKind::Repeat { value, count } => {
276 self.visit_expr(&self.thir()[value]);
277 count.has_non_region_param()
278 }
279 thir::ExprKind::Scope { .. }
280 | thir::ExprKind::If { .. }
281 | thir::ExprKind::Call { .. }
282 | thir::ExprKind::ByUse { .. }
283 | thir::ExprKind::Deref { .. }
284 | thir::ExprKind::Binary { .. }
285 | thir::ExprKind::LogicalOp { .. }
286 | thir::ExprKind::Unary { .. }
287 | thir::ExprKind::Cast { .. }
288 | thir::ExprKind::ValueExpr { .. }
289 | thir::ExprKind::NeverToAny { .. }
290 | thir::ExprKind::PointerCoercion { .. }
291 | thir::ExprKind::Loop { .. }
292 | thir::ExprKind::LoopMatch { .. }
293 | thir::ExprKind::Let { .. }
294 | thir::ExprKind::Match { .. }
295 | thir::ExprKind::Block { .. }
296 | thir::ExprKind::Assign { .. }
297 | thir::ExprKind::AssignOp { .. }
298 | thir::ExprKind::Field { .. }
299 | thir::ExprKind::Index { .. }
300 | thir::ExprKind::VarRef { .. }
301 | thir::ExprKind::UpvarRef { .. }
302 | thir::ExprKind::Borrow { .. }
303 | thir::ExprKind::RawBorrow { .. }
304 | thir::ExprKind::Break { .. }
305 | thir::ExprKind::Continue { .. }
306 | thir::ExprKind::ConstContinue { .. }
307 | thir::ExprKind::Return { .. }
308 | thir::ExprKind::Become { .. }
309 | thir::ExprKind::Array { .. }
310 | thir::ExprKind::Tuple { .. }
311 | thir::ExprKind::Adt(_)
312 | thir::ExprKind::PlaceTypeAscription { .. }
313 | thir::ExprKind::ValueTypeAscription { .. }
314 | thir::ExprKind::PlaceUnwrapUnsafeBinder { .. }
315 | thir::ExprKind::ValueUnwrapUnsafeBinder { .. }
316 | thir::ExprKind::WrapUnsafeBinder { .. }
317 | thir::ExprKind::Closure(_)
318 | thir::ExprKind::Literal { .. }
319 | thir::ExprKind::NonHirLiteral { .. }
320 | thir::ExprKind::ZstLiteral { .. }
321 | thir::ExprKind::StaticRef { .. }
322 | thir::ExprKind::InlineAsm(_)
323 | thir::ExprKind::ThreadLocalRef(_)
324 | thir::ExprKind::Yield { .. } => false,
325 thir::ExprKind::Reborrow { .. } => {
326 ::core::panicking::panic("not implemented");unimplemented!();
327 }
328 }
329 }
330 fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
331 if pat.ty.has_non_region_param() {
332 return true;
333 }
334
335 match pat.kind {
336 thir::PatKind::Constant { value } => value.has_non_region_param(),
337 thir::PatKind::Range(ref range) => {
338 let &thir::PatRange { lo, hi, .. } = range.as_ref();
339 lo.has_non_region_param() || hi.has_non_region_param()
340 }
341 _ => false,
342 }
343 }
344}
345
346impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
347 fn thir(&self) -> &'a thir::Thir<'tcx> {
348 self.thir
349 }
350
351 {}
#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("visit_expr",
"rustc_ty_utils::consts", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/fd8e211ae301c84a86d13cf52a6d3ce08c589e2e/compiler/rustc_ty_utils/src/consts.rs"),
::tracing_core::__macro_support::Option::Some(351u32),
::tracing_core::__macro_support::Option::Some("rustc_ty_utils::consts"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("expr")
}> =
::tracing::__macro_support::FieldName::new("expr");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr)
as &dyn ::tracing::field::Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
self.is_poly |= self.expr_is_poly(expr);
if !self.is_poly { visit::walk_expr(self, expr) }
}
}
}#[instrument(skip(self), level = "debug")]
352 fn visit_expr(&mut self, expr: &'a thir::Expr<'tcx>) {
353 self.is_poly |= self.expr_is_poly(expr);
354 if !self.is_poly {
355 visit::walk_expr(self, expr)
356 }
357 }
358
359 {}
#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("visit_pat",
"rustc_ty_utils::consts", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/fd8e211ae301c84a86d13cf52a6d3ce08c589e2e/compiler/rustc_ty_utils/src/consts.rs"),
::tracing_core::__macro_support::Option::Some(359u32),
::tracing_core::__macro_support::Option::Some("rustc_ty_utils::consts"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("pat")
}> =
::tracing::__macro_support::FieldName::new("pat");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
as &dyn ::tracing::field::Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
self.is_poly |= self.pat_is_poly(pat);
if !self.is_poly { visit::walk_pat(self, pat); }
}
}
}#[instrument(skip(self), level = "debug")]
360 fn visit_pat(&mut self, pat: &'a thir::Pat<'tcx>) {
361 self.is_poly |= self.pat_is_poly(pat);
362 if !self.is_poly {
363 visit::walk_pat(self, pat);
364 }
365 }
366}
367
368fn thir_abstract_const<'tcx>(
370 tcx: TyCtxt<'tcx>,
371 def: LocalDefId,
372) -> Result<Option<ty::EarlyBinder<'tcx, ty::Const<'tcx>>>, ErrorGuaranteed> {
373 if !tcx.features().generic_const_exprs() {
374 return Ok(None);
375 }
376
377 match tcx.def_kind(def) {
378 DefKind::AnonConst => (),
384 _ => return Ok(None),
385 }
386
387 let body = tcx.thir_body(def)?;
388 let (body, body_id) = (&*body.0.borrow(), body.1);
389
390 let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
391 visit::walk_expr(&mut is_poly_vis, &body[body_id]);
392 if !is_poly_vis.is_poly {
393 return Ok(None);
394 }
395
396 let root_span = body.exprs[body_id].span;
397
398 Ok(Some(ty::EarlyBinder::bind(tcx, recurse_build(tcx, body, body_id, root_span)?)))
399}
400
401pub(crate) fn provide(providers: &mut Providers) {
402 *providers = Providers { thir_abstract_const, ..*providers };
403}