diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 6c1e8e8960f7549f4b2f50f23e59714ae18375e3..86e5afa4c337f32186c5641d636fa7ffee1b37ac 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -188,10 +188,30 @@ pub fn forget(t: T) { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] pub fn size_of() -> usize { unsafe { intrinsics::size_of::() } } +/// Returns the size of a type in bytes. +/// +/// More specifically, this is the offset in bytes between successive +/// items of the same type, including alignment padding. +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::size_of::()); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(stage0))] +pub const fn size_of() -> usize { + unsafe { intrinsics::size_of::() } +} + /// Returns the size of the pointed-to value in bytes. /// /// This is usually the same as `size_of::()`. However, when `T` *has* no @@ -279,10 +299,33 @@ pub fn min_align_of_val(val: &T) -> usize { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(stage0)] pub fn align_of() -> usize { unsafe { intrinsics::min_align_of::() } } +/// Returns the [ABI]-required minimum alignment of a type. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// This is the alignment used for struct fields. It may be smaller than the preferred alignment. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Examples +/// +/// ``` +/// use std::mem; +/// +/// assert_eq!(4, mem::align_of::()); +/// ``` +#[inline] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(stage0))] +pub const fn align_of() -> usize { + unsafe { intrinsics::min_align_of::() } +} + /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. /// /// Every reference to a value of the type `T` must be a multiple of this number. diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 3bbaf5c9299f8945e301378c874a15cd57473c52..2637afdea5bcf17b5d893217ea9104207e3e7a95 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -14,7 +14,7 @@ use hir; use hir::def::Def; use hir::def_id::DefId; -use ty::TyCtxt; +use ty::{TyCtxt, layout}; use ty::subst::Substs; use util::common::ErrorReported; use rustc_const_math::*; @@ -101,6 +101,7 @@ pub enum ErrKind<'tcx> { IndexOpFeatureGated, Math(ConstMathErr), + LayoutError(layout::LayoutError<'tcx>), ErroneousReferencedConstant(Box>), @@ -164,6 +165,7 @@ pub fn description(&self) -> ConstEvalErrDescription { MiscCatchAll => simple!("unsupported constant expr"), IndexOpFeatureGated => simple!("the index operation on const values is unstable"), Math(ref err) => Simple(err.description().into_cow()), + LayoutError(ref err) => Simple(err.to_string().into_cow()), ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 4ce985568ce23adaf9bcfcf4f1d04819566712d5..72fa858e4cba8cc8f8b4a0ff6739b008e0a709f5 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -25,6 +25,7 @@ use rustc::util::common::ErrorReported; use rustc::util::nodemap::DefIdMap; +use syntax::abi::Abi; use syntax::ast; use rustc::hir::{self, Expr}; use syntax_pos::Span; @@ -340,6 +341,28 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, _ => signal!(e, TypeckError), }; + if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic { + let layout_of = |ty: Ty<'tcx>| { + ty.layout(tcx, ty::ParamEnv::empty(traits::Reveal::All)) + .map_err(|err| { + ConstEvalErr { span: e.span, kind: LayoutError(err) } + }) + }; + match &tcx.item_name(def_id).as_str()[..] { + "size_of" => { + let size = layout_of(substs.type_at(0))?.size(tcx); + return Ok(Integral(Usize(ConstUsize::new(size.bytes(), + tcx.sess.target.uint_type).unwrap()))); + } + "min_align_of" => { + let align = layout_of(substs.type_at(0))?.align(tcx); + return Ok(Integral(Usize(ConstUsize::new(align.abi(), + tcx.sess.target.uint_type).unwrap()))); + } + _ => signal!(e, TypeckError) + } + } + let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) { if fn_like.constness() == hir::Constness::Const { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 68b687a2e6182c8ba34fddadcfc442e1ded1b03f..9bb0f07aa68ac0fbed57efa502c746e2e3d3f3cc 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -749,14 +749,27 @@ fn visit_terminator_kind(&mut self, self.visit_operand(func, location); let fn_ty = func.ty(self.mir, self.tcx); - let (is_shuffle, is_const_fn) = match fn_ty.sty { - ty::TyFnDef(def_id, _) => { - (self.tcx.fn_sig(def_id).abi() == Abi::PlatformIntrinsic && - self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"), - self.tcx.is_const_fn(def_id)) + let (mut is_shuffle, mut is_const_fn) = (false, false); + if let ty::TyFnDef(def_id, _) = fn_ty.sty { + match self.tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | + Abi::PlatformIntrinsic => { + assert!(!self.tcx.is_const_fn(def_id)); + match &self.tcx.item_name(def_id).as_str()[..] { + "size_of" | "min_align_of" => is_const_fn = true, + + name if name.starts_with("simd_shuffle") => { + is_shuffle = true; + } + + _ => {} + } + } + _ => { + is_const_fn = self.tcx.is_const_fn(def_id); + } } - _ => (false, false) - }; + } for (i, arg) in args.iter().enumerate() { self.nest(|this| { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index bec4c083905a9c0144431a1a21eeb93d3eb9b89f..468646c1ced8fbdb56736b6d07a68959d3f38877 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -29,7 +29,7 @@ use rustc::middle::const_val::ConstEvalErr; use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll}; use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath}; -use rustc::middle::const_val::ErrKind::{TypeckError, Math}; +use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError}; use rustc_const_math::{ConstMathErr, Op}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; @@ -252,6 +252,9 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr) { Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) | Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {} Err(ConstEvalErr { kind: TypeckError, .. }) => {} + Err(ConstEvalErr { + kind: LayoutError(ty::layout::LayoutError::Unknown(_)), .. + }) => {} Err(msg) => { self.tcx.sess.add_lint(CONST_ERR, ex.id, diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index fcb4b25e6fe88d6a8bf6d6f7584ade9bad7ef86c..98e774a29877dd9808acf053ad435b9de0ba55b6 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -22,7 +22,8 @@ use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::subst::{Kind, Substs, Subst}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use {abi, adt, base, machine}; +use {adt, base, machine}; +use abi::{self, Abi}; use callee; use builder::Builder; use common::{self, CrateContext, const_get_elt, val_ty}; @@ -339,17 +340,34 @@ fn trans(&mut self) -> Result, ConstEvalErr<'tcx>> { func, fn_ty) }; - let mut const_args = IndexVec::with_capacity(args.len()); + let mut arg_vals = IndexVec::with_capacity(args.len()); for arg in args { match self.const_operand(arg, span) { - Ok(arg) => { const_args.push(arg); }, + Ok(arg) => { arg_vals.push(arg); }, Err(err) => if failure.is_ok() { failure = Err(err); } } } if let Some((ref dest, target)) = *destination { - match MirConstContext::trans_def(self.ccx, def_id, substs, const_args) { - Ok(value) => self.store(dest, value, span), - Err(err) => if failure.is_ok() { failure = Err(err); } + if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic { + let value = match &tcx.item_name(def_id).as_str()[..] { + "size_of" => { + let llval = C_uint(self.ccx, + self.ccx.size_of(substs.type_at(0))); + Const::new(llval, tcx.types.usize) + } + "min_align_of" => { + let llval = C_uint(self.ccx, + self.ccx.align_of(substs.type_at(0))); + Const::new(llval, tcx.types.usize) + } + _ => span_bug!(span, "{:?} in constant", terminator.kind) + }; + self.store(dest, value, span); + } else { + match MirConstContext::trans_def(self.ccx, def_id, substs, arg_vals) { + Ok(value) => self.store(dest, value, span), + Err(err) => if failure.is_ok() { failure = Err(err); } + } } target } else { diff --git a/src/test/compile-fail/const-size_of-cycle.rs b/src/test/compile-fail/const-size_of-cycle.rs new file mode 100644 index 0000000000000000000000000000000000000000..a58be33b1ae6c0be0fa1ab637bfafe6a713f7d85 --- /dev/null +++ b/src/test/compile-fail/const-size_of-cycle.rs @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +#![feature(const_fn)] + +struct Foo { + bytes: [u8; std::mem::size_of::()] + //~^ ERROR unsupported cyclic reference between types/traits detected +} + +fn main() {} diff --git a/src/test/run-pass/const-size_of-align_of.rs b/src/test/run-pass/const-size_of-align_of.rs new file mode 100644 index 0000000000000000000000000000000000000000..06fbe9bf4f63958372c055f193e81dd09fe87a97 --- /dev/null +++ b/src/test/run-pass/const-size_of-align_of.rs @@ -0,0 +1,60 @@ +// Copyright 2017 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. + +#![feature(const_fn)] + +use std::mem; + +// Get around the limitations of CTFE in today's Rust. +const fn choice_u64(c: bool, a: u64, b: u64) -> u64 { + (-(c as i64) as u64) & a | (-(!c as i64) as u64) & b +} + +const fn max_usize(a: usize, b: usize) -> usize { + choice_u64(a > b, a as u64, b as u64) as usize +} + +const fn align_to(size: usize, align: usize) -> usize { + (size + (align - 1)) & !(align - 1) +} + +const fn packed_union_size_of() -> usize { + max_usize(mem::size_of::(), mem::size_of::()) +} + +const fn union_align_of() -> usize { + max_usize(mem::align_of::(), mem::align_of::()) +} + +const fn union_size_of() -> usize { + align_to(packed_union_size_of::(), union_align_of::()) +} + +macro_rules! fake_union { + ($name:ident { $a:ty, $b:ty }) => ( + struct $name { + _align: ([$a; 0], [$b; 0]), + _bytes: [u8; union_size_of::<$a, $b>()] + } + ) +} + +// Check that we can (poorly) emulate unions by +// calling size_of and align_of at compile-time. +fake_union!(U { u16, [u8; 3] }); + +fn test(u: U) { + assert_eq!(mem::size_of_val(&u._bytes), 4); +} + +fn main() { + assert_eq!(mem::size_of::(), 4); + assert_eq!(mem::align_of::(), 2); +}