Skip to main content

rustc_ty_utils/
consts.rs

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
16/// We do not allow all binary operations in abstract consts, so filter disallowed ones.
17fn 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
27/// While we currently allow all unary operations, we still want to explicitly guard against
28/// future changes here.
29fn 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        // I dont know if handling of these 3 is correct
50        &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        // This is necessary so that the following compiles:
115        //
116        // ```
117        // fn foo<const N: usize>(a: [(); N + 1]) {
118        //     bar::<{ N + 1 }>();
119        // }
120        // ```
121        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` happens when a `hir::ExprKind::Cast` is a
129        // "coercion cast" i.e. using a coercion or is a no-op.
130        // This is important so that `N as usize as usize` doesn't unify with `N as usize`. (untested)
131        &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            // Skip reborrows for now until we allow Deref/Borrow/RawBorrow
145            // expressions.
146            // FIXME(generic_const_exprs): Verify/explain why this is sound
147            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        // FIXME(generic_const_exprs): We may want to support these.
154        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        // dont know if this is correct
179        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        // we handle valid unary/binary ops above
197        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        // FIXME(explicit_tail_calls): maybe get `become` a new error
207        ExprKind::Closure { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => {
208            error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
209        }
210        // let expressions imply control flow
211        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        // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
219        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
368/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
369fn 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        // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
379        // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
380        // we want to look into them or treat them as opaque projections.
381        //
382        // Right now we do neither of that and simply always fail to unify them.
383        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}