Skip to content

Commit 6808e41

Browse files
committed
rustc_trans::trans::consts add overflow checking
1 parent b02f7d2 commit 6808e41

File tree

1 file changed

+124
-5
lines changed

1 file changed

+124
-5
lines changed

src/librustc_trans/trans/consts.rs

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ use llvm;
1414
use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
1515
use llvm::{InternalLinkage, ValueRef, Bool, True};
1616
use middle::{check_const, const_eval, def};
17+
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
18+
use middle::const_eval::{const_int_checked_add, const_uint_checked_add};
19+
use middle::const_eval::{const_int_checked_sub, const_uint_checked_sub};
20+
use middle::const_eval::{const_int_checked_mul, const_uint_checked_mul};
21+
use middle::const_eval::{const_int_checked_div, const_uint_checked_div};
22+
use middle::const_eval::{const_int_checked_rem, const_uint_checked_rem};
23+
use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl};
24+
use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr};
1725
use trans::{adt, closure, debuginfo, expr, inline, machine};
1826
use trans::base::{self, push_ctxt};
1927
use trans::common::*;
@@ -336,6 +344,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
336344
let csize = machine::llsize_of_alloc(cx, val_ty(llconst));
337345
let tsize = machine::llsize_of_alloc(cx, llty);
338346
if csize != tsize {
347+
cx.sess().abort_if_errors();
339348
unsafe {
340349
// FIXME these values could use some context
341350
llvm::LLVMDumpValue(llconst);
@@ -348,6 +357,100 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
348357
(llconst, ety_adjusted)
349358
}
350359

360+
fn check_unary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
361+
te: ValueRef) {
362+
// The only kind of unary expression that we check for validity
363+
// here is `-expr`, to check if it "overflows" (e.g. `-i32::MIN`).
364+
if let ast::ExprUnary(ast::UnNeg, ref inner_e) = e.node {
365+
366+
// An unfortunate special case: we parse e.g. -128 as a
367+
// negation of the literal 128, which means if we're expecting
368+
// a i8 (or if it was already suffixed, e.g. `-128_i8`), then
369+
// 128 will have already overflowed to -128, and so then the
370+
// constant evaluator thinks we're trying to negate -128.
371+
//
372+
// Catch this up front by looking for ExprLit directly,
373+
// and just accepting it.
374+
if let ast::ExprLit(_) = inner_e.node { return; }
375+
376+
let result = match t.sty {
377+
ty::ty_int(int_type) => {
378+
let input = match const_to_opt_int(te) {
379+
Some(v) => v,
380+
None => return,
381+
};
382+
const_int_checked_neg(
383+
input, e, Some(const_eval::IntTy::from(cx.tcx(), int_type)))
384+
}
385+
ty::ty_uint(uint_type) => {
386+
let input = match const_to_opt_uint(te) {
387+
Some(v) => v,
388+
None => return,
389+
};
390+
const_uint_checked_neg(
391+
input, e, Some(const_eval::UintTy::from(cx.tcx(), uint_type)))
392+
}
393+
_ => return,
394+
};
395+
396+
// We do not actually care about a successful result.
397+
if let Err(err) = result {
398+
cx.tcx().sess.span_err(e.span, &err.description());
399+
}
400+
}
401+
}
402+
403+
fn check_binary_expr_validity(cx: &CrateContext, e: &ast::Expr, t: Ty,
404+
te1: ValueRef, te2: ValueRef) {
405+
let b = if let ast::ExprBinary(b, _, _) = e.node { b } else { return };
406+
407+
let result = match t.sty {
408+
ty::ty_int(int_type) => {
409+
let (lhs, rhs) = match (const_to_opt_int(te1),
410+
const_to_opt_int(te2)) {
411+
(Some(v1), Some(v2)) => (v1, v2),
412+
_ => return,
413+
};
414+
415+
let opt_ety = Some(const_eval::IntTy::from(cx.tcx(), int_type));
416+
match b.node {
417+
ast::BiAdd => const_int_checked_add(lhs, rhs, e, opt_ety),
418+
ast::BiSub => const_int_checked_sub(lhs, rhs, e, opt_ety),
419+
ast::BiMul => const_int_checked_mul(lhs, rhs, e, opt_ety),
420+
ast::BiDiv => const_int_checked_div(lhs, rhs, e, opt_ety),
421+
ast::BiRem => const_int_checked_rem(lhs, rhs, e, opt_ety),
422+
ast::BiShl => const_int_checked_shl(lhs, rhs, e, opt_ety),
423+
ast::BiShr => const_int_checked_shr(lhs, rhs, e, opt_ety),
424+
_ => return,
425+
}
426+
}
427+
ty::ty_uint(uint_type) => {
428+
let (lhs, rhs) = match (const_to_opt_uint(te1),
429+
const_to_opt_uint(te2)) {
430+
(Some(v1), Some(v2)) => (v1, v2),
431+
_ => return,
432+
};
433+
434+
let opt_ety = Some(const_eval::UintTy::from(cx.tcx(), uint_type));
435+
match b.node {
436+
ast::BiAdd => const_uint_checked_add(lhs, rhs, e, opt_ety),
437+
ast::BiSub => const_uint_checked_sub(lhs, rhs, e, opt_ety),
438+
ast::BiMul => const_uint_checked_mul(lhs, rhs, e, opt_ety),
439+
ast::BiDiv => const_uint_checked_div(lhs, rhs, e, opt_ety),
440+
ast::BiRem => const_uint_checked_rem(lhs, rhs, e, opt_ety),
441+
ast::BiShl => const_uint_checked_shl(lhs, rhs, e, opt_ety),
442+
ast::BiShr => const_uint_checked_shr(lhs, rhs, e, opt_ety),
443+
_ => return,
444+
}
445+
}
446+
_ => return,
447+
};
448+
// We do not actually care about a successful result.
449+
if let Err(err) = result {
450+
cx.tcx().sess.span_err(e.span, &err.description());
451+
}
452+
}
453+
351454
fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
352455
e: &ast::Expr,
353456
ety: Ty<'tcx>,
@@ -386,7 +489,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
386489
let signed = ty::type_is_signed(intype);
387490

388491
let (te2, _) = const_expr(cx, &**e2, param_substs);
389-
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
492+
493+
check_binary_expr_validity(cx, e, ty, te1, te2);
390494

391495
match b.node {
392496
ast::BiAdd => {
@@ -416,8 +520,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
416520
ast::BiBitXor => llvm::LLVMConstXor(te1, te2),
417521
ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2),
418522
ast::BiBitOr => llvm::LLVMConstOr(te1, te2),
419-
ast::BiShl => llvm::LLVMConstShl(te1, te2),
523+
ast::BiShl => {
524+
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
525+
llvm::LLVMConstShl(te1, te2)
526+
}
420527
ast::BiShr => {
528+
let te2 = base::cast_shift_const_rhs(b.node, te1, te2);
421529
if signed { llvm::LLVMConstAShr(te1, te2) }
422530
else { llvm::LLVMConstLShr(te1, te2) }
423531
}
@@ -439,8 +547,11 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
439547
}
440548
}
441549
},
442-
ast::ExprUnary(u, ref e) => {
443-
let (te, ty) = const_expr(cx, &**e, param_substs);
550+
ast::ExprUnary(u, ref inner_e) => {
551+
let (te, ty) = const_expr(cx, &**inner_e, param_substs);
552+
553+
check_unary_expr_validity(cx, e, ty, te);
554+
444555
let is_float = ty::type_is_fp(ty);
445556
match u {
446557
ast::UnUniq | ast::UnDeref => {
@@ -664,7 +775,15 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
664775
let n = match const_eval::eval_const_expr_partial(cx.tcx(), &**count, None) {
665776
Ok(const_eval::const_int(i)) => i as usize,
666777
Ok(const_eval::const_uint(i)) => i as usize,
667-
_ => cx.sess().span_bug(count.span, "count must be integral const expression.")
778+
Ok(_) => {
779+
cx.sess().span_bug(count.span, "count must be integral const expression.")
780+
}
781+
Err(err) => {
782+
cx.sess().span_err(count.span, &format!("error evaluating count: {}",
783+
err.description()));
784+
// return 1 to allow compilation to proceed
785+
1 as usize
786+
}
668787
};
669788
let unit_val = const_expr(cx, &**elem, param_substs).0;
670789
let vs: Vec<_> = repeat(unit_val).take(n).collect();

0 commit comments

Comments
 (0)