From c5edd22646fcde05b39490558371c7ea9d525a0f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 22 Dec 2014 20:57:14 -0500 Subject: [PATCH] Rewrite the intrinsicck to take the parameter environment into account. Also fixes #20116. --- src/librustc/middle/intrinsicck.rs | 253 ++++++++++++++---- src/librustc/middle/subst.rs | 1 + src/librustc/middle/ty.rs | 107 +++----- src/librustc_trans/trans/intrinsic.rs | 79 +++--- .../compile-fail/transmute-different-sizes.rs | 2 +- .../compile-fail/transmute-fat-pointers.rs | 41 +++ src/test/compile-fail/transmute-impl.rs | 33 +++ 7 files changed, 354 insertions(+), 162 deletions(-) create mode 100644 src/test/compile-fail/transmute-fat-pointers.rs create mode 100644 src/test/compile-fail/transmute-impl.rs diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index ba18c02f52d..1f4ddd1a5d6 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -10,9 +10,10 @@ use metadata::csearch; use middle::def::DefFn; -use middle::subst::Subst; +use middle::subst::{Subst, Substs, EnumeratedItems}; use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn}; use middle::ty::{mod, Ty}; +use util::ppaux::Repr; use syntax::abi::RustIntrinsic; use syntax::ast::DefId; @@ -23,52 +24,31 @@ use syntax::parse::token; use syntax::visit::Visitor; use syntax::visit; -fn type_size_is_affected_by_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>, typ: Ty<'tcx>) - -> bool { - let mut result = false; - ty::maybe_walk_ty(typ, |typ| { - match typ.sty { - ty::ty_uniq(_) | ty::ty_ptr(_) | ty::ty_rptr(..) | - ty::ty_bare_fn(..) | ty::ty_closure(..) => { - false - } - ty::ty_param(_) => { - result = true; - // No need to continue; we now know the result. - false - } - ty::ty_enum(did, substs) => { - for enum_variant in (*ty::enum_variants(tcx, did)).iter() { - for argument_type in enum_variant.args.iter() { - let argument_type = argument_type.subst(tcx, substs); - result = result || - type_size_is_affected_by_type_parameters( - tcx, - argument_type); - } - } - - // Don't traverse substitutions. - false - } - ty::ty_struct(did, substs) => { - for field in ty::struct_fields(tcx, did, substs).iter() { - result = result || - type_size_is_affected_by_type_parameters(tcx, - field.mt.ty); - } - - // Don't traverse substitutions. - false - } - _ => true, - } - }); - result +pub fn check_crate(tcx: &ctxt) { + let mut visitor = IntrinsicCheckingVisitor { + tcx: tcx, + param_envs: Vec::new(), + dummy_sized_ty: ty::mk_int(), + dummy_unsized_ty: ty::mk_vec(tcx, ty::mk_int(), None), + }; + visit::walk_crate(&mut visitor, tcx.map.krate()); } struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> { tcx: &'a ctxt<'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>, + + // 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>, } impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { @@ -97,26 +77,175 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { } fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) { - if type_size_is_affected_by_type_parameters(self.tcx, from) { - span_err!(self.tcx.sess, span, E0139, - "cannot transmute from a type that contains type parameters"); - } - if type_size_is_affected_by_type_parameters(self.tcx, to) { - span_err!(self.tcx.sess, span, E0140, - "cannot transmute to a type that contains type parameters"); + // Find the parameter environment for the most recent function that + // we entered. + + let param_env = match self.param_envs.last() { + Some(p) => p, + None => { + self.tcx.sess.span_bug( + span, + "transmute encountered outside of any fn"); + } + }; + + // Simple case: no type parameters involved. + if + !ty::type_has_params(from) && !ty::type_has_self(from) && + !ty::type_has_params(to) && !ty::type_has_self(to) + { + 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; } - let restriction = TransmuteRestriction { - span: span, - from: from, - to: to, - id: id, - }; + // 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 `Sized? T` 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=int, U=int + // } + // + // fn bar() { + // // T=int, U=int + // // T=[int], U=int + // } + // + // fn baz() { + // // 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 = ty::type_contents(self.tcx, from); + let to_tc = ty::type_contents(self.tcx, to); + 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 \ + type parameters in its interior"); + return; + } + + let mut substs = param_env.free_substs.clone(); + self.with_each_combination( + 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, + param_env: &ty::ParameterEnvironment<'tcx>, + mut types_in_scope: EnumeratedItems>, + 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.repr(self.tcx)); + + callback.call_mut((substs,)); + } + + Some((space, index, ¶m_ty)) => { + debug!("with_each_combination: space={}, index={}, param_ty={}", + space, index, param_ty.repr(self.tcx)); + + if !ty::type_is_sized(self.tcx, param_ty, param_env) { + 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(param_env, types_in_scope.clone(), substs, callback); + } + + substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty; + self.with_each_combination(param_env, types_in_scope, substs, callback); + } + } + } + + fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) { + debug!("Pushing transmute restriction: {}", restriction.repr(self.tcx)); self.tcx.transmute_restrictions.borrow_mut().push(restriction); } } impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { + fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, + b: &'v ast::Block, s: Span, id: ast::NodeId) { + match fk { + visit::FkItemFn(..) | visit::FkMethod(..) => { + let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); + self.param_envs.push(param_env); + visit::walk_fn(self, fk, fd, b, s); + self.param_envs.pop(); + } + visit::FkFnBlock(..) => { + visit::walk_fn(self, fk, fd, b, s); + } + } + + } + fn visit_expr(&mut self, expr: &ast::Expr) { if let ast::ExprPath(..) = expr.node { match ty::resolve_expr(self.tcx, expr) { @@ -144,7 +273,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { } } -pub fn check_crate(tcx: &ctxt) { - visit::walk_crate(&mut IntrinsicCheckingVisitor { tcx: tcx }, - tcx.map.krate()); +impl<'tcx> Repr<'tcx> for TransmuteRestriction<'tcx> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + format!("TransmuteRestriction(id={}, original=({},{}), substituted=({},{}))", + self.id, + self.original_from.repr(tcx), + self.original_to.repr(tcx), + self.substituted_from.repr(tcx), + self.substituted_to.repr(tcx)) + } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index abacad7d37c..8d920e0a821 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -487,6 +487,7 @@ impl VecPerParamSpace { } } +#[deriving(Clone)] pub struct EnumeratedItems<'a,T:'a> { vec: &'a VecPerParamSpace, space_index: uint, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 731ad64799e..b3e9f85c69c 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -62,7 +62,7 @@ use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string}; use util::ppaux::{trait_store_to_string, ty_to_string}; use util::ppaux::{Repr, UserString}; -use util::common::{indenter, memoized, ErrorReported}; +use util::common::{memoized, ErrorReported}; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::{FnvHashMap}; @@ -590,16 +590,33 @@ pub enum vtable_origin<'tcx> { pub type ObjectCastMap<'tcx> = RefCell>>>; /// A restriction that certain types must be the same size. The use of -/// `transmute` gives rise to these restrictions. +/// `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`. #[deriving(Copy)] pub struct TransmuteRestriction<'tcx> { - /// The span from whence the restriction comes. + /// The span whence the restriction comes. pub span: Span, + /// The type being transmuted from. - pub from: Ty<'tcx>, + pub original_from: Ty<'tcx>, + /// The type being transmuted to. - pub to: Ty<'tcx>, - /// NodeIf of the transmute intrinsic. + 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: ast::NodeId, } @@ -2856,6 +2873,7 @@ def_type_content_sets! { // Things that are interior to the value (first nibble): InteriorUnsized = 0b0000_0000__0000_0000__0001, InteriorUnsafe = 0b0000_0000__0000_0000__0010, + InteriorParam = 0b0000_0000__0000_0000__0100, // InteriorAll = 0b00000000__00000000__1111, // Things that are owned by the value (second and third nibbles): @@ -2910,6 +2928,10 @@ impl TypeContents { !self.intersects(TC::Nonsized) } + pub fn interior_param(&self) -> bool { + self.intersects(TC::InteriorParam) + } + pub fn interior_unsafe(&self) -> bool { self.intersects(TC::InteriorUnsafe) } @@ -3038,7 +3060,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } ty_closure(ref c) => { - closure_contents(cx, &**c) | TC::ReachesFfiUnsafe + closure_contents(&**c) | TC::ReachesFfiUnsafe } ty_uniq(typ) => { @@ -3049,7 +3071,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } ty_trait(box TyTrait { bounds, .. }) => { - object_contents(cx, bounds) | TC::ReachesFfiUnsafe | TC::Nonsized + object_contents(bounds) | TC::ReachesFfiUnsafe | TC::Nonsized } ty_ptr(ref mt) => { @@ -3159,26 +3181,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { apply_lang_items(cx, did, res) } - ty_param(p) => { - // We only ever ask for the kind of types that are defined in - // the current crate; therefore, the only type parameters that - // could be in scope are those defined in the current crate. - // If this assertion fails, it is likely because of a - // failure of the cross-crate inlining code to translate a - // def-id. - assert_eq!(p.def_id.krate, ast::LOCAL_CRATE); - - let ty_param_defs = cx.ty_param_defs.borrow(); - let tp_def = &(*ty_param_defs)[p.def_id.node]; - kind_bounds_to_contents( - cx, - tp_def.bounds.builtin_bounds, - tp_def.bounds.trait_bounds[]) - } - - ty_infer(_) => { - // This occurs during coherence, but shouldn't occur at other - // times. + ty_param(_) => { TC::All } @@ -3188,6 +3191,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { result.unsafe_pointer() | TC::Nonsized } + ty_infer(_) | ty_err => { cx.sess.bug("asked to compute contents of error type"); } @@ -3227,10 +3231,10 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { b | (TC::ReachesBorrowed).when(region != ty::ReStatic) } - fn closure_contents(cx: &ctxt, cty: &ClosureTy) -> TypeContents { + fn closure_contents(cty: &ClosureTy) -> TypeContents { // Closure contents are just like trait contents, but with potentially // even more stuff. - let st = object_contents(cx, cty.bounds); + let st = object_contents(cty.bounds); let st = match cty.store { UniqTraitStore => { @@ -3244,47 +3248,18 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { st } - fn object_contents(cx: &ctxt, - bounds: ExistentialBounds) - -> TypeContents { - // These are the type contents of the (opaque) interior - kind_bounds_to_contents(cx, bounds.builtin_bounds, &[]) - } - - fn kind_bounds_to_contents<'tcx>(cx: &ctxt<'tcx>, - bounds: BuiltinBounds, - traits: &[Rc>]) - -> TypeContents { - let _i = indenter(); - let mut tc = TC::All; - each_inherited_builtin_bound(cx, bounds, traits, |bound| { + fn object_contents(bounds: ExistentialBounds) -> TypeContents { + // These are the type contents of the (opaque) interior. We + // make no assumptions (other than that it cannot have an + // in-scope type parameter within, which makes no sense). + let mut tc = TC::All - TC::InteriorParam; + for bound in bounds.builtin_bounds.iter() { tc = tc - match bound { BoundSync | BoundSend | BoundCopy => TC::None, BoundSized => TC::Nonsized, }; - }); - return tc; - - // Iterates over all builtin bounds on the type parameter def, including - // those inherited from traits with builtin-kind-supertraits. - fn each_inherited_builtin_bound<'tcx, F>(cx: &ctxt<'tcx>, - bounds: BuiltinBounds, - traits: &[Rc>], - mut f: F) where - F: FnMut(BuiltinBound), - { - for bound in bounds.iter() { - f(bound); - } - - each_bound_trait_and_supertraits(cx, traits, |trait_ref| { - let trait_def = lookup_trait_def(cx, trait_ref.def_id()); - for bound in trait_def.bounds.builtin_bounds.iter() { - f(bound); - } - true - }); } + return tc; } } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 6b0baa5d05e..d49018e00c1 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -32,7 +32,7 @@ use middle::ty::{mod, Ty}; use syntax::abi::RustIntrinsic; use syntax::ast; use syntax::parse::token; -use util::ppaux::ty_to_string; +use util::ppaux::{Repr, ty_to_string}; pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Option { let name = match token::get_ident(item.ident).get() { @@ -90,46 +90,53 @@ pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Opti /// 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) { - for transmute_restriction in ccx.tcx() - .transmute_restrictions - .borrow() - .iter() { + 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.repr(ccx.tcx())); + + assert!(!ty::type_has_params(transmute_restriction.substituted_from)); + assert!(!ty::type_has_params(transmute_restriction.substituted_to)); + let llfromtype = type_of::sizing_type_of(ccx, - transmute_restriction.from); + transmute_restriction.substituted_from); let lltotype = type_of::sizing_type_of(ccx, - transmute_restriction.to); + 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 from_type_size != to_type_size { - ccx.sess() - .span_err(transmute_restriction.span, - format!("transmute called on types with different sizes: \ - {} ({} bit{}) to {} ({} bit{})", - ty_to_string(ccx.tcx(), transmute_restriction.from), - from_type_size as uint, - if from_type_size == 1 { - "" - } else { - "s" - }, - ty_to_string(ccx.tcx(), transmute_restriction.to), - to_type_size as uint, - if to_type_size == 1 { - "" - } else { - "s" - })[]); - } - if type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || - type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { - ccx.sess() - .add_lint(::lint::builtin::FAT_PTR_TRANSMUTES, - transmute_restriction.id, - transmute_restriction.span, - format!("Transmuting fat pointer types; {} to {}.\ - Beware of relying on the compiler's representation", - ty_to_string(ccx.tcx(), transmute_restriction.from), - ty_to_string(ccx.tcx(), transmute_restriction.to))); + last_failing_id = Some(transmute_restriction.id); + + if transmute_restriction.original_from != transmute_restriction.substituted_from { + ccx.sess().span_err( + transmute_restriction.span, + format!("transmute called on types with potentially different sizes: \ + {} (could be {} bit{}) to {} (could be {} bit{})", + ty_to_string(ccx.tcx(), transmute_restriction.original_from), + from_type_size as uint, + if from_type_size == 1 {""} else {"s"}, + ty_to_string(ccx.tcx(), transmute_restriction.original_to), + to_type_size as uint, + if to_type_size == 1 {""} else {"s"}).as_slice()); + } else { + ccx.sess().span_err( + transmute_restriction.span, + format!("transmute called on types with different sizes: \ + {} ({} bit{}) to {} ({} bit{})", + ty_to_string(ccx.tcx(), transmute_restriction.original_from), + from_type_size as uint, + if from_type_size == 1 {""} else {"s"}, + ty_to_string(ccx.tcx(), transmute_restriction.original_to), + to_type_size as uint, + if to_type_size == 1 {""} else {"s"}).as_slice()); + } } } ccx.sess().abort_if_errors(); diff --git a/src/test/compile-fail/transmute-different-sizes.rs b/src/test/compile-fail/transmute-different-sizes.rs index abdfe983e3a..5c61212a7f5 100644 --- a/src/test/compile-fail/transmute-different-sizes.rs +++ b/src/test/compile-fail/transmute-different-sizes.rs @@ -21,7 +21,7 @@ unsafe fn f() { unsafe fn g(x: &T) { let _: i8 = transmute(x); - //~^ ERROR transmute called on types with different sizes + //~^ ERROR transmute called on types with potentially different sizes } fn main() {} diff --git a/src/test/compile-fail/transmute-fat-pointers.rs b/src/test/compile-fail/transmute-fat-pointers.rs new file mode 100644 index 00000000000..5e81a4cec22 --- /dev/null +++ b/src/test/compile-fail/transmute-fat-pointers.rs @@ -0,0 +1,41 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that are conservative around thin/fat pointer mismatches. + +#![allow(dead_code)] + +use std::mem::transmute; + +fn a(x: &[T]) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn b(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn c(x: &T) -> &U { + unsafe { transmute(x) } +} + +fn d(x: &[T]) -> &[U] { + unsafe { transmute(x) } +} + +fn e(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn f(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn main() { } diff --git a/src/test/compile-fail/transmute-impl.rs b/src/test/compile-fail/transmute-impl.rs new file mode 100644 index 00000000000..8b5a8c679b2 --- /dev/null +++ b/src/test/compile-fail/transmute-impl.rs @@ -0,0 +1,33 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that are conservative around thin/fat pointer mismatches. + +#![allow(dead_code)] + +use std::mem::transmute; + +struct Foo { + t: Box +} + +impl Foo { + fn m(x: &T) -> &int where T : Sized { + // OK here, because T : Sized is in scope. + unsafe { transmute(x) } + } + + fn n(x: &T) -> &int { + // Not OK here, because T : Sized is not in scope. + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes + } +} + +fn main() { }