about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-07-19 16:58:02 +0000
committerbors <bors@rust-lang.org>2017-07-19 16:58:02 +0000
commit9bbbd29e82fb7fc9f79736653d42100979899832 (patch)
tree8f0ddaea23d5ce961f8107ee2f256f0117ac27ab
parent344f01cf1322d61bad109b503d0e6a1b55d6ed31 (diff)
parent148718b4f32b46a96e339be92102697ad58b170b (diff)
Auto merge of #42859 - eddyb:const-size-and-align-of, r=nikomatsakis
Implement const fn {size,align}_of.

Fixes #34078.

r? @nikomatsakis
-rw-r--r--src/libcore/mem.rs43
-rw-r--r--src/librustc/middle/const_val.rs4
-rw-r--r--src/librustc_const_eval/eval.rs23
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs27
-rw-r--r--src/librustc_passes/consts.rs5
-rw-r--r--src/librustc_trans/mir/constant.rs30
-rw-r--r--src/test/compile-fail/const-size_of-cycle.rs18
-rw-r--r--src/test/run-pass/const-size_of-align_of.rs60
8 files changed, 195 insertions, 15 deletions
diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs
index 6c1e8e8960f..86e5afa4c33 100644
--- a/src/libcore/mem.rs
+++ b/src/libcore/mem.rs
@@ -188,10 +188,30 @@ pub fn forget<T>(t: T) {
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(stage0)]
 pub fn size_of<T>() -> usize {
     unsafe { intrinsics::size_of::<T>() }
 }
 
+/// 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::<i32>());
+/// ```
+#[inline]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(not(stage0))]
+pub const fn size_of<T>() -> usize {
+    unsafe { intrinsics::size_of::<T>() }
+}
+
 /// Returns the size of the pointed-to value in bytes.
 ///
 /// This is usually the same as `size_of::<T>()`. However, when `T` *has* no
@@ -279,10 +299,33 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
 /// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(stage0)]
 pub fn align_of<T>() -> usize {
     unsafe { intrinsics::min_align_of::<T>() }
 }
 
+/// 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::<i32>());
+/// ```
+#[inline]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(not(stage0))]
+pub const fn align_of<T>() -> usize {
+    unsafe { intrinsics::min_align_of::<T>() }
+}
+
 /// 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 3bbaf5c9299..2637afdea5b 100644
--- a/src/librustc/middle/const_val.rs
+++ b/src/librustc/middle/const_val.rs
@@ -14,7 +14,7 @@ pub use rustc_const_math::ConstInt;
 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<ConstEvalErr<'tcx>>),
 
@@ -164,6 +165,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
             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 4ce985568ce..72fa858e4cb 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -25,6 +25,7 @@ use rustc::traits::Reveal;
 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 68b687a2e61..9bb0f07aa68 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -749,14 +749,27 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
             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 bec4c083905..468646c1ced 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -29,7 +29,7 @@ use rustc_const_eval::ConstContext;
 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 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
                 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 fcb4b25e6fe..98e774a2987 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -22,7 +22,8 @@ use rustc::ty::layout::{self, LayoutTyper};
 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 @@ impl<'a, 'tcx> MirConstContext<'a, '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 00000000000..a58be33b1ae
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(const_fn)]
+
+struct Foo {
+    bytes: [u8; std::mem::size_of::<Foo>()]
+    //~^ 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 00000000000..06fbe9bf4f6
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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<A, B>() -> usize {
+    max_usize(mem::size_of::<A>(), mem::size_of::<B>())
+}
+
+const fn union_align_of<A, B>() -> usize {
+    max_usize(mem::align_of::<A>(), mem::align_of::<B>())
+}
+
+const fn union_size_of<A, B>() -> usize {
+    align_to(packed_union_size_of::<A, B>(), union_align_of::<A, B>())
+}
+
+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::<U>(), 4);
+    assert_eq!(mem::align_of::<U>(), 2);
+}