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