From f1bacb2dca610b0babeba526020e2b87899c2d44 Mon Sep 17 00:00:00 2001 From: Mahmut Bulut Date: Fri, 14 Jun 2019 16:55:36 +0300 Subject: [PATCH 1/8] Check intrinsics for callability in const fns --- src/libcore/intrinsics.rs | 1 + src/librustc/ty/constness.rs | 70 +++++++++- src/librustc_feature/active.rs | 4 - src/librustc_metadata/rmeta/decoder.rs | 2 + src/librustc_metadata/rmeta/encoder.rs | 6 +- src/librustc_mir/interpret/intrinsics.rs | 129 +++++++++--------- .../transform/check_consts/ops.rs | 16 --- .../transform/check_consts/validation.rs | 18 --- .../transform/qualify_min_const_fn.rs | 42 +++--- src/libsyntax_pos/symbol.rs | 29 ++++ .../const-eval-intrinsic-promotion.rs | 6 + .../const-eval-intrinsic-promotion.stderr | 13 ++ .../feature-gate-const_transmute.rs | 2 +- .../feature-gate-const_transmute.stderr | 4 +- 14 files changed, 212 insertions(+), 130 deletions(-) create mode 100644 src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs create mode 100644 src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.stderr diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index d4952f53bf7fe..19928f30f2ea5 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -939,6 +939,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 676916f530a4d..0fb3f575b5255 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -2,7 +2,8 @@ use crate::ty::query::Providers; use crate::hir::def_id::DefId; use crate::hir; use crate::ty::TyCtxt; -use syntax_pos::symbol::Symbol; +use syntax_pos::symbol::{sym, Symbol}; +use rustc_target::spec::abi::Abi; use crate::hir::map::blocks::FnLikeNode; use syntax::attr; @@ -63,13 +64,76 @@ impl<'tcx> TyCtxt<'tcx> { pub fn provide(providers: &mut Providers<'_>) { - /// only checks whether the function has a `const` modifier + /// Const evaluability whitelist is here to check evaluability at the + /// top level beforehand. + fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + match tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | + Abi::PlatformIntrinsic => { + match tcx.item_name(def_id) { + // Keep this list in the same order as the match patterns in + // `librustc_mir/interpret/intrinsics.rs` + | sym::caller_location + + | sym::min_align_of + | sym::pref_align_of + | sym::needs_drop + | sym::size_of + | sym::type_id + | sym::type_name + + | sym::ctpop + | sym::cttz + | sym::cttz_nonzero + | sym::ctlz + | sym::ctlz_nonzero + | sym::bswap + | sym::bitreverse + + | sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow + + | sym::saturating_add + | sym::saturating_sub + + | sym::unchecked_shl + | sym::unchecked_shr + + | sym::rotate_left + | sym::rotate_right + + | sym::ptr_offset_from + + | sym::transmute + + | sym::simd_insert + + | sym::simd_extract + + => Some(true), + + _ => Some(false) + } + } + _ => None + } + } + + /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether + /// said intrinsic is on the whitelist for being const callable. 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) { + + if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) { + whitelisted + } else if let Some(fn_like) = FnLikeNode::from_node(node) { fn_like.constness() == hir::Constness::Const } else if let hir::Node::Ctor(_) = node { true diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 16d8ada9f24c0..fc880b9e92951 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -408,10 +408,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/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 42db642cd4ddb..b494c9bdd380b 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1364,6 +1364,8 @@ impl<'a, 'tcx> CrateMetadata { let constness = match self.kind(id) { EntryKind::Method(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, + // Some intrinsics can be const fn + EntryKind::ForeignFn(data) => data.decode(self).constness, EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const, _ => hir::Constness::NotConst, }; diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 3a318ddc1e13a..fb70e10c84f3d 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -1525,7 +1525,11 @@ impl EncodeContext<'tcx> { hir::ForeignItemKind::Fn(_, ref names, _) => { let data = FnData { asyncness: hir::IsAsync::NotAsync, - constness: hir::Constness::NotConst, + constness: if self.tcx.is_const_fn_raw(def_id) { + hir::Constness::Const + } else { + hir::Constness::NotConst + }, param_names: self.encode_fn_param_names(names), }; EntryKind::ForeignFn(self.lazy(data)) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 118dfcb3d9a01..f6fdc51a8c2b6 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,7 +2,7 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. -use syntax::symbol::Symbol; +use syntax_pos::symbol::{sym, Symbol}; use syntax_pos::Span; use rustc::ty; use rustc::ty::layout::{LayoutOf, Primitive, Size}; @@ -22,7 +22,7 @@ mod caller_location; mod type_name; fn numeric_intrinsic<'tcx, Tag>( - name: &str, + name: Symbol, bits: u128, kind: Primitive, ) -> InterpResult<'tcx, Scalar> { @@ -32,11 +32,11 @@ fn numeric_intrinsic<'tcx, Tag>( }; let extra = 128 - size.bits() as u128; let bits_out = match name { - "ctpop" => bits.count_ones() as u128, - "ctlz" => bits.leading_zeros() as u128 - extra, - "cttz" => (bits << extra).trailing_zeros() as u128 - extra, - "bswap" => (bits << extra).swap_bytes(), - "bitreverse" => (bits << extra).reverse_bits(), + sym::ctpop => bits.count_ones() as u128, + sym::ctlz => bits.leading_zeros() as u128 - extra, + sym::cttz => (bits << extra).trailing_zeros() as u128 - extra, + sym::bswap => (bits << extra).swap_bytes(), + sym::bitreverse => (bits << extra).reverse_bits(), _ => bug!("not a numeric intrinsic: {}", name), }; Ok(Scalar::from_uint(bits_out, size)) @@ -51,9 +51,9 @@ crate fn eval_nullary_intrinsic<'tcx>( substs: SubstsRef<'tcx>, ) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> { let tp_ty = substs.type_at(0); - let name = &*tcx.item_name(def_id).as_str(); + let name = tcx.item_name(def_id); Ok(match name { - "type_name" => { + sym::type_name => { let alloc = type_name::alloc_type_name(tcx, tp_ty); tcx.mk_const(ty::Const { val: ty::ConstKind::Value(ConstValue::Slice { @@ -64,20 +64,20 @@ crate fn eval_nullary_intrinsic<'tcx>( ty: tcx.mk_static_str(), }) }, - "needs_drop" => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)), - "size_of" | - "min_align_of" | - "pref_align_of" => { + sym::needs_drop => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)), + sym::size_of | + sym::min_align_of | + sym::pref_align_of => { let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?; let n = match name { - "pref_align_of" => layout.align.pref.bytes(), - "min_align_of" => layout.align.abi.bytes(), - "size_of" => layout.size.bytes(), + sym::pref_align_of => layout.align.pref.bytes(), + sym::min_align_of => layout.align.abi.bytes(), + sym::size_of => layout.size.bytes(), _ => bug!(), }; ty::Const::from_usize(tcx, n) }, - "type_id" => ty::Const::from_bits( + sym::type_id => ty::Const::from_bits( tcx, tcx.type_id_hash(tp_ty).into(), param_env.and(tcx.types.u64), @@ -96,30 +96,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>, ) -> InterpResult<'tcx, bool> { let substs = instance.substs; - let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str(); + let intrinsic_name = self.tcx.item_name(instance.def_id()); // We currently do not handle any intrinsics that are *allowed* to diverge, // but `transmute` could lack a return place in case of UB. let (dest, ret) = match ret { Some(p) => p, None => match intrinsic_name { - "transmute" => throw_ub!(Unreachable), + sym::transmute => throw_ub!(Unreachable), _ => return Ok(false), } }; + // Keep the patterns in this match ordered the same as the list in + // `src/librustc/ty/constness.rs` match intrinsic_name { - "caller_location" => { + sym::caller_location => { let location = self.alloc_caller_location_for_span(span); self.write_scalar(location.ptr, dest)?; } - "min_align_of" | - "pref_align_of" | - "needs_drop" | - "size_of" | - "type_id" | - "type_name" => { + sym::min_align_of | + sym::pref_align_of | + sym::needs_drop | + sym::size_of | + sym::type_id | + sym::type_name => { let gid = GlobalId { instance, promoted: None, @@ -129,13 +131,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.copy_op(val, dest)?; } - | "ctpop" - | "cttz" - | "cttz_nonzero" - | "ctlz" - | "ctlz_nonzero" - | "bswap" - | "bitreverse" => { + | sym::ctpop + | sym::cttz + | sym::cttz_nonzero + | sym::ctlz + | sym::ctlz_nonzero + | sym::bswap + | sym::bitreverse => { let ty = substs.type_at(0); let layout_of = self.layout_of(ty)?; let val = self.read_scalar(args[0])?.not_undef()?; @@ -144,31 +146,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::layout::Abi::Scalar(ref scalar) => scalar.value, _ => throw_unsup!(TypeNotPrimitive(ty)), }; - let out_val = if intrinsic_name.ends_with("_nonzero") { - if bits == 0 { - throw_ub_format!("`{}` called on 0", intrinsic_name); - } - numeric_intrinsic(intrinsic_name.trim_end_matches("_nonzero"), bits, kind)? - } else { - numeric_intrinsic(intrinsic_name, bits, kind)? + let (nonzero, intrinsic_name) = match intrinsic_name { + sym::cttz_nonzero => (true, sym::cttz), + sym::ctlz_nonzero => (true, sym::ctlz), + other => (false, other), }; + if nonzero && bits == 0 { + throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name); + } + let out_val = numeric_intrinsic(intrinsic_name, bits, kind)?; self.write_scalar(out_val, dest)?; } - | "wrapping_add" - | "wrapping_sub" - | "wrapping_mul" - | "add_with_overflow" - | "sub_with_overflow" - | "mul_with_overflow" => { + | sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::add_with_overflow + | sym::sub_with_overflow + | sym::mul_with_overflow => { let lhs = self.read_immediate(args[0])?; let rhs = self.read_immediate(args[1])?; let (bin_op, ignore_overflow) = match intrinsic_name { - "wrapping_add" => (BinOp::Add, true), - "wrapping_sub" => (BinOp::Sub, true), - "wrapping_mul" => (BinOp::Mul, true), - "add_with_overflow" => (BinOp::Add, false), - "sub_with_overflow" => (BinOp::Sub, false), - "mul_with_overflow" => (BinOp::Mul, false), + sym::wrapping_add => (BinOp::Add, true), + sym::wrapping_sub => (BinOp::Sub, true), + sym::wrapping_mul => (BinOp::Mul, true), + sym::add_with_overflow => (BinOp::Add, false), + sym::sub_with_overflow => (BinOp::Sub, false), + sym::mul_with_overflow => (BinOp::Mul, false), _ => bug!("Already checked for int ops") }; if ignore_overflow { @@ -177,10 +180,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.binop_with_overflow(bin_op, lhs, rhs, dest)?; } } - "saturating_add" | "saturating_sub" => { + sym::saturating_add | sym::saturating_sub => { let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; - let is_add = intrinsic_name == "saturating_add"; + let is_add = intrinsic_name == sym::saturating_add; let (val, overflowed, _ty) = self.overflowing_binary_op(if is_add { BinOp::Add } else { @@ -220,12 +223,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; self.write_scalar(val, dest)?; } - "unchecked_shl" | "unchecked_shr" => { + sym::unchecked_shl | sym::unchecked_shr => { let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; let bin_op = match intrinsic_name { - "unchecked_shl" => BinOp::Shl, - "unchecked_shr" => BinOp::Shr, + sym::unchecked_shl => BinOp::Shl, + sym::unchecked_shr => BinOp::Shr, _ => bug!("Already checked for int ops") }; let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?; @@ -236,7 +239,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } self.write_scalar(val, dest)?; } - "rotate_left" | "rotate_right" => { + sym::rotate_left | sym::rotate_right => { // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) let layout = self.layout_of(substs.type_at(0))?; @@ -247,7 +250,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let width_bits = layout.size.bits() as u128; let shift_bits = raw_shift_bits % width_bits; let inv_shift_bits = (width_bits - shift_bits) % width_bits; - let result_bits = if intrinsic_name == "rotate_left" { + let result_bits = if intrinsic_name == sym::rotate_left { (val_bits << shift_bits) | (val_bits >> inv_shift_bits) } else { (val_bits >> shift_bits) | (val_bits << inv_shift_bits) @@ -257,7 +260,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_scalar(result, dest)?; } - "ptr_offset_from" => { + sym::ptr_offset_from => { let isize_layout = self.layout_of(self.tcx.types.isize)?; let a = self.read_immediate(args[0])?.to_scalar()?; let b = self.read_immediate(args[1])?.to_scalar()?; @@ -303,10 +306,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - "transmute" => { + sym::transmute => { self.copy_op_transmute(args[0], dest)?; } - "simd_insert" => { + sym::simd_insert => { let index = u64::from(self.read_scalar(args[1])?.to_u32()?); let elem = args[2]; let input = args[0]; @@ -337,7 +340,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.copy_op(value, place)?; } } - "simd_extract" => { + sym::simd_extract => { let index = u64::from(self.read_scalar(args[1])?.to_u32()?); let (len, e_ty) = args[0].layout.ty.simd_size_and_type(self.tcx.tcx); assert!( diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index a4f12a4e54fa7..aec3cf04a97ca 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -326,22 +326,6 @@ impl NonConstOp for ThreadLocalAccess { } } -#[derive(Debug)] -pub struct Transmute; -impl NonConstOp for Transmute { - fn feature_gate(tcx: TyCtxt<'_>) -> Option { - Some(tcx.features().const_transmute) - } - - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { - feature_err( - &item.tcx.sess.parse_sess, sym::const_transmute, span, - &format!("The use of std::mem::transmute() is gated in {}s", item.const_kind()) - ) - .emit(); - } -} - #[derive(Debug)] pub struct UnionAccess; impl NonConstOp for UnionAccess { diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 9d477bfbae81f..78e6d879c4636 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -8,7 +8,6 @@ use rustc::traits::{self, TraitEngine}; use rustc::ty::cast::CastTy; use rustc::ty::{self, TyCtxt}; use rustc_index::bit_set::BitSet; -use rustc_target::spec::abi::Abi; use rustc_error_codes::*; use syntax::symbol::sym; use syntax_pos::Span; @@ -564,23 +563,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { }; // At this point, we are calling a function whose `DefId` is known... - - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = self.tcx.fn_sig(def_id).abi() { - assert!(!self.tcx.is_const_fn(def_id)); - - if self.tcx.item_name(def_id) == sym::transmute { - self.check_op(ops::Transmute); - return; - } - - // To preserve the current semantics, we return early, allowing all - // intrinsics (except `transmute`) to pass unchecked to miri. - // - // FIXME: We should keep a whitelist of allowed intrinsics (or at least a - // blacklist of unimplemented ones) and fail here instead. - return; - } - if self.tcx.is_const_fn(def_id) { return; } diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index e40d6a5952edb..b7e1b83ee05b8 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -415,28 +415,28 @@ fn check_terminator( /// /// Adding more intrinsics requires sign-off from @rust-lang/lang. fn is_intrinsic_whitelisted(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - match &*tcx.item_name(def_id).as_str() { - | "size_of" - | "min_align_of" - | "needs_drop" + match tcx.item_name(def_id) { + | sym::size_of + | sym::min_align_of + | sym::needs_drop // Arithmetic: - | "add_with_overflow" // ~> .overflowing_add - | "sub_with_overflow" // ~> .overflowing_sub - | "mul_with_overflow" // ~> .overflowing_mul - | "wrapping_add" // ~> .wrapping_add - | "wrapping_sub" // ~> .wrapping_sub - | "wrapping_mul" // ~> .wrapping_mul - | "saturating_add" // ~> .saturating_add - | "saturating_sub" // ~> .saturating_sub - | "unchecked_shl" // ~> .wrapping_shl - | "unchecked_shr" // ~> .wrapping_shr - | "rotate_left" // ~> .rotate_left - | "rotate_right" // ~> .rotate_right - | "ctpop" // ~> .count_ones - | "ctlz" // ~> .leading_zeros - | "cttz" // ~> .trailing_zeros - | "bswap" // ~> .swap_bytes - | "bitreverse" // ~> .reverse_bits + | sym::add_with_overflow // ~> .overflowing_add + | sym::sub_with_overflow // ~> .overflowing_sub + | sym::mul_with_overflow // ~> .overflowing_mul + | sym::wrapping_add // ~> .wrapping_add + | sym::wrapping_sub // ~> .wrapping_sub + | sym::wrapping_mul // ~> .wrapping_mul + | sym::saturating_add // ~> .saturating_add + | sym::saturating_sub // ~> .saturating_sub + | sym::unchecked_shl // ~> .wrapping_shl + | sym::unchecked_shr // ~> .wrapping_shr + | sym::rotate_left // ~> .rotate_left + | sym::rotate_right // ~> .rotate_right + | sym::ctpop // ~> .count_ones + | sym::ctlz // ~> .leading_zeros + | sym::cttz // ~> .trailing_zeros + | sym::bswap // ~> .swap_bytes + | sym::bitreverse // ~> .reverse_bits => true, _ => false, } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 3059b05969106..0d0b8598cc67d 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -121,6 +121,7 @@ symbols! { abi_vectorcall, abi_x86_interrupt, aborts, + add_with_overflow, advanced_slice_patterns, adx_target_feature, alias, @@ -171,7 +172,10 @@ symbols! { box_patterns, box_syntax, braced_empty_structs, + bswap, + bitreverse, C, + caller_location, cdylib, cfg, cfg_attr, @@ -226,6 +230,11 @@ symbols! { crate_name, crate_type, crate_visibility_modifier, + ctpop, + cttz, + cttz_nonzero, + ctlz, + ctlz_nonzero, custom_attribute, custom_derive, custom_inner_attributes, @@ -430,6 +439,7 @@ symbols! { member_constraints, message, meta, + min_align_of, min_const_fn, min_const_unsafe_fn, mips_target_feature, @@ -439,11 +449,13 @@ symbols! { more_struct_aliases, move_val_init, movbe_target_feature, + mul_with_overflow, must_use, naked, naked_functions, name, needs_allocator, + needs_drop, needs_panic_runtime, negate_unsigned, never, @@ -519,6 +531,7 @@ symbols! { poll_with_tls_context, powerpc_target_feature, precise_pointer_size_matching, + pref_align_of, prelude, prelude_import, primitive, @@ -535,6 +548,7 @@ symbols! { proc_macro_non_items, proc_macro_path_invoc, profiler_runtime, + ptr_offset_from, pub_restricted, pushpop_unsafe, quad_precision_float, @@ -570,6 +584,8 @@ symbols! { Return, rhs, rlib, + rotate_left, + rotate_right, rt, rtm_target_feature, rust, @@ -637,14 +653,19 @@ symbols! { rvalue_static_promotion, sanitize, sanitizer_runtime, + saturating_add, + saturating_sub, _Self, self_in_typedefs, self_struct_ctor, should_panic, simd, + simd_extract, simd_ffi, + simd_insert, since, size, + size_of, slice_patterns, slicing_syntax, soft, @@ -672,6 +693,7 @@ symbols! { structural_match, struct_variant, sty, + sub_with_overflow, suggestion, target_feature, target_has_atomic, @@ -707,6 +729,8 @@ symbols! { Ty, ty, type_alias_impl_trait, + type_id, + type_name, TyCtxt, TyKind, type_alias_enum_variants, @@ -719,6 +743,8 @@ symbols! { u64, u8, unboxed_closures, + unchecked_shl, + unchecked_shr, underscore_const_names, underscore_imports, underscore_lifetimes, @@ -752,6 +778,9 @@ symbols! { while_let, windows, windows_subsystem, + wrapping_add, + wrapping_sub, + wrapping_mul, Yield, } } 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..bdcf537859cc4 --- /dev/null +++ b/src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs @@ -0,0 +1,6 @@ +#![feature(core_intrinsics)] +fn main() { + // Test that calls to intrinsics are never promoted + 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..78143042ece7b --- /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:5: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`. diff --git a/src/test/ui/feature-gates/feature-gate-const_transmute.rs b/src/test/ui/feature-gates/feature-gate-const_transmute.rs index 6a5bbec77fdb9..da532643d9457 100644 --- a/src/test/ui/feature-gates/feature-gate-const_transmute.rs +++ b/src/test/ui/feature-gates/feature-gate-const_transmute.rs @@ -4,6 +4,6 @@ use std::mem; struct Foo(u32); const TRANSMUTED_U32: u32 = unsafe { mem::transmute(Foo(3)) }; -//~^ ERROR The use of std::mem::transmute() is gated in constants +//~^ ERROR `std::intrinsics::transmute` is not yet stable as a const fn fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_transmute.stderr b/src/test/ui/feature-gates/feature-gate-const_transmute.stderr index 41b653d98dcbf..772e8d294781a 100644 --- a/src/test/ui/feature-gates/feature-gate-const_transmute.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_transmute.stderr @@ -1,12 +1,10 @@ -error[E0658]: The use of std::mem::transmute() is gated in constants +error: `std::intrinsics::transmute` is not yet stable as a const fn --> $DIR/feature-gate-const_transmute.rs:6:38 | LL | const TRANSMUTED_U32: u32 = unsafe { mem::transmute(Foo(3)) }; | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: for more information, see https://github.com/rust-lang/rust/issues/53605 = help: add `#![feature(const_transmute)]` to the crate attributes to enable error: aborting due to previous error -For more information about this error, try `rustc --explain E0658`. From 52be0b07ae4ff025e40e04d7fe8d2a530980b637 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 26 Nov 2019 10:02:55 +0100 Subject: [PATCH 2/8] Only check `min_const_fn` for const fns --- src/librustc_mir/transform/check_consts/validation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 78e6d879c4636..8aaa6c04c1a81 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -201,7 +201,7 @@ impl Validator<'a, 'mir, 'tcx> { let Item { tcx, body, def_id, const_kind, .. } = *self.item; let use_min_const_fn_checks = - tcx.is_min_const_fn(def_id) + (const_kind == Some(ConstKind::ConstFn) && tcx.is_min_const_fn(def_id)) && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; if use_min_const_fn_checks { From 46c00a21e1e89ebbf7757dcbee35f89332575134 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 26 Nov 2019 12:55:11 +0100 Subject: [PATCH 3/8] Move all intrinsic whitelists into the constness check file --- src/librustc/ty/constness.rs | 35 ++++++++++++ .../transform/qualify_min_const_fn.rs | 55 +------------------ .../const-extern-fn-call-extern-fn.rs | 4 +- .../const-extern-fn-call-extern-fn.stderr | 4 +- 4 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 0fb3f575b5255..8ec5120d6b37d 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -36,12 +36,47 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted + /// for being called from stable `const fn`s (`min_const_fn`). + /// + /// Adding more intrinsics requires sign-off from @rust-lang/lang. + fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool { + match self.item_name(def_id) { + | sym::size_of + | sym::min_align_of + | sym::needs_drop + // Arithmetic: + | sym::add_with_overflow // ~> .overflowing_add + | sym::sub_with_overflow // ~> .overflowing_sub + | sym::mul_with_overflow // ~> .overflowing_mul + | sym::wrapping_add // ~> .wrapping_add + | sym::wrapping_sub // ~> .wrapping_sub + | sym::wrapping_mul // ~> .wrapping_mul + | sym::saturating_add // ~> .saturating_add + | sym::saturating_sub // ~> .saturating_sub + | sym::unchecked_shl // ~> .wrapping_shl + | sym::unchecked_shr // ~> .wrapping_shr + | sym::rotate_left // ~> .rotate_left + | sym::rotate_right // ~> .rotate_right + | sym::ctpop // ~> .count_ones + | sym::ctlz // ~> .leading_zeros + | sym::cttz // ~> .trailing_zeros + | sym::bswap // ~> .swap_bytes + | sym::bitreverse // ~> .reverse_bits + => true, + _ => false, + } + } + /// Returns `true` if this function must conform to `min_const_fn` pub fn is_min_const_fn(self, def_id: DefId) -> bool { // Bail out if the signature doesn't contain `const` if !self.is_const_fn_raw(def_id) { return false; } + if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() { + return self.is_intrinsic_min_const_fn(def_id); + } if self.features().staged_api { // in order for a libstd function to be considered min_const_fn diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index b7e1b83ee05b8..cf04d3e975b6f 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -2,7 +2,6 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::*; use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}}; -use rustc_target::spec::abi; use std::borrow::Cow; use syntax_pos::Span; use syntax::symbol::{sym, Symbol}; @@ -356,18 +355,8 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(def_id, _) = fn_ty.kind { - - // some intrinsics are waved through if called inside the - // standard library. Users never need to call them directly - match tcx.fn_sig(def_id).abi() { - abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) { - return Err(( - span, - "can only call a curated list of intrinsics in `min_const_fn`".into(), - )) - }, - abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {}, - abi::Abi::Rust => return Err(( + if !tcx.is_min_const_fn(def_id) { + return Err(( span, format!( "can only call other `const fn` within a `const fn`, \ @@ -375,14 +364,7 @@ fn check_terminator( func, ) .into(), - )), - abi => return Err(( - span, - format!( - "cannot call functions with `{}` abi in `min_const_fn`", - abi, - ).into(), - )), + )); } check_operand(tcx, func, span, def_id, body)?; @@ -410,34 +392,3 @@ fn check_terminator( } } -/// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted -/// for being called from stable `const fn`s (`min_const_fn`). -/// -/// Adding more intrinsics requires sign-off from @rust-lang/lang. -fn is_intrinsic_whitelisted(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - match tcx.item_name(def_id) { - | sym::size_of - | sym::min_align_of - | sym::needs_drop - // Arithmetic: - | sym::add_with_overflow // ~> .overflowing_add - | sym::sub_with_overflow // ~> .overflowing_sub - | sym::mul_with_overflow // ~> .overflowing_mul - | sym::wrapping_add // ~> .wrapping_add - | sym::wrapping_sub // ~> .wrapping_sub - | sym::wrapping_mul // ~> .wrapping_mul - | sym::saturating_add // ~> .saturating_add - | sym::saturating_sub // ~> .saturating_sub - | sym::unchecked_shl // ~> .wrapping_shl - | sym::unchecked_shr // ~> .wrapping_shr - | sym::rotate_left // ~> .rotate_left - | sym::rotate_right // ~> .rotate_right - | sym::ctpop // ~> .count_ones - | sym::ctlz // ~> .leading_zeros - | sym::cttz // ~> .trailing_zeros - | sym::bswap // ~> .swap_bytes - | sym::bitreverse // ~> .reverse_bits - => true, - _ => false, - } -} diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.rs b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.rs index 7c6a574a2110f..6469a65700dad 100644 --- a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.rs +++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.rs @@ -7,7 +7,7 @@ extern "C" { const extern fn bar() { unsafe { regular_in_block(); - //~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn` + //~^ ERROR: can only call other `const fn` within a `const fn` } } @@ -16,7 +16,7 @@ extern fn regular() {} const extern fn foo() { unsafe { regular(); - //~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn` + //~^ ERROR: can only call other `const fn` within a `const fn` } } diff --git a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr index d8bdf0a57cf66..eed279ecf7503 100644 --- a/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr +++ b/src/test/ui/consts/const-extern-fn/const-extern-fn-call-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0723]: cannot call functions with `"C"` abi in `min_const_fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `const regular_in_block` is not stable as `const fn` --> $DIR/const-extern-fn-call-extern-fn.rs:9:9 | LL | regular_in_block(); @@ -7,7 +7,7 @@ LL | regular_in_block(); = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0723]: cannot call functions with `"C"` abi in `min_const_fn` +error[E0723]: can only call other `const fn` within a `const fn`, but `const regular` is not stable as `const fn` --> $DIR/const-extern-fn-call-extern-fn.rs:18:9 | LL | regular(); From cc5b3578f1711b124f6b1304d7e7c18bf960509d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 26 Nov 2019 13:38:32 +0100 Subject: [PATCH 4/8] Tidy --- src/librustc_mir/transform/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index cf04d3e975b6f..3030d2e646b8d 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -391,4 +391,3 @@ fn check_terminator( }, } } - From 1f20b4dc19347020f535fe3e731e35fe7bbf2d1e Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Thu, 28 Nov 2019 15:12:08 +0100 Subject: [PATCH 5/8] Explain the difference between the two whitelists --- src/librustc/ty/constness.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 8ec5120d6b37d..1ea8cbba46e83 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -40,6 +40,10 @@ impl<'tcx> TyCtxt<'tcx> { /// for being called from stable `const fn`s (`min_const_fn`). /// /// Adding more intrinsics requires sign-off from @rust-lang/lang. + /// + /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this + /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from + /// stable, it must be callable at all. fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool { match self.item_name(def_id) { | sym::size_of @@ -108,6 +112,11 @@ pub fn provide(providers: &mut Providers<'_>) { match tcx.item_name(def_id) { // Keep this list in the same order as the match patterns in // `librustc_mir/interpret/intrinsics.rs` + + // This whitelist is a list of intrinsics that have a miri-engine implementation + // and can thus be called when enabling enough feature gates. The similar + // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing + // the intrinsics to be transitively called by stable const fns. | sym::caller_location | sym::min_align_of From d9cca74ffc0e5bdd2949d73a08945137171abb58 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 30 Nov 2019 11:12:48 +0100 Subject: [PATCH 6/8] const fns can read constants that invoke unstable const fns --- src/librustc/ty/constness.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 1ea8cbba46e83..6df49683f688f 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -116,7 +116,7 @@ pub fn provide(providers: &mut Providers<'_>) { // This whitelist is a list of intrinsics that have a miri-engine implementation // and can thus be called when enabling enough feature gates. The similar // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing - // the intrinsics to be transitively called by stable const fns. + // the intrinsics to be called by stable const fns. | sym::caller_location | sym::min_align_of From e2bbf0647aa1e78802607dc74d906b94ea127999 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Sat, 30 Nov 2019 18:17:38 +0100 Subject: [PATCH 7/8] Explain why "loading" constness from extern crates does not necessarily load anything --- src/librustc_metadata/rmeta/decoder.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index b494c9bdd380b..6edd17fe9abef 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -1360,11 +1360,15 @@ impl<'a, 'tcx> CrateMetadata { } } + // This replicates some of the logic of the crate-local `is_const_fn_raw` query, because we + // don't serialize constness for tuple variant and tuple struct constructors. fn is_const_fn_raw(&self, id: DefIndex) -> bool { let constness = match self.kind(id) { EntryKind::Method(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, - // Some intrinsics can be const fn + // Some intrinsics can be const fn. While we could recompute this (at least until we + // stop having hardcoded whitelists and move to stability attributes), it seems cleaner + // to treat all const fns equally. EntryKind::ForeignFn(data) => data.decode(self).constness, EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const, _ => hir::Constness::NotConst, From 5cef094a72f61966146338015f7e52874d57bcbd Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 4 Dec 2019 01:51:26 +0100 Subject: [PATCH 8/8] Leave fixme --- src/librustc/ty/constness.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index 6df49683f688f..268015a56240e 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -109,6 +109,7 @@ pub fn provide(providers: &mut Providers<'_>) { match tcx.fn_sig(def_id).abi() { Abi::RustIntrinsic | Abi::PlatformIntrinsic => { + // FIXME: deduplicate these two lists as much as possible match tcx.item_name(def_id) { // Keep this list in the same order as the match patterns in // `librustc_mir/interpret/intrinsics.rs`