]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Do proper const folding during typechecking for array capacities
authorPhilip Herron <herron.philip@googlemail.com>
Tue, 8 Jul 2025 20:13:48 +0000 (21:13 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 5 Aug 2025 14:36:54 +0000 (16:36 +0200)
This patch adds proper folding to the const expression for array capacity we
already have the const folding mechanics and the query system needed to handle cases
where the capacity is a function call in a const context. This leverages and pulls the
gcc tree capacity into the TyTy::ArrayType so it can be used for more typechecking and
eventually doing more const generics work.

Addresses Rust-GCC#3885
Fixes Rust-GCC#3882

gcc/rust/ChangeLog:

* backend/rust-compile-base.cc (HIRCompileBase::query_compile_const_expr): new wrapper
* backend/rust-compile-base.h: add prototype
* backend/rust-compile-context.cc (Context::get): singleton helper
* backend/rust-compile-context.h: likewise
* backend/rust-compile-type.cc (TyTyResolveCompile::visit): handle infer's that can default
* rust-session-manager.cc (Session::compile_crate): create the gcc context earlier for tychk
* typecheck/rust-hir-type-check-base.cc (TypeCheckBase::resolve_literal): const fold it
* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit): likewise
* typecheck/rust-hir-type-check-type.cc (TypeCheckType::visit): likewise
* typecheck/rust-tyty.cc (BaseType::monomorphized_clone): fix constructor call
(ArrayType::as_string): print capacity
(ArrayType::clone): fix constructor call
* typecheck/rust-tyty.h: track capacity
* typecheck/rust-unify.cc (UnifyRules::expect_array): check the capacities

gcc/testsuite/ChangeLog:

* rust/compile/all-cast.rs: shows array capacity now
* rust/compile/arrays2.rs: likewise
* rust/compile/const3.rs: fix error message
* rust/compile/const_generics_3.rs: disable until typecheck we get proper errors now!
* rust/compile/usize1.rs: proper capacity error message

Signed-off-by: Philip Herron <herron.philip@googlemail.com>
17 files changed:
gcc/rust/backend/rust-compile-base.cc
gcc/rust/backend/rust-compile-base.h
gcc/rust/backend/rust-compile-context.cc
gcc/rust/backend/rust-compile-context.h
gcc/rust/backend/rust-compile-type.cc
gcc/rust/rust-session-manager.cc
gcc/rust/typecheck/rust-hir-type-check-base.cc
gcc/rust/typecheck/rust-hir-type-check-expr.cc
gcc/rust/typecheck/rust-hir-type-check-type.cc
gcc/rust/typecheck/rust-tyty.cc
gcc/rust/typecheck/rust-tyty.h
gcc/rust/typecheck/rust-unify.cc
gcc/testsuite/rust/compile/all-cast.rs
gcc/testsuite/rust/compile/arrays2.rs
gcc/testsuite/rust/compile/const3.rs
gcc/testsuite/rust/compile/const_generics_3.rs
gcc/testsuite/rust/compile/usize1.rs

index c9f9fbe76bfd6a0bba2ea2b313bd453f2e4fa9d2..b2913ad3c3815f6c76ff647f3406166a8c89bab5 100644 (file)
@@ -575,6 +575,25 @@ HIRCompileBase::compile_constant_expr (
                                  expr_locus);
 }
 
+tree
+HIRCompileBase::query_compile_const_expr (Context *ctx, TyTy::BaseType *expr_ty,
+                                         HIR::Expr &const_value_expr)
+{
+  HIRCompileBase c (ctx);
+
+  ctx->push_const_context ();
+
+  HirId expr_id = const_value_expr.get_mappings ().get_hirid ();
+  location_t locus = const_value_expr.get_locus ();
+  tree capacity_expr = HIRCompileBase::compile_constant_expr (
+    ctx, expr_id, expr_ty, expr_ty, Resolver::CanonicalPath::create_empty (),
+    const_value_expr, locus, locus);
+
+  ctx->pop_const_context ();
+
+  return fold_expr (capacity_expr);
+}
+
 tree
 HIRCompileBase::indirect_expression (tree expr, location_t locus)
 {
index 6814abcf9debe074361f6d27a13cfa7d88cfa587..e9b85968fac39fcfdc4c14b8c564914ca2330fab 100644 (file)
@@ -38,6 +38,9 @@ public:
     const Resolver::CanonicalPath &canonical_path, HIR::Expr &const_value_expr,
     location_t locus, location_t expr_locus);
 
+  static tree query_compile_const_expr (Context *ctx, TyTy::BaseType *expr_ty,
+                                       HIR::Expr &const_value_expr);
+
 protected:
   HIRCompileBase (Context *ctx) : ctx (ctx) {}
 
index 3f328d33e7fb49cc27d24b8dbcd1b6270b0a2309..349d4927cb484f3abbe5a1209b5f41489c093bfc 100644 (file)
 namespace Rust {
 namespace Compile {
 
+Context *
+Context::get ()
+{
+  static Context *instance;
+  if (instance == nullptr)
+    instance = new Context ();
+
+  return instance;
+}
+
 Context::Context ()
   : tyctx (Resolver::TypeCheckContext::get ()),
     mappings (Analysis::Mappings::get ()), mangler (Mangler ())
index bb942816946d85ae41c8c6e690bcd138498ad5c9..d4a642b653ca915aa9aa17957356dddebbbd5596 100644 (file)
@@ -49,7 +49,7 @@ struct CustomDeriveInfo
 class Context
 {
 public:
-  Context ();
+  static Context *get ();
 
   void setup_builtins ();
 
@@ -390,6 +390,8 @@ public:
   }
 
 private:
+  Context ();
+
   Resolver::TypeCheckContext *tyctx;
   Analysis::Mappings &mappings;
   Mangler mangler;
index c397b4beb4ca1857149292e538d21f32e3bcb504..8f13bba534b66edb53c6b1ca0c7b975ffc9ff167 100644 (file)
@@ -121,6 +121,13 @@ TyTyResolveCompile::visit (const TyTy::InferType &type)
 
   if (orig == lookup)
     {
+      TyTy::BaseType *def = nullptr;
+      if (type.default_type (&def))
+       {
+         translated = TyTyResolveCompile::compile (ctx, def);
+         return;
+       }
+
       translated = error_mark_node;
       return;
     }
@@ -463,22 +470,7 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
 {
   tree element_type
     = TyTyResolveCompile::compile (ctx, type.get_element_type ());
-
-  ctx->push_const_context ();
-
-  HIR::Expr &hir_capacity_expr = type.get_capacity_expr ();
-  TyTy::BaseType *capacity_expr_ty = nullptr;
-  bool ok = ctx->get_tyctx ()->lookup_type (
-    hir_capacity_expr.get_mappings ().get_hirid (), &capacity_expr_ty);
-  rust_assert (ok);
-  tree capacity_expr = HIRCompileBase::compile_constant_expr (
-    ctx, hir_capacity_expr.get_mappings ().get_hirid (), capacity_expr_ty,
-    capacity_expr_ty, Resolver::CanonicalPath::create_empty (),
-    hir_capacity_expr, type.get_locus (), hir_capacity_expr.get_locus ());
-
-  ctx->pop_const_context ();
-
-  tree folded_capacity_expr = fold_expr (capacity_expr);
+  tree folded_capacity_expr = type.get_capacity ();
 
   // build_index_type takes the maximum index, which is one less than
   // the length.
index 3252fcdcc6c06d50422ce00cc1ee01e9424a9e5b..12cdc2e5dba9793fbe9212cdd6619e67c869dd0c 100644 (file)
@@ -681,6 +681,7 @@ Session::compile_crate (const char *filename)
   Resolver2_0::ImmutableNameResolutionContext::init (name_resolution_ctx);
 
   // type resolve
+  Compile::Context *ctx = Compile::Context::get ();
   Resolver::TypeResolution::Resolve (hir);
 
   Resolver::TypeCheckContext::get ()->get_variance_analysis_ctx ().solve ();
@@ -728,16 +729,15 @@ Session::compile_crate (const char *filename)
     return;
 
   // do compile to gcc generic
-  Compile::Context ctx;
-  Compile::CompileCrate::Compile (hir, &ctx);
+  Compile::CompileCrate::Compile (hir, ctx);
 
   // we can't do static analysis if there are errors to worry about
   if (!saw_errors ())
     {
       // lints
       Analysis::ScanDeadcode::Scan (hir);
-      Analysis::UnusedVariables::Lint (ctx);
-      Analysis::ReadonlyCheck::Lint (ctx);
+      Analysis::UnusedVariables::Lint (*ctx);
+      Analysis::ReadonlyCheck::Lint (*ctx);
 
       // metadata
       bool specified_emit_metadata
@@ -758,7 +758,7 @@ Session::compile_crate (const char *filename)
     }
 
   // pass to GCC middle-end
-  ctx.write_to_backend ();
+  ctx->write_to_backend ();
 }
 
 void
index 6d5806f967f8bddc09570498f89e661894002363..4bbd52a29f4580ad66831d717ecc6dcc448e3ed4 100644 (file)
@@ -17,6 +17,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-hir-type-check-base.h"
+#include "rust-compile-base.h"
 #include "rust-hir-type-check-expr.h"
 #include "rust-hir-type-check-type.h"
 #include "rust-hir-trait-resolve.h"
@@ -287,9 +288,11 @@ TypeCheckBase::resolve_literal (const Analysis::NodeMapping &expr_mappings,
                                               crate_num),
                                             UNKNOWN_LOCAL_DEFID);
 
+       auto ctx = Compile::Context::get ();
+       tree capacity = Compile::HIRCompileBase::query_compile_const_expr (
+         ctx, expected_ty, *literal_capacity);
        TyTy::ArrayType *array
-         = new TyTy::ArrayType (array_mapping.get_hirid (), locus,
-                                *literal_capacity,
+         = new TyTy::ArrayType (array_mapping.get_hirid (), locus, capacity,
                                 TyTy::TyVar (u8->get_ref ()));
        context->insert_type (array_mapping, array);
 
index eb50803814f42c155c126401ad6fd4558f2f5d90..ccde4dd77bffbdde975e2934ace55c176b51d9a2 100644 (file)
@@ -31,6 +31,7 @@
 #include "rust-hir-type-check-item.h"
 #include "rust-type-util.h"
 #include "rust-immutable-name-resolution-context.h"
+#include "rust-compile-base.h"
 
 // for flag_name_resolution_2_0
 #include "options.h"
@@ -1031,6 +1032,7 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
 
   HIR::Expr *capacity_expr = nullptr;
   TyTy::BaseType *element_type = nullptr;
+  TyTy::BaseType *capacity_type = nullptr;
   switch (elements.get_array_expr_type ())
     {
     case HIR::ArrayElems::ArrayExprType::COPIED:
@@ -1039,7 +1041,7 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
          = static_cast<HIR::ArrayElemsCopied &> (elements);
        element_type = TypeCheckExpr::Resolve (elems.get_elem_to_copy ());
 
-       auto capacity_type
+       auto capacity_expr_ty
          = TypeCheckExpr::Resolve (elems.get_num_copies_expr ());
 
        TyTy::BaseType *expected_ty = nullptr;
@@ -1048,13 +1050,14 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
        context->insert_type (elems.get_num_copies_expr ().get_mappings (),
                              expected_ty);
 
-       unify_site (expr.get_mappings ().get_hirid (),
-                   TyTy::TyWithLocation (expected_ty),
-                   TyTy::TyWithLocation (
-                     capacity_type, elems.get_num_copies_expr ().get_locus ()),
-                   expr.get_locus ());
+       unify_site (
+         expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (expected_ty),
+         TyTy::TyWithLocation (capacity_expr_ty,
+                               elems.get_num_copies_expr ().get_locus ()),
+         expr.get_locus ());
 
        capacity_expr = &elems.get_num_copies_expr ();
+       capacity_type = expected_ty;
       }
       break;
 
@@ -1096,13 +1099,20 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
        bool ok = context->lookup_builtin ("usize", &expected_ty);
        rust_assert (ok);
        context->insert_type (mapping, expected_ty);
+       capacity_type = expected_ty;
       }
       break;
     }
 
-  infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (),
-                                expr.get_locus (), *capacity_expr,
-                                TyTy::TyVar (element_type->get_ref ()));
+  rust_assert (capacity_expr);
+  rust_assert (capacity_type);
+  auto ctx = Compile::Context::get ();
+  tree capacity
+    = Compile::HIRCompileBase::query_compile_const_expr (ctx, capacity_type,
+                                                        *capacity_expr);
+  infered
+    = new TyTy::ArrayType (expr.get_mappings ().get_hirid (), expr.get_locus (),
+                          capacity, TyTy::TyVar (element_type->get_ref ()));
 }
 
 // empty struct
index f23352baacdad4506a702956bb945af04bafa483..462b3d4876733e02e3b7109404312248118ea97d 100644 (file)
@@ -29,6 +29,7 @@
 #include "rust-substitution-mapper.h"
 #include "rust-type-util.h"
 #include "rust-system.h"
+#include "rust-compile-base.h"
 
 namespace Rust {
 namespace Resolver {
@@ -710,9 +711,14 @@ TypeCheckType::visit (HIR::ArrayType &type)
              type.get_size_expr ().get_locus ());
 
   TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ());
-  translated = new TyTy::ArrayType (type.get_mappings ().get_hirid (),
-                                   type.get_locus (), type.get_size_expr (),
-                                   TyTy::TyVar (base->get_ref ()));
+
+  auto ctx = Compile::Context::get ();
+  tree capacity
+    = Compile::HIRCompileBase::query_compile_const_expr (ctx, capacity_type,
+                                                        type.get_size_expr ());
+  translated
+    = new TyTy::ArrayType (type.get_mappings ().get_hirid (), type.get_locus (),
+                          capacity, TyTy::TyVar (base->get_ref ()));
 }
 
 void
index e2dfa743b9aa13923a89828870868b9ec0e6dba0..75370e96c6627abddb040f971b253ec7b27e6d63 100644 (file)
 #include "rust-tyty-cmp.h"
 #include "rust-type-util.h"
 #include "rust-hir-type-bounds.h"
+#include "print-tree.h"
 
 #include "options.h"
 #include "rust-system.h"
+#include "tree.h"
 
 namespace Rust {
 namespace TyTy {
@@ -574,7 +576,7 @@ BaseType::monomorphized_clone () const
     {
       TyVar elm = arr->get_var_element_type ().monomorphized_clone ();
       return new ArrayType (arr->get_ref (), arr->get_ty_ref (), ident.locus,
-                           arr->get_capacity_expr (), elm,
+                           arr->get_capacity (), elm,
                            arr->get_combined_refs ());
     }
   else if (auto slice = x->try_as<const SliceType> ())
@@ -2486,7 +2488,16 @@ ArrayType::accept_vis (TyConstVisitor &vis) const
 std::string
 ArrayType::as_string () const
 {
-  return "[" + get_element_type ()->as_string () + ":" + "CAPACITY" + "]";
+  std::string capacity_str = "<error>";
+  if (!error_operand_p (capacity))
+    {
+      unsigned HOST_WIDE_INT length = wi::to_wide (capacity).to_uhwi ();
+
+      char buf[64];
+      snprintf (buf, sizeof (buf), HOST_WIDE_INT_PRINT_UNSIGNED, length);
+      capacity_str = std::string (buf);
+    }
+  return "[" + get_element_type ()->as_string () + "; " + capacity_str + "]";
 }
 
 bool
@@ -2525,7 +2536,7 @@ ArrayType::get_var_element_type () const
 BaseType *
 ArrayType::clone () const
 {
-  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr,
+  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity,
                        element_type, get_combined_refs ());
 }
 
index e8ddd3e1d91db5c2327cf2df76ce2f4f46929393..e0d0358e9e663a7d98cd8d8a62b4203d2a6c6f79 100644 (file)
@@ -29,6 +29,7 @@
 #include "rust-tyty-region.h"
 #include "rust-system.h"
 #include "rust-hir.h"
+#include "tree.h"
 
 namespace Rust {
 
@@ -1156,19 +1157,18 @@ class ArrayType : public BaseType
 public:
   static constexpr auto KIND = TypeKind::ARRAY;
 
-  ArrayType (HirId ref, location_t locus, HIR::Expr &capacity_expr, TyVar base,
+  ArrayType (HirId ref, location_t locus, tree capacity, TyVar base,
             std::set<HirId> refs = std::set<HirId> ())
     : BaseType (ref, ref, TypeKind::ARRAY,
                {Resolver::CanonicalPath::create_empty (), locus}, refs),
-      element_type (base), capacity_expr (capacity_expr)
+      element_type (base), capacity (capacity)
   {}
 
-  ArrayType (HirId ref, HirId ty_ref, location_t locus,
-            HIR::Expr &capacity_expr, TyVar base,
-            std::set<HirId> refs = std::set<HirId> ())
+  ArrayType (HirId ref, HirId ty_ref, location_t locus, tree capacity,
+            TyVar base, std::set<HirId> refs = std::set<HirId> ())
     : BaseType (ref, ty_ref, TypeKind::ARRAY,
                {Resolver::CanonicalPath::create_empty (), locus}, refs),
-      element_type (base), capacity_expr (capacity_expr)
+      element_type (base), capacity (capacity)
   {}
 
   void accept_vis (TyVisitor &vis) override;
@@ -1187,15 +1187,13 @@ public:
 
   BaseType *clone () const final override;
 
-  HIR::Expr &get_capacity_expr () const { return capacity_expr; }
+  tree get_capacity () const { return capacity; }
 
   ArrayType *handle_substitions (SubstitutionArgumentMappings &mappings);
 
 private:
   TyVar element_type;
-  // FIXME: I dont think this should be in tyty - tyty should already be const
-  // evaluated
-  HIR::Expr &capacity_expr;
+  tree capacity;
 };
 
 class SliceType : public BaseType
index 2a981acaf3a7b35a4155ed53371f2d688a8323f8..ac9019369826f62e82b9c0a03dcaeefd43c58e69 100644 (file)
@@ -17,6 +17,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-unify.h"
+#include "tree.h"
 
 namespace Rust {
 namespace Resolver {
@@ -825,14 +826,24 @@ UnifyRules::expect_array (TyTy::ArrayType *ltype, TyTy::BaseType *rtype)
          = resolve_subtype (TyTy::TyWithLocation (ltype->get_element_type ()),
                             TyTy::TyWithLocation (type.get_element_type ()));
 
-       if (element_unify->get_kind () != TyTy::TypeKind::ERROR)
-         {
-           return new TyTy::ArrayType (type.get_ref (), type.get_ty_ref (),
-                                       type.get_ident ().locus,
-                                       type.get_capacity_expr (),
-                                       TyTy::TyVar (
-                                         element_unify->get_ref ()));
-         }
+       if (element_unify->get_kind () == TyTy::TypeKind::ERROR)
+         return new TyTy::ErrorType (0);
+
+       // TODO infer capacity?
+       tree lcap = ltype->get_capacity ();
+       tree rcap = type.get_capacity ();
+       if (error_operand_p (lcap) || error_operand_p (rcap))
+         return new TyTy::ErrorType (0);
+
+       auto lc = wi::to_wide (lcap).to_uhwi ();
+       auto rc = wi::to_wide (rcap).to_uhwi ();
+       if (lc != rc)
+         return new TyTy::ErrorType (0);
+
+       return new TyTy::ArrayType (type.get_ref (), type.get_ty_ref (),
+                                   type.get_ident ().locus,
+                                   type.get_capacity (),
+                                   TyTy::TyVar (element_unify->get_ref ()));
       }
       break;
 
index fa24373a67793b69086cebca0ced0816f475673b..6d8576cc84f043027a22429a58b2d1449fa2ea5a 100644 (file)
@@ -4,7 +4,7 @@ fn main() {
 
     0u32 as char; // { dg-error "cannot cast .u32. as .char., only .u8. can be cast as .char." }
 
-    let x = &[1_usize, 2] as [usize]; // { dg-error "cast to unsized type: .& .usize:CAPACITY.. as ..usize.." }
+    let x = &[1_usize, 2] as [usize]; // { dg-error "cast to unsized type: .& .usize; 2.. as ..usize.." }
 
     let a = &0u8; // Here, `x` is a `&u8`.
     let y: u32 = a as u32; // { dg-error "casting .& u8. as .u32. is invalid" }
index 668bcf0951b16005c6329fcb76ac8dbc00f36e73..109005922c34c5569b7de0cda500998fac471ad4 100644 (file)
@@ -1,5 +1,4 @@
-// { dg-additional-options "-w" }
 fn main() {
     let array: [i32; 5] = [1, 2, 3];
-    // { dg-error "mismatched types, expected an array with a fixed size of 5 elements, found one with 3 elements" "" { target *-*-* } .-1 }
+    // { dg-error "mismatched types, expected ..i32; 5.. but got ...integer.; 3.. .E0308." "" { target *-*-* } .-1 }
 }
index 22dc3d356cacca470699724b7ee3bc17d72ba223..c1d0f29ae199f4ea70d115677632822c200e6b3a 100644 (file)
@@ -3,5 +3,5 @@ fn size() -> usize {
 }
 
 fn main() {
-    let a = [15; size()]; // { dg-error "only functions marked as .const. are allowed to be called from constant contexts" }
+    let a = [15; size()]; // { dg-error "calls in constants are limited to constant functions, tuple structs and tuple variants" }
 }
index 09d5835074d1d8683f04dcc2066c005bff40a94a..d8b2ddfedaf0b9e1e5c1ce2a81e731cc061c1702 100644 (file)
@@ -1,4 +1,4 @@
-// { dg-additional-options "-w -frust-compile-until=compilation" }
+// { dg-additional-options "-w -frust-compile-until=typecheck" }
 
 #[lang = "sized"]
 trait Sized {}
index 36cb99b5574a759f6e75000e593985271d43d9db..08f6c9cf5cb4e45c6a65d70891081f3b03cc5df9 100644 (file)
@@ -1,5 +1,5 @@
 fn main() {
     let a = [1, 2, 3];
     let b: u32 = 1;
-    let c = a[b]; // { dg-error "the type ...integer..CAPACITY.. cannot be indexed by .u32." }
+    let c = a[b]; // { dg-error "the type ...integer.; 3.. cannot be indexed by .u32." }
 }