Skip to content

Commit 9bbbd29

Browse files
committed
Auto merge of #42859 - eddyb:const-size-and-align-of, r=nikomatsakis
Implement const fn {size,align}_of. Fixes #34078. r? @nikomatsakis
2 parents 344f01c + 148718b commit 9bbbd29

File tree

8 files changed

+195
-15
lines changed

8 files changed

+195
-15
lines changed

src/libcore/mem.rs

+43
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,30 @@ pub fn forget<T>(t: T) {
188188
/// ```
189189
#[inline]
190190
#[stable(feature = "rust1", since = "1.0.0")]
191+
#[cfg(stage0)]
191192
pub fn size_of<T>() -> usize {
192193
unsafe { intrinsics::size_of::<T>() }
193194
}
194195

196+
/// Returns the size of a type in bytes.
197+
///
198+
/// More specifically, this is the offset in bytes between successive
199+
/// items of the same type, including alignment padding.
200+
///
201+
/// # Examples
202+
///
203+
/// ```
204+
/// use std::mem;
205+
///
206+
/// assert_eq!(4, mem::size_of::<i32>());
207+
/// ```
208+
#[inline]
209+
#[stable(feature = "rust1", since = "1.0.0")]
210+
#[cfg(not(stage0))]
211+
pub const fn size_of<T>() -> usize {
212+
unsafe { intrinsics::size_of::<T>() }
213+
}
214+
195215
/// Returns the size of the pointed-to value in bytes.
196216
///
197217
/// 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 {
279299
/// ```
280300
#[inline]
281301
#[stable(feature = "rust1", since = "1.0.0")]
302+
#[cfg(stage0)]
282303
pub fn align_of<T>() -> usize {
283304
unsafe { intrinsics::min_align_of::<T>() }
284305
}
285306

307+
/// Returns the [ABI]-required minimum alignment of a type.
308+
///
309+
/// Every reference to a value of the type `T` must be a multiple of this number.
310+
///
311+
/// This is the alignment used for struct fields. It may be smaller than the preferred alignment.
312+
///
313+
/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
314+
///
315+
/// # Examples
316+
///
317+
/// ```
318+
/// use std::mem;
319+
///
320+
/// assert_eq!(4, mem::align_of::<i32>());
321+
/// ```
322+
#[inline]
323+
#[stable(feature = "rust1", since = "1.0.0")]
324+
#[cfg(not(stage0))]
325+
pub const fn align_of<T>() -> usize {
326+
unsafe { intrinsics::min_align_of::<T>() }
327+
}
328+
286329
/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to.
287330
///
288331
/// Every reference to a value of the type `T` must be a multiple of this number.

src/librustc/middle/const_val.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use rustc_const_math::ConstInt;
1414
use hir;
1515
use hir::def::Def;
1616
use hir::def_id::DefId;
17-
use ty::TyCtxt;
17+
use ty::{TyCtxt, layout};
1818
use ty::subst::Substs;
1919
use util::common::ErrorReported;
2020
use rustc_const_math::*;
@@ -101,6 +101,7 @@ pub enum ErrKind<'tcx> {
101101

102102
IndexOpFeatureGated,
103103
Math(ConstMathErr),
104+
LayoutError(layout::LayoutError<'tcx>),
104105

105106
ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
106107

@@ -164,6 +165,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
164165
MiscCatchAll => simple!("unsupported constant expr"),
165166
IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
166167
Math(ref err) => Simple(err.description().into_cow()),
168+
LayoutError(ref err) => Simple(err.to_string().into_cow()),
167169

168170
ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
169171

src/librustc_const_eval/eval.rs

+23
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use rustc::traits::Reveal;
2525
use rustc::util::common::ErrorReported;
2626
use rustc::util::nodemap::DefIdMap;
2727

28+
use syntax::abi::Abi;
2829
use syntax::ast;
2930
use rustc::hir::{self, Expr};
3031
use syntax_pos::Span;
@@ -340,6 +341,28 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
340341
_ => signal!(e, TypeckError),
341342
};
342343

344+
if tcx.fn_sig(def_id).abi() == Abi::RustIntrinsic {
345+
let layout_of = |ty: Ty<'tcx>| {
346+
ty.layout(tcx, ty::ParamEnv::empty(traits::Reveal::All))
347+
.map_err(|err| {
348+
ConstEvalErr { span: e.span, kind: LayoutError(err) }
349+
})
350+
};
351+
match &tcx.item_name(def_id).as_str()[..] {
352+
"size_of" => {
353+
let size = layout_of(substs.type_at(0))?.size(tcx);
354+
return Ok(Integral(Usize(ConstUsize::new(size.bytes(),
355+
tcx.sess.target.uint_type).unwrap())));
356+
}
357+
"min_align_of" => {
358+
let align = layout_of(substs.type_at(0))?.align(tcx);
359+
return Ok(Integral(Usize(ConstUsize::new(align.abi(),
360+
tcx.sess.target.uint_type).unwrap())));
361+
}
362+
_ => signal!(e, TypeckError)
363+
}
364+
}
365+
343366
let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
344367
if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
345368
if fn_like.constness() == hir::Constness::Const {

src/librustc_mir/transform/qualify_consts.rs

+20-7
Original file line numberDiff line numberDiff line change
@@ -749,14 +749,27 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
749749
self.visit_operand(func, location);
750750

751751
let fn_ty = func.ty(self.mir, self.tcx);
752-
let (is_shuffle, is_const_fn) = match fn_ty.sty {
753-
ty::TyFnDef(def_id, _) => {
754-
(self.tcx.fn_sig(def_id).abi() == Abi::PlatformIntrinsic &&
755-
self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
756-
self.tcx.is_const_fn(def_id))
752+
let (mut is_shuffle, mut is_const_fn) = (false, false);
753+
if let ty::TyFnDef(def_id, _) = fn_ty.sty {
754+
match self.tcx.fn_sig(def_id).abi() {
755+
Abi::RustIntrinsic |
756+
Abi::PlatformIntrinsic => {
757+
assert!(!self.tcx.is_const_fn(def_id));
758+
match &self.tcx.item_name(def_id).as_str()[..] {
759+
"size_of" | "min_align_of" => is_const_fn = true,
760+
761+
name if name.starts_with("simd_shuffle") => {
762+
is_shuffle = true;
763+
}
764+
765+
_ => {}
766+
}
767+
}
768+
_ => {
769+
is_const_fn = self.tcx.is_const_fn(def_id);
770+
}
757771
}
758-
_ => (false, false)
759-
};
772+
}
760773

761774
for (i, arg) in args.iter().enumerate() {
762775
self.nest(|this| {

src/librustc_passes/consts.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_const_eval::ConstContext;
2929
use rustc::middle::const_val::ConstEvalErr;
3030
use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll};
3131
use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
32-
use rustc::middle::const_val::ErrKind::{TypeckError, Math};
32+
use rustc::middle::const_val::ErrKind::{TypeckError, Math, LayoutError};
3333
use rustc_const_math::{ConstMathErr, Op};
3434
use rustc::hir::def::{Def, CtorKind};
3535
use rustc::hir::def_id::DefId;
@@ -252,6 +252,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
252252
Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) |
253253
Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
254254
Err(ConstEvalErr { kind: TypeckError, .. }) => {}
255+
Err(ConstEvalErr {
256+
kind: LayoutError(ty::layout::LayoutError::Unknown(_)), ..
257+
}) => {}
255258
Err(msg) => {
256259
self.tcx.sess.add_lint(CONST_ERR,
257260
ex.id,

src/librustc_trans/mir/constant.rs

+24-6
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use rustc::ty::layout::{self, LayoutTyper};
2222
use rustc::ty::cast::{CastTy, IntTy};
2323
use rustc::ty::subst::{Kind, Substs, Subst};
2424
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
25-
use {abi, adt, base, machine};
25+
use {adt, base, machine};
26+
use abi::{self, Abi};
2627
use callee;
2728
use builder::Builder;
2829
use common::{self, CrateContext, const_get_elt, val_ty};
@@ -339,17 +340,34 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
339340
func, fn_ty)
340341
};
341342

342-
let mut const_args = IndexVec::with_capacity(args.len());
343+
let mut arg_vals = IndexVec::with_capacity(args.len());
343344
for arg in args {
344345
match self.const_operand(arg, span) {
345-
Ok(arg) => { const_args.push(arg); },
346+
Ok(arg) => { arg_vals.push(arg); },
346347
Err(err) => if failure.is_ok() { failure = Err(err); }
347348
}
348349
}
349350
if let Some((ref dest, target)) = *destination {
350-
match MirConstContext::trans_def(self.ccx, def_id, substs, const_args) {
351-
Ok(value) => self.store(dest, value, span),
352-
Err(err) => if failure.is_ok() { failure = Err(err); }
351+
if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic {
352+
let value = match &tcx.item_name(def_id).as_str()[..] {
353+
"size_of" => {
354+
let llval = C_uint(self.ccx,
355+
self.ccx.size_of(substs.type_at(0)));
356+
Const::new(llval, tcx.types.usize)
357+
}
358+
"min_align_of" => {
359+
let llval = C_uint(self.ccx,
360+
self.ccx.align_of(substs.type_at(0)));
361+
Const::new(llval, tcx.types.usize)
362+
}
363+
_ => span_bug!(span, "{:?} in constant", terminator.kind)
364+
};
365+
self.store(dest, value, span);
366+
} else {
367+
match MirConstContext::trans_def(self.ccx, def_id, substs, arg_vals) {
368+
Ok(value) => self.store(dest, value, span),
369+
Err(err) => if failure.is_ok() { failure = Err(err); }
370+
}
353371
}
354372
target
355373
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn)]
12+
13+
struct Foo {
14+
bytes: [u8; std::mem::size_of::<Foo>()]
15+
//~^ ERROR unsupported cyclic reference between types/traits detected
16+
}
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn)]
12+
13+
use std::mem;
14+
15+
// Get around the limitations of CTFE in today's Rust.
16+
const fn choice_u64(c: bool, a: u64, b: u64) -> u64 {
17+
(-(c as i64) as u64) & a | (-(!c as i64) as u64) & b
18+
}
19+
20+
const fn max_usize(a: usize, b: usize) -> usize {
21+
choice_u64(a > b, a as u64, b as u64) as usize
22+
}
23+
24+
const fn align_to(size: usize, align: usize) -> usize {
25+
(size + (align - 1)) & !(align - 1)
26+
}
27+
28+
const fn packed_union_size_of<A, B>() -> usize {
29+
max_usize(mem::size_of::<A>(), mem::size_of::<B>())
30+
}
31+
32+
const fn union_align_of<A, B>() -> usize {
33+
max_usize(mem::align_of::<A>(), mem::align_of::<B>())
34+
}
35+
36+
const fn union_size_of<A, B>() -> usize {
37+
align_to(packed_union_size_of::<A, B>(), union_align_of::<A, B>())
38+
}
39+
40+
macro_rules! fake_union {
41+
($name:ident { $a:ty, $b:ty }) => (
42+
struct $name {
43+
_align: ([$a; 0], [$b; 0]),
44+
_bytes: [u8; union_size_of::<$a, $b>()]
45+
}
46+
)
47+
}
48+
49+
// Check that we can (poorly) emulate unions by
50+
// calling size_of and align_of at compile-time.
51+
fake_union!(U { u16, [u8; 3] });
52+
53+
fn test(u: U) {
54+
assert_eq!(mem::size_of_val(&u._bytes), 4);
55+
}
56+
57+
fn main() {
58+
assert_eq!(mem::size_of::<U>(), 4);
59+
assert_eq!(mem::align_of::<U>(), 2);
60+
}

0 commit comments

Comments
 (0)