gccrs: Fix ICE with invalid const expression

This patch handles the overflowed var expression in the const block, so that
we error properly in the const expr code. It was missing some stuff from the
c++ implementation in how this should be handled properly.

Fixes Rust-GCC#4139

gcc/rust/ChangeLog:

	* backend/rust-compile-expr.cc (CompileExpr::compile_integer_literal): cleanup
	* backend/rust-constexpr.cc (struct constexpr_global_ctx): port over c++ helpers
	(decl_really_constant_value): likewise
	(eval_constant_expression): likewise
	(non_const_var_error): likewise

gcc/testsuite/ChangeLog:

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

Signed-off-by: Philip Herron <herron.philip@googlemail.com>
This commit is contained in:
Philip Herron
2025-09-12 16:37:32 +01:00
committed by Arthur Cohen
parent 7a01b70d1b
commit 182b2e65a6
3 changed files with 135 additions and 39 deletions

View File

@@ -1655,37 +1655,39 @@ CompileExpr::compile_integer_literal (const HIR::LiteralExpr &expr,
const TyTy::BaseType *tyty)
{
rust_assert (expr.get_lit_type () == HIR::Literal::INT);
const auto literal_value = expr.get_literal ();
const auto &literal_value = expr.get_literal ();
tree type = TyTyResolveCompile::compile (ctx, tyty);
std::string s = literal_value.as_string ();
s.erase (std::remove (s.begin (), s.end (), '_'), s.end ());
int base = 0;
mpz_t ival;
if (mpz_init_set_str (ival, literal_value.as_string ().c_str (), 10) != 0)
if (mpz_init_set_str (ival, s.c_str (), base) != 0)
{
rust_error_at (expr.get_locus (), "bad number in literal");
rust_error_at (expr.get_locus (), "failed to load number literal");
return error_mark_node;
}
if (expr.is_negative ())
mpz_neg (ival, ival);
mpz_t type_min;
mpz_t type_max;
mpz_t type_min, type_max;
mpz_init (type_min);
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 %qs",
tyty->get_name ().c_str ());
mpz_clear (type_min);
mpz_clear (type_max);
mpz_clear (ival);
return error_mark_node;
}
tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
mpz_clear (type_min);
mpz_clear (type_max);
mpz_clear (ival);

View File

@@ -101,12 +101,54 @@ struct constexpr_global_ctx
auto_vec<tree, 16> heap_vars;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
/* If non-null, only allow modification of existing values of the variables
in this set. Set by modifiable_tracker, below. */
hash_set<tree> *modifiable;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
/* Constructor. */
constexpr_global_ctx ()
: constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0)
{}
tree get_value (tree t)
{
if (tree *p = values.get (t))
if (*p != void_node)
return *p;
return NULL_TREE;
}
tree *get_value_ptr (tree t, bool initializing)
{
if (modifiable && !modifiable->contains (t))
return nullptr;
if (tree *p = values.get (t))
{
if (*p != void_node)
return p;
else if (initializing)
{
*p = NULL_TREE;
return p;
}
}
return nullptr;
}
void put_value (tree t, tree v)
{
bool already_in_map = values.put (t, v);
if (!already_in_map && modifiable)
modifiable->add (t);
}
void destroy_value (tree t)
{
if (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL
|| TREE_CODE (t) == RESULT_DECL)
values.put (t, void_node);
else
values.remove (t);
}
void clear_value (tree t) { values.remove (t); }
};
/* In constexpr.cc */
@@ -457,6 +499,7 @@ save_fundef_copy (tree fun, tree copy)
static tree constant_value_1 (tree decl, bool strict_p,
bool return_aggregate_cst_ok_p, bool unshare_p);
static tree decl_really_constant_value (tree decl, bool unshare_p /*= true*/);
tree decl_constant_value (tree decl, bool unshare_p);
static void non_const_var_error (location_t loc, tree r);
@@ -1925,30 +1968,40 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval,
}
/* fall through */
case CONST_DECL:
{
/* We used to not check lval for CONST_DECL, but darwin.cc uses
CONST_DECL for aggregate constants. */
if (lval)
return t;
else if (t == ctx->object)
return ctx->ctor;
if (VAR_P (t))
if (tree *p = ctx->global->values.get (t))
if (*p != NULL_TREE)
{
r = *p;
break;
}
/* We used to not check lval for CONST_DECL, but darwin.cc uses
CONST_DECL for aggregate constants. */
if (lval)
return t;
else if (t == ctx->object)
return ctx->ctor;
if (VAR_P (t))
{
if (tree v = ctx->global->get_value (t))
{
r = v;
break;
}
}
if (COMPLETE_TYPE_P (TREE_TYPE (t))
&& is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/ false))
{
/* If the class is empty, we aren't actually loading anything. */
r = build_constructor (TREE_TYPE (t), NULL);
TREE_CONSTANT (r) = true;
}
else if (ctx->strict)
r = decl_really_constant_value (t, /*unshare_p=*/false);
else
r = decl_constant_value (t, /*unshare_p=*/false);
if (TREE_CODE (r) == TARGET_EXPR
&& TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
r = TARGET_EXPR_INITIAL (r);
if (DECL_P (r))
{
if (TREE_CODE (r) == TARGET_EXPR
&& TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
r = TARGET_EXPR_INITIAL (r);
if (DECL_P (r) && !(VAR_P (t) && TYPE_REF_P (TREE_TYPE (t))))
{
if (!ctx->quiet)
non_const_var_error (loc, r);
return r;
}
}
*non_constant_p = true;
}
break;
case PARM_DECL:
@@ -4024,6 +4077,17 @@ constant_value_1 (tree decl, bool, bool, bool unshare_p)
return unshare_p ? unshare_expr (decl) : decl;
}
/* Like scalar_constant_value, but can also return aggregate initializers.
If UNSHARE_P, return an unshared copy of the initializer. */
tree
decl_really_constant_value (tree decl, bool unshare_p /*= true*/)
{
return constant_value_1 (decl, /*strict_p=*/true,
/*return_aggregate_cst_ok_p=*/true,
/*unshare_p=*/unshare_p);
}
// A more relaxed version of decl_really_constant_value, used by the
// common C/C++ code.
tree
@@ -4037,15 +4101,38 @@ decl_constant_value (tree decl, bool unshare_p)
static void
non_const_var_error (location_t loc, tree r)
{
error_at (loc,
"the value of %qD is not usable in a constant "
"expression",
r);
tree type = TREE_TYPE (r);
/* Avoid error cascade. */
if (DECL_INITIAL (r) == error_mark_node)
return;
// more in cp/constexpr.cc
if (DECL_DECLARED_CONSTEXPR_P (r))
inform (DECL_SOURCE_LOCATION (r), "%qD used in its own initializer", r);
else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type))
{
if (!DECL_INITIAL (r) || !TREE_CONSTANT (DECL_INITIAL (r))
|| !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (r))
inform (DECL_SOURCE_LOCATION (r),
"%qD was not initialized with a constant "
"expression",
r);
else
gcc_unreachable ();
}
else if (TYPE_REF_P (type))
inform (DECL_SOURCE_LOCATION (r),
"%qD was not initialized with a constant "
"expression",
r);
else
{
if (!DECL_DECLARED_CONSTEXPR_P (r))
inform (DECL_SOURCE_LOCATION (r), "%qD was not declared %<constexpr%>",
r);
else
inform (DECL_SOURCE_LOCATION (r),
"%qD does not have integral or enumeration type", r);
}
}
static tree

View File

@@ -0,0 +1,7 @@
// { dg-skip-if "" { *-*-* } { "-m32" } { "" } }
const X: i32 = const {
let a = 0x736f6d6570736575;
// { dg-error "integer overflows the respective type" "" { target *-*-* } .-1 }
let b = 14;
a + b
};