]>
Commit | Line | Data |
---|---|---|
74b04a01 | 1 | use rustc_attr as attr; |
dfeec247 XL |
2 | use rustc_hir as hir; |
3 | use rustc_hir::def_id::DefId; | |
ba9703b0 XL |
4 | use rustc_middle::mir::*; |
5 | use rustc_middle::ty::subst::GenericArgKind; | |
f9f354fc | 6 | use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; |
dfeec247 XL |
7 | use rustc_span::symbol::{sym, Symbol}; |
8 | use rustc_span::Span; | |
f035d41b | 9 | use rustc_target::spec::abi::Abi::RustIntrinsic; |
b7449926 | 10 | use std::borrow::Cow; |
b7449926 XL |
11 | |
12 | type McfResult = Result<(), (Span, Cow<'static, str>)>; | |
13 | ||
dc9dc135 | 14 | pub 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 | 99 | fn 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 | ||
152 | fn 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 | 247 | fn 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 | ||
281 | fn 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 | ||
297 | fn 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`. |
331 | fn 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`. |
351 | pub 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 | 370 | fn 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 | } |