Implement const fn {size,align}_of.
This commit is contained in:
parent
2e6334062e
commit
148718b4f3
8 changed files with 195 additions and 15 deletions
|
@ -188,10 +188,30 @@ pub fn forget<T>(t: T) {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[cfg(stage0)]
|
||||||
pub fn size_of<T>() -> usize {
|
pub fn size_of<T>() -> usize {
|
||||||
unsafe { intrinsics::size_of::<T>() }
|
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.
|
/// Returns the size of the pointed-to value in bytes.
|
||||||
///
|
///
|
||||||
/// This is usually the same as `size_of::<T>()`. However, when `T` *has* no
|
/// 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]
|
#[inline]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[cfg(stage0)]
|
||||||
pub fn align_of<T>() -> usize {
|
pub fn align_of<T>() -> usize {
|
||||||
unsafe { intrinsics::min_align_of::<T>() }
|
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.
|
/// 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.
|
/// Every reference to a value of the type `T` must be a multiple of this number.
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub use rustc_const_math::ConstInt;
|
||||||
use hir;
|
use hir;
|
||||||
use hir::def::Def;
|
use hir::def::Def;
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use ty::TyCtxt;
|
use ty::{TyCtxt, layout};
|
||||||
use ty::subst::Substs;
|
use ty::subst::Substs;
|
||||||
use util::common::ErrorReported;
|
use util::common::ErrorReported;
|
||||||
use rustc_const_math::*;
|
use rustc_const_math::*;
|
||||||
|
@ -101,6 +101,7 @@ pub enum ErrKind<'tcx> {
|
||||||
|
|
||||||
IndexOpFeatureGated,
|
IndexOpFeatureGated,
|
||||||
Math(ConstMathErr),
|
Math(ConstMathErr),
|
||||||
|
LayoutError(layout::LayoutError<'tcx>),
|
||||||
|
|
||||||
ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
|
ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
|
||||||
|
|
||||||
|
@ -164,6 +165,7 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
|
||||||
MiscCatchAll => simple!("unsupported constant expr"),
|
MiscCatchAll => simple!("unsupported constant expr"),
|
||||||
IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
|
IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
|
||||||
Math(ref err) => Simple(err.description().into_cow()),
|
Math(ref err) => Simple(err.description().into_cow()),
|
||||||
|
LayoutError(ref err) => Simple(err.to_string().into_cow()),
|
||||||
|
|
||||||
ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
|
ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ use rustc::traits::Reveal;
|
||||||
use rustc::util::common::ErrorReported;
|
use rustc::util::common::ErrorReported;
|
||||||
use rustc::util::nodemap::DefIdMap;
|
use rustc::util::nodemap::DefIdMap;
|
||||||
|
|
||||||
|
use syntax::abi::Abi;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use rustc::hir::{self, Expr};
|
use rustc::hir::{self, Expr};
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
@ -340,6 +341,28 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
|
||||||
_ => signal!(e, TypeckError),
|
_ => 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) {
|
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 let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
|
||||||
if fn_like.constness() == hir::Constness::Const {
|
if fn_like.constness() == hir::Constness::Const {
|
||||||
|
|
|
@ -749,14 +749,27 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
self.visit_operand(func, location);
|
self.visit_operand(func, location);
|
||||||
|
|
||||||
let fn_ty = func.ty(self.mir, self.tcx);
|
let fn_ty = func.ty(self.mir, self.tcx);
|
||||||
let (is_shuffle, is_const_fn) = match fn_ty.sty {
|
let (mut is_shuffle, mut is_const_fn) = (false, false);
|
||||||
ty::TyFnDef(def_id, _) => {
|
if let ty::TyFnDef(def_id, _) = fn_ty.sty {
|
||||||
(self.tcx.fn_sig(def_id).abi() == Abi::PlatformIntrinsic &&
|
match self.tcx.fn_sig(def_id).abi() {
|
||||||
self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
|
Abi::RustIntrinsic |
|
||||||
self.tcx.is_const_fn(def_id))
|
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() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
self.nest(|this| {
|
self.nest(|this| {
|
||||||
|
|
|
@ -29,7 +29,7 @@ use rustc_const_eval::ConstContext;
|
||||||
use rustc::middle::const_val::ConstEvalErr;
|
use rustc::middle::const_val::ConstEvalErr;
|
||||||
use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll};
|
use rustc::middle::const_val::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll};
|
||||||
use rustc::middle::const_val::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
|
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_const_math::{ConstMathErr, Op};
|
||||||
use rustc::hir::def::{Def, CtorKind};
|
use rustc::hir::def::{Def, CtorKind};
|
||||||
use rustc::hir::def_id::DefId;
|
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: Math(ConstMathErr::Overflow(Op::Shl)), .. }) |
|
||||||
Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
|
Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
|
||||||
Err(ConstEvalErr { kind: TypeckError, .. }) => {}
|
Err(ConstEvalErr { kind: TypeckError, .. }) => {}
|
||||||
|
Err(ConstEvalErr {
|
||||||
|
kind: LayoutError(ty::layout::LayoutError::Unknown(_)), ..
|
||||||
|
}) => {}
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
self.tcx.sess.add_lint(CONST_ERR,
|
self.tcx.sess.add_lint(CONST_ERR,
|
||||||
ex.id,
|
ex.id,
|
||||||
|
|
|
@ -22,7 +22,8 @@ use rustc::ty::layout::{self, LayoutTyper};
|
||||||
use rustc::ty::cast::{CastTy, IntTy};
|
use rustc::ty::cast::{CastTy, IntTy};
|
||||||
use rustc::ty::subst::{Kind, Substs, Subst};
|
use rustc::ty::subst::{Kind, Substs, Subst};
|
||||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||||
use {abi, adt, base, machine};
|
use {adt, base, machine};
|
||||||
|
use abi::{self, Abi};
|
||||||
use callee;
|
use callee;
|
||||||
use builder::Builder;
|
use builder::Builder;
|
||||||
use common::{self, CrateContext, const_get_elt, val_ty};
|
use common::{self, CrateContext, const_get_elt, val_ty};
|
||||||
|
@ -339,17 +340,34 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
||||||
func, fn_ty)
|
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 {
|
for arg in args {
|
||||||
match self.const_operand(arg, span) {
|
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); }
|
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((ref dest, target)) = *destination {
|
if let Some((ref dest, target)) = *destination {
|
||||||
match MirConstContext::trans_def(self.ccx, def_id, substs, const_args) {
|
if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic {
|
||||||
Ok(value) => self.store(dest, value, span),
|
let value = match &tcx.item_name(def_id).as_str()[..] {
|
||||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
"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
|
target
|
||||||
} else {
|
} else {
|
||||||
|
|
18
src/test/compile-fail/const-size_of-cycle.rs
Normal file
18
src/test/compile-fail/const-size_of-cycle.rs
Normal file
|
@ -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
src/test/run-pass/const-size_of-align_of.rs
Normal file
60
src/test/run-pass/const-size_of-align_of.rs
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue