Rewrite the intrinsicck to take the parameter environment into account. Also fixes #20116.
This commit is contained in:
parent
4946e1a463
commit
c5edd22646
7 changed files with 354 additions and 162 deletions
|
@ -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<ty::ParameterEnvironment<'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>,
|
||||
}
|
||||
|
||||
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,U>() {
|
||||
// // T=int, U=int
|
||||
// }
|
||||
//
|
||||
// fn bar<Sized? T,U>() {
|
||||
// // T=int, U=int
|
||||
// // T=[int], U=int
|
||||
// }
|
||||
//
|
||||
// fn baz<Sized? T, Sized?U>() {
|
||||
// // 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<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.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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -487,6 +487,7 @@ impl<T> VecPerParamSpace<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub struct EnumeratedItems<'a,T:'a> {
|
||||
vec: &'a VecPerParamSpace<T>,
|
||||
space_index: uint,
|
||||
|
|
|
@ -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<NodeMap<Rc<ty::PolyTraitRef<'tcx>>>>;
|
||||
|
||||
/// 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<PolyTraitRef<'tcx>>])
|
||||
-> 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<PolyTraitRef<'tcx>>],
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ValueRef> {
|
||||
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();
|
||||
|
|
|
@ -21,7 +21,7 @@ unsafe fn f() {
|
|||
|
||||
unsafe fn g<T>(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() {}
|
||||
|
|
41
src/test/compile-fail/transmute-fat-pointers.rs
Normal file
41
src/test/compile-fail/transmute-fat-pointers.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// Tests that are conservative around thin/fat pointer mismatches.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
fn a<T, Sized? U>(x: &[T]) -> &U {
|
||||
unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes
|
||||
}
|
||||
|
||||
fn b<Sized? T, Sized? U>(x: &T) -> &U {
|
||||
unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes
|
||||
}
|
||||
|
||||
fn c<T, U>(x: &T) -> &U {
|
||||
unsafe { transmute(x) }
|
||||
}
|
||||
|
||||
fn d<T, U>(x: &[T]) -> &[U] {
|
||||
unsafe { transmute(x) }
|
||||
}
|
||||
|
||||
fn e<Sized? T, U>(x: &T) -> &U {
|
||||
unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes
|
||||
}
|
||||
|
||||
fn f<T, Sized? U>(x: &T) -> &U {
|
||||
unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes
|
||||
}
|
||||
|
||||
fn main() { }
|
33
src/test/compile-fail/transmute-impl.rs
Normal file
33
src/test/compile-fail/transmute-impl.rs
Normal file
|
@ -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 <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.
|
||||
|
||||
// Tests that are conservative around thin/fat pointer mismatches.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
struct Foo<Sized? T> {
|
||||
t: Box<T>
|
||||
}
|
||||
|
||||
impl<Sized? T> Foo<T> {
|
||||
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() { }
|
Loading…
Add table
Reference in a new issue