rustc_trans::trans::consts add overflow checking

This commit is contained in:
Felix S. Klock II 2015-03-27 14:25:48 +01:00
parent b02f7d2fac
commit 6808e414c7

View file

@ -14,6 +14,14 @@ use llvm;
use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
use llvm::{InternalLinkage, ValueRef, Bool, True};
use middle::{check_const, const_eval, def};
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_sub, const_uint_checked_sub};
use middle::const_eval::{const_int_checked_mul, const_uint_checked_mul};
use middle::const_eval::{const_int_checked_div, const_uint_checked_div};
use middle::const_eval::{const_int_checked_rem, const_uint_checked_rem};
use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl};
use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
use trans::{adt, closure, debuginfo, expr, inline, machine};
use trans::base::{self, push_ctxt};
use trans::common::*;
@ -336,6 +344,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let csize = machine::llsize_of_alloc(cx, val_ty(llconst));
let tsize = machine::llsize_of_alloc(cx, llty);
if csize != tsize {
cx.sess().abort_if_errors();
unsafe {
// FIXME these values could use some context
llvm::LLVMDumpValue(llconst);
@ -348,6 +357,100 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
(llconst, ety_adjusted)
}
fn check_unary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
te: ValueRef) {
// The only kind of unary expression that we check for validity
// here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`).
if let ast::ExprUnary(ast::UnNeg, ref inner_e) = e.node {
// An unfortunate special case: we parse e.g. -128 as a
// negation of the literal 128, which means if we're expecting
// a i8 (or if it was already suffixed, e.g. `-128_i8`), then
// 128 will have already overflowed to -128, and so then the
// constant evaluator thinks we're trying to negate -128.
//
// Catch this up front by looking for ExprLit directly,
// and just accepting it.
if let ast::ExprLit(_) = inner_e.node { return; }
let result = match t.sty {
ty::ty_int(int_type) => {
let input = match const_to_opt_int(te) {
Some(v) => v,
None => return,
};
const_int_checked_neg(
input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
}
ty::ty_uint(uint_type) => {
let input = match const_to_opt_uint(te) {
Some(v) => v,
None => return,
};
const_uint_checked_neg(
input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
}
_ => return,
};
// We do not actually care about a successful result.
if let Err(err) = result {
cx.tcx().sess.span_err(e.span, &err.description());
}
}
}
fn check_binary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
te1: ValueRef, te2: ValueRef) {
let b = if let ast::ExprBinary(b, _, _) = e.node { b } else { return };
let result = match t.sty {
ty::ty_int(int_type) => {
let (lhs, rhs) = match (const_to_opt_int(te1),
const_to_opt_int(te2)) {
(Some(v1), Some(v2)) => (v1, v2),
_ => return,
};
let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
match b.node {
ast::BiAdd => const_int_checked_add(lhs, rhs, e, opt_ety),
ast::BiSub => const_int_checked_sub(lhs, rhs, e, opt_ety),
ast::BiMul => const_int_checked_mul(lhs, rhs, e, opt_ety),
ast::BiDiv => const_int_checked_div(lhs, rhs, e, opt_ety),
ast::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
ast::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
ast::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
_ => return,
}
}
ty::ty_uint(uint_type) => {
let (lhs, rhs) = match (const_to_opt_uint(te1),
const_to_opt_uint(te2)) {
(Some(v1), Some(v2)) => (v1, v2),
_ => return,
};
let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
match b.node {
ast::BiAdd => const_uint_checked_add(lhs, rhs, e, opt_ety),
ast::BiSub => const_uint_checked_sub(lhs, rhs, e, opt_ety),
ast::BiMul => const_uint_checked_mul(lhs, rhs, e, opt_ety),
ast::BiDiv => const_uint_checked_div(lhs, rhs, e, opt_ety),
ast::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
ast::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
ast::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
_ => return,
}
}
_ => return,
};
// We do not actually care about a successful result.
if let Err(err) = result {
cx.tcx().sess.span_err(e.span, &err.description());
}
}
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
e: &ast::Expr,
ety: Ty<'tcx>,
@ -386,7 +489,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let signed = ty::type_is_signed(intype);
let (te2, _) = const_expr(cx, &**e2, param_substs);
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
check_binary_expr_validity(cx, e, ty, te1, te2);
match b.node {
ast::BiAdd => {
@ -416,8 +520,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ast::BiBitXor => llvm::LLVMConstXor(te1, te2),
ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2),
ast::BiBitOr => llvm::LLVMConstOr(te1, te2),
ast::BiShl => llvm::LLVMConstShl(te1, te2),
ast::BiShl => {
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
llvm::LLVMConstShl(te1, te2)
}
ast::BiShr => {
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
if signed { llvm::LLVMConstAShr(te1, te2) }
else { llvm::LLVMConstLShr(te1, te2) }
}
@ -439,8 +547,11 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
}
},
ast::ExprUnary(u, ref e) => {
let (te, ty) = const_expr(cx, &**e, param_substs);
ast::ExprUnary(u, ref inner_e) => {
let (te, ty) = const_expr(cx, &**inner_e, param_substs);
check_unary_expr_validity(cx, e, ty, te);
let is_float = ty::type_is_fp(ty);
match u {
ast::UnUniq | ast::UnDeref => {
@ -664,7 +775,15 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
let n = match const_eval::eval_const_expr_partial(cx.tcx(), &**count, None) {
Ok(const_eval::const_int(i)) => i as usize,
Ok(const_eval::const_uint(i)) => i as usize,
_ => cx.sess().span_bug(count.span, "count must be integral const expression.")
Ok(_) => {
cx.sess().span_bug(count.span, "count must be integral const expression.")
}
Err(err) => {
cx.sess().span_err(count.span, &format!("error evaluating count: {}",
err.description()));
// return 1 to allow compilation to proceed
1 as usize
}
};
let unit_val = const_expr(cx, &**elem, param_substs).0;
let vs: Vec<_> = repeat(unit_val).take(n).collect();