-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Organize intrinsics const evaluability checks #61835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c2a54d
49fdb57
9f6ef64
21a5963
da6cc10
9484246
34951a9
5354f7f
c41c07b
54127de
07d1419
ab96b18
9328ee0
63cc638
e62acd4
3bc84dd
7ba0a57
ddaf56d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ use crate::hir::def_id::DefId; | |
use crate::hir; | ||
use crate::ty::TyCtxt; | ||
use syntax_pos::symbol::{sym, Symbol}; | ||
use rustc_target::spec::abi::Abi; | ||
use crate::hir::map::blocks::FnLikeNode; | ||
use syntax::attr; | ||
|
||
|
@@ -68,14 +69,60 @@ impl<'tcx> TyCtxt<'tcx> { | |
|
||
|
||
pub fn provide(providers: &mut Providers<'_>) { | ||
/// only checks whether the function has a `const` modifier | ||
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ||
// Const evaluability whitelist is here to check evaluability at the | ||
// top level beforehand. | ||
match tcx.fn_sig(def_id).abi() { | ||
Abi::RustIntrinsic | | ||
Abi::PlatformIntrinsic => { | ||
match &tcx.item_name(def_id).as_str()[..] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we change this to match on a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh, this isn't actually exported in |
||
| "size_of" | ||
| "min_align_of" | ||
| "needs_drop" | ||
| "type_id" | ||
| "bswap" | ||
| "bitreverse" | ||
| "ctpop" | ||
| "cttz" | ||
| "cttz_nonzero" | ||
| "ctlz" | ||
| "ctlz_nonzero" | ||
| "overflowing_add" | ||
| "overflowing_sub" | ||
| "overflowing_mul" | ||
| "unchecked_shl" | ||
| "unchecked_shr" | ||
| "rotate_left" | ||
| "rotate_right" | ||
| "add_with_overflow" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The wrapping intrinsics seem to be missing from this list. The errors mean the check is working! Awesome! Please add anything to this list that is needed to get the tests passing |
||
| "sub_with_overflow" | ||
| "mul_with_overflow" | ||
| "saturating_add" | ||
| "saturating_sub" | ||
| "transmute" | ||
| "wrapping_add" | ||
| "wrapping_sub" | ||
| "wrapping_mul" | ||
=> true, | ||
|
||
_ => false | ||
} | ||
} | ||
_ => false | ||
} | ||
} | ||
|
||
/// Checks whether the function has a `const` modifier and intrinsics can be promotable in it | ||
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ||
let hir_id = tcx.hir().as_local_hir_id(def_id) | ||
.expect("Non-local call to local provider is_const_fn"); | ||
|
||
let node = tcx.hir().get(hir_id); | ||
if let Some(fn_like) = FnLikeNode::from_node(node) { | ||
fn_like.constness() == hir::Constness::Const | ||
|
||
if is_const_intrinsic(tcx, def_id) { | ||
true | ||
} else if let Some(fn_like) = FnLikeNode::from_node(node) { | ||
(fn_like.constness() == hir::Constness::Const) | ||
} else if let hir::Node::Ctor(_) = node { | ||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
true | ||
} else { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -529,51 +529,12 @@ impl Qualif for IsNotPromotable { | |
let fn_ty = callee.ty(cx.body, cx.tcx); | ||
match fn_ty.kind { | ||
ty::FnDef(def_id, _) => { | ||
match cx.tcx.fn_sig(def_id).abi() { | ||
Abi::RustIntrinsic | | ||
Abi::PlatformIntrinsic => { | ||
assert!(!cx.tcx.is_const_fn(def_id)); | ||
match &cx.tcx.item_name(def_id).as_str()[..] { | ||
| "size_of" | ||
| "min_align_of" | ||
| "needs_drop" | ||
| "type_id" | ||
| "bswap" | ||
| "bitreverse" | ||
| "ctpop" | ||
| "cttz" | ||
| "cttz_nonzero" | ||
| "ctlz" | ||
| "ctlz_nonzero" | ||
| "wrapping_add" | ||
| "wrapping_sub" | ||
| "wrapping_mul" | ||
| "unchecked_shl" | ||
| "unchecked_shr" | ||
| "rotate_left" | ||
| "rotate_right" | ||
| "add_with_overflow" | ||
| "sub_with_overflow" | ||
| "mul_with_overflow" | ||
| "saturating_add" | ||
| "saturating_sub" | ||
| "transmute" | ||
| "simd_insert" | ||
| "simd_extract" | ||
=> return true, | ||
|
||
_ => {} | ||
} | ||
} | ||
_ => { | ||
let is_const_fn = | ||
cx.tcx.is_const_fn(def_id) || | ||
cx.tcx.is_unstable_const_fn(def_id).is_some() || | ||
cx.is_const_panic_fn(def_id); | ||
if !is_const_fn { | ||
return true; | ||
} | ||
} | ||
let is_const_fn = | ||
cx.tcx.is_const_fn(def_id) || | ||
cx.tcx.is_unstable_const_fn(def_id).is_some() || | ||
cx.is_const_panic_fn(def_id); | ||
Comment on lines
+532
to
+535
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah dang I might have mixed this up again with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this can't go wrong anymore, without triggering a mismatch with @ecstatic-morse and I will be removing the copy from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just hope we get the intrinsic thing landed as part of this refactor, it's been 4 months or so.^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah this PR can land at any time, and I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, but what about the new const checking pass? Does that still allow calling intrinsics? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good question for @ecstatic-morse. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reserved a spot for these checks here. I think we just need to call the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I missed that this was baked into |
||
if !is_const_fn { | ||
return true; | ||
} | ||
} | ||
_ => return true, | ||
|
@@ -943,6 +904,68 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { | |
} | ||
} | ||
|
||
/// Check fn in const context with supplied feature gates | ||
fn check_fn_in_const_ctx(&mut self, def_id: DefId) -> () { | ||
// Only in non-const functions it is perfectly fine to call any | ||
// function, even ones whose constness is unstable. Everywhere else | ||
// we need to check the appropriate feature gates. | ||
if self.mode.requires_const_checking() { | ||
let unleash_miri = self | ||
.tcx | ||
.sess | ||
.opts | ||
.debugging_opts | ||
.unleash_the_miri_inside_of_you; | ||
if self.tcx.is_const_fn(def_id) || unleash_miri { | ||
// stable const fns or unstable const fns | ||
// with their feature gate active | ||
// FIXME(eddyb) move stability checks from `is_const_fn` here. | ||
} else if self.is_const_panic_fn(def_id) { | ||
// Check the const_panic feature gate. | ||
// FIXME: cannot allow this inside `allow_internal_unstable` | ||
// because that would make `panic!` insta stable in constants, | ||
// since the macro is marked with the attribute. | ||
if !self.tcx.features().const_panic { | ||
// Don't allow panics in constants without the feature gate. | ||
emit_feature_err( | ||
&self.tcx.sess.parse_sess, | ||
sym::const_panic, | ||
self.span, | ||
GateIssue::Language, | ||
&format!("panicking in {}s is unstable", self.mode), | ||
); | ||
} | ||
} else if let Some(feature) | ||
= self.tcx.is_unstable_const_fn(def_id) { | ||
// Check `#[unstable]` const fns or `#[rustc_const_unstable]` | ||
// functions without the feature gate active in this crate in | ||
// order to report a better error message than the one below. | ||
if !self.span.allows_unstable(feature) { | ||
let mut err = self.tcx.sess.struct_span_err(self.span, | ||
&format!("`{}` is not yet stable as a const fn", | ||
self.tcx.def_path_str(def_id))); | ||
if nightly_options::is_nightly_build() { | ||
help!(&mut err, | ||
"add `#![feature({})]` to the \ | ||
crate attributes to enable", | ||
feature); | ||
} | ||
err.emit(); | ||
} | ||
} else { | ||
let mut err = struct_span_err!( | ||
self.tcx.sess, | ||
self.span, | ||
E0015, | ||
"calls in {}s are limited to constant functions, \ | ||
tuple structs and tuple variants", | ||
self.mode, | ||
); | ||
err.emit(); | ||
} | ||
} | ||
} | ||
|
||
/// Check a whole const, static initializer or const fn. | ||
fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) { | ||
use crate::transform::check_consts as new_checker; | ||
|
@@ -1397,101 +1420,20 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { | |
ty::FnDef(def_id, _) => { | ||
callee_def_id = Some(def_id); | ||
match self.tcx.fn_sig(def_id).abi() { | ||
Abi::RustIntrinsic | | ||
Abi::PlatformIntrinsic => { | ||
assert!(!self.tcx.is_const_fn(def_id)); | ||
match &self.tcx.item_name(def_id).as_str()[..] { | ||
// special intrinsic that can be called diretly without an intrinsic | ||
// feature gate needs a language feature gate | ||
"transmute" => { | ||
if self.mode.requires_const_checking() | ||
&& !self.suppress_errors | ||
{ | ||
// const eval transmute calls only with the feature gate | ||
if !self.tcx.features().const_transmute { | ||
self.record_error(ops::Transmute); | ||
emit_feature_err( | ||
&self.tcx.sess.parse_sess, sym::const_transmute, | ||
self.span, GateIssue::Language, | ||
&format!("The use of std::mem::transmute() \ | ||
is gated in {}s", self.mode)); | ||
} | ||
} | ||
} | ||
|
||
name if name.starts_with("simd_shuffle") => { | ||
is_shuffle = true; | ||
} | ||
|
||
// no need to check feature gates, intrinsics are only callable | ||
// from the libstd or with forever unstable feature gates | ||
_ => {} | ||
} | ||
} | ||
_ => { | ||
// In normal functions no calls are feature-gated. | ||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if self.mode.requires_const_checking() { | ||
let unleash_miri = self | ||
.tcx | ||
.sess | ||
.opts | ||
.debugging_opts | ||
.unleash_the_miri_inside_of_you; | ||
if self.tcx.is_const_fn(def_id) | ||
|| unleash_miri | ||
|| self.suppress_errors | ||
{ | ||
// stable const fns or unstable const fns | ||
// with their feature gate active | ||
// FIXME(eddyb) move stability checks from `is_const_fn` here. | ||
} else if self.is_const_panic_fn(def_id) { | ||
// Check the const_panic feature gate. | ||
// FIXME: cannot allow this inside `allow_internal_unstable` | ||
// because that would make `panic!` insta stable in constants, | ||
// since the macro is marked with the attribute. | ||
if !self.tcx.features().const_panic { | ||
// Don't allow panics in constants without the feature gate. | ||
self.record_error(ops::Panic); | ||
emit_feature_err( | ||
&self.tcx.sess.parse_sess, | ||
sym::const_panic, | ||
self.span, | ||
GateIssue::Language, | ||
&format!("panicking in {}s is unstable", self.mode), | ||
); | ||
} | ||
} else if let Some(feature) | ||
= self.tcx.is_unstable_const_fn(def_id) { | ||
// Check `#[unstable]` const fns or `#[rustc_const_unstable]` | ||
// functions without the feature gate active in this crate in | ||
// order to report a better error message than the one below. | ||
if !self.span.allows_unstable(feature) { | ||
self.record_error(ops::FnCallUnstable(def_id, feature)); | ||
let mut err = self.tcx.sess.struct_span_err(self.span, | ||
&format!("`{}` is not yet stable as a const fn", | ||
self.tcx.def_path_str(def_id))); | ||
if nightly_options::is_nightly_build() { | ||
help!(&mut err, | ||
"add `#![feature({})]` to the \ | ||
crate attributes to enable", | ||
feature); | ||
} | ||
err.emit(); | ||
} | ||
} else { | ||
self.record_error(ops::FnCallNonConst(def_id)); | ||
let mut err = struct_span_err!( | ||
self.tcx.sess, | ||
self.span, | ||
E0015, | ||
"calls in {}s are limited to constant functions, \ | ||
tuple structs and tuple variants", | ||
self.mode, | ||
); | ||
err.emit(); | ||
_ => { | ||
// check for other platform intrinsics which | ||
// may not be usable with their unstable feature gates | ||
// in const fn. | ||
self.check_fn_in_const_ctx(def_id) | ||
} | ||
} | ||
} | ||
_ => self.check_fn_in_const_ctx(def_id) | ||
} | ||
} | ||
ty::FnPtr(_) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#![feature(core_intrinsics)] | ||
fn main() { | ||
let x: &'static usize = | ||
&std::intrinsics::size_of::<i32>(); //~ ERROR temporary value dropped while borrowed | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
error[E0716]: temporary value dropped while borrowed | ||
--> $DIR/const-eval-intrinsic-promotion.rs:4:10 | ||
| | ||
LL | let x: &'static usize = | ||
| -------------- type annotation requires that borrow lasts for `'static` | ||
LL | &std::intrinsics::size_of::<i32>(); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use | ||
LL | } | ||
| - temporary value is freed at the end of this statement | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0716`. |
Uh oh!
There was an error while loading. Please reload this page.