Check transmutes between types without statically known sizes.

This commit is contained in:
Eduard Burtescu 2016-04-19 17:03:30 +03:00
parent 24ca1ec07d
commit c7d564d8c9
14 changed files with 350 additions and 360 deletions

View File

@ -71,7 +71,6 @@ pub enum DepNode<D: Clone + Debug> {
DeadCheck,
StabilityCheck,
LateLintCheck,
IntrinsicUseCheck,
TransCrate,
TransCrateItem(D),
TransInlinedItem(D),
@ -169,7 +168,6 @@ impl<D: Clone + Debug> DepNode<D> {
DeadCheck => Some(DeadCheck),
StabilityCheck => Some(StabilityCheck),
LateLintCheck => Some(LateLintCheck),
IntrinsicUseCheck => Some(IntrinsicUseCheck),
TransCrate => Some(TransCrate),
TransWriteMetadata => Some(TransWriteMetadata),
Hir(ref d) => op(d).map(Hir),

View File

@ -1410,6 +1410,32 @@ It is not possible to use stability attributes outside of the standard library.
Also, for now, it is not possible to write deprecation messages either.
"##,
E0512: r##"
Transmute with two differently sized types was attempted. Erroneous code
example:
```compile_fail
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0u16)); }
// error: transmute called with differently sized types
}
```
Please use types with same size or use the expected type directly. Example:
```
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
// or:
unsafe { takes_u8(0u8); } // ok!
}
```
"##,
E0517: r##"
This error indicates that a `#[repr(..)]` attribute was placed on an
unsupported item.

View File

@ -11,11 +11,10 @@
use dep_graph::DepNode;
use hir::def::Def;
use hir::def_id::DefId;
use ty::subst::{Subst, Substs, EnumeratedItems};
use ty::{TransmuteRestriction, TyCtxt};
use ty::{self, Ty, TypeFoldable};
use std::fmt;
use infer::{InferCtxt, new_infer_ctxt};
use traits::ProjectionMode;
use ty::{self, Ty, TyCtxt};
use ty::layout::{LayoutError, Pointer, SizeSkeleton};
use syntax::abi::Abi::RustIntrinsic;
use syntax::ast;
@ -24,219 +23,148 @@ use hir::intravisit::{self, Visitor, FnKind};
use hir;
pub fn check_crate(tcx: &TyCtxt) {
let mut visitor = IntrinsicCheckingVisitor {
tcx: tcx,
param_envs: Vec::new(),
dummy_sized_ty: tcx.types.isize,
dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
let mut visitor = ItemVisitor {
tcx: tcx
};
tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
}
struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>,
// As we traverse the AST, we keep a stack of the parameter
// environments for each function we encounter. When we find a
// call to `transmute`, we can check it in the context of the top
// of the stack (which ought not to be empty).
param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
// Dummy sized/unsized types that use to substitute for type
// parameters in order to estimate how big a type will be for any
// possible instantiation of the type parameters in scope. See
// `check_transmute` for more details.
dummy_sized_ty: Ty<'tcx>,
dummy_unsized_ty: Ty<'tcx>,
struct ItemVisitor<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>
}
impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
impl<'a, 'tcx> ItemVisitor<'a, 'tcx> {
fn visit_const(&mut self, item_id: ast::NodeId, expr: &hir::Expr) {
let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id);
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
Some(param_env),
ProjectionMode::Any);
let mut visitor = ExprVisitor {
infcx: &infcx
};
visitor.visit_expr(expr);
}
}
struct ExprVisitor<'a, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'tcx>
}
impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
let intrinsic = match self.infcx.tcx.lookup_item_type(def_id).ty.sty {
ty::TyFnDef(_, _, ref bfty) => bfty.abi == RustIntrinsic,
_ => return false
};
intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
intrinsic && self.infcx.tcx.item_name(def_id).as_str() == "transmute"
}
fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
// Find the parameter environment for the most recent function that
// we entered.
let sk_from = SizeSkeleton::compute(from, self.infcx);
let sk_to = SizeSkeleton::compute(to, self.infcx);
let param_env = match self.param_envs.last() {
Some(p) => p,
None => {
span_bug!(
span,
"transmute encountered outside of any fn");
// Check for same size using the skeletons.
if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
if sk_from.same_size(sk_to) {
return;
}
match (&from.sty, sk_to) {
(&ty::TyFnDef(..), SizeSkeleton::Known(size_to))
if size_to == Pointer.size(&self.infcx.tcx.data_layout) => {
// FIXME #19925 Remove this warning after a release cycle.
let msg = format!("`{}` is now zero-sized and has to be cast \
to a pointer before transmuting to `{}`",
from, to);
self.infcx.tcx.sess.add_lint(
::lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES, id, span, msg);
return;
}
_ => {}
}
}
// Try to display a sensible error with as much information as possible.
let skeleton_string = |ty: Ty<'tcx>, sk| {
match sk {
Ok(SizeSkeleton::Known(size)) => {
format!("{} bits", size.bits())
}
Ok(SizeSkeleton::Pointer { tail, .. }) => {
format!("pointer to {}", tail)
}
Err(LayoutError::Unknown(bad)) => {
if bad == ty {
format!("size can vary")
} else {
format!("size can vary because of {}", bad)
}
}
Err(err) => err.to_string()
}
};
// Simple case: no type parameters involved.
if
!from.has_param_types() && !from.has_self_ty() &&
!to.has_param_types() && !to.has_self_ty()
{
let restriction = TransmuteRestriction {
span: span,
original_from: from,
original_to: to,
substituted_from: from,
substituted_to: to,
id: id,
};
self.push_transmute_restriction(restriction);
return;
}
// The rules around type parameters are a bit subtle. We are
// checking these rules before monomorphization, so there may
// be unsubstituted type parameters present in the
// types. Obviously we cannot create LLVM types for those.
// However, if a type parameter appears only indirectly (i.e.,
// through a pointer), it does not necessarily affect the
// size, so that should be allowed. The only catch is that we
// DO want to be careful around unsized type parameters, since
// fat pointers have a different size than a thin pointer, and
// hence `&T` and `&U` have different sizes if `T : Sized` but
// `U : Sized` does not hold.
//
// However, it's not as simple as checking whether `T :
// Sized`, because even if `T : Sized` does not hold, that
// just means that `T` *may* not be sized. After all, even a
// type parameter `T: ?Sized` could be bound to a sized
// type. (Issue #20116)
//
// To handle this, we first check for "interior" type
// parameters, which are always illegal. If there are none of
// those, then we know that the only way that all type
// parameters `T` are referenced indirectly, e.g. via a
// pointer type like `&T`. In that case, we only care whether
// `T` is sized or not, because that influences whether `&T`
// is a thin or fat pointer.
//
// One could imagine establishing a sophisticated constraint
// system to ensure that the transmute is legal, but instead
// we do something brutally dumb. We just substitute dummy
// sized or unsized types for every type parameter in scope,
// exhaustively checking all possible combinations. Here are some examples:
//
// ```
// fn foo<T, U>() {
// // T=int, U=int
// }
//
// fn bar<T: ?Sized, U>() {
// // T=int, U=int
// // T=[int], U=int
// }
//
// fn baz<T: ?Sized, U: ?Sized>() {
// // T=int, U=int
// // T=[int], U=int
// // T=int, U=[int]
// // T=[int], U=[int]
// }
// ```
//
// In all cases, we keep the original unsubstituted types
// around for error reporting.
let from_tc = from.type_contents(self.tcx);
let to_tc = to.type_contents(self.tcx);
if from_tc.interior_param() || to_tc.interior_param() {
span_err!(self.tcx.sess, span, E0139,
"cannot transmute to or from a type that contains \
unsubstituted type parameters");
return;
}
let mut substs = param_env.free_substs.clone();
self.with_each_combination(
span,
param_env,
param_env.free_substs.types.iter_enumerated(),
&mut substs,
&mut |substs| {
let restriction = TransmuteRestriction {
span: span,
original_from: from,
original_to: to,
substituted_from: from.subst(self.tcx, substs),
substituted_to: to.subst(self.tcx, substs),
id: id,
};
self.push_transmute_restriction(restriction);
});
}
fn with_each_combination(&self,
span: Span,
param_env: &ty::ParameterEnvironment<'a,'tcx>,
mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
substs: &mut Substs<'tcx>,
callback: &mut FnMut(&Substs<'tcx>))
{
// This parameter invokes `callback` many times with different
// substitutions that replace all the parameters in scope with
// either `int` or `[int]`, depending on whether the type
// parameter is known to be sized. See big comment above for
// an explanation of why this is a reasonable thing to do.
match types_in_scope.next() {
None => {
debug!("with_each_combination(substs={:?})",
substs);
callback(substs);
}
Some((space, index, &param_ty)) => {
debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
space, index, param_ty);
if !param_ty.is_sized(param_env, span) {
debug!("with_each_combination: param_ty is not known to be sized");
substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
self.with_each_combination(span, param_env, types_in_scope.clone(),
substs, callback);
}
substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
self.with_each_combination(span, param_env, types_in_scope,
substs, callback);
}
}
}
fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
debug!("Pushing transmute restriction: {:?}", restriction);
self.tcx.transmute_restrictions.borrow_mut().push(restriction);
span_err!(self.infcx.tcx.sess, span, E0512,
"transmute called with differently sized types: \
{} ({}) to {} ({})",
from, skeleton_string(from, sk_from),
to, skeleton_string(to, sk_to));
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
// const, static and N in [T; N].
fn visit_expr(&mut self, expr: &hir::Expr) {
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
None, ProjectionMode::Any);
let mut visitor = ExprVisitor {
infcx: &infcx
};
visitor.visit_expr(expr);
}
fn visit_trait_item(&mut self, item: &hir::TraitItem) {
if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
self.visit_const(item.id, expr);
} else {
intravisit::walk_trait_item(self, item);
}
}
fn visit_impl_item(&mut self, item: &hir::ImplItem) {
if let hir::ImplItemKind::Const(_, ref expr) = item.node {
self.visit_const(item.id, expr);
} else {
intravisit::walk_impl_item(self, item);
}
}
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
b: &'v hir::Block, s: Span, id: ast::NodeId) {
match fk {
FnKind::ItemFn(..) | FnKind::Method(..) => {
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
self.param_envs.push(param_env);
intravisit::walk_fn(self, fk, fd, b, s);
self.param_envs.pop();
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
Some(param_env),
ProjectionMode::Any);
let mut visitor = ExprVisitor {
infcx: &infcx
};
visitor.visit_fn(fk, fd, b, s, id);
}
FnKind::Closure(..) => {
intravisit::walk_fn(self, fk, fd, b, s);
span_bug!(s, "intrinsicck: closure outside of function")
}
}
}
}
impl<'a, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &hir::Expr) {
if let hir::ExprPath(..) = expr.node {
match self.tcx.resolve_expr(expr) {
match self.infcx.tcx.resolve_expr(expr) {
Def::Fn(did) if self.def_id_is_transmute(did) => {
let typ = self.tcx.node_id_to_type(expr.id);
let typ = self.infcx.tcx.node_id_to_type(expr.id);
match typ.sty {
ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
@ -256,14 +184,3 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
intravisit::walk_expr(self, expr);
}
}
impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
self.id,
self.original_from,
self.original_to,
self.substituted_from,
self.substituted_to)
}
}

View File

@ -357,11 +357,6 @@ pub struct TyCtxt<'tcx> {
pub node_lint_levels: RefCell<FnvHashMap<(NodeId, lint::LintId),
lint::LevelSource>>,
/// The types that must be asserted to be the same size for `transmute`
/// to be valid. We gather up these restrictions in the intrinsicck pass
/// and check them in trans.
pub transmute_restrictions: RefCell<Vec<ty::TransmuteRestriction<'tcx>>>,
/// Maps any item's def-id to its stability index.
pub stability: RefCell<stability::Index<'tcx>>,
@ -605,7 +600,6 @@ impl<'tcx> TyCtxt<'tcx> {
extern_const_statics: RefCell::new(DefIdMap()),
extern_const_fns: RefCell::new(DefIdMap()),
node_lint_levels: RefCell::new(FnvHashMap()),
transmute_restrictions: RefCell::new(Vec::new()),
stability: RefCell::new(stability),
selection_cache: traits::SelectionCache::new(),
evaluation_cache: traits::EvaluationCache::new(),

View File

@ -1199,3 +1199,138 @@ impl Layout {
}
}
}
/// Type size "skeleton", i.e. the only information determining a type's size.
/// While this is conservative, (aside from constant sizes, only pointers,
/// newtypes thereof and null pointer optimized enums are allowed), it is
/// enough to statically check common usecases of transmute.
#[derive(Copy, Clone, Debug)]
pub enum SizeSkeleton<'tcx> {
/// Any statically computable Layout.
Known(Size),
/// A potentially-fat pointer.
Pointer {
// If true, this pointer is never null.
non_zero: bool,
// The type which determines the unsized metadata, if any,
// of this pointer. Either a type parameter or a projection
// depending on one, with regions erased.
tail: Ty<'tcx>
}
}
impl<'tcx> SizeSkeleton<'tcx> {
pub fn compute<'a>(ty: Ty<'tcx>, infcx: &InferCtxt<'a, 'tcx>)
-> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
let tcx = infcx.tcx;
assert!(!ty.has_infer_types());
// First try computing a static layout.
let err = match ty.layout(infcx) {
Ok(layout) => {
return Ok(SizeSkeleton::Known(layout.size(&tcx.data_layout)));
}
Err(err) => err
};
match ty.sty {
ty::TyBox(pointee) |
ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
let non_zero = !ty.is_unsafe_ptr();
let tail = tcx.struct_tail(pointee);
match tail.sty {
ty::TyParam(_) | ty::TyProjection(_) => {
assert!(tail.has_param_types() || tail.has_self_ty());
Ok(SizeSkeleton::Pointer {
non_zero: non_zero,
tail: tcx.erase_regions(&tail)
})
}
_ => {
bug!("SizeSkeleton::compute({}): layout errored ({}), yet \
tail `{}` is not a type parameter or a projection",
ty, err, tail)
}
}
}
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
// Only newtypes and enums w/ nullable pointer optimization.
if def.variants.is_empty() || def.variants.len() > 2 {
return Err(err);
}
// If there's a drop flag, it can't be just a pointer.
if def.dtor_kind().has_drop_flag() {
return Err(err);
}
// Get a zero-sized variant or a pointer newtype.
let zero_or_ptr_variant = |i: usize| {
let fields = def.variants[i].fields.iter().map(|field| {
let ty = normalize_associated_type(infcx, &field.ty(tcx, substs));
SizeSkeleton::compute(ty, infcx)
});
let mut ptr = None;
for field in fields {
let field = field?;
match field {
SizeSkeleton::Known(size) => {
if size.bytes() > 0 {
return Err(err);
}
}
SizeSkeleton::Pointer {..} => {
if ptr.is_some() {
return Err(err);
}
ptr = Some(field);
}
}
}
Ok(ptr)
};
let v0 = zero_or_ptr_variant(0)?;
// Newtype.
if def.variants.len() == 1 {
if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
return Ok(SizeSkeleton::Pointer {
non_zero: non_zero ||
Some(def.did) == tcx.lang_items.non_zero(),
tail: tail
});
} else {
return Err(err);
}
}
let v1 = zero_or_ptr_variant(1)?;
// Nullable pointer enum optimization.
match (v0, v1) {
(Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) |
(None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => {
Ok(SizeSkeleton::Pointer {
non_zero: false,
tail: tail
})
}
_ => Err(err)
}
}
_ => Err(err)
}
}
pub fn same_size(self, other: SizeSkeleton) -> bool {
match (self, other) {
(SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
(SizeSkeleton::Pointer { tail: a, .. },
SizeSkeleton::Pointer { tail: b, .. }) => a == b,
_ => false
}
}
}

View File

@ -480,37 +480,6 @@ pub struct CReaderCacheKey {
pub pos: usize,
}
/// A restriction that certain types must be the same size. The use of
/// `transmute` gives rise to these restrictions. These generally
/// cannot be checked until trans; therefore, each call to `transmute`
/// will push one or more such restriction into the
/// `transmute_restrictions` vector during `intrinsicck`. They are
/// then checked during `trans` by the fn `check_intrinsics`.
#[derive(Copy, Clone)]
pub struct TransmuteRestriction<'tcx> {
/// The span whence the restriction comes.
pub span: Span,
/// The type being transmuted from.
pub original_from: Ty<'tcx>,
/// The type being transmuted to.
pub original_to: Ty<'tcx>,
/// The type being transmuted from, with all type parameters
/// substituted for an arbitrary representative. Not to be shown
/// to the end user.
pub substituted_from: Ty<'tcx>,
/// The type being transmuted to, with all type parameters
/// substituted for an arbitrary representative. Not to be shown
/// to the end user.
pub substituted_to: Ty<'tcx>,
/// NodeId of the transmute intrinsic.
pub id: NodeId,
}
/// Describes the fragment-state associated with a NodeId.
///
/// Currently only unfragmented paths have entries in the table,

View File

@ -78,7 +78,6 @@ use declare;
use expr;
use glue;
use inline;
use intrinsic;
use machine;
use machine::{llalign_of_min, llsize_of, llsize_of_real};
use meth;
@ -2750,13 +2749,9 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
{
let ccx = shared_ccx.get_ccx(0);
// First, verify intrinsics.
intrinsic::check_intrinsics(&ccx);
collect_translation_items(&ccx);
// Next, translate all items. See `TransModVisitor` for
// Translate all items. See `TransModVisitor` for
// details on why we walk in this particular way.
{
let _icx = push_ctxt("text");

View File

@ -83,32 +83,6 @@ unsafe { simd_add(i32x1(0), i32x1(1)); } // ok!
```
"##,
E0512: r##"
Transmute with two differently sized types was attempted. Erroneous code
example:
```compile_fail
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0u16)); }
// error: transmute called with differently sized types
}
```
Please use types with same size or use the expected type directly. Example:
```
fn takes_u8(_: u8) {}
fn main() {
unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
// or:
unsafe { takes_u8(0u8); } // ok!
}
```
"##,
E0515: r##"
A constant index expression was out of bounds. Erroneous code example:

View File

@ -36,16 +36,14 @@ use glue;
use type_of;
use machine;
use type_::Type;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::{self, Ty};
use Disr;
use rustc::ty::subst::Substs;
use rustc::dep_graph::DepNode;
use rustc::hir;
use syntax::ast;
use syntax::ptr::P;
use syntax::parse::token;
use rustc::lint;
use rustc::session::Session;
use syntax::codemap::{Span, DUMMY_SP};
@ -97,76 +95,6 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
Some(ccx.get_intrinsic(&llvm_name))
}
pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) {
span_err!(a, b, E0512, "{}", msg);
}
/// Performs late verification that intrinsics are used correctly. At present,
/// the only intrinsic that needs such verification is `transmute`.
pub fn check_intrinsics(ccx: &CrateContext) {
let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck);
let mut last_failing_id = None;
for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() {
// Sometimes, a single call to transmute will push multiple
// type pairs to test in order to exhaustively test the
// possibility around a type parameter. If one of those fails,
// there is no sense reporting errors on the others.
if last_failing_id == Some(transmute_restriction.id) {
continue;
}
debug!("transmute_restriction: {:?}", transmute_restriction);
assert!(!transmute_restriction.substituted_from.has_param_types());
assert!(!transmute_restriction.substituted_to.has_param_types());
let llfromtype = type_of::sizing_type_of(ccx,
transmute_restriction.substituted_from);
let lltotype = type_of::sizing_type_of(ccx,
transmute_restriction.substituted_to);
let from_type_size = machine::llbitsize_of_real(ccx, llfromtype);
let to_type_size = machine::llbitsize_of_real(ccx, lltotype);
if let ty::TyFnDef(..) = transmute_restriction.substituted_from.sty {
if to_type_size == machine::llbitsize_of_real(ccx, ccx.int_type()) {
// FIXME #19925 Remove this warning after a release cycle.
lint::raw_emit_lint(&ccx.tcx().sess,
&ccx.tcx().sess.lint_store.borrow(),
lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES,
(lint::Warn, lint::LintSource::Default),
Some(transmute_restriction.span),
&format!("`{}` is now zero-sized and has to be cast \
to a pointer before transmuting to `{}`",
transmute_restriction.substituted_from,
transmute_restriction.substituted_to));
continue;
}
}
if from_type_size != to_type_size {
last_failing_id = Some(transmute_restriction.id);
if transmute_restriction.original_from != transmute_restriction.substituted_from {
span_transmute_size_error(ccx.sess(), transmute_restriction.span,
&format!("transmute called with differently sized types: \
{} (could be {} bits) to {} (could be {} bits)",
transmute_restriction.original_from,
from_type_size,
transmute_restriction.original_to,
to_type_size));
} else {
span_transmute_size_error(ccx.sess(), transmute_restriction.span,
&format!("transmute called with differently sized types: \
{} ({} bits) to {} ({} bits)",
transmute_restriction.original_from,
from_type_size,
transmute_restriction.original_to,
to_type_size));
}
}
}
ccx.sess().abort_if_errors();
}
/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
/// add them to librustc_trans/trans/context.rs

View File

@ -15,7 +15,7 @@ trait Trait<'a> {
fn foo<'a, T: Trait<'a>>(value: T::A) {
let new: T::B = unsafe { std::mem::transmute(value) };
//~^ ERROR: cannot transmute to or from a type that contains unsubstituted type parameters [E0139]
//~^ ERROR: transmute called with differently sized types
}
fn main() { }

View File

@ -0,0 +1,27 @@
// Copyright 2016 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
use std::marker::PhantomData;
trait Foo {
type Error;
}
struct Bar<U: Foo> {
stream: PhantomData<U::Error>,
}
fn foo<U: Foo>(x: [usize; 2]) -> Bar<U> {
unsafe { mem::transmute(x) }
//~^ ERROR transmute called with differently sized types
}
fn main() {}

View File

@ -0,0 +1,23 @@
// Copyright 2016 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
unsafe fn bar() {
// Error, still, if the resulting type is not pointer-sized.
mem::transmute::<_, u8>(main);
//~^ ERROR transmute called with differently sized types
}
fn main() {
unsafe {
bar();
}
}

View File

@ -8,39 +8,37 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(transmute_from_fn_item_types)]
use std::mem;
unsafe fn foo() -> (isize, *const (), Option<fn()>) {
let i = mem::transmute(bar);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
//~^^ ERROR was previously accepted
let p = mem::transmute(foo);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
//~^^ ERROR was previously accepted
let of = mem::transmute(main);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
//~^^ ERROR was previously accepted
(i, p, of)
}
unsafe fn bar() {
mem::transmute::<_, *mut ()>(foo);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
//~^^ ERROR was previously accepted
mem::transmute::<_, fn()>(bar);
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
//~^^ WARN was previously accepted
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
//~^^ ERROR was previously accepted
// No error if a coercion would otherwise occur.
mem::transmute::<fn(), usize>(main);
// Error, still, if the resulting type is not pointer-sized.
mem::transmute::<_, u8>(main);
//~^ ERROR transmute called with differently sized types
}
fn main() {

View File

@ -13,15 +13,18 @@
use std::mem::transmute;
unsafe fn f<T>(x: T) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: T (size can vary) to isize
}
unsafe fn g<T>(x: (T, isize)) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: (T, isize) (size can vary because of T) to isize
}
unsafe fn h<T>(x: [T; 10]) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: [T; 10] (size can vary because of T) to isize
}
struct Bad<T> {
@ -29,7 +32,8 @@ struct Bad<T> {
}
unsafe fn i<T>(x: Bad<T>) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: Bad<T> (size can vary because of T) to isize
}
enum Worse<T> {
@ -38,11 +42,13 @@ enum Worse<T> {
}
unsafe fn j<T>(x: Worse<T>) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: Worse<T> (size can vary because of T) to isize
}
unsafe fn k<T>(x: Option<T>) {
let _: isize = transmute(x); //~ ERROR cannot transmute
let _: isize = transmute(x);
//~^ ERROR differently sized types: std::option::Option<T> (size can vary because of T) to isize
}
fn main() {}