Skip to content

Commit

Permalink
[Rust-GCC#3141] Fix incorrect handling of overflow in numeric types
Browse files Browse the repository at this point in the history
gcc/rust/ChangeLog:

	* backend/rust-compile-expr.cc: Fix range checking for both integers and floats.
	* hir/tree/rust-hir-expr.h: Add "negative_number" boolean to LiteralExpr class.

gcc/testsuite/ChangeLog:

	* rust/compile/issue-3141.rs: New test.

Signed-off-by: Joan Vilardaga <[email protected]>
  • Loading branch information
JoanVC100 committed Sep 10, 2024
1 parent 14528a8 commit 306c37b
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 5 deletions.
67 changes: 62 additions & 5 deletions gcc/rust/backend/rust-compile-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,21 @@ void
CompileExpr::visit (HIR::NegationExpr &expr)
{
auto op = expr.get_expr_type ();
auto negated_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);

const auto literal_expr = expr.get_expr ().get ();
if (op == NegationOperator::NEGATE
&& literal_expr->get_expression_type () == HIR::Expr::ExprType::Lit)
{
auto new_literal_expr = static_cast<HIR::LiteralExpr *> (literal_expr);
auto lit_type = new_literal_expr->get_lit_type ();
if (lit_type == HIR::Literal::LitType::INT
|| lit_type == HIR::Literal::LitType::FLOAT)
{
new_literal_expr->set_negative ();
}
}
auto negated_expr = CompileExpr::Compile (literal_expr, ctx);

auto location = expr.get_locus ();

// this might be an operator overload situation lets check
Expand Down Expand Up @@ -1506,13 +1520,22 @@ CompileExpr::compile_integer_literal (const HIR::LiteralExpr &expr,
mpz_init (type_max);
get_type_static_bounds (type, type_min, type_max);

if (expr.is_negative ())
{
mpz_neg (ival, ival);
}
if (mpz_cmp (ival, type_min) < 0 || mpz_cmp (ival, type_max) > 0)
{
rust_error_at (expr.get_locus (),
"integer overflows the respective type %<%s%>",
tyty->get_name ().c_str ());
return error_mark_node;
}
// Other tests break if we don't reverse the negation
if (expr.is_negative ())
{
mpz_neg (ival, ival);
}

tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));

Expand All @@ -1530,6 +1553,8 @@ CompileExpr::compile_float_literal (const HIR::LiteralExpr &expr,
rust_assert (expr.get_lit_type () == HIR::Literal::FLOAT);
const auto literal_value = expr.get_literal ();

tree type = TyTyResolveCompile::compile (ctx, tyty);

mpfr_t fval;
if (mpfr_init_set_str (fval, literal_value.as_string ().c_str (), 10,
MPFR_RNDN)
Expand All @@ -1539,12 +1564,44 @@ CompileExpr::compile_float_literal (const HIR::LiteralExpr &expr,
return error_mark_node;
}

tree type = TyTyResolveCompile::compile (ctx, tyty);

// taken from:
// see go/gofrontend/expressions.cc:check_float_type
mpfr_exp_t exp = mpfr_get_exp (fval);
bool real_value_overflow = exp > TYPE_PRECISION (type);
bool real_value_overflow;

if (mpfr_regular_p (fval) != 0)
{
mpfr_exp_t exp = mpfr_get_exp (fval);
mpfr_exp_t min_exp;
mpfr_exp_t max_exp;

/*
* By convention, the radix point of the significand is just before the
* first digit (which is always 1 due to normalization), like in the C
* language, but unlike in IEEE 754 (thus, for a given number, the
* exponent values in MPFR and in IEEE 754 differ by 1).
*/
switch (TYPE_PRECISION (type))
{
case 32:
min_exp = -128 + 1;
max_exp = 127 + 1;
break;
case 64:
min_exp = -1024 + 1;
max_exp = 1023 + 1;
break;
default:
rust_error_at (expr.get_locus (),
"precision of type %<%s%> not supported",
tyty->get_name ().c_str ());
return error_mark_node;
}
real_value_overflow = exp < min_exp || exp > max_exp;
}
else
{
real_value_overflow = false;
}

REAL_VALUE_TYPE r1;
real_from_mpfr (&r1, fval, type, GMP_RNDN);
Expand Down
9 changes: 9 additions & 0 deletions gcc/rust/hir/tree/rust-hir-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class LiteralExpr : public ExprWithoutBlock
{
Literal literal;
location_t locus;
bool negative_number = false;

public:
std::string as_string () const override
Expand Down Expand Up @@ -132,6 +133,14 @@ class LiteralExpr : public ExprWithoutBlock

ExprType get_expression_type () const override final { return ExprType::Lit; }

bool is_negative () const { return negative_number; }
void set_negative ()
{
rust_assert (get_lit_type () == Literal::LitType::INT
|| get_lit_type () == Literal::LitType::FLOAT);
negative_number = true;
}

protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
Expand Down
53 changes: 53 additions & 0 deletions gcc/testsuite/rust/compile/issue-3141.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
fn main() {
// Signed integers
let _i8_min: i8 = -128;
let _i8_max: i8 = 127;

let _i16_min: i16 = -32768;
let _i16_max: i16 = 32767;

let _i32_min: i32 = -2147483648;
let _i32_max: i32 = 2147483647;

let _i64_min: i64 = -9223372036854775808;
let _i64_max: i64 = 9223372036854775807;

let _i128_min: i128 = -170141183460469231731687303715884105728;
let _i128_max: i128 = 170141183460469231731687303715884105727;

// Unsigned integers
let _u8_min: u8 = 0;
let _u8_max: u8 = 255;

let _u16_min: u16 = 0;
let _u16_max: u16 = 65535;

let _u32_min: u32 = 0;
let _u32_max: u32 = 4294967295;

let _u64_min: u64 = 0;
let _u64_max: u64 = 18446744073709551615;

let _u128_min: u128 = 0;
let _u128_max: u128 = 340282366920938463463374607431768211455;

// isize and usize
let _isize_min: isize = -9223372036854775808;
let _isize_max: isize = 9223372036854775807;

let _usize_min: usize = 0;
let _usize_max: usize = 18446744073709551615;

// Floating point
let _f32_min: f32 = -3.40282347E+38f32;
let _f32_max: f32 = 3.40282347E+38f32;

let _f64_min: f64 = 1.7976931348623157E+308f64;
let _f64_max: f64 = -1.7976931348623157E+308f64;

// Some values although not on the limit also seem to throw
// compiler error.
let _f32_random_fail_1: f32 = 1.40282347E+30f32;
let _f32_random_fail_2: f32 = 1.40282347E+10f32;
let _f32_random_pass: f32 = 1.40282347E+9f32; // this passes
}

0 comments on commit 306c37b

Please sign in to comment.