1 //! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`,
2 //! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const
5 //! By the time the MIR const-checker runs, these high-level constructs have been lowered to
6 //! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute
7 //! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
8 //! through, but errors for structured control flow in a `const` should be emitted here.
10 use rustc_ast
::ast
::Mutability
;
11 use rustc_errors
::struct_span_err
;
13 use rustc_hir
::def_id
::DefId
;
14 use rustc_hir
::intravisit
::{self, NestedVisitorMap, Visitor}
;
15 use rustc_middle
::hir
::map
::Map
;
16 use rustc_middle
::ty
::query
::Providers
;
17 use rustc_middle
::ty
::TyCtxt
;
18 use rustc_session
::config
::nightly_options
;
19 use rustc_session
::parse
::feature_err
;
20 use rustc_span
::{sym, Span, Symbol}
;
24 /// An expression that is not *always* legal in a const context.
25 #[derive(Clone, Copy)]
27 Loop(hir
::LoopSource
),
28 Match(hir
::MatchSource
),
33 fn name(self) -> String
{
35 Self::Loop(src
) => format
!("`{}`", src
.name()),
36 Self::Match(src
) => format
!("`{}`", src
.name()),
37 Self::OrPattern
=> "or-pattern".to_string(),
41 fn required_feature_gates(self) -> Option
<&'
static [Symbol
]> {
42 use hir
::LoopSource
::*;
43 use hir
::MatchSource
::*;
45 let gates
: &[_
] = match self {
47 | Self::Match(IfDesugar { .. }
)
48 | Self::Match(IfLetDesugar { .. }
)
49 | Self::OrPattern
=> &[sym
::const_if_match
],
51 Self::Loop(Loop
) => &[sym
::const_loop
],
54 | Self::Loop(WhileLet
)
55 | Self::Match(WhileDesugar
| WhileLetDesugar
) => {
56 &[sym
::const_loop
, sym
::const_if_match
]
59 // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
60 // so they are not yet allowed with `#![feature(const_loop)]`.
68 #[derive(Copy, Clone)]
78 fn for_body(body
: &hir
::Body
<'_
>, tcx
: TyCtxt
<'_
>) -> Option
<Self> {
79 let owner
= tcx
.hir().body_owner(body
.id());
80 let const_kind
= match tcx
.hir().body_owner_kind(owner
) {
81 hir
::BodyOwnerKind
::Const
=> Self::Const
,
82 hir
::BodyOwnerKind
::Static(Mutability
::Mut
) => Self::StaticMut
,
83 hir
::BodyOwnerKind
::Static(Mutability
::Not
) => Self::Static
,
85 hir
::BodyOwnerKind
::Fn
if tcx
.is_const_fn_raw(tcx
.hir().local_def_id(owner
)) => {
88 hir
::BodyOwnerKind
::Fn
| hir
::BodyOwnerKind
::Closure
=> return None
,
95 impl fmt
::Display
for ConstKind
{
96 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
98 Self::Static
=> "static",
99 Self::StaticMut
=> "static mut",
100 Self::Const
| Self::AnonConst
=> "const",
101 Self::ConstFn
=> "const fn",
108 fn check_mod_const_bodies(tcx
: TyCtxt
<'_
>, module_def_id
: DefId
) {
109 let mut vis
= CheckConstVisitor
::new(tcx
);
110 tcx
.hir().visit_item_likes_in_module(module_def_id
, &mut vis
.as_deep_visitor());
113 pub(crate) fn provide(providers
: &mut Providers
<'_
>) {
114 *providers
= Providers { check_mod_const_bodies, ..*providers }
;
117 #[derive(Copy, Clone)]
118 struct CheckConstVisitor
<'tcx
> {
120 const_kind
: Option
<ConstKind
>,
123 impl<'tcx
> CheckConstVisitor
<'tcx
> {
124 fn new(tcx
: TyCtxt
<'tcx
>) -> Self {
125 CheckConstVisitor { tcx, const_kind: None }
128 /// Emits an error when an unsupported expression is found in a const context.
129 fn const_check_violated(&self, expr
: NonConstExpr
, span
: Span
) {
130 let features
= self.tcx
.features();
131 let required_gates
= expr
.required_feature_gates();
132 match required_gates
{
133 // Don't emit an error if the user has enabled the requisite feature gates.
134 Some(gates
) if gates
.iter().all(|&g
| features
.enabled(g
)) => return,
136 // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
137 // corresponding feature gate. This encourages nightly users to use feature gates when
139 None
if self.tcx
.sess
.opts
.debugging_opts
.unleash_the_miri_inside_of_you
=> {
140 self.tcx
.sess
.span_warn(span
, "skipping const checks");
147 let const_kind
= self
149 .expect("`const_check_violated` may only be called inside a const context");
150 let msg
= format
!("{} is not allowed in a `{}`", expr
.name(), const_kind
);
152 let required_gates
= required_gates
.unwrap_or(&[]);
153 let missing_gates
: Vec
<_
> =
154 required_gates
.iter().copied().filter(|&g
| !features
.enabled(g
)).collect();
156 match missing_gates
.as_slice() {
157 &[] => struct_span_err
!(self.tcx
.sess
, span
, E0744
, "{}", msg
).emit(),
159 // If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
160 // explain why their `while` loop is being rejected.
161 &[gate @ sym
::const_if_match
] if required_gates
.contains(&sym
::const_loop
) => {
162 feature_err(&self.tcx
.sess
.parse_sess
, gate
, span
, &msg
)
164 "`#![feature(const_loop)]` alone is not sufficient, \
165 since this loop expression contains an implicit conditional",
170 &[missing_primary
, ref missing_secondary @
..] => {
171 let mut err
= feature_err(&self.tcx
.sess
.parse_sess
, missing_primary
, span
, &msg
);
173 // If multiple feature gates would be required to enable this expression, include
174 // them as help messages. Don't emit a separate error for each missing feature gate.
176 // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
177 // is a pretty narrow case, however.
178 if nightly_options
::is_nightly_build() {
179 for gate
in missing_secondary
{
181 "add `#![feature({})]` to the crate attributes to enable",
193 /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
194 fn recurse_into(&mut self, kind
: Option
<ConstKind
>, f
: impl FnOnce(&mut Self)) {
195 let parent_kind
= self.const_kind
;
196 self.const_kind
= kind
;
198 self.const_kind
= parent_kind
;
202 impl<'tcx
> Visitor
<'tcx
> for CheckConstVisitor
<'tcx
> {
203 type Map
= Map
<'tcx
>;
205 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
206 NestedVisitorMap
::OnlyBodies(self.tcx
.hir())
209 fn visit_anon_const(&mut self, anon
: &'tcx hir
::AnonConst
) {
210 let kind
= Some(ConstKind
::AnonConst
);
211 self.recurse_into(kind
, |this
| intravisit
::walk_anon_const(this
, anon
));
214 fn visit_body(&mut self, body
: &'tcx hir
::Body
<'tcx
>) {
215 let kind
= ConstKind
::for_body(body
, self.tcx
);
216 self.recurse_into(kind
, |this
| intravisit
::walk_body(this
, body
));
219 fn visit_pat(&mut self, p
: &'tcx hir
::Pat
<'tcx
>) {
220 if self.const_kind
.is_some() {
221 if let hir
::PatKind
::Or { .. }
= p
.kind
{
222 self.const_check_violated(NonConstExpr
::OrPattern
, p
.span
);
225 intravisit
::walk_pat(self, p
)
228 fn visit_expr(&mut self, e
: &'tcx hir
::Expr
<'tcx
>) {
230 // Skip the following checks if we are not currently in a const context.
231 _
if self.const_kind
.is_none() => {}
233 hir
::ExprKind
::Loop(_
, _
, source
) => {
234 self.const_check_violated(NonConstExpr
::Loop(*source
), e
.span
);
237 hir
::ExprKind
::Match(_
, _
, source
) => {
238 let non_const_expr
= match source
{
239 // These are handled by `ExprKind::Loop` above.
240 hir
::MatchSource
::WhileDesugar
241 | hir
::MatchSource
::WhileLetDesugar
242 | hir
::MatchSource
::ForLoopDesugar
=> None
,
244 _
=> Some(NonConstExpr
::Match(*source
)),
247 if let Some(expr
) = non_const_expr
{
248 self.const_check_violated(expr
, e
.span
);
255 intravisit
::walk_expr(self, e
);