Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow calling const unsafe fn in const fn behind a feature gate #55635

Merged
merged 23 commits into from
Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f2ae7b7
Allow calling `const unsafe fn` in `const fn` behind a feature gate
oli-obk Nov 2, 2018
906a49e
Document unsafe rules with comments and `bug!` calls
oli-obk Nov 3, 2018
cc3470c
Add test for dereferencing raw pointers and immediately referencing a…
oli-obk Nov 3, 2018
02b2232
Make sure the initialization of constrained int range newtypes is unsafe
oli-obk Nov 3, 2018
ec6573f
Make `newtype_index` safe
oli-obk Nov 3, 2018
693c553
Move ref to packed struct field check into projection arm
oli-obk Nov 3, 2018
8bdb11c
Forbid the creation of mutable borrows to fields of layout constraine…
oli-obk Nov 3, 2018
14218e3
Trailing newlines again
oli-obk Nov 3, 2018
c4a8500
Adjust a rustc test to the safety changes
oli-obk Nov 4, 2018
081c497
generalize the message about the creation of layout restricted types
oli-obk Nov 4, 2018
1894a5f
Also make immutable references to non-freeze restricted value range t…
oli-obk Nov 4, 2018
55abc0b
Also prevent mutation fields directly
oli-obk Nov 5, 2018
e5d9065
Comment on the unsafety code for layout constrained fields
oli-obk Nov 5, 2018
4497ff3
Emit feature gate suggestion
oli-obk Nov 5, 2018
37ef5e4
Add tests for stable unsafe features in const fn
oli-obk Nov 6, 2018
3ce211d
Increase code-reuse and -readability
oli-obk Nov 19, 2018
137a640
Automatically generate imports for newtype_index `Deserialize` impls
oli-obk Nov 19, 2018
ae0b00c
Add and update tests
oli-obk Nov 19, 2018
b75d5f1
Clean up the logic in `is_min_const_fn`
oli-obk Nov 30, 2018
932dbe8
Explain unsafety trickery of const functions
oli-obk Nov 30, 2018
b779694
Clear up some code
oli-obk Nov 30, 2018
f411576
Intrinsic checks are just needed for `qualify_min_const_fn`
oli-obk Dec 1, 2018
cb71752
Tidy fixup
oli-obk Dec 4, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libcore/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: ?Sized> !Freeze for UnsafeCell<T> {}
unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
Expand Down
17 changes: 13 additions & 4 deletions src/libcore/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(pub(crate) T);
pub(crate) struct NonZero<T: Freeze>(pub(crate) T);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad this is not exported anymore!


impl<T: CoerceUnsized<U>, U> CoerceUnsized<NonZero<U>> for NonZero<T> {}
// Do not call `T::clone` as theoretically it could turn the field into `0`
// invalidating `NonZero`'s invariant.
impl<T: Copy + Freeze> Clone for NonZero<T> {
fn clone(&self) -> Self {
unsafe { NonZero(self.0) }
}
}

impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<NonZero<U>> for NonZero<T> {}
impl<T: CoerceUnsized<U> + Freeze, U: Freeze> CoerceUnsized<NonZero<U>> for NonZero<T> {}

impl<T: DispatchFromDyn<U> + Freeze, U: Freeze> DispatchFromDyn<NonZero<U>> for NonZero<T> {}
4 changes: 2 additions & 2 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ assert_eq!(size_of::<Option<std::num::", stringify!($Ty), ">>(), 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) })
pnkfelix marked this conversation as resolved.
Show resolved Hide resolved
}

/// Create a non-zero if the given value is not zero.
#[stable(feature = "nonzero", since = "1.28.0")]
#[inline]
pub fn new(n: $Int) -> Option<Self> {
if n != 0 {
Some($Ty(NonZero(n)))
Some($Ty(unsafe { NonZero(n) }))
} else {
None
}
Expand Down
14 changes: 7 additions & 7 deletions src/libcore/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2759,7 +2759,7 @@ impl<T: ?Sized> Unique<T> {
/// Creates a new `Unique` if `ptr` is non-null.
pub fn new(ptr: *mut T) -> Option<Self> {
if !ptr.is_null() {
Some(Unique { pointer: NonZero(ptr as _), _marker: PhantomData })
Some(Unique { pointer: unsafe { NonZero(ptr as _) }, _marker: PhantomData })
} else {
None
}
Expand Down Expand Up @@ -2815,14 +2815,14 @@ impl<T: ?Sized> fmt::Pointer for Unique<T> {
#[unstable(feature = "ptr_internals", issue = "0")]
impl<'a, T: ?Sized> From<&'a mut T> for Unique<T> {
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<T> {
fn from(reference: &'a T) -> Self {
Unique { pointer: NonZero(reference as _), _marker: PhantomData }
Unique { pointer: unsafe { NonZero(reference as _) }, _marker: PhantomData }
}
}

Expand Down Expand Up @@ -2895,15 +2895,15 @@ impl<T: ?Sized> NonNull<T> {
#[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.
#[stable(feature = "nonnull", since = "1.25.0")]
#[inline]
pub fn new(ptr: *mut T) -> Option<Self> {
if !ptr.is_null() {
Some(NonNull { pointer: NonZero(ptr as _) })
Some(unsafe { Self::new_unchecked(ptr) })
} else {
None
}
Expand Down Expand Up @@ -3025,14 +3025,14 @@ impl<T: ?Sized> From<Unique<T>> for NonNull<T> {
impl<'a, T: ?Sized> From<&'a mut T> for NonNull<T> {
#[inline]
fn from(reference: &'a mut T) -> Self {
NonNull { pointer: NonZero(reference as _) }
NonNull { pointer: unsafe { NonZero(reference as _) } }
}
}

#[stable(feature = "nonnull", since = "1.25.0")]
impl<'a, T: ?Sized> From<&'a T> for NonNull<T> {
#[inline]
fn from(reference: &'a T) -> Self {
NonNull { pointer: NonZero(reference as _) }
NonNull { pointer: unsafe { NonZero(reference as _) } }
}
}
3 changes: 2 additions & 1 deletion src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
});
Expand Down
9 changes: 6 additions & 3 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -2770,8 +2770,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),
}
Expand Down
18 changes: 5 additions & 13 deletions src/librustc/ty/constness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
32 changes: 28 additions & 4 deletions src/librustc_data_structures/indexed_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, feel free to add a comment about using const _ = instead or something.
But also, why not just use absolute paths?
E.g. impl ::serialize::Decodable for super::$type { and ::serialize::Decoder::read_u32(d).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use decl_macro you could even rely on hygiene, but idk how @nikomatsakis and @petrochenkov feel about using that in the compiler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... I wouldn't bank on decl_macro staying the same given that it has barely gone through an experimental RFC...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolute paths don't work, because not all crates have extern crate serialize;. Some have extern crate serialize as rustc_serialize;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I was hoping that wouldn't be a problem but... we'll have to wait for serde/some rustc-specific custom serialization solution, post-#49219.

extern crate serialize;
use self::serialize::{Decodable, Decoder};
impl Decodable for super::$type {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
d.read_u32().map(Self::from)
}
}
}
}
}
);

// Rewrite final without comma to one that includes comma
Expand Down
11 changes: 9 additions & 2 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) => {
Expand All @@ -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,
};

Expand Down
Loading