Skip to main content

rustc_mir_transform/
trivial_const.rs

1use std::ops::Deref;
2
3use rustc_hir::def::DefKind;
4use rustc_hir::def_id::LocalDefId;
5use rustc_middle::mir::{
6    Body, Const, ConstValue, Operand, Place, RETURN_PLACE, Rvalue, START_BLOCK, StatementKind,
7    TerminatorKind, UnevaluatedConst,
8};
9use rustc_middle::ty::{Ty, TyCtxt, TypeVisitableExt};
10
11/// If the given def is a trivial const, returns the value and type the const evaluates to.
12///
13/// A "trivial const" is a const which can be easily proven to evaluate successfully, and the value
14/// that it evaluates to can be easily found without going through the usual MIR phases for a const.
15///
16/// Currently, we support two forms of trivial const.
17///
18/// The base case is this:
19/// ```
20/// const A: usize = 0;
21/// ```
22/// which has this MIR:
23/// ```text
24/// const A: usize = {
25///     let mut _0: usize;
26///
27///     bb0: {
28///         _0 = const 0_usize;
29///         return;
30///     }
31/// }
32/// ```
33/// Which we recognize by looking for a Body which has a single basic block with a return
34/// terminator and a single statement which assigns an `Operand::Constant(Const::Val)` to the
35/// return place.
36/// This scenario meets the required criteria because:
37/// * Control flow cannot panic, we don't have any calls or assert terminators
38/// * The value of the const is already computed, so it cannot fail
39///
40/// In addition to assignment of literals, assignments of trivial consts are also considered
41/// trivial consts. In this case, both `A` and `B` are trivial:
42/// ```
43/// const A: usize = 0;
44/// const B: usize = A;
45/// ```
46pub(crate) fn trivial_const<'a, 'tcx: 'a, F, B>(
47    tcx: TyCtxt<'tcx>,
48    def: LocalDefId,
49    body_provider: F,
50) -> Option<(ConstValue, Ty<'tcx>)>
51where
52    F: FnOnce() -> B,
53    B: Deref<Target = Body<'tcx>>,
54{
55    if !#[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(def) {
    DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst =>
        true,
    _ => false,
}matches!(
56        tcx.def_kind(def),
57        DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst
58    ) {
59        return None;
60    }
61
62    // If there are impossible predicates then MIR passes will replace the body with
63    // `unreachable` causing const eval errors when trying to evaluate the body. For
64    // now we avoid using trivial consts for such bodies so that the behaviour doesn't
65    // change.
66    if crate::impossible_predicates::has_impossible_predicates(tcx, def.into()) {
67        return None;
68    }
69
70    if !tcx.opaque_types_defined_by(def).is_empty() {
71        return None;
72    }
73
74    let body = body_provider();
75
76    if body.has_opaque_types() {
77        return None;
78    }
79
80    if body.basic_blocks.len() != 1 {
81        return None;
82    }
83
84    let block = &body.basic_blocks[START_BLOCK];
85    if block.statements.len() != 1 {
86        return None;
87    }
88
89    if block.terminator().kind != TerminatorKind::Return {
90        return None;
91    }
92
93    let StatementKind::Assign((place, rvalue)) = &block.statements[0].kind else {
94        return None;
95    };
96
97    if *place != Place::from(RETURN_PLACE) {
98        return None;
99    }
100
101    let Rvalue::Use(Operand::Constant(c), _) = rvalue else {
102        return None;
103    };
104    match c.const_ {
105        Const::Ty(..) => None,
106        Const::Unevaluated(UnevaluatedConst { def, args, .. }, _ty) => {
107            if !args.is_empty() {
108                return None;
109            }
110            tcx.trivial_const(def)
111        }
112        Const::Val(v, ty) => Some((v, ty)),
113    }
114}
115
116// The query provider is based on calling the free function trivial_const, which calls mir_built,
117// which internally has a fast-path for trivial consts so it too calls trivial_const. This isn't
118// recursive, but we are checking if the const is trivial twice. A better design might detect
119// trivial consts before getting to MIR, which would hopefully straighten this out.
120pub(crate) fn trivial_const_provider<'tcx>(
121    tcx: TyCtxt<'tcx>,
122    def: LocalDefId,
123) -> Option<(ConstValue, Ty<'tcx>)> {
124    trivial_const(tcx, def, || tcx.mir_built(def).borrow())
125}