diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index b240d059114eb..1569213d01baf 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -935,6 +935,7 @@ extern "rust-intrinsic" { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_transmute")] pub fn transmute(e: T) -> U; /// Returns `true` if the actual type given as `T` requires drop diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index e4022bb24ad44..0584589e7a11f 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -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()[..] { + | "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" + | "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 { true } else { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index da1abb9747c1a..2eede6d9e31a3 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -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); + 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) { 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. - 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(_) => { diff --git a/src/libsyntax/feature_gate/active.rs b/src/libsyntax/feature_gate/active.rs index 4f92401d2bc57..14946e35f4cf9 100644 --- a/src/libsyntax/feature_gate/active.rs +++ b/src/libsyntax/feature_gate/active.rs @@ -431,10 +431,6 @@ declare_features! ( /// Allows using `#[doc(keyword = "...")]`. (active, doc_keyword, "1.28.0", Some(51315), None), - /// Allows reinterpretation of the bits of a value of one type as another - /// type during const eval. - (active, const_transmute, "1.29.0", Some(53605), None), - /// Allows using `try {...}` expressions. (active, try_blocks, "1.29.0", Some(31436), None), diff --git a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs new file mode 100644 index 0000000000000..bd51e2bbcf6f5 --- /dev/null +++ b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs @@ -0,0 +1,5 @@ +#![feature(core_intrinsics)] +fn main() { + let x: &'static usize = + &std::intrinsics::size_of::(); //~ ERROR temporary value dropped while borrowed +} diff --git a/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr new file mode 100644 index 0000000000000..a670c3b172707 --- /dev/null +++ b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr @@ -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::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`.