Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement const fn {size,align}_of. #42859

Merged
merged 1 commit into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/libcore/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I necessarily object to this, but this would be an "insta-stable" change, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change itself yes, but calling any function in a const context still requires #[feature(const_fn)].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, does it? I thought we enabled calls, but not declarations, of const fns.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least I couldn't remember which it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s discussion of doing this, but it hasn’t happened yet as far as #24111 says.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -101,6 +101,7 @@ pub enum ErrKind<'tcx> {

IndexOpFeatureGated,
Math(ConstMathErr),
LayoutError(layout::LayoutError<'tcx>),

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

Expand Down Expand Up @@ -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"),

Expand Down
23 changes: 23 additions & 0 deletions src/librustc_const_eval/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
27 changes: 20 additions & 7 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

_ => {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add assert!(!self.tcx.is_const_fn(def_id))?

}
}
_ => {
is_const_fn = self.tcx.is_const_fn(def_id);
}
}
_ => (false, false)
};
}

for (i, arg) in args.iter().enumerate() {
self.nest(|this| {
Expand Down
5 changes: 4 additions & 1 deletion src/librustc_passes/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
30 changes: 24 additions & 6 deletions src/librustc_trans/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down
18 changes: 18 additions & 0 deletions src/test/compile-fail/const-size_of-cycle.rs
Original file line number Diff line number Diff line change
@@ -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() {}
60 changes: 60 additions & 0 deletions src/test/run-pass/const-size_of-align_of.rs
Original file line number Diff line number Diff line change
@@ -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);
}