]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Fix ICE with invalid const expression
authorPhilip Herron <herron.philip@googlemail.com>
Fri, 12 Sep 2025 15:37:32 +0000 (16:37 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Thu, 30 Oct 2025 19:59:13 +0000 (20:59 +0100)
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>
gcc/rust/backend/rust-compile-expr.cc
gcc/rust/backend/rust-constexpr.cc
gcc/testsuite/rust/compile/issue-4139.rs [new file with mode: 0644]

index 2d910e3e0a4567a0c3ec4d1006867a6d525ab43f..d09ac727529f251f2dc058f79774d61b516bf618 100644 (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);
index d524d09f88ecbd5fd3ee52d6dd6a6d588f9ba9d5..0dbfe895f649e2f0c29fad4a9e2ec4ca9cbf01d3 100644 (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
diff --git a/gcc/testsuite/rust/compile/issue-4139.rs b/gcc/testsuite/rust/compile/issue-4139.rs
new file mode 100644 (file)
index 0000000..dc62d1c
--- /dev/null
@@ -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
+};