diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 3bcdfabbb245e..23f07773f3f34 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -596,7 +596,7 @@ mod impls { /// This affects, for example, whether a `static` of that type is /// placed in read-only static memory or writable static memory. #[lang = "freeze"] -unsafe auto trait Freeze {} +pub(crate) unsafe auto trait Freeze {} impl !Freeze for UnsafeCell {} unsafe impl Freeze for PhantomData {} diff --git a/src/libcore/nonzero.rs b/src/libcore/nonzero.rs index 436cd1fc05728..a89c6ca60cbea 100644 --- a/src/libcore/nonzero.rs +++ b/src/libcore/nonzero.rs @@ -11,14 +11,23 @@ //! Exposes the NonZero lang item which provides optimization hints. use ops::{CoerceUnsized, DispatchFromDyn}; +use marker::Freeze; /// A wrapper type for raw pointers and integers that will never be /// NULL or 0 that might allow certain optimizations. #[rustc_layout_scalar_valid_range_start(1)] -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] -pub(crate) struct NonZero(pub(crate) T); +pub(crate) struct NonZero(pub(crate) T); -impl, U> CoerceUnsized> for NonZero {} +// Do not call `T::clone` as theoretically it could turn the field into `0` +// invalidating `NonZero`'s invariant. +impl Clone for NonZero { + fn clone(&self) -> Self { + unsafe { NonZero(self.0) } + } +} -impl, U> DispatchFromDyn> for NonZero {} +impl + Freeze, U: Freeze> CoerceUnsized> for NonZero {} + +impl + Freeze, U: Freeze> DispatchFromDyn> for NonZero {} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 805be431328e2..7f5d596b220b9 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -70,7 +70,7 @@ assert_eq!(size_of::>(), size_of::<", st #[stable(feature = "nonzero", since = "1.28.0")] #[inline] pub const unsafe fn new_unchecked(n: $Int) -> Self { - $Ty(NonZero(n)) + $Ty(unsafe { NonZero(n) }) } /// Create a non-zero if the given value is not zero. @@ -78,7 +78,7 @@ assert_eq!(size_of::>(), size_of::<", st #[inline] pub fn new(n: $Int) -> Option { if n != 0 { - Some($Ty(NonZero(n))) + Some($Ty(unsafe { NonZero(n) })) } else { None } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index d3a74ed2a6856..0387708033b53 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2759,7 +2759,7 @@ impl Unique { /// Creates a new `Unique` if `ptr` is non-null. pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { - Some(Unique { pointer: NonZero(ptr as _), _marker: PhantomData }) + Some(Unique { pointer: unsafe { NonZero(ptr as _) }, _marker: PhantomData }) } else { None } @@ -2815,14 +2815,14 @@ impl fmt::Pointer for Unique { #[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a mut T> for Unique { fn from(reference: &'a mut T) -> Self { - Unique { pointer: NonZero(reference as _), _marker: PhantomData } + Unique { pointer: unsafe { NonZero(reference as _) }, _marker: PhantomData } } } #[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a T> for Unique { fn from(reference: &'a T) -> Self { - Unique { pointer: NonZero(reference as _), _marker: PhantomData } + Unique { pointer: unsafe { NonZero(reference as _) }, _marker: PhantomData } } } @@ -2895,7 +2895,7 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[inline] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - NonNull { pointer: NonZero(ptr as _) } + NonNull { pointer: unsafe { NonZero(ptr as _) } } } /// Creates a new `NonNull` if `ptr` is non-null. @@ -2903,7 +2903,7 @@ impl NonNull { #[inline] pub fn new(ptr: *mut T) -> Option { if !ptr.is_null() { - Some(NonNull { pointer: NonZero(ptr as _) }) + Some(unsafe { Self::new_unchecked(ptr) }) } else { None } @@ -3025,7 +3025,7 @@ impl From> for NonNull { impl<'a, T: ?Sized> From<&'a mut T> for NonNull { #[inline] fn from(reference: &'a mut T) -> Self { - NonNull { pointer: NonZero(reference as _) } + NonNull { pointer: unsafe { NonZero(reference as _) } } } } @@ -3033,6 +3033,6 @@ impl<'a, T: ?Sized> From<&'a mut T> for NonNull { impl<'a, T: ?Sized> From<&'a T> for NonNull { #[inline] fn from(reference: &'a T) -> Self { - NonNull { pointer: NonZero(reference as _) } + NonNull { pointer: unsafe { NonZero(reference as _) } } } } diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index c29872266312a..c72887124aa0b 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -46,7 +46,8 @@ impl_stable_hash_for!(enum mir::BorrowKind { impl_stable_hash_for!(enum mir::UnsafetyViolationKind { General, - MinConstFn, + GeneralAndConstFn, + GatedConstFnCall, ExternStatic(lint_node_id), BorrowPacked(lint_node_id), }); diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 8c4a40c00c35e..91663557409f2 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -25,7 +25,7 @@ use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::MappedReadGuard; -use rustc_serialize as serialize; +use rustc_serialize::{self as serialize}; use smallvec::SmallVec; use std::borrow::Cow; use std::fmt::{self, Debug, Formatter, Write}; @@ -2778,8 +2778,11 @@ impl Location { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum UnsafetyViolationKind { General, - /// unsafety is not allowed at all in min const fn - MinConstFn, + /// Right now function calls to `const unsafe fn` are only permitted behind a feature gate + /// Also, even `const unsafe fn` need an `unsafe` block to do the allowed operations. + GatedConstFnCall, + /// Permitted in const fn and regular fns + GeneralAndConstFn, ExternStatic(ast::NodeId), BorrowPacked(ast::NodeId), } diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs index e32913b8905b7..bc06167399533 100644 --- a/src/librustc/ty/constness.rs +++ b/src/librustc/ty/constness.rs @@ -5,7 +5,6 @@ use ty::TyCtxt; use syntax_pos::symbol::Symbol; use hir::map::blocks::FnLikeNode; use syntax::attr; -use rustc_target::spec::abi; impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// Whether the `def_id` counts as const fn in your current crate, considering all active @@ -40,19 +39,12 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { /// 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 self.features().staged_api { - // some intrinsics are waved through if called inside the - // standard library. Users never need to call them directly - if let abi::Abi::RustIntrinsic = self.fn_sig(def_id).abi() { - assert!(!self.is_const_fn(def_id)); - match &self.item_name(def_id).as_str()[..] { - | "size_of" - | "min_align_of" - | "needs_drop" - => return true, - _ => {}, - } - } // in order for a libstd function to be considered min_const_fn // it needs to be stable and have no `rustc_const_unstable` attribute match self.lookup_stability(def_id) { diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index a59bf9d530c4d..6522dbe117994 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -98,12 +98,18 @@ macro_rules! newtype_index { @max [$max:expr] @vis [$v:vis] @debug_format [$debug_format:tt]) => ( - #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)] + #[derive(Copy, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)] #[rustc_layout_scalar_valid_range_end($max)] $v struct $type { private: u32 } + impl Clone for $type { + fn clone(&self) -> Self { + *self + } + } + impl $type { $v const MAX_AS_U32: u32 = $max; @@ -145,7 +151,7 @@ macro_rules! newtype_index { #[inline] $v const unsafe fn from_u32_unchecked(value: u32) -> Self { - $type { private: value } + unsafe { $type { private: value } } } /// Extract value of this index as an integer. @@ -328,12 +334,13 @@ macro_rules! newtype_index { derive [$($derives:ident,)+] $($tokens:tt)*) => ( newtype_index!( - @derives [$($derives,)+ RustcDecodable, RustcEncodable,] + @derives [$($derives,)+ RustcEncodable,] @type [$type] @max [$max] @vis [$v] @debug_format [$debug_format] $($tokens)*); + newtype_index!(@decodable $type); ); // The case where no derives are added, but encodable is overridden. Don't @@ -360,12 +367,29 @@ macro_rules! newtype_index { @debug_format [$debug_format:tt] $($tokens:tt)*) => ( newtype_index!( - @derives [RustcDecodable, RustcEncodable,] + @derives [RustcEncodable,] @type [$type] @max [$max] @vis [$v] @debug_format [$debug_format] $($tokens)*); + newtype_index!(@decodable $type); + ); + + (@decodable $type:ident) => ( + impl $type { + fn __decodable__impl__hack() { + mod __more_hacks_because__self_doesnt_work_in_functions { + extern crate serialize; + use self::serialize::{Decodable, Decoder}; + impl Decodable for super::$type { + fn decode(d: &mut D) -> Result { + d.read_u32().map(Self::from) + } + } + } + } + } ); // Rewrite final without comma to one that includes comma diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index d95a74be77696..90a204ce00d53 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -91,8 +91,9 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t // types/lifetimes replaced) let fn_hir_id = tcx.hir.node_to_hir_id(id); let fn_sig = cx.tables().liberated_fn_sigs()[fn_hir_id].clone(); + let fn_def_id = tcx.hir.local_def_id(id); - let ty = tcx.type_of(tcx.hir.local_def_id(id)); + let ty = tcx.type_of(fn_def_id); let mut abi = fn_sig.abi; let implicit_argument = match ty.sty { ty::Closure(..) => { @@ -108,9 +109,15 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t _ => None, }; - // FIXME: safety in closures let safety = match fn_sig.unsafety { hir::Unsafety::Normal => Safety::Safe, + hir::Unsafety::Unsafe if tcx.is_min_const_fn(fn_def_id) => { + // As specified in #55607, a `const unsafe fn` differs + // from an `unsafe fn` in that its body is still considered + // safe code by default. + assert!(implicit_argument.is_none()); + Safety::Safe + }, hir::Unsafety::Unsafe => Safety::FnUnsafe, }; diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 3404772f8255f..75f8045cfae99 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -23,6 +23,9 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext}; use syntax::ast; use syntax::symbol::Symbol; +use syntax::feature_gate::{emit_feature_err, GateIssue}; + +use std::ops::Bound; use util; @@ -34,6 +37,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> { source_info: SourceInfo, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, + /// mark an `unsafe` block as used, so we don't lint it used_unsafe: FxHashSet, inherited_blocks: Vec<(ast::NodeId, bool)>, } @@ -93,7 +97,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { if let hir::Unsafety::Unsafe = sig.unsafety() { self.require_unsafe("call to unsafe function", "consult the function's documentation for information on how to avoid \ - undefined behavior") + undefined behavior", UnsafetyViolationKind::GatedConstFnCall) } } } @@ -121,7 +125,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { StatementKind::InlineAsm { .. } => { self.require_unsafe("use of inline assembly", - "inline assembly is entirely unchecked and can cause undefined behavior") + "inline assembly is entirely unchecked and can cause undefined behavior", + UnsafetyViolationKind::General) }, } self.super_statement(block, statement, location); @@ -134,8 +139,18 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { if let &Rvalue::Aggregate(box ref aggregate, _) = rvalue { match aggregate { &AggregateKind::Array(..) | - &AggregateKind::Tuple | - &AggregateKind::Adt(..) => {} + &AggregateKind::Tuple => {} + &AggregateKind::Adt(ref def, ..) => { + match self.tcx.layout_scalar_valid_range(def.did) { + (Bound::Unbounded, Bound::Unbounded) => {}, + _ => self.require_unsafe( + "initializing type with `rustc_layout_scalar_valid_range` attr", + "initializing a layout restricted type's field with a value outside \ + the valid range is undefined behavior", + UnsafetyViolationKind::GeneralAndConstFn, + ), + } + } &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => { let UnsafetyCheckResult { @@ -152,28 +167,43 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { place: &Place<'tcx>, context: PlaceContext<'tcx>, location: Location) { - if context.is_borrow() { - if util::is_disaligned(self.tcx, self.mir, self.param_env, place) { - let source_info = self.source_info; - let lint_root = - self.source_scope_local_data[source_info.scope].lint_root; - self.register_violations(&[UnsafetyViolation { - source_info, - description: Symbol::intern("borrow of packed field").as_interned_str(), - details: - Symbol::intern("fields of packed structs might be misaligned: \ - dereferencing a misaligned pointer or even just creating a \ - misaligned reference is undefined behavior") - .as_interned_str(), - kind: UnsafetyViolationKind::BorrowPacked(lint_root) - }], &[]); - } - } - match place { &Place::Projection(box Projection { ref base, ref elem }) => { + if context.is_borrow() { + if util::is_disaligned(self.tcx, self.mir, self.param_env, place) { + let source_info = self.source_info; + let lint_root = + self.source_scope_local_data[source_info.scope].lint_root; + self.register_violations(&[UnsafetyViolation { + source_info, + description: Symbol::intern("borrow of packed field").as_interned_str(), + details: + Symbol::intern("fields of packed structs might be misaligned: \ + dereferencing a misaligned pointer or even just \ + creating a misaligned reference is undefined \ + behavior") + .as_interned_str(), + kind: UnsafetyViolationKind::BorrowPacked(lint_root) + }], &[]); + } + } + let is_borrow_of_interior_mut = context.is_borrow() && !base + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_freeze(self.tcx, self.param_env, self.source_info.span); + // prevent + // * `&mut x.field` + // * `x.field = y;` + // * `&x.field` if `field`'s type has interior mutability + // because either of these would allow modifying the layout constrained field and + // insert values that violate the layout constraints. + if context.is_mutating_use() || is_borrow_of_interior_mut { + self.check_mut_borrowing_layout_constrained_field( + place, context.is_mutating_use(), + ); + } let old_source_info = self.source_info; if let &Place::Local(local) = base { if self.mir.local_decls[local].internal { @@ -189,7 +219,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { self.require_unsafe("dereference of raw pointer", "raw pointers may be NULL, dangling or unaligned; they can violate \ aliasing rules and cause data races: all of these are undefined \ - behavior") + behavior", UnsafetyViolationKind::General) } ty::Adt(adt, _) => { if adt.is_union() { @@ -212,14 +242,15 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { "assignment to non-`Copy` union field", "the previous content of the field will be dropped, which \ causes undefined behavior if the field was not properly \ - initialized") + initialized", UnsafetyViolationKind::General) } else { // write to non-move union, safe } } else { self.require_unsafe("access to union field", "the field may not be properly initialized: using \ - uninitialized data will cause undefined behavior") + uninitialized data will cause undefined behavior", + UnsafetyViolationKind::General) } } } @@ -237,7 +268,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { if self.tcx.is_static(def_id) == Some(hir::Mutability::MutMutable) { self.require_unsafe("use of mutable static", "mutable statics can be mutated by multiple threads: aliasing violations \ - or data races will cause undefined behavior"); + or data races will cause undefined behavior", + UnsafetyViolationKind::General); } else if self.tcx.is_foreign_item(def_id) { let source_info = self.source_info; let lint_root = @@ -260,45 +292,96 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { - fn require_unsafe(&mut self, - description: &'static str, - details: &'static str) - { + fn require_unsafe( + &mut self, + description: &'static str, + details: &'static str, + kind: UnsafetyViolationKind, + ) { let source_info = self.source_info; self.register_violations(&[UnsafetyViolation { source_info, description: Symbol::intern(description).as_interned_str(), details: Symbol::intern(details).as_interned_str(), - kind: UnsafetyViolationKind::General, + kind, }], &[]); } fn register_violations(&mut self, violations: &[UnsafetyViolation], unsafe_blocks: &[(ast::NodeId, bool)]) { - if self.min_const_fn { - for violation in violations { - let mut violation = violation.clone(); - violation.kind = UnsafetyViolationKind::MinConstFn; - if !self.violations.contains(&violation) { - self.violations.push(violation) - } - } - } - let within_unsafe = match self.source_scope_local_data[self.source_info.scope].safety { - Safety::Safe => { + let safety = self.source_scope_local_data[self.source_info.scope].safety; + let within_unsafe = match (safety, self.min_const_fn) { + // Erring on the safe side, pun intended + (Safety::BuiltinUnsafe, true) | + // mir building encodes const fn bodies as safe, even for `const unsafe fn` + (Safety::FnUnsafe, true) => bug!("const unsafe fn body treated as inherently unsafe"), + // `unsafe` blocks are required in safe code + (Safety::Safe, _) => { for violation in violations { - if !self.violations.contains(violation) { - self.violations.push(violation.clone()) + let mut violation = violation.clone(); + match violation.kind { + UnsafetyViolationKind::GeneralAndConstFn | + UnsafetyViolationKind::General => {}, + UnsafetyViolationKind::BorrowPacked(_) | + UnsafetyViolationKind::ExternStatic(_) => if self.min_const_fn { + // const fns don't need to be backwards compatible and can + // emit these violations as a hard error instead of a backwards + // compat lint + violation.kind = UnsafetyViolationKind::General; + }, + UnsafetyViolationKind::GatedConstFnCall => { + // safe code can't call unsafe const fns, this `UnsafetyViolationKind` + // is only relevant for `Safety::ExplicitUnsafe` in `unsafe const fn`s + violation.kind = UnsafetyViolationKind::General; + } + } + if !self.violations.contains(&violation) { + self.violations.push(violation) } } false } - Safety::BuiltinUnsafe | Safety::FnUnsafe => true, - Safety::ExplicitUnsafe(node_id) => { + // regular `unsafe` function bodies allow unsafe without additional unsafe blocks + (Safety::BuiltinUnsafe, false) | (Safety::FnUnsafe, false) => true, + (Safety::ExplicitUnsafe(node_id), _) => { + // mark unsafe block as used if there are any unsafe operations inside if !violations.is_empty() { self.used_unsafe.insert(node_id); } + // only some unsafety is allowed in const fn + if self.min_const_fn { + let min_const_unsafe_fn = self.tcx.features().min_const_unsafe_fn; + for violation in violations { + match violation.kind { + UnsafetyViolationKind::GatedConstFnCall if min_const_unsafe_fn => { + // these function calls to unsafe functions are allowed + // if `#![feature(min_const_unsafe_fn)]` is active + }, + UnsafetyViolationKind::GatedConstFnCall => { + // without the feature gate, we report errors + if !self.violations.contains(&violation) { + self.violations.push(violation.clone()) + } + } + // these unsafe things are stable in const fn + UnsafetyViolationKind::GeneralAndConstFn => {}, + // these things are forbidden in const fns + UnsafetyViolationKind::General | + UnsafetyViolationKind::BorrowPacked(_) | + UnsafetyViolationKind::ExternStatic(_) => { + let mut violation = violation.clone(); + // const fns don't need to be backwards compatible and can + // emit these violations as a hard error instead of a backwards + // compat lint + violation.kind = UnsafetyViolationKind::General; + if !self.violations.contains(&violation) { + self.violations.push(violation) + } + }, + } + } + } true } }; @@ -306,6 +389,53 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { (node_id, is_used && !within_unsafe) })); } + fn check_mut_borrowing_layout_constrained_field( + &mut self, + mut place: &Place<'tcx>, + is_mut_use: bool, + ) { + while let &Place::Projection(box Projection { + ref base, ref elem + }) = place { + match *elem { + ProjectionElem::Field(..) => { + let ty = base.ty(&self.mir.local_decls, self.tcx).to_ty(self.tcx); + match ty.sty { + ty::Adt(def, _) => match self.tcx.layout_scalar_valid_range(def.did) { + (Bound::Unbounded, Bound::Unbounded) => {}, + _ => { + let (description, details) = if is_mut_use { + ( + "mutation of layout constrained field", + "mutating layout constrained fields cannot statically be \ + checked for valid values", + ) + } else { + ( + "borrow of layout constrained field with interior \ + mutability", + "references to fields of layout constrained fields \ + lose the constraints. Coupled with interior mutability, \ + the field can be changed to invalid values", + ) + }; + let source_info = self.source_info; + self.register_violations(&[UnsafetyViolation { + source_info, + description: Symbol::intern(description).as_interned_str(), + details: Symbol::intern(details).as_interned_str(), + kind: UnsafetyViolationKind::GeneralAndConstFn, + }], &[]); + } + }, + _ => {} + } + } + _ => {} + } + place = base; + } + } } pub(crate) fn provide(providers: &mut Providers) { @@ -384,7 +514,7 @@ fn unsafety_check_result<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) let param_env = tcx.param_env(def_id); let mut checker = UnsafetyChecker::new( - tcx.is_const_fn(def_id) && tcx.is_min_const_fn(def_id), + tcx.is_min_const_fn(def_id), mir, source_scope_local_data, tcx, param_env); checker.visit_mir(mir); @@ -486,6 +616,22 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { } in violations.iter() { // Report an error. match kind { + UnsafetyViolationKind::General if tcx.is_min_const_fn(def_id) => { + let mut err = tcx.sess.struct_span_err( + source_info.span, + &format!("{} is unsafe and unsafe operations \ + are not allowed in const fn", description)); + err.span_label(source_info.span, &description.as_str()[..]) + .note(&details.as_str()[..]); + if tcx.fn_sig(def_id).unsafety() == hir::Unsafety::Unsafe { + err.note( + "unsafe action within a `const unsafe fn` still require an `unsafe` \ + block in contrast to regular `unsafe fn`." + ); + } + err.emit(); + } + UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { struct_span_err!( tcx.sess, source_info.span, E0133, @@ -494,14 +640,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { .note(&details.as_str()[..]) .emit(); } - UnsafetyViolationKind::MinConstFn => { - tcx.sess.struct_span_err( + UnsafetyViolationKind::GatedConstFnCall => { + emit_feature_err( + &tcx.sess.parse_sess, + "min_const_unsafe_fn", source_info.span, - &format!("{} is unsafe and unsafe operations \ - are not allowed in const fn", description)) - .span_label(source_info.span, &description.as_str()[..]) - .note(&details.as_str()[..]) - .emit(); + GateIssue::Language, + "calls to `const unsafe fn` in const fns are unstable", + ); + } UnsafetyViolationKind::ExternStatic(lint_node_id) => { tcx.lint_node_note(SAFE_EXTERN_STATICS, diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 13e134ba85928..3c1b9dbd91fa8 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -2,6 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::*; use rustc::ty::{self, Predicate, TyCtxt}; +use rustc_target::spec::abi; use std::borrow::Cow; use syntax_pos::Span; @@ -338,19 +339,40 @@ fn check_terminator( } => { let fn_ty = func.ty(mir, tcx); if let ty::FnDef(def_id, _) = fn_ty.sty { - if tcx.is_min_const_fn(def_id) { - check_operand(tcx, mir, func, span)?; - for arg in args { - check_operand(tcx, mir, arg, span)?; - } - Ok(()) - } else { - Err(( + // 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 => match &tcx.item_name(def_id).as_str()[..] { + | "size_of" + | "min_align_of" + | "needs_drop" + => {}, + _ => 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(( span, "can only call other `min_const_fn` within a `min_const_fn`".into(), - )) + )), + abi => return Err(( + span, + format!( + "cannot call functions with `{}` abi in `min_const_fn`", + abi, + ).into(), + )), + } + + check_operand(tcx, mir, func, span)?; + + for arg in args { + check_operand(tcx, mir, arg, span)?; } + Ok(()) } else { Err((span, "can only call other const fns within const fn".into())) } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index fac7ff2bf342d..bfdc75378d5e1 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -492,6 +492,9 @@ declare_features! ( // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`. (active, extern_crate_self, "1.31.0", Some(56409), None), + + // Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. + (active, min_const_unsafe_fn, "1.31.0", Some(55607), None), ); declare_features! ( diff --git a/src/test/run-pass-fulldeps/newtype_index.rs b/src/test/run-pass-fulldeps/newtype_index.rs index 3cd622a33b173..0fd7ccd55fb2d 100644 --- a/src/test/run-pass-fulldeps/newtype_index.rs +++ b/src/test/run-pass-fulldeps/newtype_index.rs @@ -1,4 +1,4 @@ -#![feature(rustc_attrs, rustc_private, step_trait)] +#![feature(rustc_attrs, rustc_private, step_trait, min_const_unsafe_fn)] #[macro_use] extern crate rustc_data_structures; extern crate rustc_serialize; diff --git a/src/test/ui/consts/min_const_fn/min_const_fn.rs b/src/test/ui/consts/min_const_fn/min_const_fn.rs index 0dba3a7de5378..43ca9e7539309 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn.rs +++ b/src/test/ui/consts/min_const_fn/min_const_fn.rs @@ -78,9 +78,9 @@ const fn i32_ops2(c: i32, d: i32) -> bool { c < d } const fn i32_ops3(c: i32, d: i32) -> bool { c != d } const fn i32_ops4(c: i32, d: i32) -> i32 { c + d } const fn char_cast(u: u8) -> char { u as char } -const unsafe fn foo4() -> i32 { 42 } -const unsafe fn foo5() -> *const T { 0 as *const T } -const unsafe fn foo6() -> *mut T { 0 as *mut T } +const unsafe fn ret_i32_no_unsafe() -> i32 { 42 } +const unsafe fn ret_null_ptr_no_unsafe() -> *const T { 0 as *const T } +const unsafe fn ret_null_mut_ptr_no_unsafe() -> *mut T { 0 as *mut T } // not ok const fn foo11(t: T) -> T { t } diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs index 67332c6d2cfb2..f11b43dcd865c 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.rs @@ -8,23 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// gate-test-min_const_unsafe_fn + // ok -const unsafe fn foo4() -> i32 { 42 } -const unsafe fn foo5() -> *const T { 0 as *const T } -const unsafe fn foo6() -> *mut T { 0 as *mut T } +const unsafe fn ret_i32_no_unsafe() -> i32 { 42 } +const unsafe fn ret_null_ptr_no_unsafe() -> *const T { 0 as *const T } +const unsafe fn ret_null_mut_ptr_no_unsafe() -> *mut T { 0 as *mut T } const fn no_unsafe() { unsafe {} } // not ok -const fn foo8() -> i32 { - unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn +const fn call_unsafe_const_fn() -> i32 { + unsafe { ret_i32_no_unsafe() } //~ ERROR calls to `const unsafe fn` in const fns are unstable } -const fn foo9() -> *const String { - unsafe { foo5::() } //~ ERROR unsafe operations are not allowed in const fn +const fn call_unsafe_generic_const_fn() -> *const String { + unsafe { ret_null_ptr_no_unsafe::() } + //~^ ERROR calls to `const unsafe fn` in const fns are unstable } -const fn foo10() -> *const Vec> { - unsafe { foo6::>>() } //~ ERROR not allowed in const fn +const fn call_unsafe_generic_cell_const_fn() -> *const Vec> { + unsafe { ret_null_mut_ptr_no_unsafe::>>() } + //~^ ERROR calls to `const unsafe fn` in const fns } -const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn +const unsafe fn deref_forbidden(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn //~^ dereferencing raw pointers in constant functions fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr index 8cff0d491d8a4..922a7883b9f2d 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe.stderr @@ -1,58 +1,60 @@ error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911) - --> $DIR/min_const_fn_unsafe.rs:27:51 + --> $DIR/min_const_fn_unsafe.rs:31:59 | -LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn - | ^^ +LL | const unsafe fn deref_forbidden(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn + | ^^ | = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable error[E0658]: unions in const fn are unstable (see issue #51909) - --> $DIR/min_const_fn_unsafe.rs:34:5 + --> $DIR/min_const_fn_unsafe.rs:38:5 | LL | Foo { x: () }.y //~ ERROR not allowed in const fn | ^^^^^^^^^^^^^^^ | = help: add #![feature(const_fn_union)] to the crate attributes to enable -error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn - --> $DIR/min_const_fn_unsafe.rs:19:14 +error[E0658]: calls to `const unsafe fn` in const fns are unstable (see issue #55607) + --> $DIR/min_const_fn_unsafe.rs:21:14 | -LL | unsafe { foo4() } //~ ERROR unsafe operations are not allowed in const fn - | ^^^^^^ call to unsafe function +LL | unsafe { ret_i32_no_unsafe() } //~ ERROR calls to `const unsafe fn` in const fns are unstable + | ^^^^^^^^^^^^^^^^^^^ | - = note: consult the function's documentation for information on how to avoid undefined behavior + = help: add #![feature(min_const_unsafe_fn)] to the crate attributes to enable -error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn - --> $DIR/min_const_fn_unsafe.rs:22:14 +error[E0658]: calls to `const unsafe fn` in const fns are unstable (see issue #55607) + --> $DIR/min_const_fn_unsafe.rs:24:14 | -LL | unsafe { foo5::() } //~ ERROR unsafe operations are not allowed in const fn - | ^^^^^^^^^^^^^^^^ call to unsafe function +LL | unsafe { ret_null_ptr_no_unsafe::() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consult the function's documentation for information on how to avoid undefined behavior + = help: add #![feature(min_const_unsafe_fn)] to the crate attributes to enable -error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn - --> $DIR/min_const_fn_unsafe.rs:25:14 +error[E0658]: calls to `const unsafe fn` in const fns are unstable (see issue #55607) + --> $DIR/min_const_fn_unsafe.rs:28:14 | -LL | unsafe { foo6::>>() } //~ ERROR not allowed in const fn - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function +LL | unsafe { ret_null_mut_ptr_no_unsafe::>>() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: consult the function's documentation for information on how to avoid undefined behavior + = help: add #![feature(min_const_unsafe_fn)] to the crate attributes to enable error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn - --> $DIR/min_const_fn_unsafe.rs:27:51 + --> $DIR/min_const_fn_unsafe.rs:31:59 | -LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn - | ^^ dereference of raw pointer +LL | const unsafe fn deref_forbidden(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn + | ^^ dereference of raw pointer | = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. error: access to union field is unsafe and unsafe operations are not allowed in const fn - --> $DIR/min_const_fn_unsafe.rs:34:5 + --> $DIR/min_const_fn_unsafe.rs:38:5 | LL | Foo { x: () }.y //~ ERROR not allowed in const fn | ^^^^^^^^^^^^^^^ access to union field | = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. error: aborting due to 7 previous errors diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs new file mode 100644 index 0000000000000..8a6884bc6b93c --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.rs @@ -0,0 +1,62 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(min_const_unsafe_fn)] + +// ok +const unsafe fn foo4() -> i32 { 42 } +const unsafe fn foo5() -> *const T { 0 as *const T } +const unsafe fn foo6() -> *mut T { 0 as *mut T } +const fn no_unsafe() { unsafe {} } + +const fn foo8() -> i32 { + unsafe { foo4() } +} +const fn foo9() -> *const String { + unsafe { foo5::() } +} +const fn foo10() -> *const Vec> { + unsafe { foo6::>>() } +} +const unsafe fn foo8_3() -> i32 { + unsafe { foo4() } +} +const unsafe fn foo9_3() -> *const String { + unsafe { foo5::() } +} +const unsafe fn foo10_3() -> *const Vec> { + unsafe { foo6::>>() } +} +// not ok +const unsafe fn foo8_2() -> i32 { + foo4() //~ ERROR not allowed in const fn +} +const unsafe fn foo9_2() -> *const String { + foo5::() //~ ERROR not allowed in const fn +} +const unsafe fn foo10_2() -> *const Vec> { + foo6::>>() //~ ERROR not allowed in const fn +} +const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn +//~^ dereferencing raw pointers in constant functions + +const unsafe fn foo30_4(x: *mut usize) -> &'static usize { &*x } //~ ERROR not allowed in const fn +//~^ dereferencing raw pointers in constant functions + +const fn foo30_5(x: *mut usize) -> &'static usize { unsafe { &*x } } //~ ERROR not allowed +//~^ dereferencing raw pointers in constant functions + +fn main() {} + +const unsafe fn no_union() { + union Foo { x: (), y: () } + Foo { x: () }.y //~ ERROR not allowed in const fn + //~^ unions in const fn +} diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr new file mode 100644 index 0000000000000..20c75afbe638b --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_fn_unsafe_feature_gate.stderr @@ -0,0 +1,97 @@ +error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911) + --> $DIR/min_const_fn_unsafe_feature_gate.rs:47:51 + | +LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn + | ^^ + | + = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable + +error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911) + --> $DIR/min_const_fn_unsafe_feature_gate.rs:50:60 + | +LL | const unsafe fn foo30_4(x: *mut usize) -> &'static usize { &*x } //~ ERROR not allowed in const fn + | ^^^ + | + = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable + +error[E0658]: dereferencing raw pointers in constant functions is unstable (see issue #51911) + --> $DIR/min_const_fn_unsafe_feature_gate.rs:53:62 + | +LL | const fn foo30_5(x: *mut usize) -> &'static usize { unsafe { &*x } } //~ ERROR not allowed + | ^^^ + | + = help: add #![feature(const_raw_ptr_deref)] to the crate attributes to enable + +error[E0658]: unions in const fn are unstable (see issue #51909) + --> $DIR/min_const_fn_unsafe_feature_gate.rs:60:5 + | +LL | Foo { x: () }.y //~ ERROR not allowed in const fn + | ^^^^^^^^^^^^^^^ + | + = help: add #![feature(const_fn_union)] to the crate attributes to enable + +error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:39:5 + | +LL | foo4() //~ ERROR not allowed in const fn + | ^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:42:5 + | +LL | foo5::() //~ ERROR not allowed in const fn + | ^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: call to unsafe function is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:45:5 + | +LL | foo6::>>() //~ ERROR not allowed in const fn + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:47:51 + | +LL | const unsafe fn foo30_3(x: *mut usize) -> usize { *x } //~ ERROR not allowed in const fn + | ^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:50:60 + | +LL | const unsafe fn foo30_4(x: *mut usize) -> &'static usize { &*x } //~ ERROR not allowed in const fn + | ^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: dereference of raw pointer is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:53:62 + | +LL | const fn foo30_5(x: *mut usize) -> &'static usize { unsafe { &*x } } //~ ERROR not allowed + | ^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: access to union field is unsafe and unsafe operations are not allowed in const fn + --> $DIR/min_const_fn_unsafe_feature_gate.rs:60:5 + | +LL | Foo { x: () }.y //~ ERROR not allowed in const fn + | ^^^^^^^^^^^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + = note: unsafe action within a `const unsafe fn` still require an `unsafe` block in contrast to regular `unsafe fn`. + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs new file mode 100644 index 0000000000000..f559c23ff0f54 --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.rs @@ -0,0 +1,47 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "humans", + reason = "who ever let humans program computers, + we're apparently really bad at it", + issue = "0")] + +#![feature(rustc_const_unstable, const_fn, foo, foo2)] +#![feature(min_const_unsafe_fn)] +#![feature(staged_api)] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature="foo")] +const unsafe fn foo() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar() -> u32 { unsafe { foo() } } //~ ERROR can only call other `min_const_fn` + +#[unstable(feature = "rust1", issue="0")] +const unsafe fn foo2() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2() -> u32 { unsafe { foo2() } } //~ ERROR can only call other `min_const_fn` + +#[stable(feature = "rust1", since = "1.0.0")] +// conformity is required, even with `const_fn` feature gate +const unsafe fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` op + +// check whether this function cannot be called even with the feature gate active +#[unstable(feature = "foo2", issue="0")] +const unsafe fn foo2_gated() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } //~ ERROR can only call other + +fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr new file mode 100644 index 0000000000000..37be2889173f8 --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -0,0 +1,26 @@ +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:26:41 + | +LL | const unsafe fn bar() -> u32 { unsafe { foo() } } //~ ERROR can only call other `min_const_fn` + | ^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:33:42 + | +LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } //~ ERROR can only call other `min_const_fn` + | ^^^^^^ + +error: only int, `bool` and `char` operations are stable in const fn + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:37:33 + | +LL | const unsafe fn bar3() -> u32 { (5f32 + 6f32) as u32 } //~ ERROR only int, `bool` and `char` op + | ^^^^^^^^^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability.rs:45:48 + | +LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } //~ ERROR can only call other + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs new file mode 100644 index 0000000000000..131bc97c85a2a --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.rs @@ -0,0 +1,43 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "humans", + reason = "who ever let humans program computers, + we're apparently really bad at it", + issue = "0")] + +#![feature(rustc_const_unstable, const_fn, foo, foo2)] +#![feature(min_const_unsafe_fn)] +#![feature(staged_api)] + +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature="foo")] +const fn foo() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn` + +#[unstable(feature = "rust1", issue="0")] +const fn foo2() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn` + +// check whether this function cannot be called even with the feature gate active +#[unstable(feature = "foo2", issue="0")] +const fn foo2_gated() -> u32 { 42 } + +#[stable(feature = "rust1", since = "1.0.0")] +// can't call non-min_const_fn +const unsafe fn bar2_gated() -> u32 { foo2_gated() } //~ ERROR can only call other `min_const_fn` + +fn main() {} diff --git a/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr new file mode 100644 index 0000000000000..0b58dc1294be0 --- /dev/null +++ b/src/test/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -0,0 +1,20 @@ +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:26:32 + | +LL | const unsafe fn bar() -> u32 { foo() } //~ ERROR can only call other `min_const_fn` + | ^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:33:33 + | +LL | const unsafe fn bar2() -> u32 { foo2() } //~ ERROR can only call other `min_const_fn` + | ^^^^^^ + +error: can only call other `min_const_fn` within a `min_const_fn` + --> $DIR/min_const_unsafe_fn_libstd_stability2.rs:41:39 + | +LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } //~ ERROR can only call other `min_const_fn` + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/unsafe/ranged_ints.rs b/src/test/ui/unsafe/ranged_ints.rs new file mode 100644 index 0000000000000..0fa2da917e9f8 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints.rs @@ -0,0 +1,8 @@ +#![feature(rustc_attrs)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() { + let _x = NonZero(0); //~ ERROR initializing type with `rustc_layout_scalar_valid_range` attr +} diff --git a/src/test/ui/unsafe/ranged_ints.stderr b/src/test/ui/unsafe/ranged_ints.stderr new file mode 100644 index 0000000000000..f59a930b5a901 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints.stderr @@ -0,0 +1,11 @@ +error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block + --> $DIR/ranged_ints.rs:7:14 + | +LL | let _x = NonZero(0); //~ ERROR initializing type with `rustc_layout_scalar_valid_range` attr + | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr + | + = note: initializing a layout restricted type's field with a value outside the valid range is undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints2.rs b/src/test/ui/unsafe/ranged_ints2.rs new file mode 100644 index 0000000000000..68ba120b279c0 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints2.rs @@ -0,0 +1,9 @@ +#![feature(rustc_attrs)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() { + let mut x = unsafe { NonZero(1) }; + let y = &mut x.0; //~ ERROR mutation of layout constrained field is unsafe +} diff --git a/src/test/ui/unsafe/ranged_ints2.stderr b/src/test/ui/unsafe/ranged_ints2.stderr new file mode 100644 index 0000000000000..ae63f47ed74a7 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints2.stderr @@ -0,0 +1,11 @@ +error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block + --> $DIR/ranged_ints2.rs:8:13 + | +LL | let y = &mut x.0; //~ ERROR mutation of layout constrained field is unsafe + | ^^^^^^^^ mutation of layout constrained field + | + = note: mutating layout constrained fields cannot statically be checked for valid values + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints2_const.rs b/src/test/ui/unsafe/ranged_ints2_const.rs new file mode 100644 index 0000000000000..a61e3329bdce8 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints2_const.rs @@ -0,0 +1,20 @@ +#![feature(rustc_attrs, const_let, const_fn)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() { +} + +const fn foo() -> NonZero { + let mut x = unsafe { NonZero(1) }; + let y = &mut x.0; //~ ERROR references in constant functions may only refer to immutable + //~^ ERROR mutation of layout constrained field is unsafe + unsafe { NonZero(1) } +} + +const fn bar() -> NonZero { + let mut x = unsafe { NonZero(1) }; + let y = unsafe { &mut x.0 }; //~ ERROR references in constant functions may only refer to immut + unsafe { NonZero(1) } +} diff --git a/src/test/ui/unsafe/ranged_ints2_const.stderr b/src/test/ui/unsafe/ranged_ints2_const.stderr new file mode 100644 index 0000000000000..f79792ffba9be --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints2_const.stderr @@ -0,0 +1,24 @@ +error[E0017]: references in constant functions may only refer to immutable values + --> $DIR/ranged_ints2_const.rs:11:13 + | +LL | let y = &mut x.0; //~ ERROR references in constant functions may only refer to immutable + | ^^^^^^^^ constant functions require immutable values + +error[E0017]: references in constant functions may only refer to immutable values + --> $DIR/ranged_ints2_const.rs:18:22 + | +LL | let y = unsafe { &mut x.0 }; //~ ERROR references in constant functions may only refer to immut + | ^^^^^^^^ constant functions require immutable values + +error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block + --> $DIR/ranged_ints2_const.rs:11:13 + | +LL | let y = &mut x.0; //~ ERROR references in constant functions may only refer to immutable + | ^^^^^^^^ mutation of layout constrained field + | + = note: mutating layout constrained fields cannot statically be checked for valid values + +error: aborting due to 3 previous errors + +Some errors occurred: E0017, E0133. +For more information about an error, try `rustc --explain E0017`. diff --git a/src/test/ui/unsafe/ranged_ints3.rs b/src/test/ui/unsafe/ranged_ints3.rs new file mode 100644 index 0000000000000..47d67fac6785c --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints3.rs @@ -0,0 +1,11 @@ +#![feature(rustc_attrs)] + +use std::cell::Cell; + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() { + let mut x = unsafe { NonZero(Cell::new(1)) }; + let y = &x.0; //~ ERROR borrow of layout constrained field with interior mutability +} diff --git a/src/test/ui/unsafe/ranged_ints3.stderr b/src/test/ui/unsafe/ranged_ints3.stderr new file mode 100644 index 0000000000000..311a058fdb07f --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints3.stderr @@ -0,0 +1,11 @@ +error[E0133]: borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block + --> $DIR/ranged_ints3.rs:10:13 + | +LL | let y = &x.0; //~ ERROR borrow of layout constrained field with interior mutability + | ^^^^ borrow of layout constrained field with interior mutability + | + = note: references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints3_const.rs b/src/test/ui/unsafe/ranged_ints3_const.rs new file mode 100644 index 0000000000000..6497b611224b6 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints3_const.rs @@ -0,0 +1,21 @@ +#![feature(rustc_attrs, const_let, const_fn)] + +use std::cell::Cell; + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() {} + +const fn foo() -> NonZero> { + let mut x = unsafe { NonZero(Cell::new(1)) }; + let y = &x.0; //~ ERROR cannot borrow a constant which may contain interior mutability + //~^ ERROR borrow of layout constrained field with interior mutability + unsafe { NonZero(Cell::new(1)) } +} + +const fn bar() -> NonZero> { + let mut x = unsafe { NonZero(Cell::new(1)) }; + let y = unsafe { &x.0 }; //~ ERROR cannot borrow a constant which may contain interior mut + unsafe { NonZero(Cell::new(1)) } +} diff --git a/src/test/ui/unsafe/ranged_ints3_const.stderr b/src/test/ui/unsafe/ranged_ints3_const.stderr new file mode 100644 index 0000000000000..d83d75787d948 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints3_const.stderr @@ -0,0 +1,24 @@ +error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead + --> $DIR/ranged_ints3_const.rs:12:13 + | +LL | let y = &x.0; //~ ERROR cannot borrow a constant which may contain interior mutability + | ^^^^ + +error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead + --> $DIR/ranged_ints3_const.rs:19:22 + | +LL | let y = unsafe { &x.0 }; //~ ERROR cannot borrow a constant which may contain interior mut + | ^^^^ + +error[E0133]: borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block + --> $DIR/ranged_ints3_const.rs:12:13 + | +LL | let y = &x.0; //~ ERROR cannot borrow a constant which may contain interior mutability + | ^^^^ borrow of layout constrained field with interior mutability + | + = note: references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values + +error: aborting due to 3 previous errors + +Some errors occurred: E0133, E0492. +For more information about an error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints4.rs b/src/test/ui/unsafe/ranged_ints4.rs new file mode 100644 index 0000000000000..d8632c48434f2 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints4.rs @@ -0,0 +1,9 @@ +#![feature(rustc_attrs)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() { + let mut x = unsafe { NonZero(1) }; + x.0 = 0; //~ ERROR mutation of layout constrained field is unsafe +} diff --git a/src/test/ui/unsafe/ranged_ints4.stderr b/src/test/ui/unsafe/ranged_ints4.stderr new file mode 100644 index 0000000000000..c6468b643b41a --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints4.stderr @@ -0,0 +1,11 @@ +error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block + --> $DIR/ranged_ints4.rs:8:5 + | +LL | x.0 = 0; //~ ERROR mutation of layout constrained field is unsafe + | ^^^^^^^ mutation of layout constrained field + | + = note: mutating layout constrained fields cannot statically be checked for valid values + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints4_const.rs b/src/test/ui/unsafe/ranged_ints4_const.rs new file mode 100644 index 0000000000000..f589e4739baf1 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints4_const.rs @@ -0,0 +1,19 @@ +#![feature(rustc_attrs, const_let, const_fn)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() {} + +const fn foo() -> NonZero { + let mut x = unsafe { NonZero(1) }; + x.0 = 0; + //~^ ERROR mutation of layout constrained field is unsafe + x +} + +const fn bar() -> NonZero { + let mut x = unsafe { NonZero(1) }; + unsafe { x.0 = 0 }; // this is UB + x +} diff --git a/src/test/ui/unsafe/ranged_ints4_const.stderr b/src/test/ui/unsafe/ranged_ints4_const.stderr new file mode 100644 index 0000000000000..fe83b15ce5cec --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints4_const.stderr @@ -0,0 +1,11 @@ +error[E0133]: mutation of layout constrained field is unsafe and requires unsafe function or block + --> $DIR/ranged_ints4_const.rs:10:5 + | +LL | x.0 = 0; + | ^^^^^^^ mutation of layout constrained field + | + = note: mutating layout constrained fields cannot statically be checked for valid values + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/ranged_ints_const.rs b/src/test/ui/unsafe/ranged_ints_const.rs new file mode 100644 index 0000000000000..8477772867e91 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints_const.rs @@ -0,0 +1,11 @@ +#![feature(rustc_attrs)] + +#[rustc_layout_scalar_valid_range_start(1)] +#[repr(transparent)] +pub(crate) struct NonZero(pub(crate) T); +fn main() {} + +const fn foo() -> NonZero { NonZero(0) } +//~^ ERROR initializing type with `rustc_layout_scalar_valid_range` attr is unsafe + +const fn bar() -> NonZero { unsafe { NonZero(0) } } diff --git a/src/test/ui/unsafe/ranged_ints_const.stderr b/src/test/ui/unsafe/ranged_ints_const.stderr new file mode 100644 index 0000000000000..584ad40a92bb0 --- /dev/null +++ b/src/test/ui/unsafe/ranged_ints_const.stderr @@ -0,0 +1,11 @@ +error[E0133]: initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block + --> $DIR/ranged_ints_const.rs:8:34 + | +LL | const fn foo() -> NonZero { NonZero(0) } + | ^^^^^^^^^^ initializing type with `rustc_layout_scalar_valid_range` attr + | + = note: initializing a layout restricted type's field with a value outside the valid range is undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`.