diff options
| author | Eduard Burtescu <edy.burt@gmail.com> | 2016-04-19 17:03:30 +0300 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2016-05-04 11:28:15 -0700 |
| commit | 0ab80b651626666a6215fbeac23231f2fc04f97b (patch) | |
| tree | 4eb14c5abf955b8fdcc16eabb5dc715515fb50f4 | |
| parent | aacc89072a3d8c7f290641d69b3c3879995a119a (diff) | |
| download | rust-0ab80b651626666a6215fbeac23231f2fc04f97b.tar.gz rust-0ab80b651626666a6215fbeac23231f2fc04f97b.zip | |
Check transmutes between types without statically known sizes.
| -rw-r--r-- | src/librustc/dep_graph/dep_node.rs | 2 | ||||
| -rw-r--r-- | src/librustc/diagnostics.rs | 26 | ||||
| -rw-r--r-- | src/librustc/middle/intrinsicck.rs | 289 | ||||
| -rw-r--r-- | src/librustc/ty/context.rs | 6 | ||||
| -rw-r--r-- | src/librustc/ty/layout.rs | 135 | ||||
| -rw-r--r-- | src/librustc/ty/mod.rs | 31 | ||||
| -rw-r--r-- | src/librustc_trans/base.rs | 7 | ||||
| -rw-r--r-- | src/librustc_trans/diagnostics.rs | 26 | ||||
| -rw-r--r-- | src/librustc_trans/intrinsic.rs | 74 | ||||
| -rw-r--r-- | src/test/compile-fail/issue-21174.rs | 2 | ||||
| -rw-r--r-- | src/test/compile-fail/issue-32377.rs | 27 | ||||
| -rw-r--r-- | src/test/compile-fail/transmute-from-fn-item-types-error.rs | 23 | ||||
| -rw-r--r-- | src/test/compile-fail/transmute-from-fn-item-types-lint.rs | 26 | ||||
| -rw-r--r-- | src/test/compile-fail/transmute-type-parameters.rs | 18 |
14 files changed, 341 insertions, 351 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 446313f7037..536c739bf16 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -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), diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 4abb1c8b98a..0ef130127a4 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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. diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 767c5adc81e..e84be7e4560 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -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>, +struct ItemVisitor<'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>>, +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); + } +} - // 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 ExprVisitor<'a, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'tcx> } -impl<'a, 'tcx> IntrinsicCheckingVisitor<'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; } - }; - - // 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; + 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; + } + _ => {} + } } - 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); + // 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() } + }; - 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"); + 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)); + } +} - substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty; - self.with_each_combination(span, param_env, types_in_scope.clone(), - substs, callback); - } +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); + } - substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty; - self.with_each_combination(span, param_env, types_in_scope, - substs, callback); - } + 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 push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) { - debug!("Pushing transmute restriction: {:?}", restriction); - self.tcx.transmute_restrictions.borrow_mut().push(restriction); + 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); + } } -} -impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { 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) - } -} diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 1f40d9cecde..8b07a97b1a1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -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(), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 494335933b6..3ea691b4dc7 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -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 + } + } +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 0cf1be186d8..76e18565d65 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -472,37 +472,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, diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 17230eff6e6..104a74a63c9 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -77,7 +77,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; @@ -2745,13 +2744,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"); diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs index 5ae60d18240..5e4902cf3ca 100644 --- a/src/librustc_trans/diagnostics.rs +++ b/src/librustc_trans/diagnostics.rs @@ -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: diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 5924ae1ad84..0f9b04c04f6 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -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 diff --git a/src/test/compile-fail/issue-21174.rs b/src/test/compile-fail/issue-21174.rs index 30fd2eb4d2f..c92a404b71a 100644 --- a/src/test/compile-fail/issue-21174.rs +++ b/src/test/compile-fail/issue-21174.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() { } diff --git a/src/test/compile-fail/issue-32377.rs b/src/test/compile-fail/issue-32377.rs new file mode 100644 index 00000000000..6e8126348da --- /dev/null +++ b/src/test/compile-fail/issue-32377.rs @@ -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() {} diff --git a/src/test/compile-fail/transmute-from-fn-item-types-error.rs b/src/test/compile-fail/transmute-from-fn-item-types-error.rs new file mode 100644 index 00000000000..50bcd53ecb8 --- /dev/null +++ b/src/test/compile-fail/transmute-from-fn-item-types-error.rs @@ -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(); + } +} diff --git a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs index 3eae76f9492..42c3cb7f181 100644 --- a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs +++ b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs @@ -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() { diff --git a/src/test/compile-fail/transmute-type-parameters.rs b/src/test/compile-fail/transmute-type-parameters.rs index b06966bd867..b6e7e32663e 100644 --- a/src/test/compile-fail/transmute-type-parameters.rs +++ b/src/test/compile-fail/transmute-type-parameters.rs @@ -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() {} |
