diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 446313f7037f3..536c739bf1615 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -71,7 +71,6 @@ pub enum DepNode { DeadCheck, StabilityCheck, LateLintCheck, - IntrinsicUseCheck, TransCrate, TransCrateItem(D), TransInlinedItem(D), @@ -169,7 +168,6 @@ impl DepNode { 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 34b98a3467780..e230836ef4515 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 767c5adc81e25..e84be7e45606b 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>, +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=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 = 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>, - 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 61c49475ac4cd..10000607b5409 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -31,6 +31,7 @@ use hir::FreevarMap; use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; +use ty::layout::{Layout, TargetDataLayout}; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -55,6 +56,7 @@ pub struct CtxtArenas<'tcx> { bare_fn: TypedArena>, region: TypedArena, stability: TypedArena, + layout: TypedArena, // references trait_defs: TypedArena>, @@ -69,6 +71,7 @@ impl<'tcx> CtxtArenas<'tcx> { bare_fn: TypedArena::new(), region: TypedArena::new(), stability: TypedArena::new(), + layout: TypedArena::new(), trait_defs: TypedArena::new(), adt_defs: TypedArena::new() @@ -229,6 +232,7 @@ pub struct TyCtxt<'tcx> { bare_fn_interner: RefCell, &'tcx BareFnTy<'tcx>>>, region_interner: RefCell>, stability_interner: RefCell>, + layout_interner: RefCell>, pub dep_graph: DepGraph, @@ -353,11 +357,6 @@ pub struct TyCtxt<'tcx> { pub node_lint_levels: RefCell>, - /// 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>>, - /// Maps any item's def-id to its stability index. pub stability: RefCell>, @@ -419,6 +418,12 @@ pub struct TyCtxt<'tcx> { /// The definite name of the current crate after taking into account /// attributes, commandline parameters, etc. pub crate_name: token::InternedString, + + /// Data layout specification for the current target. + pub data_layout: TargetDataLayout, + + /// Cache for layouts computed from types. + pub layout_cache: RefCell, &'tcx Layout>>, } impl<'tcx> TyCtxt<'tcx> { @@ -500,6 +505,20 @@ impl<'tcx> TyCtxt<'tcx> { interned } + pub fn intern_layout(&self, layout: Layout) -> &'tcx Layout { + if let Some(layout) = self.layout_interner.borrow().get(&layout) { + return layout; + } + + let interned = self.arenas.layout.alloc(layout); + if let Some(prev) = self.layout_interner + .borrow_mut() + .insert(interned, interned) { + bug!("Tried to overwrite interned Layout: {:?}", prev) + } + interned + } + pub fn store_free_region_map(&self, id: NodeId, map: FreeRegionMap) { if self.free_region_maps.borrow_mut().insert(id, map).is_some() { bug!("Tried to overwrite interned FreeRegionMap for NodeId {:?}", id) @@ -531,6 +550,7 @@ impl<'tcx> TyCtxt<'tcx> { f: F) -> R where F: FnOnce(&TyCtxt<'tcx>) -> R { + let data_layout = TargetDataLayout::parse(s); let interner = RefCell::new(FnvHashMap()); let common_types = CommonTypes::new(&arenas.type_, &interner); let dep_graph = map.dep_graph.clone(); @@ -542,6 +562,7 @@ impl<'tcx> TyCtxt<'tcx> { bare_fn_interner: RefCell::new(FnvHashMap()), region_interner: RefCell::new(FnvHashMap()), stability_interner: RefCell::new(FnvHashMap()), + layout_interner: RefCell::new(FnvHashMap()), dep_graph: dep_graph.clone(), types: common_types, named_region_map: named_region_map, @@ -579,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(), @@ -589,6 +609,8 @@ impl<'tcx> TyCtxt<'tcx> { cast_kinds: RefCell::new(NodeMap()), fragment_infos: RefCell::new(DefIdMap()), crate_name: token::intern_and_get_ident(crate_name), + data_layout: data_layout, + layout_cache: RefCell::new(FnvHashMap()), }, f) } } @@ -762,6 +784,7 @@ impl<'tcx> TyCtxt<'tcx> { println!("BareFnTy interner: #{}", self.bare_fn_interner.borrow().len()); println!("Region interner: #{}", self.region_interner.borrow().len()); println!("Stability interner: #{}", self.stability_interner.borrow().len()); + println!("Layout interner: #{}", self.layout_interner.borrow().len()); } } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs new file mode 100644 index 0000000000000..3ea691b4dc766 --- /dev/null +++ b/src/librustc/ty/layout.rs @@ -0,0 +1,1336 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::Integer::*; +pub use self::Layout::*; +pub use self::Primitive::*; + +use infer::{InferCtxt, drain_fulfillment_cx_or_panic}; +use session::Session; +use traits; +use ty::{self, Ty, TyCtxt, TypeFoldable}; + +use syntax::ast::{FloatTy, IntTy, UintTy}; +use syntax::attr; +use syntax::codemap::DUMMY_SP; + +use std::cmp; +use std::fmt; +use std::i64; + +/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) +/// for a target, which contains everything needed to compute layouts. +pub struct TargetDataLayout { + pub endian: Endian, + pub i1_align: Align, + pub i8_align: Align, + pub i16_align: Align, + pub i32_align: Align, + pub i64_align: Align, + pub f32_align: Align, + pub f64_align: Align, + pub pointer_size: Size, + pub pointer_align: Align, + pub aggregate_align: Align, + + /// Alignments for vector types. + pub vector_align: Vec<(Size, Align)> +} + +impl Default for TargetDataLayout { + fn default() -> TargetDataLayout { + TargetDataLayout { + endian: Endian::Big, + i1_align: Align::from_bits(8, 8).unwrap(), + i8_align: Align::from_bits(8, 8).unwrap(), + i16_align: Align::from_bits(16, 16).unwrap(), + i32_align: Align::from_bits(32, 32).unwrap(), + i64_align: Align::from_bits(32, 64).unwrap(), + f32_align: Align::from_bits(32, 32).unwrap(), + f64_align: Align::from_bits(64, 64).unwrap(), + pointer_size: Size::from_bits(64), + pointer_align: Align::from_bits(64, 64).unwrap(), + aggregate_align: Align::from_bits(0, 64).unwrap(), + vector_align: vec![ + (Size::from_bits(64), Align::from_bits(64, 64).unwrap()), + (Size::from_bits(128), Align::from_bits(128, 128).unwrap()) + ] + } + } +} + +impl TargetDataLayout { + pub fn parse(sess: &Session) -> TargetDataLayout { + // Parse a bit count from a string. + let parse_bits = |s: &str, kind: &str, cause: &str| { + s.parse::().unwrap_or_else(|err| { + sess.err(&format!("invalid {} `{}` for `{}` in \"data-layout\": {}", + kind, s, cause, err)); + 0 + }) + }; + + // Parse a size string. + let size = |s: &str, cause: &str| { + Size::from_bits(parse_bits(s, "size", cause)) + }; + + // Parse an alignment string. + let align = |s: &[&str], cause: &str| { + if s.is_empty() { + sess.err(&format!("missing alignment for `{}` in \"data-layout\"", cause)); + } + let abi = parse_bits(s[0], "alignment", cause); + let pref = s.get(1).map_or(abi, |pref| parse_bits(pref, "alignment", cause)); + Align::from_bits(abi, pref).unwrap_or_else(|err| { + sess.err(&format!("invalid alignment for `{}` in \"data-layout\": {}", + cause, err)); + Align::from_bits(8, 8).unwrap() + }) + }; + + let mut dl = TargetDataLayout::default(); + for spec in sess.target.target.data_layout.split("-") { + match &spec.split(":").collect::>()[..] { + ["e"] => dl.endian = Endian::Little, + ["E"] => dl.endian = Endian::Big, + ["a", a..] => dl.aggregate_align = align(a, "a"), + ["f32", a..] => dl.f32_align = align(a, "f32"), + ["f64", a..] => dl.f64_align = align(a, "f64"), + [p @ "p", s, a..] | [p @ "p0", s, a..] => { + dl.pointer_size = size(s, p); + dl.pointer_align = align(a, p); + } + [s, a..] if s.starts_with("i") => { + let ty_align = match s[1..].parse::() { + Ok(1) => &mut dl.i8_align, + Ok(8) => &mut dl.i8_align, + Ok(16) => &mut dl.i16_align, + Ok(32) => &mut dl.i32_align, + Ok(64) => &mut dl.i64_align, + Ok(_) => continue, + Err(_) => { + size(&s[1..], "i"); // For the user error. + continue; + } + }; + *ty_align = align(a, s); + } + [s, a..] if s.starts_with("v") => { + let v_size = size(&s[1..], "v"); + let a = align(a, s); + if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { + v.1 = a; + continue; + } + // No existing entry, add a new one. + dl.vector_align.push((v_size, a)); + } + _ => {} // Ignore everything else. + } + } + + // Perform consistency checks against the Target information. + let endian_str = match dl.endian { + Endian::Little => "little", + Endian::Big => "big" + }; + if endian_str != sess.target.target.target_endian { + sess.err(&format!("inconsistent target specification: \"data-layout\" claims \ + architecture is {}-endian, while \"target-endian\" is `{}`", + endian_str, sess.target.target.target_endian)); + } + + if dl.pointer_size.bits().to_string() != sess.target.target.target_pointer_width { + sess.err(&format!("inconsistent target specification: \"data-layout\" claims \ + pointers are {}-bit, while \"target-pointer-width\" is `{}`", + dl.pointer_size.bits(), sess.target.target.target_pointer_width)); + } + + dl + } + + /// Return exclusive upper bound on object size. + /// + /// The theoretical maximum object size is defined as the maximum positive `isize` value. + /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly + /// index every address within an object along with one byte past the end, along with allowing + /// `isize` to store the difference between any two pointers into an object. + /// + /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer + /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is + /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable + /// address space on 64-bit ARMv8 and x86_64. + pub fn obj_size_bound(&self) -> u64 { + match self.pointer_size.bits() { + 32 => 1 << 31, + 64 => 1 << 47, + bits => bug!("obj_size_bound: unknown pointer bit size {}", bits) + } + } + + pub fn ptr_sized_integer(&self) -> Integer { + match self.pointer_size.bits() { + 32 => I32, + 64 => I64, + bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits) + } + } +} + +/// Endianness of the target, which must match cfg(target-endian). +#[derive(Copy, Clone)] +pub enum Endian { + Little, + Big +} + +/// Size of a type in bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Size { + raw: u64 +} + +impl Size { + pub fn from_bits(bits: u64) -> Size { + Size::from_bytes((bits + 7) / 8) + } + + pub fn from_bytes(bytes: u64) -> Size { + if bytes >= (1 << 61) { + bug!("Size::from_bytes: {} bytes in bits doesn't fit in u64", bytes) + } + Size { + raw: bytes + } + } + + pub fn bytes(self) -> u64 { + self.raw + } + + pub fn bits(self) -> u64 { + self.bytes() * 8 + } + + pub fn abi_align(self, align: Align) -> Size { + let mask = align.abi() - 1; + Size::from_bytes((self.bytes() + mask) & !mask) + } + + pub fn checked_add(self, offset: Size, dl: &TargetDataLayout) -> Option { + // Each Size is less than dl.obj_size_bound(), so the sum is + // also less than 1 << 62 (and therefore can't overflow). + let bytes = self.bytes() + offset.bytes(); + + if bytes < dl.obj_size_bound() { + Some(Size::from_bytes(bytes)) + } else { + None + } + } + + pub fn checked_mul(self, count: u64, dl: &TargetDataLayout) -> Option { + // Each Size is less than dl.obj_size_bound(), so the sum is + // also less than 1 << 62 (and therefore can't overflow). + match self.bytes().checked_mul(count) { + Some(bytes) if bytes < dl.obj_size_bound() => { + Some(Size::from_bytes(bytes)) + } + _ => None + } + } +} + +/// Alignment of a type in bytes, both ABI-mandated and preferred. +/// Since alignments are always powers of 2, we can pack both in one byte, +/// giving each a nibble (4 bits) for a maximum alignment of 2^15 = 32768. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Align { + raw: u8 +} + +impl Align { + pub fn from_bits(abi: u64, pref: u64) -> Result { + Align::from_bytes((abi + 7) / 8, (pref + 7) / 8) + } + + pub fn from_bytes(abi: u64, pref: u64) -> Result { + let pack = |align: u64| { + // Treat an alignment of 0 bytes like 1-byte alignment. + if align == 0 { + return Ok(0); + } + + let mut bytes = align; + let mut pow: u8 = 0; + while (bytes & 1) == 0 { + pow += 1; + bytes >>= 1; + } + if bytes != 1 { + Err(format!("`{}` is not a power of 2", align)) + } else if pow > 0x0f { + Err(format!("`{}` is too large", align)) + } else { + Ok(pow) + } + }; + + Ok(Align { + raw: pack(abi)? | (pack(pref)? << 4) + }) + } + + pub fn abi(self) -> u64 { + 1 << (self.raw & 0xf) + } + + pub fn pref(self) -> u64 { + 1 << (self.raw >> 4) + } + + pub fn min(self, other: Align) -> Align { + let abi = cmp::min(self.raw & 0x0f, other.raw & 0x0f); + let pref = cmp::min(self.raw & 0xf0, other.raw & 0xf0); + Align { + raw: abi | pref + } + } + + pub fn max(self, other: Align) -> Align { + let abi = cmp::max(self.raw & 0x0f, other.raw & 0x0f); + let pref = cmp::max(self.raw & 0xf0, other.raw & 0xf0); + Align { + raw: abi | pref + } + } +} + +/// Integers, also used for enum discriminants. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Integer { + I1, + I8, + I16, + I32, + I64 +} + +impl Integer { + /// Find the smallest Integer type which can represent the signed value. + pub fn fit_signed(x: i64) -> Integer { + match x { + -0x0000_0001...0x0000_0000 => I1, + -0x0000_0080...0x0000_007f => I8, + -0x0000_8000...0x0000_7fff => I16, + -0x8000_0000...0x7fff_ffff => I32, + _ => I64 + } + } + + /// Find the smallest Integer type which can represent the unsigned value. + pub fn fit_unsigned(x: u64) -> Integer { + match x { + 0...0x0000_0001 => I1, + 0...0x0000_00ff => I8, + 0...0x0000_ffff => I16, + 0...0xffff_ffff => I32, + _ => I64 + } + } + + /// Get the Integer type from an attr::IntType. + pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer { + match ity { + attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, + attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, + attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, + attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, + attr::SignedInt(IntTy::Is) | attr::UnsignedInt(UintTy::Us) => { + dl.ptr_sized_integer() + } + } + } + + /// Find the appropriate Integer type and signedness for the given + /// signed discriminant range and #[repr] attribute. + /// N.B.: u64 values above i64::MAX will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + pub fn repr_discr(tcx: &TyCtxt, hint: attr::ReprAttr, min: i64, max: i64) + -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u64 + // which can fit all i64 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64)); + let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + let at_least = match hint { + attr::ReprInt(span, ity) => { + let discr = Integer::from_attr(&tcx.data_layout, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + span_bug!(span, "representation hint insufficient for discriminant range") + } + return (discr, ity.is_signed()); + } + attr::ReprExtern => { + match &tcx.sess.target.target.arch[..] { + // WARNING: the ARM EABI has two variants; the one corresponding + // to `at_least == I32` appears to be used on Linux and NetBSD, + // but some systems may use the variant corresponding to no + // lower bound. However, we don't run on those yet...? + "arm" => I32, + _ => I32, + } + } + attr::ReprAny => I8, + attr::ReprPacked => { + bug!("Integer::repr_discr: found #[repr(packed)] on an enum"); + } + attr::ReprSimd => { + bug!("Integer::repr_discr: found #[repr(simd)] on an enum"); + } + }; + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (cmp::max(unsigned_fit, at_least), false) + } else { + (cmp::max(signed_fit, at_least), true) + } + } +} + +/// Fundamental unit of memory access and layout. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Primitive { + Int(Integer), + F32, + F64, + Pointer +} + +impl Primitive { + pub fn size(self, dl: &TargetDataLayout) -> Size { + match self { + Int(I1) | Int(I8) => Size::from_bits(8), + Int(I16) => Size::from_bits(16), + Int(I32) | F32 => Size::from_bits(32), + Int(I64) | F64 => Size::from_bits(64), + Pointer => dl.pointer_size + } + } + + pub fn align(self, dl: &TargetDataLayout) -> Align { + match self { + Int(I1) => dl.i1_align, + Int(I8) => dl.i8_align, + Int(I16) => dl.i16_align, + Int(I32) => dl.i32_align, + Int(I64) => dl.i64_align, + F32 => dl.f32_align, + F64 => dl.f64_align, + Pointer => dl.pointer_align + } + } +} + +/// Path through fields of nested structures. +// FIXME(eddyb) use small vector optimization for the common case. +pub type FieldPath = Vec; + +/// A structure, a product type in ADT terms. +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct Struct { + pub align: Align, + + /// If true, no alignment padding is used. + pub packed: bool, + + /// If true, the size is exact, otherwise it's only a lower bound. + pub sized: bool, + + /// Offsets for the first byte after each field. + /// That is, field_offset(i) = offset_after_field[i - 1] and the + /// whole structure's size is the last offset, excluding padding. + // FIXME(eddyb) use small vector optimization for the common case. + pub offset_after_field: Vec +} + +impl Struct { + pub fn new(dl: &TargetDataLayout, packed: bool) -> Struct { + Struct { + align: if packed { dl.i8_align } else { dl.aggregate_align }, + packed: packed, + sized: true, + offset_after_field: vec![] + } + } + + /// Extend the Struct with more fields. + pub fn extend<'a, 'tcx, I>(&mut self, dl: &TargetDataLayout, + fields: I, + scapegoat: Ty<'tcx>) + -> Result<(), LayoutError<'tcx>> + where I: Iterator>> { + self.offset_after_field.reserve(fields.size_hint().0); + + for field in fields { + if !self.sized { + bug!("Struct::compute: field #{} of `{}` comes after unsized field", + self.offset_after_field.len(), scapegoat); + } + + let field = field?; + if field.is_unsized() { + self.sized = false; + } + + // Invariant: offset < dl.obj_size_bound() <= 1<<61 + let mut offset = if !self.packed { + let align = field.align(dl); + self.align = self.align.max(align); + self.offset_after_field.last_mut().map_or(Size::from_bytes(0), |last| { + *last = last.abi_align(align); + *last + }) + } else { + self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last) + }; + + offset = offset.checked_add(field.size(dl), dl) + .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; + + self.offset_after_field.push(offset); + } + + Ok(()) + } + + /// Get the size without trailing alignment padding. + pub fn min_size(&self) -> Size { + self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last) + } + + /// Get the size with trailing aligment padding. + pub fn stride(&self) -> Size { + self.min_size().abi_align(self.align) + } + + /// Determine whether a structure would be zero-sized, given its fields. + pub fn would_be_zero_sized<'a, 'tcx, I>(dl: &TargetDataLayout, fields: I) + -> Result> + where I: Iterator>> { + for field in fields { + let field = field?; + if field.is_unsized() || field.size(dl).bytes() > 0 { + return Ok(false); + } + } + Ok(true) + } + + /// Find the path leading to a non-zero leaf field, starting from + /// the given type and recursing through aggregates. + // FIXME(eddyb) track value ranges and traverse already optimized enums. + pub fn non_zero_field_in_type<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + ty: Ty<'tcx>) + -> Result, LayoutError<'tcx>> { + let tcx = infcx.tcx; + match (ty.layout(infcx)?, &ty.sty) { + (&Scalar { non_zero: true, .. }, _) => Ok(Some(vec![])), + (&FatPointer { non_zero: true, .. }, _) => { + Ok(Some(vec![FAT_PTR_ADDR as u32])) + } + + // Is this the NonZero lang item wrapping a pointer or integer type? + (&Univariant { non_zero: true, .. }, &ty::TyStruct(def, substs)) => { + let fields = &def.struct_variant().fields; + assert_eq!(fields.len(), 1); + let ty = normalize_associated_type(infcx, fields[0].ty(tcx, substs)); + match *ty.layout(infcx)? { + // FIXME(eddyb) also allow floating-point types here. + Scalar { value: Int(_), non_zero: false } | + Scalar { value: Pointer, non_zero: false } => { + Ok(Some(vec![0])) + } + FatPointer { non_zero: false, .. } => { + Ok(Some(vec![FAT_PTR_ADDR as u32, 0])) + } + _ => Ok(None) + } + } + + // Perhaps one of the fields of this struct is non-zero + // let's recurse and find out + (_, &ty::TyStruct(def, substs)) => { + Struct::non_zero_field_path(infcx, def.struct_variant().fields + .iter().map(|field| { + normalize_associated_type(infcx, field.ty(tcx, substs)) + })) + } + + // Perhaps one of the upvars of this closure is non-zero + // Let's recurse and find out! + (_, &ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. })) | + // Can we use one of the fields in this tuple? + (_, &ty::TyTuple(ref tys)) => { + Struct::non_zero_field_path(infcx, tys.iter().cloned()) + } + + // Is this a fixed-size array of something non-zero + // with at least one element? + (_, &ty::TyArray(ety, d)) if d > 0 => { + Struct::non_zero_field_path(infcx, Some(ety).into_iter()) + } + + // Anything else is not a non-zero type. + _ => Ok(None) + } + } + + /// Find the path leading to a non-zero leaf field, starting from + /// the given set of fields and recursing through aggregates. + pub fn non_zero_field_path<'a, 'tcx, I>(infcx: &InferCtxt<'a, 'tcx>, + fields: I) + -> Result, LayoutError<'tcx>> + where I: Iterator> { + for (i, ty) in fields.enumerate() { + if let Some(mut path) = Struct::non_zero_field_in_type(infcx, ty)? { + path.push(i as u32); + return Ok(Some(path)); + } + } + Ok(None) + } +} + +/// The first half of a fat pointer. +/// - For a trait object, this is the address of the box. +/// - For a slice, this is the base address. +pub const FAT_PTR_ADDR: usize = 0; + +/// The second half of a fat pointer. +/// - For a trait object, this is the address of the vtable. +/// - For a slice, this is the length. +pub const FAT_PTR_EXTRA: usize = 1; + +/// Type layout, from which size and alignment can be cheaply computed. +/// For ADTs, it also includes field placement and enum optimizations. +/// NOTE: Because Layout is interned, redundant information should be +/// kept to a minimum, e.g. it includes no sub-component Ty or Layout. +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum Layout { + /// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr. + Scalar { + value: Primitive, + // If true, the value cannot represent a bit pattern of all zeroes. + non_zero: bool + }, + + /// SIMD vectors, from TyStruct marked with #[repr(simd)]. + Vector { + element: Primitive, + count: u64 + }, + + /// TyArray, TySlice or TyStr. + Array { + /// If true, the size is exact, otherwise it's only a lower bound. + sized: bool, + align: Align, + size: Size + }, + + /// TyRawPtr or TyRef with a !Sized pointee. + FatPointer { + metadata: Primitive, + // If true, the pointer cannot be null. + non_zero: bool + }, + + // Remaining variants are all ADTs such as TyStruct, TyEnum or TyTuple. + + /// C-like enums; basically an integer. + CEnum { + discr: Integer, + signed: bool, + // Inclusive discriminant range. + // If min > max, it represents min...u64::MAX followed by 0...max. + // FIXME(eddyb) always use the shortest range, e.g. by finding + // the largest space between two consecutive discriminants and + // taking everything else as the (shortest) discriminant range. + min: u64, + max: u64 + }, + + /// Single-case enums, and structs/tuples. + Univariant { + variant: Struct, + // If true, the structure is NonZero. + // FIXME(eddyb) use a newtype Layout kind for this. + non_zero: bool + }, + + /// General-case enums: for each case there is a struct, and they + /// all start with a field for the discriminant. + General { + discr: Integer, + variants: Vec, + size: Size, + align: Align + }, + + /// Two cases distinguished by a nullable pointer: the case with discriminant + /// `nndiscr` must have single field which is known to be nonnull due to its type. + /// The other case is known to be zero sized. Hence we represent the enum + /// as simply a nullable pointer: if not null it indicates the `nndiscr` variant, + /// otherwise it indicates the other case. + /// + /// For example, `std::option::Option` instantiated at a safe pointer type + /// is represented such that `None` is a null pointer and `Some` is the + /// identity function. + RawNullablePointer { + nndiscr: u64, + value: Primitive + }, + + /// Two cases distinguished by a nullable pointer: the case with discriminant + /// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th + /// field is known to be nonnull due to its type; if that field is null, then + /// it represents the other case, which is known to be zero sized. + StructWrappedNullablePointer { + nndiscr: u64, + nonnull: Struct, + // N.B. There is a 0 at the start, for LLVM GEP through a pointer. + discrfield: FieldPath + } +} + +#[derive(Copy, Clone, Debug)] +pub enum LayoutError<'tcx> { + Unknown(Ty<'tcx>), + SizeOverflow(Ty<'tcx>) +} + +impl<'tcx> fmt::Display for LayoutError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LayoutError::Unknown(ty) => { + write!(f, "the type `{:?}` has an unknown layout", ty) + } + LayoutError::SizeOverflow(ty) => { + write!(f, "the type `{:?}` is too big for the current architecture", ty) + } + } + } +} + +/// Helper function for normalizing associated types in an inference context. +fn normalize_associated_type<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, + ty: Ty<'tcx>) + -> Ty<'tcx> { + if !ty.has_projection_types() { + return ty; + } + + let mut selcx = traits::SelectionContext::new(infcx); + let cause = traits::ObligationCause::dummy(); + let traits::Normalized { value: result, obligations } = + traits::normalize(&mut selcx, cause, &ty); + + let mut fulfill_cx = traits::FulfillmentContext::new(); + + for obligation in obligations { + fulfill_cx.register_predicate_obligation(infcx, obligation); + } + + drain_fulfillment_cx_or_panic(DUMMY_SP, infcx, &mut fulfill_cx, &result) +} + +impl Layout { + pub fn compute_uncached<'a, 'tcx>(ty: Ty<'tcx>, + infcx: &InferCtxt<'a, 'tcx>) + -> Result> { + let tcx = infcx.tcx; + let dl = &tcx.data_layout; + assert!(!ty.has_infer_types()); + + let layout = match ty.sty { + // Basic scalars. + ty::TyBool => Scalar { value: Int(I1), non_zero: false }, + ty::TyChar => Scalar { value: Int(I32), non_zero: false }, + ty::TyInt(ity) => { + Scalar { + value: Int(Integer::from_attr(dl, attr::SignedInt(ity))), + non_zero: false + } + } + ty::TyUint(ity) => { + Scalar { + value: Int(Integer::from_attr(dl, attr::UnsignedInt(ity))), + non_zero: false + } + } + ty::TyFloat(FloatTy::F32) => Scalar { value: F32, non_zero: false }, + ty::TyFloat(FloatTy::F64) => Scalar { value: F64, non_zero: false }, + ty::TyFnPtr(_) => Scalar { value: Pointer, non_zero: true }, + + // Potentially-fat pointers. + ty::TyBox(pointee) | + ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let non_zero = !ty.is_unsafe_ptr(); + if pointee.is_sized(&infcx.parameter_environment, DUMMY_SP) { + Scalar { value: Pointer, non_zero: non_zero } + } else { + let unsized_part = tcx.struct_tail(pointee); + let meta = match unsized_part.sty { + ty::TySlice(_) | ty::TyStr => { + Int(dl.ptr_sized_integer()) + } + ty::TyTrait(_) => Pointer, + _ => return Err(LayoutError::Unknown(unsized_part)) + }; + FatPointer { metadata: meta, non_zero: non_zero } + } + } + + // Arrays and slices. + ty::TyArray(element, count) => { + let element = element.layout(infcx)?; + Array { + sized: true, + align: element.align(dl), + size: element.size(dl).checked_mul(count as u64, dl) + .map_or(Err(LayoutError::SizeOverflow(ty)), Ok)? + } + } + ty::TySlice(element) => { + Array { + sized: false, + align: element.layout(infcx)?.align(dl), + size: Size::from_bytes(0) + } + } + ty::TyStr => { + Array { + sized: false, + align: dl.i8_align, + size: Size::from_bytes(0) + } + } + + // Odd unit types. + ty::TyFnDef(..) => { + Univariant { + variant: Struct::new(dl, false), + non_zero: false + } + } + ty::TyTrait(_) => { + let mut unit = Struct::new(dl, false); + unit.sized = false; + Univariant { variant: unit, non_zero: false } + } + + // Tuples. + ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. }) | + ty::TyTuple(ref tys) => { + let mut st = Struct::new(dl, false); + st.extend(dl, tys.iter().map(|ty| ty.layout(infcx)), ty)?; + Univariant { variant: st, non_zero: false } + } + + // ADTs. + ty::TyStruct(def, substs) => { + if ty.is_simd() { + // SIMD vector types. + let element = ty.simd_type(tcx); + match *element.layout(infcx)? { + Scalar { value, .. } => { + return Ok(Vector { + element: value, + count: ty.simd_size(tcx) as u64 + }); + } + _ => { + tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \ + a non-machine element type `{}`", + ty, element)); + } + } + } + let fields = def.struct_variant().fields.iter().map(|field| { + normalize_associated_type(infcx, field.ty(tcx, substs)) + .layout(infcx) + }); + let packed = tcx.lookup_packed(def.did); + let mut st = Struct::new(dl, packed); + st.extend(dl, fields, ty)?; + + // FIXME(16758) don't add a drop flag to unsized structs, as it + // won't actually be in the location we say it is because it'll be after + // the unsized field. Several other pieces of code assume that the unsized + // field is definitely the last one. + if def.dtor_kind().has_drop_flag() && + ty.is_sized(&infcx.parameter_environment, DUMMY_SP) { + st.extend(dl, Some(Ok(&Scalar { + value: Int(I8), + non_zero: false + })).into_iter(), ty)?; + } + Univariant { + variant: st, + non_zero: Some(def.did) == tcx.lang_items.non_zero() + } + } + ty::TyEnum(def, substs) => { + let hint = *tcx.lookup_repr_hints(def.did).get(0) + .unwrap_or(&attr::ReprAny); + + let dtor = def.dtor_kind().has_drop_flag(); + let drop_flag = if dtor { + Some(Scalar { value: Int(I8), non_zero: false }) + } else { + None + }; + + if def.variants.is_empty() { + // Uninhabitable; represent as unit + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); + + let mut st = Struct::new(dl, false); + st.extend(dl, drop_flag.iter().map(Ok), ty)?; + return Ok(Univariant { variant: st, non_zero: false }); + } + + if !dtor && def.variants.iter().all(|v| v.fields.is_empty()) { + // All bodies empty -> intlike + let (mut min, mut max) = (i64::MAX, i64::MIN); + for v in &def.variants { + let x = v.disr_val.to_u64_unchecked() as i64; + if x < min { min = x; } + if x > max { max = x; } + } + + let (discr, signed) = Integer::repr_discr(tcx, hint, min, max); + return Ok(CEnum { + discr: discr, + signed: signed, + min: min as u64, + max: max as u64 + }); + } + + // Since there's at least one + // non-empty body, explicit discriminants should have + // been rejected by a checker before this point. + for (i, v) in def.variants.iter().enumerate() { + if i as u64 != v.disr_val.to_u64_unchecked() { + bug!("non-C-like enum {} with specified discriminants", + tcx.item_path_str(def.did)); + } + } + + if def.variants.len() == 1 { + // Equivalent to a struct/tuple/newtype. + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); + let fields = def.variants[0].fields.iter().map(|field| { + normalize_associated_type(infcx, field.ty(tcx, substs)) + .layout(infcx) + }); + let mut st = Struct::new(dl, false); + st.extend(dl, fields.chain(drop_flag.iter().map(Ok)), ty)?; + return Ok(Univariant { variant: st, non_zero: false }); + } + + // Cache the substituted and normalized variant field types. + let variants = def.variants.iter().map(|v| { + v.fields.iter().map(|field| { + normalize_associated_type(infcx, field.ty(tcx, substs)) + }).collect::>() + }).collect::>(); + + if !dtor && variants.len() == 2 && hint == attr::ReprAny { + // Nullable pointer optimization + for discr in 0..2 { + let other_fields = variants[1 - discr].iter().map(|ty| { + ty.layout(infcx) + }); + if !Struct::would_be_zero_sized(dl, other_fields)? { + continue; + } + let path = Struct::non_zero_field_path(infcx, + variants[discr].iter().cloned())?; + let mut path = if let Some(p) = path { p } else { continue }; + + // FIXME(eddyb) should take advantage of a newtype. + if path == &[0] && variants[discr].len() == 1 { + match *variants[discr][0].layout(infcx)? { + Scalar { value, .. } => { + return Ok(RawNullablePointer { + nndiscr: discr as u64, + value: value + }); + } + _ => { + bug!("Layout::compute: `{}`'s non-zero \ + `{}` field not scalar?!", + ty, variants[discr][0]) + } + } + } + + path.push(0); // For GEP through a pointer. + path.reverse(); + let mut st = Struct::new(dl, false); + st.extend(dl, variants[discr].iter().map(|ty| { + ty.layout(infcx) + }), ty)?; + return Ok(StructWrappedNullablePointer { + nndiscr: discr as u64, + nonnull: st, + discrfield: path + }); + } + } + + // The general case. + let discr_max = (variants.len() - 1) as i64; + assert!(discr_max >= 0); + let (min_ity, _) = Integer::repr_discr(tcx, hint, 0, discr_max); + + let mut align = dl.aggregate_align; + let mut size = Size::from_bytes(0); + + // We're interested in the smallest alignment, so start large. + let mut start_align = Align::from_bytes(256, 256).unwrap(); + + // Create the set of structs that represent each variant + // Use the minimum integer type we figured out above + let discr = Some(Scalar { value: Int(min_ity), non_zero: false }); + let mut variants = variants.into_iter().map(|fields| { + let mut found_start = false; + let fields = fields.into_iter().map(|field| { + let field = field.layout(infcx)?; + if !found_start { + // Find the first field we can't move later + // to make room for a larger discriminant. + let field_align = field.align(dl); + if field.size(dl).bytes() != 0 || field_align.abi() != 1 { + start_align = start_align.min(field_align); + found_start = true; + } + } + Ok(field) + }); + let mut st = Struct::new(dl, false); + st.extend(dl, discr.iter().map(Ok).chain(fields) + .chain(drop_flag.iter().map(Ok)), ty)?; + size = cmp::max(size, st.min_size()); + align = align.max(st.align); + Ok(st) + }).collect::, _>>()?; + + // Align the maximum variant size to the largest alignment. + size = size.abi_align(align); + + if size.bytes() >= dl.obj_size_bound() { + return Err(LayoutError::SizeOverflow(ty)); + } + + // Check to see if we should use a different type for the + // discriminant. We can safely use a type with the same size + // as the alignment of the first field of each variant. + // We increase the size of the discriminant to avoid LLVM copying + // padding when it doesn't need to. This normally causes unaligned + // load/stores and excessive memcpy/memset operations. By using a + // bigger integer size, LLVM can be sure about it's contents and + // won't be so conservative. + + // Use the initial field alignment + let wanted = start_align.abi(); + let mut ity = min_ity; + for &candidate in &[I16, I32, I64] { + let ty = Int(candidate); + if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() { + ity = candidate; + break; + } + } + + // FIXME(eddyb) conservative only to avoid diverging from trans::adt. + if align.abi() != start_align.abi() { + ity = min_ity; + } + + // If the alignment is not larger than the chosen discriminant size, + // don't use the alignment as the final size. + if ity <= min_ity { + ity = min_ity; + } else { + // Patch up the variants' first few fields. + let old_ity_size = Int(min_ity).size(dl); + let new_ity_size = Int(ity).size(dl); + for variant in &mut variants { + for offset in &mut variant.offset_after_field { + if *offset > old_ity_size { + break; + } + *offset = new_ity_size; + } + } + } + + General { + discr: ity, + variants: variants, + size: size, + align: align + } + } + + // Types with no meaningful known layout. + ty::TyProjection(_) | ty::TyParam(_) => { + return Err(LayoutError::Unknown(ty)); + } + ty::TyInfer(_) | ty::TyError => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + }; + + Ok(layout) + } + + /// Returns true if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + match *self { + Scalar {..} | Vector {..} | FatPointer {..} | + CEnum {..} | General {..} | + RawNullablePointer {..} | + StructWrappedNullablePointer {..} => false, + + Array { sized, .. } | + Univariant { variant: Struct { sized, .. }, .. } => !sized + } + } + + pub fn size(&self, dl: &TargetDataLayout) -> Size { + match *self { + Scalar { value, .. } | RawNullablePointer { value, .. } => { + value.size(dl) + } + + Vector { element, count } => { + let elem_size = element.size(dl); + let vec_size = match elem_size.checked_mul(count, dl) { + Some(size) => size, + None => bug!("Layout::size({:?}): {} * {} overflowed", + self, elem_size.bytes(), count) + }; + vec_size.abi_align(self.align(dl)) + } + + FatPointer { metadata, .. } => { + // Effectively a (ptr, meta) tuple. + Pointer.size(dl).abi_align(metadata.align(dl)) + .checked_add(metadata.size(dl), dl).unwrap() + .abi_align(self.align(dl)) + } + + CEnum { discr, .. } => Int(discr).size(dl), + Array { size, .. } | General { size, .. } => size, + + Univariant { ref variant, .. } | + StructWrappedNullablePointer { nonnull: ref variant, .. } => { + variant.stride() + } + } + } + + pub fn align(&self, dl: &TargetDataLayout) -> Align { + match *self { + Scalar { value, .. } | RawNullablePointer { value, .. } => { + value.align(dl) + } + + Vector { element, count } => { + let elem_size = element.size(dl); + let vec_size = match elem_size.checked_mul(count, dl) { + Some(size) => size, + None => bug!("Layout::align({:?}): {} * {} overflowed", + self, elem_size.bytes(), count) + }; + for &(size, align) in &dl.vector_align { + if size == vec_size { + return align; + } + } + // Default to natural alignment, which is what LLVM does. + // That is, use the size, rounded up to a power of 2. + let align = vec_size.bytes().next_power_of_two(); + Align::from_bytes(align, align).unwrap() + } + + FatPointer { metadata, .. } => { + // Effectively a (ptr, meta) tuple. + Pointer.align(dl).max(metadata.align(dl)) + } + + CEnum { discr, .. } => Int(discr).align(dl), + Array { align, .. } | General { align, .. } => align, + + Univariant { ref variant, .. } | + StructWrappedNullablePointer { nonnull: ref variant, .. } => { + variant.align + } + } + } +} + +/// 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, 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 dda5f699bae94..238856197feed 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -84,6 +84,7 @@ pub mod error; pub mod fast_reject; pub mod fold; pub mod item_path; +pub mod layout; pub mod _match; pub mod maps; pub mod outlives; @@ -479,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, diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 2251794def497..2e4f37f1cc1d4 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -18,6 +18,7 @@ use hir::pat_util; use traits::{self, ProjectionMode}; use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable}; use ty::{Disr, ParameterEnvironment}; +use ty::layout::{Layout, LayoutError}; use ty::TypeVariants::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize}; @@ -597,6 +598,24 @@ impl<'tcx> ty::TyS<'tcx> { result } + #[inline] + pub fn layout<'a>(&'tcx self, infcx: &infer::InferCtxt<'a, 'tcx>) + -> Result<&'tcx Layout, LayoutError<'tcx>> { + let can_cache = !self.has_param_types() && !self.has_self_ty(); + if can_cache { + if let Some(&cached) = infcx.tcx.layout_cache.borrow().get(&self) { + return Ok(cached); + } + } + + let layout = Layout::compute_uncached(self, infcx)?; + let layout = infcx.tcx.intern_layout(layout); + if can_cache { + infcx.tcx.layout_cache.borrow_mut().insert(self, layout); + } + Ok(layout) + } + /// Check whether a type is representable. This means it cannot contain unboxed /// structural recursion. This check is needed for structs and enums. diff --git a/src/librustc_back/target/aarch64_apple_ios.rs b/src/librustc_back/target/aarch64_apple_ios.rs index e1242560e62c7..2f0a043f9a7de 100644 --- a/src/librustc_back/target/aarch64_apple_ios.rs +++ b/src/librustc_back/target/aarch64_apple_ios.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "arm64-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "ios".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs index c6901a4cc4270..c4212e702129c 100644 --- a/src/librustc_back/target/aarch64_linux_android.rs +++ b/src/librustc_back/target/aarch64_linux_android.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { llvm_target: "aarch64-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "android".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 51abab6609a86..3bf4e92fb6aab 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { target_endian: "little".to_string(), target_pointer_width: "64".to_string(), target_env: "gnu".to_string(), + data_layout: "e-m:e-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), target_os: "linux".to_string(), target_vendor: "unknown".to_string(), diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs index 732f1a353a8bd..0a61b14763476 100644 --- a/src/librustc_back/target/arm_linux_androideabi.rs +++ b/src/librustc_back/target/arm_linux_androideabi.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "arm-linux-androideabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "android".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs index 7c35b43fd4b75..0cb0949d4622f 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "arm-unknown-linux-gnueabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), target_env: "gnueabi".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs index a99ec45996c2e..05b9401a0635c 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "arm-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), target_env: "gnueabihf".to_string(), diff --git a/src/librustc_back/target/armv7_apple_ios.rs b/src/librustc_back/target/armv7_apple_ios.rs index d30648002912e..d131f8b2ef002 100644 --- a/src/librustc_back/target/armv7_apple_ios.rs +++ b/src/librustc_back/target/armv7_apple_ios.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "armv7-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), target_os: "ios".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs index d7dcd714a10b7..9c9bb72f76cfd 100644 --- a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), target_os: "linux".to_string(), target_env: "gnueabihf".to_string(), diff --git a/src/librustc_back/target/armv7s_apple_ios.rs b/src/librustc_back/target/armv7s_apple_ios.rs index 66ec6efca0e67..d317589bf3680 100644 --- a/src/librustc_back/target/armv7s_apple_ios.rs +++ b/src/librustc_back/target/armv7s_apple_ios.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "armv7s-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), target_os: "ios".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index 4330e2e7b5fb4..546f9df605b30 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -31,6 +31,7 @@ pub fn target() -> Target { target_os: "emscripten".to_string(), target_env: "".to_string(), target_vendor: "unknown".to_string(), + data_layout: "e-p:32:32-i64:64-v128:32:128-n32-S128".to_string(), arch: "asmjs".to_string(), options: opts, } diff --git a/src/librustc_back/target/i386_apple_ios.rs b/src/librustc_back/target/i386_apple_ios.rs index 52b5901192c65..d149d4bbdc2d6 100644 --- a/src/librustc_back/target/i386_apple_ios.rs +++ b/src/librustc_back/target/i386_apple_ios.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "i386-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "ios".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs index 98f4654ecab41..b6e2f4d8e8ac9 100644 --- a/src/librustc_back/target/i686_apple_darwin.rs +++ b/src/librustc_back/target/i686_apple_darwin.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "i686-apple-darwin".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "macos".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs index f548fdad3cbed..b338a971ff74c 100644 --- a/src/librustc_back/target/i686_linux_android.rs +++ b/src/librustc_back/target/i686_linux_android.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "i686-linux-android".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "android".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs index fa12bbd89323c..48203cc74d6a1 100644 --- a/src/librustc_back/target/i686_pc_windows_gnu.rs +++ b/src/librustc_back/target/i686_pc_windows_gnu.rs @@ -22,6 +22,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(), arch: "x86".to_string(), target_os: "windows".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/i686_pc_windows_msvc.rs b/src/librustc_back/target/i686_pc_windows_msvc.rs index 7fe6590618941..501219ad607cf 100644 --- a/src/librustc_back/target/i686_pc_windows_msvc.rs +++ b/src/librustc_back/target/i686_pc_windows_msvc.rs @@ -27,6 +27,7 @@ pub fn target() -> Target { llvm_target: "i686-pc-windows-msvc".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(), arch: "x86".to_string(), target_os: "windows".to_string(), target_env: "msvc".to_string(), diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs index 32a15b9f2d4d1..cdbbd5eafddbb 100644 --- a/src/librustc_back/target/i686_unknown_dragonfly.rs +++ b/src/librustc_back/target/i686_unknown_dragonfly.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-dragonfly".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "dragonfly".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/i686_unknown_freebsd.rs b/src/librustc_back/target/i686_unknown_freebsd.rs index 812ba11cd796b..fadedc24149c7 100644 --- a/src/librustc_back/target/i686_unknown_freebsd.rs +++ b/src/librustc_back/target/i686_unknown_freebsd.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-freebsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "freebsd".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index ac2af0c64fd6a..a1f3ab769070f 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs index 77bc7bb51755d..cce023b843016 100644 --- a/src/librustc_back/target/i686_unknown_linux_musl.rs +++ b/src/librustc_back/target/i686_unknown_linux_musl.rs @@ -37,6 +37,7 @@ pub fn target() -> Target { llvm_target: "i686-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(), arch: "x86".to_string(), target_os: "linux".to_string(), target_env: "musl".to_string(), diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs index d37102e558d4a..472b73302a358 100644 --- a/src/librustc_back/target/le32_unknown_nacl.rs +++ b/src/librustc_back/target/le32_unknown_nacl.rs @@ -34,6 +34,7 @@ pub fn target() -> Target { target_os: "nacl".to_string(), target_env: "newlib".to_string(), target_vendor: "unknown".to_string(), + data_layout: "e-i64:64:64-p:32:32:32-v128:32:32".to_string(), arch: "le32".to_string(), options: opts, } diff --git a/src/librustc_back/target/mips_unknown_linux_gnu.rs b/src/librustc_back/target/mips_unknown_linux_gnu.rs index 01f2de4a2694a..863f5ceab0ae0 100644 --- a/src/librustc_back/target/mips_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mips_unknown_linux_gnu.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { llvm_target: "mips-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/mips_unknown_linux_musl.rs b/src/librustc_back/target/mips_unknown_linux_musl.rs index 050fb91aa1903..ac0fde5449ff4 100644 --- a/src/librustc_back/target/mips_unknown_linux_musl.rs +++ b/src/librustc_back/target/mips_unknown_linux_musl.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { llvm_target: "mips-unknown-linux-musl".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), target_env: "musl".to_string(), diff --git a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs index e9eef72e8c393..ff33effa3e79b 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { llvm_target: "mipsel-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/mipsel_unknown_linux_musl.rs b/src/librustc_back/target/mipsel_unknown_linux_musl.rs index 383a0d891ca7c..d9fb14050365d 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_musl.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_musl.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { llvm_target: "mipsel-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), target_os: "linux".to_string(), target_env: "musl".to_string(), diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 72c99d57963c5..3f75201aad2cc 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -39,10 +39,10 @@ //! this module defines the format the JSON file should take, though each //! underscore in the field names should be replaced with a hyphen (`-`) in the //! JSON file. Some fields are required in every target specification, such as -//! `llvm-target`, `target-endian`, `target-pointer-width`, `arch`, and -//! `os`. In general, options passed to rustc with `-C` override the target's -//! settings, though `target-feature` and `link-args` will *add* to the list -//! specified by the target, rather than replace. +//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, +//! `arch`, and `os`. In general, options passed to rustc with `-C` override +//! the target's settings, though `target-feature` and `link-args` will *add* +//! to the list specified by the target, rather than replace. use serialize::json::Json; use std::default::Default; @@ -75,7 +75,8 @@ macro_rules! supported_targets { if false { } $( else if target == stringify!($module) { - let t = $module::target(); + let mut t = $module::target(); + t.options.is_builtin = true; debug!("Got builtin target: {:?}", t); return Some(t); } @@ -161,6 +162,8 @@ pub struct Target { /// Architecture to use for ABI considerations. Valid options: "x86", /// "x86_64", "arm", "aarch64", "mips", "powerpc", and "powerpc64". pub arch: String, + /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. + pub data_layout: String, /// Optional settings with defaults. pub options: TargetOptions, } @@ -171,8 +174,9 @@ pub struct Target { /// these try to take "minimal defaults" that don't assume anything about the runtime they run in. #[derive(Clone, Debug)] pub struct TargetOptions { - /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. - pub data_layout: Option, + /// Whether the target is built-in or loaded from a custom target specification. + pub is_builtin: bool, + /// Linker to invoke. Defaults to "cc". pub linker: String, /// Archive utility to use when managing archives. Defaults to "ar". @@ -293,7 +297,7 @@ impl Default for TargetOptions { /// incomplete, and if used for compilation, will certainly not work. fn default() -> TargetOptions { TargetOptions { - data_layout: None, + is_builtin: false, linker: option_env!("CFG_DEFAULT_LINKER").unwrap_or("cc").to_string(), ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(), pre_link_args: Vec::new(), @@ -378,6 +382,7 @@ impl Target { llvm_target: get_req_field("llvm-target"), target_endian: get_req_field("target-endian"), target_pointer_width: get_req_field("target-pointer-width"), + data_layout: get_req_field("data-layout"), arch: get_req_field("arch"), target_os: get_req_field("os"), target_env: get_opt_field("env", ""), @@ -426,7 +431,6 @@ impl Target { key!(staticlib_prefix); key!(staticlib_suffix); key!(features); - key!(data_layout, optional); key!(dynamic_linking, bool); key!(executables, bool); key!(disable_redzone, bool); diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs index 3ba0c671d2ef0..fe7daaec1cdeb 100644 --- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "64".to_string(), + data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs index f0fac14dae08a..4aab2b1802c98 100644 --- a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs index 6664abf5458b7..1df36442c0688 100644 --- a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "powerpc-unknown-linux-gnu".to_string(), target_endian: "big".to_string(), target_pointer_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs index 3e19e1482909e..c8b5dd0eccc5c 100644 --- a/src/librustc_back/target/x86_64_apple_darwin.rs +++ b/src/librustc_back/target/x86_64_apple_darwin.rs @@ -20,6 +20,7 @@ pub fn target() -> Target { llvm_target: "x86_64-apple-darwin".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "macos".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_ios.rs b/src/librustc_back/target/x86_64_apple_ios.rs index 63234c0baee8c..d038e88f2b4fd 100644 --- a/src/librustc_back/target/x86_64_apple_ios.rs +++ b/src/librustc_back/target/x86_64_apple_ios.rs @@ -16,6 +16,7 @@ pub fn target() -> Target { llvm_target: "x86_64-apple-ios".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "ios".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_pc_windows_gnu.rs b/src/librustc_back/target/x86_64_pc_windows_gnu.rs index 3e8438539156f..f0a09ae71efeb 100644 --- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs +++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-pc-windows-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "windows".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs index 14ce27350519c..b3fbd6ef05170 100644 --- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "x86_64-pc-windows-msvc".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "windows".to_string(), target_env: "msvc".to_string(), diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index c97b434b9e0d1..652159d10fda2 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -27,6 +27,7 @@ pub fn target() -> Target { llvm_target: "x86_64-rumprun-netbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "netbsd".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_sun_solaris.rs b/src/librustc_back/target/x86_64_sun_solaris.rs index 541c1306b3340..5aa08ea9c8ca9 100644 --- a/src/librustc_back/target/x86_64_sun_solaris.rs +++ b/src/librustc_back/target/x86_64_sun_solaris.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-pc-solaris".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "solaris".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_bitrig.rs b/src/librustc_back/target/x86_64_unknown_bitrig.rs index 04456b1b2714a..e8b95ed80d9e6 100644 --- a/src/librustc_back/target/x86_64_unknown_bitrig.rs +++ b/src/librustc_back/target/x86_64_unknown_bitrig.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-bitrig".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "bitrig".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_dragonfly.rs b/src/librustc_back/target/x86_64_unknown_dragonfly.rs index 62654176aa486..3fa46c31a5eb1 100644 --- a/src/librustc_back/target/x86_64_unknown_dragonfly.rs +++ b/src/librustc_back/target/x86_64_unknown_dragonfly.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-dragonfly".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "dragonfly".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_freebsd.rs b/src/librustc_back/target/x86_64_unknown_freebsd.rs index 888b7f58bffca..d345a32179423 100644 --- a/src/librustc_back/target/x86_64_unknown_freebsd.rs +++ b/src/librustc_back/target/x86_64_unknown_freebsd.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-freebsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "freebsd".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs index e3ccd9c4c7e7d..69e333a135002 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "linux".to_string(), target_env: "gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index dafbb924a9ca5..622a1fe8baf14 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -73,6 +73,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "linux".to_string(), target_env: "musl".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs index 4101fabe73480..74bf71891199a 100644 --- a/src/librustc_back/target/x86_64_unknown_netbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-netbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "netbsd".to_string(), target_env: "".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_openbsd.rs b/src/librustc_back/target/x86_64_unknown_openbsd.rs index 8c995113c2175..521de5373d0b1 100644 --- a/src/librustc_back/target/x86_64_unknown_openbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_openbsd.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { llvm_target: "x86_64-unknown-openbsd".to_string(), target_endian: "little".to_string(), target_pointer_width: "64".to_string(), + data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), target_os: "openbsd".to_string(), target_env: "".to_string(), diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index cb29f27b83fd7..9bbe0cb5f6957 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -32,18 +32,7 @@ use rustc::ty::{self, Ty}; use libc::c_uint; pub use syntax::abi::Abi; - -/// The first half of a fat pointer. -/// - For a closure, this is the code address. -/// - For an object or trait instance, this is the address of the box. -/// - For a slice, this is the base address. -pub const FAT_PTR_ADDR: usize = 0; - -/// The second half of a fat pointer. -/// - For a closure, this is the address of the environment. -/// - For an object or trait instance, this is the address of the vtable. -/// - For a slice, this is the length. -pub const FAT_PTR_EXTRA: usize = 1; +pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; #[derive(Clone, Copy, PartialEq, Debug)] enum ArgKind { diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index f084d30e0c087..8052fc21cc705 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -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"); diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index c1802a5f0a9c2..1217b2b5a1b17 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -36,11 +36,12 @@ use session::Session; use util::sha2::Sha256; use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::ptr; use std::rc::Rc; +use std::str; use syntax::ast; use syntax::parse::token::InternedString; @@ -255,15 +256,28 @@ unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextR let mod_name = CString::new(mod_name).unwrap(); let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); - if let Some(ref custom_data_layout) = sess.target.target.options.data_layout { - let data_layout = CString::new(&custom_data_layout[..]).unwrap(); - llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); - } else { + // Ensure the data-layout values hardcoded remain the defaults. + if sess.target.target.options.is_builtin { let tm = ::back::write::create_target_machine(sess); llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); llvm::LLVMRustDisposeTargetMachine(tm); + + let data_layout = llvm::LLVMGetDataLayout(llmod); + let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes()) + .ok().expect("got a non-UTF8 data-layout from LLVM"); + + if sess.target.target.data_layout != data_layout { + bug!("data-layout for builtin `{}` target, `{}`, \ + differs from LLVM default, `{}`", + sess.target.target.llvm_target, + sess.target.target.data_layout, + data_layout); + } } + let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); + llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); + let llvm_target = sess.target.target.llvm_target.as_bytes(); let llvm_target = CString::new(llvm_target).unwrap(); llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); @@ -770,23 +784,8 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local.trait_cache } - /// Return exclusive upper bound on object size. - /// - /// The theoretical maximum object size is defined as the maximum positive `int` value. This - /// ensures that the `offset` semantics remain well-defined by allowing it to correctly index - /// every address within an object along with one byte past the end, along with allowing `int` - /// to store the difference between any two pointers into an object. - /// - /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer to - /// represent object size in bits. It would need to be 1 << 61 to account for this, but is - /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable - /// address space on 64-bit ARMv8 and x86_64. pub fn obj_size_bound(&self) -> u64 { - match &self.sess().target.target.target_pointer_width[..] { - "32" => 1 << 31, - "64" => 1 << 47, - _ => bug!() // error handled by config::build_target_config - } + self.tcx().data_layout.obj_size_bound() } pub fn report_overbig_object(&self, obj: Ty<'tcx>) -> ! { diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs index 5ae60d182407f..5e4902cf3caf2 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 d79320462430c..1220fbafa29c9 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 { 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/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index 1f0a18ad4faa2..863ae3f942e93 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -17,6 +17,7 @@ use abi::FnType; use adt; use common::*; use machine; +use rustc::traits::ProjectionMode; use rustc::ty::{self, Ty, TypeFoldable}; use type_::Type; @@ -121,6 +122,37 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ debug!("--> mapped t={:?} to llsizingty={:?}", t, llsizingty); cx.llsizingtypes().borrow_mut().insert(t, llsizingty); + + // FIXME(eddyb) Temporary sanity check for ty::layout. + let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables, ProjectionMode::Any); + match t.layout(&infcx) { + Ok(layout) => { + if !type_is_sized(cx.tcx(), t) { + if !layout.is_unsized() { + bug!("layout should be unsized for type `{}` / {:#?}", + t, layout); + } + + // Unsized types get turned into a fat pointer for LLVM. + return llsizingty; + } + let r = layout.size(&cx.tcx().data_layout).bytes(); + let l = machine::llsize_of_alloc(cx, llsizingty); + if r != l { + bug!("size differs (rustc: {}, llvm: {}) for type `{}` / {:#?}", + r, l, t, layout); + } + let r = layout.align(&cx.tcx().data_layout).abi(); + let l = machine::llalign_of_min(cx, llsizingty) as u64; + if r != l { + bug!("align differs (rustc: {}, llvm: {}) for type `{}` / {:#?}", + r, l, t, layout); + } + } + Err(e) => { + bug!("failed to get layout for `{}`: {}", t, e); + } + } llsizingty } diff --git a/src/test/compile-fail/issue-21174.rs b/src/test/compile-fail/issue-21174.rs index 30fd2eb4d2f9a..c92a404b71a6f 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 0000000000000..6e8126348da69 --- /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 or the MIT license +// , 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 { + stream: PhantomData, +} + +fn foo(x: [usize; 2]) -> Bar { + 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 0000000000000..50bcd53ecb82c --- /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 or the MIT license +// , 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 3eae76f9492a2..42c3cb7f18131 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) { 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::(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 b06966bd867ca..b6e7e32663ec1 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(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(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(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 { @@ -29,7 +32,8 @@ struct Bad { } unsafe fn i(x: Bad) { - let _: isize = transmute(x); //~ ERROR cannot transmute + let _: isize = transmute(x); +//~^ ERROR differently sized types: Bad (size can vary because of T) to isize } enum Worse { @@ -38,11 +42,13 @@ enum Worse { } unsafe fn j(x: Worse) { - let _: isize = transmute(x); //~ ERROR cannot transmute + let _: isize = transmute(x); +//~^ ERROR differently sized types: Worse (size can vary because of T) to isize } unsafe fn k(x: Option) { - let _: isize = transmute(x); //~ ERROR cannot transmute + let _: isize = transmute(x); +//~^ ERROR differently sized types: std::option::Option (size can vary because of T) to isize } fn main() {} diff --git a/src/test/run-make/target-specs/my-awesome-platform.json b/src/test/run-make/target-specs/my-awesome-platform.json index e3080d29ec330..b7083c2776aec 100644 --- a/src/test/run-make/target-specs/my-awesome-platform.json +++ b/src/test/run-make/target-specs/my-awesome-platform.json @@ -1,4 +1,5 @@ { + "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", "llvm-target": "i686-unknown-linux-gnu", "target-endian": "little", "target-pointer-width": "32",