Check transmutes between types without statically known sizes.
This commit is contained in:
parent
24ca1ec07d
commit
c7d564d8c9
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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, ¶m_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)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() { }
|
||||
|
27
src/test/compile-fail/issue-32377.rs
Normal file
27
src/test/compile-fail/issue-32377.rs
Normal 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() {}
|
23
src/test/compile-fail/transmute-from-fn-item-types-error.rs
Normal file
23
src/test/compile-fail/transmute-from-fn-item-types-error.rs
Normal 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();
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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() {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user