]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/qualify_min_const_fn.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_mir / transform / qualify_min_const_fn.rs
CommitLineData
74b04a01 1use rustc_attr as attr;
dfeec247
XL
2use rustc_hir as hir;
3use rustc_hir::def_id::DefId;
ba9703b0
XL
4use rustc_middle::mir::*;
5use rustc_middle::ty::subst::GenericArgKind;
f9f354fc 6use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
dfeec247
XL
7use rustc_span::symbol::{sym, Symbol};
8use rustc_span::Span;
f035d41b 9use rustc_target::spec::abi::Abi::RustIntrinsic;
b7449926 10use std::borrow::Cow;
b7449926
XL
11
12type McfResult = Result<(), (Span, Cow<'static, str>)>;
13
dc9dc135 14pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
74b04a01
XL
15 // Prevent const trait methods from being annotated as `stable`.
16 if tcx.features().staged_api {
3dfed10e 17 let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
74b04a01
XL
18 if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
19 return Err((body.span, "trait methods cannot be stable const fn".into()));
20 }
21 }
22
b7449926
XL
23 let mut current = def_id;
24 loop {
25 let predicates = tcx.predicates_of(current);
e74abb32 26 for (predicate, _) in predicates.predicates {
3dfed10e
XL
27 match predicate.skip_binders() {
28 ty::PredicateAtom::RegionOutlives(_)
29 | ty::PredicateAtom::TypeOutlives(_)
30 | ty::PredicateAtom::WellFormed(_)
31 | ty::PredicateAtom::Projection(_)
32 | ty::PredicateAtom::ConstEvaluatable(..)
33 | ty::PredicateAtom::ConstEquate(..) => continue,
34 ty::PredicateAtom::ObjectSafe(_) => {
b7449926
XL
35 bug!("object safe predicate on function: {:#?}", predicate)
36 }
3dfed10e 37 ty::PredicateAtom::ClosureKind(..) => {
b7449926
XL
38 bug!("closure kind predicate on function: {:#?}", predicate)
39 }
3dfed10e 40 ty::PredicateAtom::Subtype(_) => {
f9f354fc
XL
41 bug!("subtype predicate on function: {:#?}", predicate)
42 }
3dfed10e 43 ty::PredicateAtom::Trait(pred, constness) => {
b7449926
XL
44 if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
45 continue;
46 }
3dfed10e 47 match pred.self_ty().kind {
b7449926 48 ty::Param(ref p) => {
dfeec247 49 // Allow `T: ?const Trait`
f9f354fc 50 if constness == hir::Constness::NotConst
dfeec247
XL
51 && feature_allowed(tcx, def_id, sym::const_trait_bound_opt_out)
52 {
53 continue;
54 }
55
b7449926
XL
56 let generics = tcx.generics_of(current);
57 let def = generics.type_param(p, tcx);
58 let span = tcx.def_span(def.def_id);
59 return Err((
60 span,
61 "trait bounds other than `Sized` \
62 on const fn parameters are unstable"
63 .into(),
64 ));
65 }
66 // other kinds of bounds are either tautologies
67 // or cause errors in other passes
68 _ => continue,
69 }
70 }
71 }
72 }
73 match predicates.parent {
74 Some(parent) => current = parent,
75 None => break,
76 }
77 }
78
dc9dc135 79 for local in &body.local_decls {
48663c56 80 check_ty(tcx, local.ty, local.source_info.span, def_id)?;
b7449926
XL
81 }
82 // impl trait is gone in MIR, so check the return type manually
83 check_ty(
84 tcx,
85 tcx.fn_sig(def_id).output().skip_binder(),
dc9dc135 86 body.local_decls.iter().next().unwrap().source_info.span,
48663c56 87 def_id,
b7449926
XL
88 )?;
89
dc9dc135 90 for bb in body.basic_blocks() {
e1599b0c 91 check_terminator(tcx, body, def_id, bb.terminator())?;
b7449926 92 for stmt in &bb.statements {
e1599b0c 93 check_statement(tcx, body, def_id, stmt)?;
b7449926
XL
94 }
95 }
96 Ok(())
97}
98
dc9dc135 99fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
ba9703b0
XL
100 for arg in ty.walk() {
101 let ty = match arg.unpack() {
102 GenericArgKind::Type(ty) => ty,
103
104 // No constraints on lifetimes or constants, except potentially
105 // constants' types, but `walk` will get to them as well.
106 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
107 };
108
e74abb32 109 match ty.kind {
dfeec247 110 ty::Ref(_, _, hir::Mutability::Mut) => {
60c5eb7d 111 if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
dfeec247 112 return Err((span, "mutable references in const fn are unstable".into()));
60c5eb7d
XL
113 }
114 }
b7449926
XL
115 ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
116 ty::FnPtr(..) => {
48663c56 117 if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
dfeec247 118 return Err((span, "function pointers in const fn are unstable".into()));
48663c56 119 }
b7449926
XL
120 }
121 ty::Dynamic(preds, _) => {
122 for pred in preds.iter() {
123 match pred.skip_binder() {
dfeec247 124 ty::ExistentialPredicate::AutoTrait(_)
b7449926
XL
125 | ty::ExistentialPredicate::Projection(_) => {
126 return Err((
127 span,
128 "trait bounds other than `Sized` \
129 on const fn parameters are unstable"
130 .into(),
dfeec247 131 ));
b7449926
XL
132 }
133 ty::ExistentialPredicate::Trait(trait_ref) => {
134 if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
135 return Err((
136 span,
137 "trait bounds other than `Sized` \
138 on const fn parameters are unstable"
139 .into(),
140 ));
141 }
142 }
143 }
144 }
145 }
146 _ => {}
147 }
148 }
149 Ok(())
150}
151
152fn check_rvalue(
dc9dc135 153 tcx: TyCtxt<'tcx>,
e1599b0c
XL
154 body: &Body<'tcx>,
155 def_id: DefId,
b7449926
XL
156 rvalue: &Rvalue<'tcx>,
157 span: Span,
158) -> McfResult {
159 match rvalue {
f9f354fc
XL
160 Rvalue::ThreadLocalRef(_) => {
161 Err((span, "cannot access thread local storage in const fn".into()))
162 }
b7449926 163 Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
e1599b0c 164 check_operand(tcx, operand, span, def_id, body)
b7449926 165 }
dfeec247
XL
166 Rvalue::Len(place)
167 | Rvalue::Discriminant(place)
168 | Rvalue::Ref(_, _, place)
ba9703b0 169 | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, def_id, body),
0bf4aa26 170 Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
ba9703b0 171 use rustc_middle::ty::cast::CastTy;
dc9dc135 172 let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
b7449926
XL
173 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
174 match (cast_in, cast_out) {
ba9703b0 175 (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
dfeec247
XL
176 Err((span, "casting pointers to ints is unstable in const fn".into()))
177 }
e1599b0c 178 _ => check_operand(tcx, operand, span, def_id, body),
b7449926
XL
179 }
180 }
ba9703b0
XL
181 Rvalue::Cast(
182 CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
183 operand,
184 _,
185 ) => check_operand(tcx, operand, span, def_id, body),
186 Rvalue::Cast(
187 CastKind::Pointer(
188 PointerCast::UnsafeFnPointer
189 | PointerCast::ClosureFnPointer(_)
190 | PointerCast::ReifyFnPointer,
191 ),
192 _,
193 _,
194 ) => Err((span, "function pointer casts are not allowed in const fn".into())),
f035d41b 195 Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
f9652781
XL
196 let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
197 deref_ty.ty
198 } else {
199 // We cannot allow this for now.
200 return Err((
201 span,
202 "unsizing casts are only allowed for references right now".into(),
203 ));
204 };
f035d41b
XL
205 let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
206 if let ty::Slice(_) | ty::Str = unsized_ty.kind {
207 check_operand(tcx, op, span, def_id, body)?;
208 // Casting/coercing things to slices is fine.
209 Ok(())
210 } else {
211 // We just can't allow trait objects until we have figured out trait method calls.
212 Err((span, "unsizing casts are not allowed in const fn".into()))
213 }
dfeec247 214 }
b7449926
XL
215 // binops are fine on integers
216 Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
e1599b0c
XL
217 check_operand(tcx, lhs, span, def_id, body)?;
218 check_operand(tcx, rhs, span, def_id, body)?;
dc9dc135 219 let ty = lhs.ty(body, tcx);
b7449926
XL
220 if ty.is_integral() || ty.is_bool() || ty.is_char() {
221 Ok(())
222 } else {
dfeec247 223 Err((span, "only int, `bool` and `char` operations are stable in const fn".into()))
b7449926
XL
224 }
225 }
0bf4aa26 226 Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
dfeec247
XL
227 Rvalue::NullaryOp(NullOp::Box, _) => {
228 Err((span, "heap allocations are not allowed in const fn".into()))
229 }
b7449926 230 Rvalue::UnaryOp(_, operand) => {
dc9dc135 231 let ty = operand.ty(body, tcx);
b7449926 232 if ty.is_integral() || ty.is_bool() {
e1599b0c 233 check_operand(tcx, operand, span, def_id, body)
b7449926 234 } else {
dfeec247 235 Err((span, "only int and `bool` operations are stable in const fn".into()))
b7449926
XL
236 }
237 }
238 Rvalue::Aggregate(_, operands) => {
239 for operand in operands {
e1599b0c 240 check_operand(tcx, operand, span, def_id, body)?;
b7449926
XL
241 }
242 Ok(())
243 }
244 }
245}
246
b7449926 247fn check_statement(
dc9dc135 248 tcx: TyCtxt<'tcx>,
e1599b0c
XL
249 body: &Body<'tcx>,
250 def_id: DefId,
b7449926
XL
251 statement: &Statement<'tcx>,
252) -> McfResult {
253 let span = statement.source_info.span;
254 match &statement.kind {
dfeec247 255 StatementKind::Assign(box (place, rval)) => {
ba9703b0 256 check_place(tcx, *place, span, def_id, body)?;
e1599b0c 257 check_rvalue(tcx, body, def_id, rval, span)
dc9dc135
XL
258 }
259
ba9703b0 260 StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, def_id, body),
b7449926
XL
261
262 // just an assignment
ba9703b0
XL
263 StatementKind::SetDiscriminant { place, .. } => {
264 check_place(tcx, **place, span, def_id, body)
265 }
b7449926 266
ba9703b0 267 StatementKind::LlvmInlineAsm { .. } => {
b7449926
XL
268 Err((span, "cannot use inline assembly in const fn".into()))
269 }
270
271 // These are all NOPs
dfeec247 272 StatementKind::StorageLive(_)
b7449926 273 | StatementKind::StorageDead(_)
a1dfa0c6 274 | StatementKind::Retag { .. }
b7449926 275 | StatementKind::AscribeUserType(..)
3dfed10e 276 | StatementKind::Coverage(..)
b7449926
XL
277 | StatementKind::Nop => Ok(()),
278 }
279}
280
281fn check_operand(
e1599b0c 282 tcx: TyCtxt<'tcx>,
b7449926
XL
283 operand: &Operand<'tcx>,
284 span: Span,
e1599b0c 285 def_id: DefId,
dfeec247 286 body: &Body<'tcx>,
b7449926
XL
287) -> McfResult {
288 match operand {
ba9703b0 289 Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, def_id, body),
60c5eb7d
XL
290 Operand::Constant(c) => match c.check_static_ptr(tcx) {
291 Some(_) => Err((span, "cannot access `static` items in const fn".into())),
292 None => Ok(()),
293 },
b7449926
XL
294 }
295}
296
297fn check_place(
e1599b0c 298 tcx: TyCtxt<'tcx>,
ba9703b0 299 place: Place<'tcx>,
b7449926 300 span: Span,
e1599b0c 301 def_id: DefId,
dfeec247 302 body: &Body<'tcx>,
b7449926 303) -> McfResult {
e74abb32
XL
304 let mut cursor = place.projection.as_ref();
305 while let &[ref proj_base @ .., elem] = cursor {
e1599b0c
XL
306 cursor = proj_base;
307 match elem {
e1599b0c 308 ProjectionElem::Field(..) => {
74b04a01 309 let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
e1599b0c
XL
310 if let Some(def) = base_ty.ty_adt_def() {
311 // No union field accesses in `const fn`
312 if def.is_union() {
313 if !feature_allowed(tcx, def_id, sym::const_fn_union) {
314 return Err((span, "accessing union fields is unstable".into()));
315 }
316 }
b7449926
XL
317 }
318 }
e1599b0c 319 ProjectionElem::ConstantIndex { .. }
f9f354fc 320 | ProjectionElem::Downcast(..)
e1599b0c
XL
321 | ProjectionElem::Subslice { .. }
322 | ProjectionElem::Deref
323 | ProjectionElem::Index(_) => {}
b7449926 324 }
e1599b0c 325 }
dc9dc135 326
60c5eb7d 327 Ok(())
e1599b0c
XL
328}
329
dfeec247
XL
330/// Returns `true` if the given feature gate is allowed within the function with the given `DefId`.
331fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
332 // All features require that the corresponding gate be enabled,
333 // even if the function has `#[allow_internal_unstable(the_gate)]`.
334 if !tcx.features().enabled(feature_gate) {
335 return false;
336 }
337
338 // If this crate is not using stability attributes, or this function is not claiming to be a
339 // stable `const fn`, that is all that is required.
340 if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
341 return true;
342 }
343
344 // However, we cannot allow stable `const fn`s to use unstable features without an explicit
345 // opt-in via `allow_internal_unstable`.
3dfed10e 346 attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
e1599b0c 347 .map_or(false, |mut features| features.any(|name| name == feature_gate))
b7449926
XL
348}
349
f035d41b
XL
350/// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`.
351pub fn lib_feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
352 // All features require that the corresponding gate be enabled,
353 // even if the function has `#[allow_internal_unstable(the_gate)]`.
354 if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_gate) {
355 return false;
356 }
357
358 // If this crate is not using stability attributes, or this function is not claiming to be a
359 // stable `const fn`, that is all that is required.
360 if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
361 return true;
362 }
363
364 // However, we cannot allow stable `const fn`s to use unstable features without an explicit
365 // opt-in via `allow_internal_unstable`.
3dfed10e 366 attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
f035d41b
XL
367 .map_or(false, |mut features| features.any(|name| name == feature_gate))
368}
369
b7449926 370fn check_terminator(
dc9dc135
XL
371 tcx: TyCtxt<'tcx>,
372 body: &'a Body<'tcx>,
e1599b0c 373 def_id: DefId,
b7449926
XL
374 terminator: &Terminator<'tcx>,
375) -> McfResult {
376 let span = terminator.source_info.span;
377 match &terminator.kind {
f035d41b 378 TerminatorKind::FalseEdge { .. }
dfeec247 379 | TerminatorKind::FalseUnwind { .. }
b7449926
XL
380 | TerminatorKind::Goto { .. }
381 | TerminatorKind::Return
f9f354fc
XL
382 | TerminatorKind::Resume
383 | TerminatorKind::Unreachable => Ok(()),
b7449926 384
f035d41b
XL
385 TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, def_id, body),
386 TerminatorKind::DropAndReplace { place, value, .. } => {
387 check_place(tcx, *place, span, def_id, body)?;
e1599b0c 388 check_operand(tcx, value, span, def_id, body)
dfeec247 389 }
0bf4aa26 390
60c5eb7d
XL
391 TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
392 check_operand(tcx, discr, span, def_id, body)
393 }
394
f9f354fc 395 TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
dfeec247 396 TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
b7449926
XL
397 Err((span, "const fn generators are unstable".into()))
398 }
399
f035d41b
XL
400 TerminatorKind::Call {
401 func,
402 args,
403 from_hir_call: _,
404 destination: _,
405 cleanup: _,
406 fn_span: _,
407 } => {
dc9dc135 408 let fn_ty = func.ty(body, tcx);
f035d41b
XL
409 if let ty::FnDef(fn_def_id, _) = fn_ty.kind {
410 // Allow unstable const if we opt in by using #[allow_internal_unstable]
411 // on function or macro declaration.
412 if !crate::const_eval::is_min_const_fn(tcx, fn_def_id)
413 && !crate::const_eval::is_unstable_const_fn(tcx, fn_def_id)
414 .map(|feature| {
415 span.allows_unstable(feature)
416 || lib_feature_allowed(tcx, def_id, feature)
417 })
418 .unwrap_or(false)
419 {
60c5eb7d 420 return Err((
b7449926 421 span,
48663c56
XL
422 format!(
423 "can only call other `const fn` within a `const fn`, \
424 but `{:?}` is not stable as `const fn`",
425 func,
426 )
427 .into(),
60c5eb7d 428 ));
b7449926 429 }
0731742a 430
f035d41b
XL
431 // HACK: This is to "unstabilize" the `transmute` intrinsic
432 // within const fns. `transmute` is allowed in all other const contexts.
433 // This won't really scale to more intrinsics or functions. Let's allow const
434 // transmutes in const fn before we add more hacks to this.
435 if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic
436 && tcx.item_name(fn_def_id) == sym::transmute
437 && !feature_allowed(tcx, def_id, sym::const_fn_transmute)
438 {
439 return Err((
440 span,
441 "can only call `transmute` from const items, not `const fn`".into(),
442 ));
443 }
444
445 check_operand(tcx, func, span, fn_def_id, body)?;
0731742a
XL
446
447 for arg in args {
f035d41b 448 check_operand(tcx, arg, span, fn_def_id, body)?;
0731742a
XL
449 }
450 Ok(())
b7449926
XL
451 } else {
452 Err((span, "can only call other const fns within const fn".into()))
453 }
454 }
455
dfeec247
XL
456 TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
457 check_operand(tcx, cond, span, def_id, body)
60c5eb7d 458 }
f9f354fc
XL
459
460 TerminatorKind::InlineAsm { .. } => {
461 Err((span, "cannot use inline assembly in const fn".into()))
462 }
0731742a
XL
463 }
464}