// https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582
auto possible_coercion
= TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus,
- true /*allow-autoderef*/);
+ true /*allow-autoderef*/,
+ true /*is_cast_site*/);
if (!possible_coercion.is_error ())
{
// given the attempt was ok we need to ensure we perform it so that any
// inference variables are unified correctly
return TypeCoercionRules::Coerce (from.get_ty (), to.get_ty (), locus,
- true /*allow-autoderef*/);
+ true /*allow-autoderef*/,
+ true /*is_cast_site*/);
}
// try the simple cast rules
TypeCoercionRules::CoercionResult
TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
- Location locus, bool allow_autoderef)
+ Location locus, bool allow_autoderef,
+ bool is_cast_site)
{
- TypeCoercionRules resolver (expected, locus, true, allow_autoderef, false);
+ TypeCoercionRules resolver (expected, locus, true, allow_autoderef, false,
+ is_cast_site);
bool ok = resolver.do_coercion (receiver);
return ok ? resolver.try_result : CoercionResult::get_error ();
}
TypeCoercionRules::CoercionResult
TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus,
- bool allow_autoderef)
+ bool allow_autoderef, bool is_cast_site)
{
- TypeCoercionRules resolver (expected, locus, false, allow_autoderef, true);
+ TypeCoercionRules resolver (expected, locus, false, allow_autoderef, true,
+ is_cast_site);
bool ok = resolver.do_coercion (receiver);
return ok ? resolver.try_result : CoercionResult::get_error ();
}
TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected, Location locus,
bool emit_errors, bool allow_autoderef,
- bool try_flag)
+ bool try_flag, bool is_cast_site)
: AutoderefCycle (!allow_autoderef), mappings (Analysis::Mappings::get ()),
context (TypeCheckContext::get ()), expected (expected), locus (locus),
try_result (CoercionResult::get_error ()), emit_errors (emit_errors),
- try_flag (try_flag)
+ try_flag (try_flag), is_cast_site (is_cast_site)
{}
bool
TyTy::TyWithLocation (expected),
TyTy::TyWithLocation (receiver),
locus /*unify_locus*/, false /*emit_errors*/,
- !try_flag /*commit_if_ok*/, true /*infer*/,
+ !try_flag /*commit_if_ok*/, try_flag /*infer*/,
try_flag /*cleanup on error*/);
- rust_debug ("result");
- result->debug ();
if (result->get_kind () != TyTy::TypeKind::ERROR)
{
try_result = CoercionResult{{}, result};
= new TyTy::PointerType (receiver->get_ref (),
TyTy::TyVar (element->get_ref ()), to_mutbl);
+ rust_debug ("coerce_unsafe_ptr unify-site");
+
+ // this is a really annoying case rust allows casts of any ptr to another ptr
+ // types
+ //
+ // *? vs *i32 - simple coercion valid
+ // *? vs *T - simple coercion valid
+ // *i32 vs *i32 - simple coercion valid
+ // *i32 vs *u8 - simple coercion not valid but allowed in cast site
+ // *T vs *u8 - not valid but is allowed in cast site
+
TyTy::BaseType *result
= unify_site_and (receiver->get_ref (), TyTy::TyWithLocation (expected),
TyTy::TyWithLocation (coerced_mutability),
- locus /*unify_locus*/, false /*emit_errors*/,
- !try_flag /*commit_if_ok*/, true /*infer*/,
+ locus /*unify_locus*/, !try_flag /*emit_errors*/,
+ !try_flag /*commit_if_ok*/,
+ try_flag && !is_cast_site /*infer*/,
try_flag /*cleanup on error*/);
bool unsafe_ptr_coerceion_ok = result->get_kind () != TyTy::TypeKind::ERROR;
if (unsafe_ptr_coerceion_ok)
= unify_site_and (receiver->get_ref (),
TyTy::TyWithLocation (receiver),
TyTy::TyWithLocation (expected), locus,
- false /*emit_errors*/, true /*commit_if_ok*/,
- false /* FIXME infer do we want to allow this?? */,
- true /*cleanup_on_failure*/);
- return CoercionResult{{}, result};
+ false /*emit_errors*/, !try_flag /*commit_if_ok*/,
+ try_flag /* infer */,
+ try_flag /*cleanup_on_failure*/);
+ bool default_coerceion_ok
+ = result->get_kind () != TyTy::TypeKind::ERROR;
+ if (default_coerceion_ok)
+ return CoercionResult{{}, result};
+
+ return TypeCoercionRules::CoercionResult::get_error ();
}
}
static CoercionResult Coerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus,
- bool allow_autoderef);
+ bool allow_autoderef,
+ bool is_cast_site = false);
static CoercionResult TryCoerce (TyTy::BaseType *receiver,
TyTy::BaseType *expected, Location locus,
- bool allow_autoderef);
+ bool allow_autoderef,
+ bool is_cast_site = false);
CoercionResult coerce_unsafe_ptr (TyTy::BaseType *receiver,
TyTy::PointerType *expected,
protected:
TypeCoercionRules (TyTy::BaseType *expected, Location locus, bool emit_errors,
- bool allow_autoderef, bool try_flag);
+ bool allow_autoderef, bool try_flag, bool is_cast_site);
bool select (TyTy::BaseType &autoderefed) override;
CoercionResult try_result;
bool emit_errors;
bool try_flag;
+ bool is_cast_site;
};
} // namespace Resolver
}
pub unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
- let x = x as *mut T;
- let y = y as *mut T;
+ let x = x as *mut u8;
+ let y = y as *mut u8;
let len = mem::size_of::<T>() * count;
swap_nonoverlapping_bytes(x, y, len)
}
--- /dev/null
+// { dg-options "-w" }
+#![feature(intrinsics)]
+
+pub use option::Option::{self, None, Some};
+pub use result::Result::{self, Err, Ok};
+
+mod option {
+ pub enum Option<T> {
+ None,
+ Some(T),
+ }
+}
+
+mod result {
+ pub enum Result<T, E> {
+ Ok(T),
+ Err(E),
+ }
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "clone"]
+pub trait Clone: Sized {
+ fn clone(&self) -> Self;
+
+ fn clone_from(&mut self, source: &Self) {
+ *self = source.clone()
+ }
+}
+
+mod impls {
+ use super::Clone;
+
+ macro_rules! impl_clone {
+ ($($t:ty)*) => {
+ $(
+ impl Clone for $t {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ )*
+ }
+ }
+
+ impl_clone! {
+ usize u8 u16 u32 u64 // u128
+ isize i8 i16 i32 i64 // i128
+ f32 f64
+ bool char
+ }
+}
+
+#[lang = "copy"]
+pub trait Copy: Clone {
+ // Empty.
+}
+
+mod copy_impls {
+ use super::Copy;
+
+ macro_rules! impl_copy {
+ ($($t:ty)*) => {
+ $(
+ impl Copy for $t {}
+ )*
+ }
+ }
+
+ impl_copy! {
+ usize u8 u16 u32 u64 // u128
+ isize i8 i16 i32 i64 // i128
+ f32 f64
+ bool char
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ pub fn wrapping_add<T>(a: T, b: T) -> T;
+ pub fn wrapping_sub<T>(a: T, b: T) -> T;
+ pub fn rotate_left<T>(a: T, b: T) -> T;
+ pub fn rotate_right<T>(a: T, b: T) -> T;
+ pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+ pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ pub fn move_val_init<T>(dst: *mut T, src: T);
+ pub fn uninit<T>() -> T;
+ }
+}
+
+mod ptr {
+ #[lang = "const_ptr"]
+ impl<T> *const T {
+ pub unsafe fn offset(self, count: isize) -> *const T {
+ intrinsics::offset(self, count)
+ }
+ }
+
+ #[lang = "mut_ptr"]
+ impl<T> *mut T {
+ pub unsafe fn offset(self, count: isize) -> *mut T {
+ intrinsics::offset(self, count) as *mut T
+ }
+ }
+
+ pub unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
+ let x = x as *mut u8;
+ let y = y as *mut u8;
+ let len = mem::size_of::<T>() * count;
+ swap_nonoverlapping_bytes(x, y, len)
+ }
+
+ pub unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
+ // For types smaller than the block optimization below,
+ // just swap directly to avoid pessimizing codegen.
+ if mem::size_of::<T>() < 32 {
+ let z = read(x);
+ intrinsics::copy_nonoverlapping(y, x, 1);
+ write(y, z);
+ } else {
+ swap_nonoverlapping(x, y, 1);
+ }
+ }
+
+ pub unsafe fn write<T>(dst: *mut T, src: T) {
+ intrinsics::move_val_init(&mut *dst, src)
+ }
+
+ pub unsafe fn read<T>(src: *const T) -> T {
+ let mut tmp: T = mem::uninitialized();
+ intrinsics::copy_nonoverlapping(src, &mut tmp, 1);
+ tmp
+ }
+
+ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
+ struct Block(u64, u64, u64, u64);
+ struct UnalignedBlock(u64, u64, u64, u64);
+
+ let block_size = mem::size_of::<Block>();
+
+ // Loop through x & y, copying them `Block` at a time
+ // The optimizer should unroll the loop fully for most types
+ // N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
+ let mut i: usize = 0;
+ while i + block_size <= len {
+ // Create some uninitialized memory as scratch space
+ // Declaring `t` here avoids aligning the stack when this loop is unused
+ let mut t: Block = mem::uninitialized();
+ let t = &mut t as *mut _ as *mut u8;
+ let x = x.offset(i as isize);
+ let y = y.offset(i as isize);
+
+ // Swap a block of bytes of x & y, using t as a temporary buffer
+ // This should be optimized into efficient SIMD operations where available
+ intrinsics::copy_nonoverlapping(x, t, block_size);
+ intrinsics::copy_nonoverlapping(y, x, block_size);
+ intrinsics::copy_nonoverlapping(t, y, block_size);
+ i += block_size;
+ }
+
+ if i < len {
+ // Swap any remaining bytes
+ let mut t: UnalignedBlock = mem::uninitialized();
+ let rem = len - i;
+
+ let t = &mut t as *mut _ as *mut u8;
+ let x = x.offset(i as isize);
+ let y = y.offset(i as isize);
+
+ intrinsics::copy_nonoverlapping(x, t, rem);
+ intrinsics::copy_nonoverlapping(y, x, rem);
+ intrinsics::copy_nonoverlapping(t, y, rem);
+ }
+ }
+}
+
+mod mem {
+ extern "rust-intrinsic" {
+ pub fn transmute<T, U>(_: T) -> U;
+ pub fn size_of<T>() -> usize;
+ }
+
+ pub fn swap<T>(x: &mut T, y: &mut T) {
+ unsafe {
+ ptr::swap_nonoverlapping_one(x, y);
+ }
+ }
+
+ pub fn test(dest: &mut i32, mut src: i32) -> i32 {
+ swap(dest, &mut src);
+ src
+ }
+
+ pub unsafe fn uninitialized<T>() -> T {
+ intrinsics::uninit()
+ }
+}