implement RFC 1229
const eval errors outside of true constant enviroments are not reported anymore, but instead forwarded to a lint.
This commit is contained in:
parent
81b3b27cf5
commit
09d3adf52e
23 changed files with 376 additions and 194 deletions
|
@ -16,6 +16,12 @@
|
||||||
|
|
||||||
use lint::{LintPass, LateLintPass, LintArray};
|
use lint::{LintPass, LateLintPass, LintArray};
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
pub CONST_ERR,
|
||||||
|
Warn,
|
||||||
|
"constant evaluation detected erroneous expression"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub UNUSED_IMPORTS,
|
pub UNUSED_IMPORTS,
|
||||||
Warn,
|
Warn,
|
||||||
|
@ -134,7 +140,8 @@ impl LintPass for HardwiredLints {
|
||||||
VARIANT_SIZE_DIFFERENCES,
|
VARIANT_SIZE_DIFFERENCES,
|
||||||
FAT_PTR_TRANSMUTES,
|
FAT_PTR_TRANSMUTES,
|
||||||
TRIVIAL_CASTS,
|
TRIVIAL_CASTS,
|
||||||
TRIVIAL_NUMERIC_CASTS
|
TRIVIAL_NUMERIC_CASTS,
|
||||||
|
CONST_ERR
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,8 +176,8 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
||||||
if mode == Mode::ConstFn {
|
if mode == Mode::ConstFn {
|
||||||
for arg in &fd.inputs {
|
for arg in &fd.inputs {
|
||||||
match arg.pat.node {
|
match arg.pat.node {
|
||||||
hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {}
|
|
||||||
hir::PatWild(_) => {}
|
hir::PatWild(_) => {}
|
||||||
|
hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {}
|
||||||
_ => {
|
_ => {
|
||||||
span_err!(self.tcx.sess, arg.pat.span, E0022,
|
span_err!(self.tcx.sess, arg.pat.span, E0022,
|
||||||
"arguments of constant functions can only \
|
"arguments of constant functions can only \
|
||||||
|
@ -476,9 +476,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||||
self.tcx, ex, ExprTypeChecked) {
|
self.tcx, ex, ExprTypeChecked) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
span_err!(self.tcx.sess, msg.span, E0020,
|
self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,
|
||||||
"{} in a constant expression",
|
msg.span,
|
||||||
msg.description())
|
msg.description().into_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,19 +275,30 @@ impl<'a, 'tcx> Opt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trans<'blk>(&self, mut bcx: Block<'blk, 'tcx>) -> OptResult<'blk, 'tcx> {
|
fn trans<'blk>(&self, mut bcx: Block<'blk, 'tcx>) -> OptResult<'blk, 'tcx> {
|
||||||
|
use trans::consts::TrueConst::Yes;
|
||||||
let _icx = push_ctxt("match::trans_opt");
|
let _icx = push_ctxt("match::trans_opt");
|
||||||
let ccx = bcx.ccx();
|
let ccx = bcx.ccx();
|
||||||
match *self {
|
match *self {
|
||||||
ConstantValue(ConstantExpr(lit_expr), _) => {
|
ConstantValue(ConstantExpr(lit_expr), _) => {
|
||||||
let lit_ty = bcx.tcx().node_id_to_type(lit_expr.id);
|
let lit_ty = bcx.tcx().node_id_to_type(lit_expr.id);
|
||||||
let (llval, _) = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs, None);
|
let expr = consts::const_expr(ccx, &*lit_expr, bcx.fcx.param_substs, None, Yes);
|
||||||
|
let llval = match expr {
|
||||||
|
Ok((llval, _)) => llval,
|
||||||
|
Err(err) => bcx.ccx().sess().span_fatal(lit_expr.span, &err.description()),
|
||||||
|
};
|
||||||
let lit_datum = immediate_rvalue(llval, lit_ty);
|
let lit_datum = immediate_rvalue(llval, lit_ty);
|
||||||
let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
|
let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
|
||||||
SingleResult(Result::new(bcx, lit_datum.val))
|
SingleResult(Result::new(bcx, lit_datum.val))
|
||||||
}
|
}
|
||||||
ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2), _) => {
|
ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2), _) => {
|
||||||
let (l1, _) = consts::const_expr(ccx, &**l1, bcx.fcx.param_substs, None);
|
let l1 = match consts::const_expr(ccx, &**l1, bcx.fcx.param_substs, None, Yes) {
|
||||||
let (l2, _) = consts::const_expr(ccx, &**l2, bcx.fcx.param_substs, None);
|
Ok((l1, _)) => l1,
|
||||||
|
Err(err) => bcx.ccx().sess().span_fatal(l1.span, &err.description()),
|
||||||
|
};
|
||||||
|
let l2 = match consts::const_expr(ccx, &**l2, bcx.fcx.param_substs, None, Yes) {
|
||||||
|
Ok((l2, _)) => l2,
|
||||||
|
Err(err) => bcx.ccx().sess().span_fatal(l2.span, &err.description()),
|
||||||
|
};
|
||||||
RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
|
RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
|
||||||
}
|
}
|
||||||
Variant(disr_val, ref repr, _, _) => {
|
Variant(disr_val, ref repr, _, _) => {
|
||||||
|
|
|
@ -2125,7 +2125,10 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
|
||||||
let mut v = TransItemVisitor{ ccx: ccx };
|
let mut v = TransItemVisitor{ ccx: ccx };
|
||||||
v.visit_expr(&**expr);
|
v.visit_expr(&**expr);
|
||||||
|
|
||||||
let g = consts::trans_static(ccx, m, expr, item.id, &item.attrs);
|
let g = match consts::trans_static(ccx, m, expr, item.id, &item.attrs) {
|
||||||
|
Ok(g) => g,
|
||||||
|
Err(err) => ccx.tcx().sess.span_fatal(expr.span, &err.description()),
|
||||||
|
};
|
||||||
set_global_section(ccx, g, item);
|
set_global_section(ccx, g, item);
|
||||||
update_linkage(ccx, g, Some(item.id), OriginalTranslation);
|
update_linkage(ccx, g, Some(item.id), OriginalTranslation);
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,7 +15,7 @@ use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
|
||||||
use llvm::{InternalLinkage, ValueRef, Bool, True};
|
use llvm::{InternalLinkage, ValueRef, Bool, True};
|
||||||
use metadata::cstore::LOCAL_CRATE;
|
use metadata::cstore::LOCAL_CRATE;
|
||||||
use middle::{check_const, def};
|
use middle::{check_const, def};
|
||||||
use middle::const_eval::{self, ConstVal};
|
use middle::const_eval::{self, ConstVal, ConstEvalErr};
|
||||||
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
|
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
|
||||||
use middle::const_eval::{const_int_checked_add, const_uint_checked_add};
|
use middle::const_eval::{const_int_checked_add, const_uint_checked_add};
|
||||||
use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub};
|
use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub};
|
||||||
|
@ -29,7 +29,10 @@ use middle::const_eval::eval_const_expr_partial;
|
||||||
use middle::def_id::DefId;
|
use middle::def_id::DefId;
|
||||||
use trans::{adt, closure, debuginfo, expr, inline, machine};
|
use trans::{adt, closure, debuginfo, expr, inline, machine};
|
||||||
use trans::base::{self, push_ctxt};
|
use trans::base::{self, push_ctxt};
|
||||||
use trans::common::*;
|
use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty};
|
||||||
|
use trans::common::{type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt};
|
||||||
|
use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint};
|
||||||
|
use trans::common::{type_is_fat_ptr, Field, C_vector, C_array, C_null, ExprId, MethodCallKey};
|
||||||
use trans::declare;
|
use trans::declare;
|
||||||
use trans::monomorphize;
|
use trans::monomorphize;
|
||||||
use trans::type_::Type;
|
use trans::type_::Type;
|
||||||
|
@ -44,6 +47,7 @@ use util::nodemap::NodeMap;
|
||||||
use rustc_front::hir;
|
use rustc_front::hir;
|
||||||
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::borrow::Cow;
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
@ -191,7 +195,8 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
node: ExprOrMethodCall,
|
node: ExprOrMethodCall,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
arg_vals: &[ValueRef],
|
arg_vals: &[ValueRef],
|
||||||
param_substs: &'tcx Substs<'tcx>) -> ValueRef {
|
param_substs: &'tcx Substs<'tcx>,
|
||||||
|
trueconst: TrueConst) -> Result<ValueRef, ConstEvalFailure> {
|
||||||
let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id);
|
let fn_like = const_eval::lookup_const_fn_by_id(ccx.tcx(), def_id);
|
||||||
let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call");
|
let fn_like = fn_like.expect("lookup_const_fn_by_id failed in const_fn_call");
|
||||||
|
|
||||||
|
@ -204,9 +209,9 @@ fn const_fn_call<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs));
|
let substs = ccx.tcx().mk_substs(node_id_substs(ccx, node, param_substs));
|
||||||
match fn_like.body().expr {
|
match fn_like.body().expr {
|
||||||
Some(ref expr) => {
|
Some(ref expr) => {
|
||||||
const_expr(ccx, &**expr, substs, Some(&fn_args)).0
|
const_expr(ccx, &**expr, substs, Some(&fn_args), trueconst).map(|(res, _)| res)
|
||||||
}
|
},
|
||||||
None => C_nil(ccx)
|
None => Ok(C_nil(ccx)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,19 +234,57 @@ pub fn get_const_expr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ConstEvalFailure {
|
||||||
|
/// in case the const evaluator failed on something that panic at runtime
|
||||||
|
/// as defined in RFC 1229
|
||||||
|
Runtime(ConstEvalErr),
|
||||||
|
// in case we found a true constant
|
||||||
|
Compiletime(ConstEvalErr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstEvalFailure {
|
||||||
|
fn into_inner(self) -> ConstEvalErr {
|
||||||
|
match self {
|
||||||
|
Runtime(e) => e,
|
||||||
|
Compiletime(e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn description(&self) -> Cow<str> {
|
||||||
|
match self {
|
||||||
|
&Runtime(ref e) => e.description(),
|
||||||
|
&Compiletime(ref e) => e.description(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum TrueConst {
|
||||||
|
Yes, No
|
||||||
|
}
|
||||||
|
|
||||||
|
use self::ConstEvalFailure::*;
|
||||||
|
|
||||||
fn get_const_val(ccx: &CrateContext,
|
fn get_const_val(ccx: &CrateContext,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
ref_expr: &hir::Expr) -> ValueRef {
|
ref_expr: &hir::Expr) -> Result<ValueRef, ConstEvalFailure> {
|
||||||
let expr = get_const_expr(ccx, def_id, ref_expr);
|
let expr = get_const_expr(ccx, def_id, ref_expr);
|
||||||
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
||||||
get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(), empty_substs)
|
match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(),
|
||||||
|
empty_substs, TrueConst::Yes) {
|
||||||
|
Err(Runtime(err)) => {
|
||||||
|
ccx.tcx().sess.span_err(expr.span, &err.description());
|
||||||
|
Err(Compiletime(err))
|
||||||
|
},
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
qualif: check_const::ConstQualif,
|
qualif: check_const::ConstQualif,
|
||||||
param_substs: &'tcx Substs<'tcx>)
|
param_substs: &'tcx Substs<'tcx>,
|
||||||
-> ValueRef {
|
trueconst: TrueConst)
|
||||||
|
-> Result<ValueRef, ConstEvalFailure> {
|
||||||
debug!("get_const_expr_as_global: {:?}", expr.id);
|
debug!("get_const_expr_as_global: {:?}", expr.id);
|
||||||
// Special-case constants to cache a common global for all uses.
|
// Special-case constants to cache a common global for all uses.
|
||||||
match expr.node {
|
match expr.node {
|
||||||
|
@ -263,7 +306,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
|
|
||||||
let key = (expr.id, param_substs);
|
let key = (expr.id, param_substs);
|
||||||
match ccx.const_values().borrow().get(&key) {
|
match ccx.const_values().borrow().get(&key) {
|
||||||
Some(&val) => return val,
|
Some(&val) => return Ok(val),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
|
let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
|
||||||
|
@ -271,9 +314,12 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
|
let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
|
||||||
// Avoid autorefs as they would create global instead of stack
|
// Avoid autorefs as they would create global instead of stack
|
||||||
// references, even when only the latter are correct.
|
// references, even when only the latter are correct.
|
||||||
const_expr_unadjusted(ccx, expr, ty, param_substs, None)
|
try!(const_expr_unadjusted(ccx, expr, ty, param_substs, None, trueconst))
|
||||||
} else {
|
} else {
|
||||||
const_expr(ccx, expr, param_substs, None).0
|
match const_expr(ccx, expr, param_substs, None, trueconst) {
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
Ok((ok, _)) => ok,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
||||||
|
@ -288,17 +334,18 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||||
|
|
||||||
let lvalue = addr_of(ccx, val, type_of::align_of(ccx, ty), "const");
|
let lvalue = addr_of(ccx, val, type_of::align_of(ccx, ty), "const");
|
||||||
ccx.const_values().borrow_mut().insert(key, lvalue);
|
ccx.const_values().borrow_mut().insert(key, lvalue);
|
||||||
lvalue
|
Ok(lvalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
e: &hir::Expr,
|
e: &hir::Expr,
|
||||||
param_substs: &'tcx Substs<'tcx>,
|
param_substs: &'tcx Substs<'tcx>,
|
||||||
fn_args: FnArgMap)
|
fn_args: FnArgMap,
|
||||||
-> (ValueRef, Ty<'tcx>) {
|
trueconst: TrueConst)
|
||||||
|
-> Result<(ValueRef, Ty<'tcx>), ConstEvalFailure> {
|
||||||
let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs,
|
let ety = monomorphize::apply_param_substs(cx.tcx(), param_substs,
|
||||||
&cx.tcx().expr_ty(e));
|
&cx.tcx().expr_ty(e));
|
||||||
let llconst = const_expr_unadjusted(cx, e, ety, param_substs, fn_args);
|
let llconst = try!(const_expr_unadjusted(cx, e, ety, param_substs, fn_args, trueconst));
|
||||||
let mut llconst = llconst;
|
let mut llconst = llconst;
|
||||||
let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs,
|
let mut ety_adjusted = monomorphize::apply_param_substs(cx.tcx(), param_substs,
|
||||||
&cx.tcx().expr_ty_adjusted(e));
|
&cx.tcx().expr_ty_adjusted(e));
|
||||||
|
@ -393,11 +440,11 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
e, ety_adjusted,
|
e, ety_adjusted,
|
||||||
csize, tsize));
|
csize, tsize));
|
||||||
}
|
}
|
||||||
(llconst, ety_adjusted)
|
Ok((llconst, ety_adjusted))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
te: ValueRef) {
|
te: ValueRef, trueconst: TrueConst) -> Result<(), ConstEvalFailure> {
|
||||||
// The only kind of unary expression that we check for validity
|
// The only kind of unary expression that we check for validity
|
||||||
// here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`).
|
// here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`).
|
||||||
if let hir::ExprUnary(hir::UnNeg, ref inner_e) = e.node {
|
if let hir::ExprUnary(hir::UnNeg, ref inner_e) = e.node {
|
||||||
|
@ -410,13 +457,13 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
//
|
//
|
||||||
// Catch this up front by looking for ExprLit directly,
|
// Catch this up front by looking for ExprLit directly,
|
||||||
// and just accepting it.
|
// and just accepting it.
|
||||||
if let hir::ExprLit(_) = inner_e.node { return; }
|
if let hir::ExprLit(_) = inner_e.node { return Ok(()); }
|
||||||
|
|
||||||
let result = match t.sty {
|
let result = match t.sty {
|
||||||
ty::TyInt(int_type) => {
|
ty::TyInt(int_type) => {
|
||||||
let input = match const_to_opt_int(te) {
|
let input = match const_to_opt_int(te) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return,
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
const_int_checked_neg(
|
const_int_checked_neg(
|
||||||
input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
|
input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
|
||||||
|
@ -424,31 +471,51 @@ fn check_unary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
ty::TyUint(uint_type) => {
|
ty::TyUint(uint_type) => {
|
||||||
let input = match const_to_opt_uint(te) {
|
let input = match const_to_opt_uint(te) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return,
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
const_uint_checked_neg(
|
const_uint_checked_neg(
|
||||||
input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
|
input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
const_err(cx, e, result, trueconst)
|
||||||
// We do not actually care about a successful result.
|
} else {
|
||||||
if let Err(err) = result {
|
Ok(())
|
||||||
cx.tcx().sess.span_err(e.span, &err.description());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn const_err(cx: &CrateContext,
|
||||||
|
e: &hir::Expr,
|
||||||
|
result: Result<ConstVal, ConstEvalErr>,
|
||||||
|
trueconst: TrueConst)
|
||||||
|
-> Result<(), ConstEvalFailure> {
|
||||||
|
match (result, trueconst) {
|
||||||
|
(Ok(_), _) => {
|
||||||
|
// We do not actually care about a successful result.
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
(Err(err), TrueConst::Yes) => {
|
||||||
|
cx.tcx().sess.span_err(e.span, &err.description());
|
||||||
|
Err(Compiletime(err))
|
||||||
|
},
|
||||||
|
(Err(err), TrueConst::No) => {
|
||||||
|
cx.tcx().sess.span_warn(e.span, &err.description());
|
||||||
|
Err(Runtime(err))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
te1: ValueRef, te2: ValueRef) {
|
te1: ValueRef, te2: ValueRef,
|
||||||
let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { return };
|
trueconst: TrueConst) -> Result<(), ConstEvalFailure> {
|
||||||
|
let b = if let hir::ExprBinary(b, _, _) = e.node { b } else { unreachable!() };
|
||||||
|
|
||||||
let result = match t.sty {
|
let result = match t.sty {
|
||||||
ty::TyInt(int_type) => {
|
ty::TyInt(int_type) => {
|
||||||
let (lhs, rhs) = match (const_to_opt_int(te1),
|
let (lhs, rhs) = match (const_to_opt_int(te1),
|
||||||
const_to_opt_int(te2)) {
|
const_to_opt_int(te2)) {
|
||||||
(Some(v1), Some(v2)) => (v1, v2),
|
(Some(v1), Some(v2)) => (v1, v2),
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
|
let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
|
||||||
|
@ -460,14 +527,14 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
hir::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
|
hir::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
|
||||||
hir::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
|
hir::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
|
||||||
hir::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
|
hir::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty::TyUint(uint_type) => {
|
ty::TyUint(uint_type) => {
|
||||||
let (lhs, rhs) = match (const_to_opt_uint(te1),
|
let (lhs, rhs) = match (const_to_opt_uint(te1),
|
||||||
const_to_opt_uint(te2)) {
|
const_to_opt_uint(te2)) {
|
||||||
(Some(v1), Some(v2)) => (v1, v2),
|
(Some(v1), Some(v2)) => (v1, v2),
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
|
let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
|
||||||
|
@ -479,43 +546,44 @@ fn check_binary_expr_validity(cx: &CrateContext, e: &hir::Expr, t: Ty,
|
||||||
hir::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
|
hir::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
|
||||||
hir::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
|
hir::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
|
||||||
hir::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
|
hir::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
// We do not actually care about a successful result.
|
const_err(cx, e, result, trueconst)
|
||||||
if let Err(err) = result {
|
|
||||||
cx.tcx().sess.span_err(e.span, &err.description());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
e: &hir::Expr,
|
e: &hir::Expr,
|
||||||
ety: Ty<'tcx>,
|
ety: Ty<'tcx>,
|
||||||
param_substs: &'tcx Substs<'tcx>,
|
param_substs: &'tcx Substs<'tcx>,
|
||||||
fn_args: FnArgMap)
|
fn_args: FnArgMap,
|
||||||
-> ValueRef
|
trueconst: TrueConst)
|
||||||
|
-> Result<ValueRef, ConstEvalFailure>
|
||||||
{
|
{
|
||||||
debug!("const_expr_unadjusted(e={:?}, ety={:?}, param_substs={:?})",
|
debug!("const_expr_unadjusted(e={:?}, ety={:?}, param_substs={:?})",
|
||||||
e,
|
e,
|
||||||
ety,
|
ety,
|
||||||
param_substs);
|
param_substs);
|
||||||
|
|
||||||
let map_list = |exprs: &[P<hir::Expr>]| -> Vec<ValueRef> {
|
let map_list = |exprs: &[P<hir::Expr>]| -> Result<Vec<ValueRef>, ConstEvalFailure> {
|
||||||
exprs.iter()
|
exprs.iter()
|
||||||
.map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
|
.map(|e| const_expr(cx, &**e, param_substs, fn_args, trueconst).map(|(l, _)| l))
|
||||||
|
.collect::<Vec<Result<ValueRef, ConstEvalFailure>>>()
|
||||||
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
|
// this dance is necessary to eagerly run const_expr so all errors are reported
|
||||||
};
|
};
|
||||||
let _icx = push_ctxt("const_expr");
|
let _icx = push_ctxt("const_expr");
|
||||||
match e.node {
|
Ok(match e.node {
|
||||||
hir::ExprLit(ref lit) => {
|
hir::ExprLit(ref lit) => {
|
||||||
const_lit(cx, e, &**lit)
|
const_lit(cx, e, &**lit)
|
||||||
},
|
},
|
||||||
hir::ExprBinary(b, ref e1, ref e2) => {
|
hir::ExprBinary(b, ref e1, ref e2) => {
|
||||||
/* Neither type is bottom, and we expect them to be unified
|
/* Neither type is bottom, and we expect them to be unified
|
||||||
* already, so the following is safe. */
|
* already, so the following is safe. */
|
||||||
let (te1, ty) = const_expr(cx, &**e1, param_substs, fn_args);
|
let (te1, ty) = try!(const_expr(cx, &**e1, param_substs, fn_args, trueconst));
|
||||||
debug!("const_expr_unadjusted: te1={}, ty={:?}",
|
debug!("const_expr_unadjusted: te1={}, ty={:?}",
|
||||||
cx.tn().val_to_string(te1),
|
cx.tn().val_to_string(te1),
|
||||||
ty);
|
ty);
|
||||||
|
@ -523,9 +591,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
let is_float = ty.is_fp();
|
let is_float = ty.is_fp();
|
||||||
let signed = ty.is_signed();
|
let signed = ty.is_signed();
|
||||||
|
|
||||||
let (te2, _) = const_expr(cx, &**e2, param_substs, fn_args);
|
let (te2, _) = try!(const_expr(cx, &**e2, param_substs, fn_args, trueconst));
|
||||||
|
|
||||||
check_binary_expr_validity(cx, e, ty, te1, te2);
|
try!(check_binary_expr_validity(cx, e, ty, te1, te2, trueconst));
|
||||||
|
|
||||||
unsafe { match b.node {
|
unsafe { match b.node {
|
||||||
hir::BiAdd if is_float => llvm::LLVMConstFAdd(te1, te2),
|
hir::BiAdd if is_float => llvm::LLVMConstFAdd(te1, te2),
|
||||||
|
@ -571,9 +639,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
} } // unsafe { match b.node {
|
} } // unsafe { match b.node {
|
||||||
},
|
},
|
||||||
hir::ExprUnary(u, ref inner_e) => {
|
hir::ExprUnary(u, ref inner_e) => {
|
||||||
let (te, ty) = const_expr(cx, &**inner_e, param_substs, fn_args);
|
let (te, ty) = try!(const_expr(cx, &**inner_e, param_substs, fn_args, trueconst));
|
||||||
|
|
||||||
check_unary_expr_validity(cx, e, ty, te);
|
try!(check_unary_expr_validity(cx, e, ty, te, trueconst));
|
||||||
|
|
||||||
let is_float = ty.is_fp();
|
let is_float = ty.is_fp();
|
||||||
unsafe { match u {
|
unsafe { match u {
|
||||||
|
@ -584,21 +652,21 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
} }
|
} }
|
||||||
},
|
},
|
||||||
hir::ExprField(ref base, field) => {
|
hir::ExprField(ref base, field) => {
|
||||||
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
|
let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst));
|
||||||
let brepr = adt::represent_type(cx, bt);
|
let brepr = adt::represent_type(cx, bt);
|
||||||
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
|
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
|
||||||
let ix = vinfo.field_index(field.node);
|
let ix = vinfo.field_index(field.node);
|
||||||
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, ix)
|
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, ix)
|
||||||
},
|
},
|
||||||
hir::ExprTupField(ref base, idx) => {
|
hir::ExprTupField(ref base, idx) => {
|
||||||
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
|
let (bv, bt) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst));
|
||||||
let brepr = adt::represent_type(cx, bt);
|
let brepr = adt::represent_type(cx, bt);
|
||||||
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
|
let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
|
||||||
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node)
|
adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node)
|
||||||
},
|
},
|
||||||
|
|
||||||
hir::ExprIndex(ref base, ref index) => {
|
hir::ExprIndex(ref base, ref index) => {
|
||||||
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
|
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) {
|
||||||
Ok(ConstVal::Int(i)) => i as u64,
|
Ok(ConstVal::Int(i)) => i as u64,
|
||||||
Ok(ConstVal::Uint(u)) => u,
|
Ok(ConstVal::Uint(u)) => u,
|
||||||
|
@ -650,10 +718,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
hir::ExprCast(ref base, _) => {
|
hir::ExprCast(ref base, _) => {
|
||||||
let t_cast = ety;
|
let t_cast = ety;
|
||||||
let llty = type_of::type_of(cx, t_cast);
|
let llty = type_of::type_of(cx, t_cast);
|
||||||
let (v, t_expr) = const_expr(cx, &**base, param_substs, fn_args);
|
let (v, t_expr) = try!(const_expr(cx, &**base, param_substs, fn_args, trueconst));
|
||||||
debug!("trans_const_cast({:?} as {:?})", t_expr, t_cast);
|
debug!("trans_const_cast({:?} as {:?})", t_expr, t_cast);
|
||||||
if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) {
|
if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) {
|
||||||
return v;
|
return Ok(v);
|
||||||
}
|
}
|
||||||
if type_is_fat_ptr(cx.tcx(), t_expr) {
|
if type_is_fat_ptr(cx.tcx(), t_expr) {
|
||||||
// Fat pointer casts.
|
// Fat pointer casts.
|
||||||
|
@ -664,9 +732,9 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
ptr_ty);
|
ptr_ty);
|
||||||
if type_is_fat_ptr(cx.tcx(), t_cast) {
|
if type_is_fat_ptr(cx.tcx(), t_cast) {
|
||||||
let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]);
|
let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]);
|
||||||
return C_struct(cx, &[addr, info], false)
|
return Ok(C_struct(cx, &[addr, info], false))
|
||||||
} else {
|
} else {
|
||||||
return addr;
|
return Ok(addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe { match (
|
unsafe { match (
|
||||||
|
@ -732,35 +800,47 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
} else {
|
} else {
|
||||||
// If this isn't the address of a static, then keep going through
|
// If this isn't the address of a static, then keep going through
|
||||||
// normal constant evaluation.
|
// normal constant evaluation.
|
||||||
let (v, ty) = const_expr(cx, &**sub, param_substs, fn_args);
|
let (v, ty) = try!(const_expr(cx, &**sub, param_substs, fn_args, trueconst));
|
||||||
addr_of(cx, v, type_of::align_of(cx, ty), "ref")
|
addr_of(cx, v, type_of::align_of(cx, ty), "ref")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hir::ExprAddrOf(hir::MutMutable, ref sub) => {
|
hir::ExprAddrOf(hir::MutMutable, ref sub) => {
|
||||||
let (v, ty) = const_expr(cx, &**sub, param_substs, fn_args);
|
let (v, ty) = try!(const_expr(cx, &**sub, param_substs, fn_args, trueconst));
|
||||||
addr_of_mut(cx, v, type_of::align_of(cx, ty), "ref_mut_slice")
|
addr_of_mut(cx, v, type_of::align_of(cx, ty), "ref_mut_slice")
|
||||||
},
|
},
|
||||||
hir::ExprTup(ref es) => {
|
hir::ExprTup(ref es) => {
|
||||||
let repr = adt::represent_type(cx, ety);
|
let repr = adt::represent_type(cx, ety);
|
||||||
let vals = map_list(&es[..]);
|
let vals = try!(map_list(&es[..]));
|
||||||
adt::trans_const(cx, &*repr, 0, &vals[..])
|
adt::trans_const(cx, &*repr, 0, &vals[..])
|
||||||
},
|
},
|
||||||
hir::ExprStruct(_, ref fs, ref base_opt) => {
|
hir::ExprStruct(_, ref fs, ref base_opt) => {
|
||||||
let repr = adt::represent_type(cx, ety);
|
let repr = adt::represent_type(cx, ety);
|
||||||
|
|
||||||
let base_val = match *base_opt {
|
let base_val = match *base_opt {
|
||||||
Some(ref base) => Some(const_expr(cx, &**base, param_substs, fn_args)),
|
Some(ref base) => Some(try!(const_expr(
|
||||||
|
cx,
|
||||||
|
&**base,
|
||||||
|
param_substs,
|
||||||
|
fn_args,
|
||||||
|
trueconst,
|
||||||
|
))),
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let VariantInfo { discr, fields } = VariantInfo::of_node(cx.tcx(), ety, e.id);
|
let VariantInfo { discr, fields } = VariantInfo::of_node(cx.tcx(), ety, e.id);
|
||||||
let cs = fields.iter().enumerate().map(|(ix, &Field(f_name, _))| {
|
let cs = fields.iter().enumerate().map(|(ix, &Field(f_name, _))| {
|
||||||
match (fs.iter().find(|f| f_name == f.name.node), base_val) {
|
match (fs.iter().find(|f| f_name == f.name.node), base_val) {
|
||||||
(Some(ref f), _) => const_expr(cx, &*f.expr, param_substs, fn_args).0,
|
(Some(ref f), _) => {
|
||||||
(_, Some((bv, _))) => adt::const_get_field(cx, &*repr, bv, discr, ix),
|
const_expr(cx, &*f.expr, param_substs, fn_args, trueconst).map(|(l, _)| l)
|
||||||
|
},
|
||||||
|
(_, Some((bv, _))) => Ok(adt::const_get_field(cx, &*repr, bv, discr, ix)),
|
||||||
(_, None) => cx.sess().span_bug(e.span, "missing struct field"),
|
(_, None) => cx.sess().span_bug(e.span, "missing struct field"),
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>();
|
})
|
||||||
|
.collect::<Vec<Result<_, ConstEvalFailure>>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>,ConstEvalFailure>>();
|
||||||
|
let cs = try!(cs);
|
||||||
if ety.is_simd() {
|
if ety.is_simd() {
|
||||||
C_vector(&cs[..])
|
C_vector(&cs[..])
|
||||||
} else {
|
} else {
|
||||||
|
@ -771,8 +851,17 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
let unit_ty = ety.sequence_element_type(cx.tcx());
|
let unit_ty = ety.sequence_element_type(cx.tcx());
|
||||||
let llunitty = type_of::type_of(cx, unit_ty);
|
let llunitty = type_of::type_of(cx, unit_ty);
|
||||||
let vs = es.iter()
|
let vs = es.iter()
|
||||||
.map(|e| const_expr(cx, &**e, param_substs, fn_args).0)
|
.map(|e| const_expr(
|
||||||
.collect::<Vec<_>>();
|
cx,
|
||||||
|
&**e,
|
||||||
|
param_substs,
|
||||||
|
fn_args,
|
||||||
|
trueconst,
|
||||||
|
).map(|(l, _)| l))
|
||||||
|
.collect::<Vec<Result<_, ConstEvalFailure>>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>, ConstEvalFailure>>();
|
||||||
|
let vs = try!(vs);
|
||||||
// If the vector contains enums, an LLVM array won't work.
|
// If the vector contains enums, an LLVM array won't work.
|
||||||
if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
|
if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
|
||||||
C_struct(cx, &vs[..], false)
|
C_struct(cx, &vs[..], false)
|
||||||
|
@ -784,7 +873,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
let unit_ty = ety.sequence_element_type(cx.tcx());
|
let unit_ty = ety.sequence_element_type(cx.tcx());
|
||||||
let llunitty = type_of::type_of(cx, unit_ty);
|
let llunitty = type_of::type_of(cx, unit_ty);
|
||||||
let n = cx.tcx().eval_repeat_count(count);
|
let n = cx.tcx().eval_repeat_count(count);
|
||||||
let unit_val = const_expr(cx, &**elem, param_substs, fn_args).0;
|
let unit_val = try!(const_expr(cx, &**elem, param_substs, fn_args, trueconst)).0;
|
||||||
let vs = vec![unit_val; n];
|
let vs = vec![unit_val; n];
|
||||||
if val_ty(unit_val) != llunitty {
|
if val_ty(unit_val) != llunitty {
|
||||||
C_struct(cx, &vs[..], false)
|
C_struct(cx, &vs[..], false)
|
||||||
|
@ -806,7 +895,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
|
expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
|
||||||
}
|
}
|
||||||
def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
|
def::DefConst(def_id) | def::DefAssociatedConst(def_id) => {
|
||||||
const_deref_ptr(cx, get_const_val(cx, def_id, e))
|
const_deref_ptr(cx, try!(get_const_val(cx, def_id, e)))
|
||||||
}
|
}
|
||||||
def::DefVariant(enum_did, variant_did, _) => {
|
def::DefVariant(enum_did, variant_did, _) => {
|
||||||
let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
|
let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
|
||||||
|
@ -850,10 +939,17 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let def = cx.tcx().def_map.borrow()[&callee.id].full_def();
|
let def = cx.tcx().def_map.borrow()[&callee.id].full_def();
|
||||||
let arg_vals = map_list(args);
|
let arg_vals = try!(map_list(args));
|
||||||
match def {
|
match def {
|
||||||
def::DefFn(did, _) | def::DefMethod(did) => {
|
def::DefFn(did, _) | def::DefMethod(did) => {
|
||||||
const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs)
|
try!(const_fn_call(
|
||||||
|
cx,
|
||||||
|
ExprId(callee.id),
|
||||||
|
did,
|
||||||
|
&arg_vals,
|
||||||
|
param_substs,
|
||||||
|
trueconst,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
def::DefStruct(_) => {
|
def::DefStruct(_) => {
|
||||||
if ety.is_simd() {
|
if ety.is_simd() {
|
||||||
|
@ -875,15 +971,21 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hir::ExprMethodCall(_, _, ref args) => {
|
hir::ExprMethodCall(_, _, ref args) => {
|
||||||
let arg_vals = map_list(args);
|
let arg_vals = try!(map_list(args));
|
||||||
let method_call = ty::MethodCall::expr(e.id);
|
let method_call = ty::MethodCall::expr(e.id);
|
||||||
let method_did = cx.tcx().tables.borrow().method_map[&method_call].def_id;
|
let method_did = cx.tcx().tables.borrow().method_map[&method_call].def_id;
|
||||||
const_fn_call(cx, MethodCallKey(method_call),
|
try!(const_fn_call(cx, MethodCallKey(method_call),
|
||||||
method_did, &arg_vals, param_substs)
|
method_did, &arg_vals, param_substs, trueconst))
|
||||||
},
|
},
|
||||||
hir::ExprBlock(ref block) => {
|
hir::ExprBlock(ref block) => {
|
||||||
match block.expr {
|
match block.expr {
|
||||||
Some(ref expr) => const_expr(cx, &**expr, param_substs, fn_args).0,
|
Some(ref expr) => try!(const_expr(
|
||||||
|
cx,
|
||||||
|
&**expr,
|
||||||
|
param_substs,
|
||||||
|
fn_args,
|
||||||
|
trueconst,
|
||||||
|
)).0,
|
||||||
None => C_nil(cx),
|
None => C_nil(cx),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -902,20 +1004,27 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
},
|
},
|
||||||
_ => cx.sess().span_bug(e.span,
|
_ => cx.sess().span_bug(e.span,
|
||||||
"bad constant expression type in consts::const_expr"),
|
"bad constant expression type in consts::const_expr"),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trans_static(ccx: &CrateContext,
|
pub fn trans_static(ccx: &CrateContext,
|
||||||
m: hir::Mutability,
|
m: hir::Mutability,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
id: ast::NodeId,
|
id: ast::NodeId,
|
||||||
attrs: &Vec<ast::Attribute>)
|
attrs: &Vec<ast::Attribute>)
|
||||||
-> ValueRef {
|
-> Result<ValueRef, ConstEvalErr> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _icx = push_ctxt("trans_static");
|
let _icx = push_ctxt("trans_static");
|
||||||
let g = base::get_item_val(ccx, id);
|
let g = base::get_item_val(ccx, id);
|
||||||
|
|
||||||
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
||||||
let (v, _) = const_expr(ccx, expr, empty_substs, None);
|
let (v, _) = try!(const_expr(
|
||||||
|
ccx,
|
||||||
|
expr,
|
||||||
|
empty_substs,
|
||||||
|
None,
|
||||||
|
TrueConst::Yes,
|
||||||
|
).map_err(|e| e.into_inner()));
|
||||||
|
|
||||||
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
||||||
// otherwise some LLVM optimization passes don't work as expected
|
// otherwise some LLVM optimization passes don't work as expected
|
||||||
|
@ -964,7 +1073,7 @@ pub fn trans_static(ccx: &CrateContext,
|
||||||
"thread_local") {
|
"thread_local") {
|
||||||
llvm::set_thread_local(g, true);
|
llvm::set_thread_local(g, true);
|
||||||
}
|
}
|
||||||
g
|
Ok(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,13 +133,25 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
) {
|
) {
|
||||||
if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) {
|
if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) {
|
||||||
if let SaveIn(lldest) = dest {
|
if let SaveIn(lldest) = dest {
|
||||||
let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
||||||
bcx.fcx.param_substs);
|
bcx.fcx.param_substs,
|
||||||
|
consts::TrueConst::No) {
|
||||||
|
Ok(global) => {
|
||||||
// Cast pointer to destination, because constants
|
// Cast pointer to destination, because constants
|
||||||
// have different types.
|
// have different types.
|
||||||
let lldest = PointerCast(bcx, lldest, val_ty(global));
|
let lldest = PointerCast(bcx, lldest, val_ty(global));
|
||||||
memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr));
|
memcpy_ty(bcx, lldest, global, expr_ty_adjusted(bcx, expr));
|
||||||
return bcx;
|
return bcx;
|
||||||
|
},
|
||||||
|
Err(consts::ConstEvalFailure::Runtime(_)) => {
|
||||||
|
// in case const evaluation errors, translate normally
|
||||||
|
// debug assertions catch the same errors
|
||||||
|
// see RFC 1229
|
||||||
|
},
|
||||||
|
Err(consts::ConstEvalFailure::Compiletime(_)) => {
|
||||||
|
return bcx;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Even if we don't have a value to emit, and the expression
|
// Even if we don't have a value to emit, and the expression
|
||||||
// doesn't have any side-effects, we still have to translate the
|
// doesn't have any side-effects, we still have to translate the
|
||||||
|
@ -221,9 +233,10 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
check_const::ConstQualif::NOT_CONST |
|
check_const::ConstQualif::NOT_CONST |
|
||||||
check_const::ConstQualif::NEEDS_DROP
|
check_const::ConstQualif::NEEDS_DROP
|
||||||
) {
|
) {
|
||||||
let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
||||||
bcx.fcx.param_substs);
|
bcx.fcx.param_substs,
|
||||||
|
consts::TrueConst::No) {
|
||||||
|
Ok(global) => {
|
||||||
if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) {
|
if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) {
|
||||||
// Is borrowed as 'static, must return lvalue.
|
// Is borrowed as 'static, must return lvalue.
|
||||||
|
|
||||||
|
@ -263,6 +276,21 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
memcpy_ty(bcx, lldest, global, const_ty);
|
memcpy_ty(bcx, lldest, global, const_ty);
|
||||||
Datum::new(scratch, const_ty, Rvalue::new(ByRef))
|
Datum::new(scratch, const_ty, Rvalue::new(ByRef))
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
Err(consts::ConstEvalFailure::Runtime(_)) => {
|
||||||
|
// in case const evaluation errors, translate normally
|
||||||
|
// debug assertions catch the same errors
|
||||||
|
// see RFC 1229
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Err(consts::ConstEvalFailure::Compiletime(_)) => {
|
||||||
|
// generate a dummy llvm value
|
||||||
|
let const_ty = expr_ty(bcx, expr);
|
||||||
|
let llty = type_of::type_of(bcx.ccx(), const_ty);
|
||||||
|
let dummy = C_undef(llty.ptr_to());
|
||||||
|
Some(Datum::new(dummy, const_ty, Rvalue::new(ByRef)))
|
||||||
|
},
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -1563,7 +1563,16 @@ fn generic_simd_intrinsic<'blk, 'tcx, 'a>
|
||||||
None => bcx.sess().span_bug(call_info.span,
|
None => bcx.sess().span_bug(call_info.span,
|
||||||
"intrinsic call with unexpected argument shape"),
|
"intrinsic call with unexpected argument shape"),
|
||||||
};
|
};
|
||||||
let vector = consts::const_expr(bcx.ccx(), vector, tcx.mk_substs(substs), None).0;
|
let vector = match consts::const_expr(
|
||||||
|
bcx.ccx(),
|
||||||
|
vector,
|
||||||
|
tcx.mk_substs(substs),
|
||||||
|
None,
|
||||||
|
consts::TrueConst::Yes, // this should probably help simd error reporting
|
||||||
|
) {
|
||||||
|
Ok((vector, _)) => vector,
|
||||||
|
Err(err) => bcx.sess().span_fatal(call_info.span, &err.description()),
|
||||||
|
};
|
||||||
|
|
||||||
let indices: Option<Vec<_>> = (0..n)
|
let indices: Option<Vec<_>> = (0..n)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
|
|
36
src/test/compile-fail/const-err.rs
Normal file
36
src/test/compile-fail/const-err.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
#[allow(exceeding_bitshifts)]
|
||||||
|
#[deny(const_err)]
|
||||||
|
|
||||||
|
fn black_box<T>(_: T) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLA: u8 = 200u8 + 200u8;
|
||||||
|
//~^ ERROR attempted to add with overflow
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = -std::i8::MIN;
|
||||||
|
//~^ WARN attempted to negate with overflow
|
||||||
|
let b = 200u8 + 200u8 + 200u8;
|
||||||
|
//~^ WARN attempted to add with overflow
|
||||||
|
//~^^ WARN attempted to add with overflow
|
||||||
|
let c = 200u8 * 4;
|
||||||
|
//~^ WARN attempted to mul with overflow
|
||||||
|
let d = 42u8 - (42u8 + 1);
|
||||||
|
//~^ WARN attempted to sub with overflow
|
||||||
|
let _e = BLA;
|
||||||
|
black_box(a);
|
||||||
|
black_box(b);
|
||||||
|
black_box(c);
|
||||||
|
black_box(d);
|
||||||
|
}
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(negate_unsigned)]
|
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
#![feature(negate_unsigned)]
|
#![feature(negate_unsigned)]
|
||||||
|
|
||||||
|
|
|
@ -8,48 +8,50 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![deny(const_err)]
|
||||||
|
|
||||||
use std::{isize, i8, i16, i32, i64};
|
use std::{isize, i8, i16, i32, i64};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err());
|
assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide with overflow in a constant expression
|
//~^ ERROR attempted to divide with overflow
|
||||||
assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide with overflow in a constant expression
|
//~^ ERROR attempted to divide with overflow
|
||||||
assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide with overflow in a constant expression
|
//~^ ERROR attempted to divide with overflow
|
||||||
assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide with overflow in a constant expression
|
//~^ ERROR attempted to divide with overflow
|
||||||
assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide with overflow in a constant expression
|
//~^ ERROR attempted to divide with overflow
|
||||||
assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide by zero in a constant expression
|
//~^ ERROR attempted to divide by zero
|
||||||
assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide by zero in a constant expression
|
//~^ ERROR attempted to divide by zero
|
||||||
assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide by zero in a constant expression
|
//~^ ERROR attempted to divide by zero
|
||||||
assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide by zero in a constant expression
|
//~^ ERROR attempted to divide by zero
|
||||||
assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err());
|
||||||
//~^ ERROR attempted to divide by zero in a constant expression
|
//~^ ERROR attempted to divide by zero
|
||||||
assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
|
assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with overflow in a constant expression
|
//~^ ERROR attempted remainder with overflow
|
||||||
assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with overflow in a constant expression
|
//~^ ERROR attempted remainder with overflow
|
||||||
assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with overflow in a constant expression
|
//~^ ERROR attempted remainder with overflow
|
||||||
assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with overflow in a constant expression
|
//~^ ERROR attempted remainder with overflow
|
||||||
assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
|
assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with overflow in a constant expression
|
//~^ ERROR attempted remainder with overflow
|
||||||
assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with a divisor of zero in a constant expression
|
//~^ ERROR attempted remainder with a divisor of zero
|
||||||
assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with a divisor of zero in a constant expression
|
//~^ ERROR attempted remainder with a divisor of zero
|
||||||
assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with a divisor of zero in a constant expression
|
//~^ ERROR attempted remainder with a divisor of zero
|
||||||
assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with a divisor of zero in a constant expression
|
//~^ ERROR attempted remainder with a divisor of zero
|
||||||
assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err());
|
assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err());
|
||||||
//~^ ERROR attempted remainder with a divisor of zero in a constant expression
|
//~^ ERROR attempted remainder with a divisor of zero
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
|
||||||
fn value() -> u8 { 200 }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = value() + value() + value();
|
let _x = 200u8 + 200u8 + 200u8;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = 1_i32 << id(32);
|
let _x = 1_i32 << 32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = 1 << id(-1);
|
let _x = 1 << -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = 1_u64 << id(64);
|
let _x = 1_u64 << 64;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,11 @@
|
||||||
// This function is checking that our automatic truncation does not
|
// This function is checking that our automatic truncation does not
|
||||||
// sidestep the overflow checking.
|
// sidestep the overflow checking.
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// this signals overflow when checking is on
|
// this signals overflow when checking is on
|
||||||
let x = 1_i8 << id(17);
|
let x = 1_i8 << 17;
|
||||||
|
|
||||||
// ... but when checking is off, the fallback will truncate the
|
// ... but when checking is off, the fallback will truncate the
|
||||||
// input to its lower three bits (= 1). Note that this is *not*
|
// input to its lower three bits (= 1). Note that this is *not*
|
||||||
|
|
|
@ -11,9 +11,6 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
|
||||||
fn value() -> u8 { 200 }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = value() * 4;
|
let x = 200u8 * 4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,6 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'attempted to negate with overflow'
|
// error-pattern:thread '<main>' panicked at 'attempted to negate with overflow'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
|
||||||
fn value() -> i8 { std::i8::MIN }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = -value();
|
let _x = -std::i8::MIN;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = -1_i32 >> id(32);
|
let _x = -1_i32 >> 32;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = -1_i32 >> id(-1);
|
let _x = -1_i32 >> -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = -1_i64 >> id(64);
|
let _x = -1_i64 >> 64;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,11 @@
|
||||||
// This function is checking that our (type-based) automatic
|
// This function is checking that our (type-based) automatic
|
||||||
// truncation does not sidestep the overflow checking.
|
// truncation does not sidestep the overflow checking.
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
#![warn(exceeding_bitshifts)]
|
||||||
fn id<T>(x: T) -> T { x }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// this signals overflow when checking is on
|
// this signals overflow when checking is on
|
||||||
let x = 2_i8 >> id(17);
|
let x = 2_i8 >> 17;
|
||||||
|
|
||||||
// ... but when checking is off, the fallback will truncate the
|
// ... but when checking is off, the fallback will truncate the
|
||||||
// input to its lower three bits (= 1). Note that this is *not*
|
// input to its lower three bits (= 1). Note that this is *not*
|
||||||
|
|
|
@ -11,9 +11,6 @@
|
||||||
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
|
||||||
// compile-flags: -C debug-assertions
|
// compile-flags: -C debug-assertions
|
||||||
|
|
||||||
// (Work around constant-evaluation)
|
|
||||||
fn value() -> u8 { 42 }
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _x = value() - (value() + 1);
|
let _x = 42u8 - (42u8 + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(num_wrapping)]
|
|
||||||
|
|
||||||
// Test inherent wrapping_* methods for {i,u}{size,8,16,32,64}.
|
// Test inherent wrapping_* methods for {i,u}{size,8,16,32,64}.
|
||||||
|
|
||||||
use std::{i8, i16, i32, i64, isize};
|
use std::{i8, i16, i32, i64, isize};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue