Auto merge of #26848 - oli-obk:const_fn_const_eval, r=pnkfelix
this has the funky side-effect of also allowing constant evaluation of function calls to functions that are not `const fn` as long as `check_const` didn't mark that function `NOT_CONST` It's still not possible to call a normal function from a `const fn`, but let statements' initialization value can get const evaluated (this caused the fallout in the overflowing tests) we can now do this: ```rust const fn add(x: usize, y: usize) -> usize { x + y } const ARR: [i32; add(1, 2)] = [5, 6, 7]; ``` also added a test for destructuring in const fn args ```rust const fn i((a, b): (u32, u32)) -> u32 { a + b } //~ ERROR: E0022 ``` This is a **[breaking change]**, since it turns some runtime panics into compile-time errors. This statement is true for ANY improvement to the const evaluator.
This commit is contained in:
commit
540fd3aa71
@ -473,7 +473,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
|
||||
if !self.qualif.intersects(ConstQualif::NOT_CONST) {
|
||||
match const_eval::eval_const_expr_partial(
|
||||
self.tcx, ex, ExprTypeChecked) {
|
||||
self.tcx, ex, ExprTypeChecked, None) {
|
||||
Ok(_) => {}
|
||||
Err(msg) => {
|
||||
self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,
|
||||
|
@ -272,7 +272,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
|
||||
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
|
||||
front_util::walk_pat(pat, |p| {
|
||||
if let hir::PatLit(ref expr) = p.node {
|
||||
match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked) {
|
||||
match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked, None) {
|
||||
Ok(ConstVal::Float(f)) if f.is_nan() => {
|
||||
span_warn!(cx.tcx.sess, p.span, E0003,
|
||||
"unmatchable NaN in pattern, \
|
||||
|
@ -19,13 +19,14 @@ use front::map::blocks::FnLikeNode;
|
||||
use metadata::csearch;
|
||||
use metadata::inline::InlinedItem;
|
||||
use middle::{astencode, def, infer, subst, traits};
|
||||
use middle::def_id::{DefId};
|
||||
use middle::def_id::DefId;
|
||||
use middle::pat_util::def_to_path;
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::astconv_util::ast_ty_to_prim_ty;
|
||||
use util::num::ToPrimitive;
|
||||
use util::nodemap::NodeMap;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::{ast, abi};
|
||||
use rustc_front::hir::Expr;
|
||||
use rustc_front::hir;
|
||||
use rustc_front::visit::FnKind;
|
||||
@ -253,6 +254,7 @@ pub enum ConstVal {
|
||||
Bool(bool),
|
||||
Struct(ast::NodeId),
|
||||
Tuple(ast::NodeId),
|
||||
Function(DefId),
|
||||
}
|
||||
|
||||
/// Note that equality for `ConstVal` means that the it is the same
|
||||
@ -260,7 +262,6 @@ pub enum ConstVal {
|
||||
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
|
||||
/// are considering unequal).
|
||||
impl PartialEq for ConstVal {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn eq(&self, other: &ConstVal) -> bool {
|
||||
match (self, other) {
|
||||
(&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
|
||||
@ -271,6 +272,7 @@ impl PartialEq for ConstVal {
|
||||
(&Bool(a), &Bool(b)) => a == b,
|
||||
(&Struct(a), &Struct(b)) => a == b,
|
||||
(&Tuple(a), &Tuple(b)) => a == b,
|
||||
(&Function(a), &Function(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -288,6 +290,7 @@ impl ConstVal {
|
||||
Bool(_) => "boolean",
|
||||
Struct(_) => "struct",
|
||||
Tuple(_) => "tuple",
|
||||
Function(_) => "function definition",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,12 +353,13 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<hir::Pat>
|
||||
}
|
||||
|
||||
pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> ConstVal {
|
||||
match eval_const_expr_partial(tcx, e, ExprTypeChecked) {
|
||||
match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) {
|
||||
Ok(r) => r,
|
||||
Err(s) => tcx.sess.span_fatal(s.span, &s.description())
|
||||
}
|
||||
}
|
||||
|
||||
pub type FnArgMap<'a> = Option<&'a NodeMap<ConstVal>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConstEvalErr {
|
||||
@ -739,7 +743,8 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
|
||||
/// computing the length of an array. (See also the FIXME above EvalHint.)
|
||||
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
e: &Expr,
|
||||
ty_hint: EvalHint<'tcx>) -> EvalResult {
|
||||
ty_hint: EvalHint<'tcx>,
|
||||
fn_args: FnArgMap) -> EvalResult {
|
||||
fn fromb(b: bool) -> ConstVal { Int(b as i64) }
|
||||
|
||||
// Try to compute the type of the expression based on the EvalHint.
|
||||
@ -776,7 +781,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
|
||||
let result = match e.node {
|
||||
hir::ExprUnary(hir::UnNeg, ref inner) => {
|
||||
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
|
||||
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) {
|
||||
Float(f) => Float(-f),
|
||||
Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
|
||||
Uint(i) => {
|
||||
@ -786,7 +791,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
}
|
||||
}
|
||||
hir::ExprUnary(hir::UnNot, ref inner) => {
|
||||
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
|
||||
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) {
|
||||
Int(i) => Int(!i),
|
||||
Uint(i) => const_uint_not(i, expr_uint_type),
|
||||
Bool(b) => Bool(!b),
|
||||
@ -804,8 +809,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
}
|
||||
_ => ty_hint
|
||||
};
|
||||
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint)),
|
||||
try!(eval_const_expr_partial(tcx, &**b, b_ty))) {
|
||||
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)),
|
||||
try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) {
|
||||
(Float(a), Float(b)) => {
|
||||
match op.node {
|
||||
hir::BiAdd => Float(a + b),
|
||||
@ -912,7 +917,7 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
}
|
||||
};
|
||||
|
||||
let val = try!(eval_const_expr_partial(tcx, &**base, base_hint));
|
||||
let val = try!(eval_const_expr_partial(tcx, &**base, base_hint, fn_args));
|
||||
match cast_const(tcx, val, ety) {
|
||||
Ok(val) => val,
|
||||
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
|
||||
@ -990,6 +995,16 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
Some(def::DefStruct(_)) => {
|
||||
return Ok(ConstVal::Struct(e.id))
|
||||
}
|
||||
Some(def::DefLocal(_, id)) => {
|
||||
debug!("DefLocal({:?}): {:?}", id, fn_args);
|
||||
if let Some(val) = fn_args.and_then(|args| args.get(&id)) {
|
||||
return Ok(val.clone());
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
},
|
||||
Some(def::DefFn(id, _)) => return Ok(Function(id)),
|
||||
// FIXME: implement const methods?
|
||||
_ => (None, None)
|
||||
};
|
||||
let const_expr = match const_expr {
|
||||
@ -1007,14 +1022,76 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
} else {
|
||||
ty_hint
|
||||
};
|
||||
try!(eval_const_expr_partial(tcx, const_expr, item_hint))
|
||||
try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args))
|
||||
}
|
||||
hir::ExprCall(ref callee, ref args) => {
|
||||
let sub_ty_hint = if let ExprTypeChecked = ty_hint {
|
||||
ExprTypeChecked
|
||||
} else {
|
||||
UncheckedExprNoHint // we cannot reason about UncheckedExprHint here
|
||||
};
|
||||
let (
|
||||
decl,
|
||||
unsafety,
|
||||
abi,
|
||||
block,
|
||||
constness,
|
||||
) = match try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)) {
|
||||
Function(did) => if did.is_local() {
|
||||
match tcx.map.find(did.index.as_u32()) {
|
||||
Some(ast_map::NodeItem(it)) => match it.node {
|
||||
hir::ItemFn(
|
||||
ref decl,
|
||||
unsafety,
|
||||
constness,
|
||||
abi,
|
||||
_, // ducktype generics? types are funky in const_eval
|
||||
ref block,
|
||||
) => (decl, unsafety, abi, block, constness),
|
||||
_ => signal!(e, NonConstPath),
|
||||
},
|
||||
_ => signal!(e, NonConstPath),
|
||||
}
|
||||
} else {
|
||||
signal!(e, NonConstPath)
|
||||
},
|
||||
_ => signal!(e, NonConstPath),
|
||||
};
|
||||
if let ExprTypeChecked = ty_hint {
|
||||
// no need to check for constness... either check_const
|
||||
// already forbids this or we const eval over whatever
|
||||
// we want
|
||||
} else {
|
||||
// we don't know much about the function, so we force it to be a const fn
|
||||
// so compilation will fail later in case the const fn's body is not const
|
||||
assert_eq!(constness, hir::Constness::Const)
|
||||
}
|
||||
assert_eq!(decl.inputs.len(), args.len());
|
||||
assert_eq!(unsafety, hir::Unsafety::Normal);
|
||||
assert_eq!(abi, abi::Abi::Rust);
|
||||
|
||||
let mut call_args = NodeMap();
|
||||
for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) {
|
||||
let arg_val = try!(eval_const_expr_partial(
|
||||
tcx,
|
||||
arg_expr,
|
||||
sub_ty_hint,
|
||||
fn_args
|
||||
));
|
||||
debug!("const call arg: {:?}", arg);
|
||||
let old = call_args.insert(arg.pat.id, arg_val);
|
||||
assert!(old.is_none());
|
||||
}
|
||||
let result = block.expr.as_ref().unwrap();
|
||||
debug!("const call({:?})", call_args);
|
||||
try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args)))
|
||||
},
|
||||
hir::ExprLit(ref lit) => {
|
||||
lit_to_const(&**lit, ety)
|
||||
}
|
||||
hir::ExprBlock(ref block) => {
|
||||
match block.expr {
|
||||
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint)),
|
||||
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)),
|
||||
None => Int(0)
|
||||
}
|
||||
}
|
||||
@ -1026,11 +1103,11 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
} else {
|
||||
UncheckedExprNoHint
|
||||
};
|
||||
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
|
||||
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
|
||||
if let Tuple(tup_id) = c {
|
||||
if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
|
||||
if index.node < fields.len() {
|
||||
return eval_const_expr_partial(tcx, &fields[index.node], base_hint)
|
||||
return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args)
|
||||
} else {
|
||||
signal!(e, TupleIndexOutOfBounds);
|
||||
}
|
||||
@ -1051,14 +1128,14 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
} else {
|
||||
UncheckedExprNoHint
|
||||
};
|
||||
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
|
||||
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) {
|
||||
if let Struct(struct_id) = c {
|
||||
if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
|
||||
// Check that the given field exists and evaluate it
|
||||
// if the idents are compared run-pass/issue-19244 fails
|
||||
if let Some(f) = fields.iter().find(|f| f.name.node
|
||||
== field_name.node) {
|
||||
return eval_const_expr_partial(tcx, &*f.expr, base_hint)
|
||||
return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args)
|
||||
} else {
|
||||
signal!(e, MissingStructField);
|
||||
}
|
||||
@ -1237,14 +1314,14 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
|
||||
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
a: &Expr,
|
||||
b: &Expr) -> Option<Ordering> {
|
||||
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked) {
|
||||
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tcx.sess.span_err(a.span, &e.description());
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked) {
|
||||
let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
tcx.sess.span_err(b.span, &e.description());
|
||||
|
@ -335,7 +335,7 @@ impl<'tcx> ty::ctxt<'tcx> {
|
||||
/// Returns the repeat count for a repeating vector expression.
|
||||
pub fn eval_repeat_count(&self, count_expr: &hir::Expr) -> usize {
|
||||
let hint = UncheckedExprHint(self.types.usize);
|
||||
match const_eval::eval_const_expr_partial(self, count_expr, hint) {
|
||||
match const_eval::eval_const_expr_partial(self, count_expr, hint, None) {
|
||||
Ok(val) => {
|
||||
let found = match val {
|
||||
ConstVal::Uint(count) => return count as usize,
|
||||
|
@ -120,7 +120,7 @@ impl LateLintPass for TypeLimits {
|
||||
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
|
||||
else { false }
|
||||
} else {
|
||||
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
|
||||
match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) {
|
||||
Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
|
||||
Ok(ConstVal::Uint(shift)) => { shift >= bits },
|
||||
_ => { false }
|
||||
@ -674,4 +674,3 @@ impl LateLintPass for ImproperCTypes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,8 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> {
|
||||
let opt_value =
|
||||
const_eval::eval_const_expr_partial(
|
||||
cx.tcx, const_expr,
|
||||
const_eval::EvalHint::ExprTypeChecked);
|
||||
const_eval::EvalHint::ExprTypeChecked,
|
||||
None);
|
||||
let literal = if let Ok(value) = opt_value {
|
||||
Literal::Value { value: value }
|
||||
} else {
|
||||
|
@ -664,10 +664,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
|
||||
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node)
|
||||
},
|
||||
|
||||
hir::ExprIndex(ref base, ref index) => {
|
||||
let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst));
|
||||
let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked) {
|
||||
let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked, None) {
|
||||
Ok(ConstVal::Int(i)) => i as u64,
|
||||
Ok(ConstVal::Uint(u)) => u,
|
||||
_ => cx.sess().span_bug(index.span,
|
||||
|
@ -1673,7 +1673,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
||||
}
|
||||
hir::TyFixedLengthVec(ref ty, ref e) => {
|
||||
let hint = UncheckedExprHint(tcx.types.usize);
|
||||
match const_eval::eval_const_expr_partial(tcx, &e, hint) {
|
||||
match const_eval::eval_const_expr_partial(tcx, &e, hint, None) {
|
||||
Ok(r) => {
|
||||
match r {
|
||||
ConstVal::Int(i) =>
|
||||
|
@ -1148,7 +1148,7 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
debug!("disr expr, checking {}", pprust::expr_to_string(e));
|
||||
|
||||
let hint = UncheckedExprHint(repr_ty);
|
||||
match const_eval::eval_const_expr_partial(tcx, e, hint) {
|
||||
match const_eval::eval_const_expr_partial(tcx, e, hint, None) {
|
||||
Ok(ConstVal::Int(val)) => Some(val as ty::Disr),
|
||||
Ok(ConstVal::Uint(val)) => Some(val as ty::Disr),
|
||||
Ok(_) => {
|
||||
|
@ -14,7 +14,7 @@
|
||||
struct S(i32);
|
||||
|
||||
const CONSTANT: S = S(0);
|
||||
//~^ ERROR: constant evaluation error: unsupported constant expr
|
||||
//~^ ERROR: constant evaluation error: non-constant path in constant expression [E0080]
|
||||
|
||||
enum E {
|
||||
V = CONSTANT,
|
||||
|
18
src/test/compile-fail/const-fn-destructuring-arg.rs
Normal file
18
src/test/compile-fail/const-fn-destructuring-arg.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// test that certain things are disallowed in const fn signatures
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
// no destructuring
|
||||
const fn i((a, b): (u32, u32)) -> u32 { a + b } //~ ERROR: E0022
|
||||
|
||||
fn main() {}
|
@ -17,5 +17,5 @@ extern crate const_fn_lib;
|
||||
use const_fn_lib::foo;
|
||||
|
||||
fn main() {
|
||||
let x: [usize; foo()] = []; //~ ERROR unsupported constant expr
|
||||
let x: [usize; foo()] = []; //~ ERROR non-constant path in constant expr
|
||||
}
|
||||
|
19
src/test/run-pass/const-fn-const-eval.rs
Normal file
19
src/test/run-pass/const-fn-const-eval.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2012-2014 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)]
|
||||
|
||||
const fn add(x: usize, y: usize) -> usize {
|
||||
x + y
|
||||
}
|
||||
|
||||
const ARR: [i32; add(1, 2)] = [5, 6, 7];
|
||||
|
||||
pub fn main() {}
|
@ -13,9 +13,6 @@
|
||||
// Check that we do *not* overflow on a number of edge cases.
|
||||
// (compare with test/run-fail/overflowing-{lsh,rsh}*.rs)
|
||||
|
||||
// (Work around constant-evaluation)
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
test_left_shift();
|
||||
test_right_shift();
|
||||
@ -26,34 +23,34 @@ fn test_left_shift() {
|
||||
|
||||
macro_rules! tests {
|
||||
($iN:ty, $uN:ty, $max_rhs:expr, $expect_i:expr, $expect_u:expr) => { {
|
||||
let x = (1 as $iN) << id(0);
|
||||
let x = (1 as $iN) << 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = (1 as $uN) << id(0);
|
||||
let x = (1 as $uN) << 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = (1 as $iN) << id($max_rhs);
|
||||
let x = (1 as $iN) << $max_rhs;
|
||||
assert_eq!(x, $expect_i);
|
||||
let x = (1 as $uN) << id($max_rhs);
|
||||
let x = (1 as $uN) << $max_rhs;
|
||||
assert_eq!(x, $expect_u);
|
||||
// high-order bits on LHS are silently discarded without panic.
|
||||
let x = (3 as $iN) << id($max_rhs);
|
||||
let x = (3 as $iN) << $max_rhs;
|
||||
assert_eq!(x, $expect_i);
|
||||
let x = (3 as $uN) << id($max_rhs);
|
||||
let x = (3 as $uN) << $max_rhs;
|
||||
assert_eq!(x, $expect_u);
|
||||
} }
|
||||
}
|
||||
|
||||
let x = 1_i8 << id(0);
|
||||
let x = 1_i8 << 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = 1_u8 << id(0);
|
||||
let x = 1_u8 << 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = 1_i8 << id(7);
|
||||
let x = 1_i8 << 7;
|
||||
assert_eq!(x, std::i8::MIN);
|
||||
let x = 1_u8 << id(7);
|
||||
let x = 1_u8 << 7;
|
||||
assert_eq!(x, 0x80);
|
||||
// high-order bits on LHS are silently discarded without panic.
|
||||
let x = 3_i8 << id(7);
|
||||
let x = 3_i8 << 7;
|
||||
assert_eq!(x, std::i8::MIN);
|
||||
let x = 3_u8 << id(7);
|
||||
let x = 3_u8 << 7;
|
||||
assert_eq!(x, 0x80);
|
||||
|
||||
// above is (approximately) expanded from:
|
||||
@ -71,23 +68,23 @@ fn test_right_shift() {
|
||||
($iN:ty, $uN:ty, $max_rhs:expr,
|
||||
$signbit_i:expr, $highbit_i:expr, $highbit_u:expr) =>
|
||||
{ {
|
||||
let x = (1 as $iN) >> id(0);
|
||||
let x = (1 as $iN) >> 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = (1 as $uN) >> id(0);
|
||||
let x = (1 as $uN) >> 0;
|
||||
assert_eq!(x, 1);
|
||||
let x = ($highbit_i) >> id($max_rhs-1);
|
||||
let x = ($highbit_i) >> $max_rhs-1;
|
||||
assert_eq!(x, 1);
|
||||
let x = ($highbit_u) >> id($max_rhs);
|
||||
let x = ($highbit_u) >> $max_rhs;
|
||||
assert_eq!(x, 1);
|
||||
// sign-bit is carried by arithmetic right shift
|
||||
let x = ($signbit_i) >> id($max_rhs);
|
||||
let x = ($signbit_i) >> $max_rhs;
|
||||
assert_eq!(x, -1);
|
||||
// low-order bits on LHS are silently discarded without panic.
|
||||
let x = ($highbit_i + 1) >> id($max_rhs-1);
|
||||
let x = ($highbit_i + 1) >> $max_rhs-1;
|
||||
assert_eq!(x, 1);
|
||||
let x = ($highbit_u + 1) >> id($max_rhs);
|
||||
let x = ($highbit_u + 1) >> $max_rhs;
|
||||
assert_eq!(x, 1);
|
||||
let x = ($signbit_i + 1) >> id($max_rhs);
|
||||
let x = ($signbit_i + 1) >> $max_rhs;
|
||||
assert_eq!(x, -1);
|
||||
} }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user