From: Egas Ribeiro <[email protected]>

This adds the is_refutable() and is_refutable(BaseType&) virtual methods
to HIR pattern nodes, distinguishing patterns whose refutability can be
determined syntactically from those requiring type context.

The syntactic overload returns a definitive answer for wildcards and
literals. Other patterns that depend on type context to be checked for
refutability, or call is_refutable() recursively, implement the typed
overload and ICE if called via the syntactic overload.

        Addresses: Rust-GCC#2082

gcc/rust/ChangeLog:

        * Make-lang.in: Add rust-hir-pattern-abstract.o.
        * hir/tree/rust-hir-path.h: Add is_refutable overrides.
        * hir/tree/rust-hir-pattern-abstract.h (class BaseType): Add
        typed is_refutable virtual method.
        * hir/tree/rust-hir-pattern.h: Add is_refutable to all pattern
        kinds.
        * hir/tree/rust-hir-pattern-abstract.cc: New file. Implement
        typed is_refutable for PathPattern, SlicePattern,
        TupleStructPattern.

Co-authored-by: João Novo <[email protected]>
Signed-off-by: Egas Ribeiro <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/0927016f337aac133517e692f008e6ca2ec81564

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4571

 gcc/rust/Make-lang.in                         |   1 +
 gcc/rust/hir/tree/rust-hir-path.h             |   6 +
 .../hir/tree/rust-hir-pattern-abstract.cc     | 326 ++++++++++++++++++
 gcc/rust/hir/tree/rust-hir-pattern-abstract.h |  20 +-
 gcc/rust/hir/tree/rust-hir-pattern.h          | 103 ++++++
 5 files changed, 453 insertions(+), 3 deletions(-)
 create mode 100644 gcc/rust/hir/tree/rust-hir-pattern-abstract.cc

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 443c441df..9103fbe23 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -210,6 +210,7 @@ GRS_OBJS = \
     rust/rust-hir-type-check-path.o \
     rust/rust-unsafe-checker.o \
     rust/rust-hir-pattern-analysis.o \
+    rust/rust-hir-pattern-abstract.o \
     rust/rust-intrinsic-handlers.o \
     rust/rust-compile-intrinsic.o \
     rust/rust-compile-pattern.o \
diff --git a/gcc/rust/hir/tree/rust-hir-path.h 
b/gcc/rust/hir/tree/rust-hir-path.h
index b5bed4eaa..c6b3cbfab 100644
--- a/gcc/rust/hir/tree/rust-hir-path.h
+++ b/gcc/rust/hir/tree/rust-hir-path.h
@@ -258,6 +258,12 @@ protected:
   PathPattern (LangItem::Kind lang_item)
     : segments ({}), lang_item (lang_item), kind (Kind::LangItem)
   {}
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+  bool is_refutable () const override
+  {
+    // Needs to be called with the other overload
+    rust_unreachable ();
+  }
 
   // Returns whether path has segments.
   bool has_segments () const
diff --git a/gcc/rust/hir/tree/rust-hir-pattern-abstract.cc 
b/gcc/rust/hir/tree/rust-hir-pattern-abstract.cc
new file mode 100644
index 000000000..f33af84f3
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-pattern-abstract.cc
@@ -0,0 +1,326 @@
+// Copyright (C) 2026 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-hir-pattern-abstract.h"
+#include "rust-hir-pattern.h"
+#include "rust-hir-item.h"
+#include "optional.h"
+#include "rust-tyty.h"
+#include "rust-hir-type-check.h"
+namespace Rust {
+namespace HIR {
+
+static bool
+is_refutable_with_lookup (Pattern &pattern)
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  HirId hir_id = pattern.get_mappings ().get_hirid ();
+  if (hir_id)
+    {
+      TyTy::BaseType *ty = nullptr;
+      if (context->lookup_type (hir_id, &ty))
+       {
+         if (pattern.is_refutable (*ty))
+           return true;
+       }
+      else
+       {
+         rust_internal_error_at (
+           pattern.get_locus (),
+           "failed to lookup hir item during refutability checks");
+         return true;
+       }
+    }
+  return false;
+}
+
+bool
+PathPattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  rust_assert (kind != Kind::LangItem);
+
+  auto &mappings = Analysis::Mappings::get ();
+  HirId hir_id = get_final_segment ().get_mappings ().get_hirid ();
+  if (hir_id)
+    {
+      auto item = mappings.lookup_hir_item (hir_id);
+      if (item && item.value ()->get_item_kind () == Item::ItemKind::Constant)
+       {
+         return true;
+       }
+      else
+       {
+         rust_internal_error_at (
+           get_locus (),
+           "failed to lookup hir item during refutability checks");
+         return true;
+       }
+    }
+
+  // A path pattern is irrefutable if it corresponds to an enum with one 
variant
+  if (scrutinee.get_kind () == TyTy::TypeKind::ADT)
+    {
+      const auto &adt = static_cast<const TyTy::ADTType &> (scrutinee);
+      if (adt.is_enum ())
+       {
+         return adt.number_of_variants () > 1;
+       }
+    }
+  // cannot have a Path that is neither a constant or an enum-like ADT
+  rust_unreachable ();
+}
+
+bool
+SlicePattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  // A slice pattern is refutable if the scrutinee is a dynamic slice,
+  // and the pattern contains anything other than just a rest pattern
+  if (scrutinee.get_kind () == TyTy::TypeKind::SLICE)
+    if (items->get_item_type () == SlicePatternItems::ItemType::HAS_REST)
+      {
+       const auto &items_has_rest
+         = static_cast<const SlicePatternItemsHasRest &> (*items);
+       if (items_has_rest.get_lower_patterns ().empty ()
+           && items_has_rest.get_upper_patterns ().empty ())
+         return false;
+      }
+
+  rust_assert (scrutinee.get_kind () == TyTy::TypeKind::ARRAY);
+  const auto &arr = static_cast<const TyTy::ArrayType &> (scrutinee);
+  switch (items->get_item_type ())
+    {
+    case SlicePatternItems::ItemType::NO_REST:
+      {
+       const auto &items_no_rest
+         = static_cast<const SlicePatternItemsNoRest &> (*items);
+       const auto *capacity_ty = arr.get_capacity ();
+       rust_assert (capacity_ty->get_kind () == TyTy::TypeKind::CONST);
+       auto *capacity_const = capacity_ty->as_const_type ();
+       rust_assert (capacity_const->const_kind ()
+                    == TyTy::BaseConstType::ConstKind::Value);
+       auto &capacity_value
+         = *static_cast<const TyTy::ConstValueType *> (capacity_const);
+       auto cap_tree = capacity_value.get_value ();
+       rust_assert (!error_operand_p (cap_tree));
+       size_t cap = (size_t) wi::to_wide (cap_tree).to_uhwi ();
+
+       if (items_no_rest.get_patterns ().size () != cap)
+         return true;
+       for (const auto &pattern : items_no_rest.get_patterns ())
+         if (is_refutable_with_lookup (*pattern))
+           {
+             return true;
+           }
+       break;
+      }
+    case SlicePatternItems::ItemType::HAS_REST:
+      {
+       const auto &items_has_rest
+         = static_cast<const SlicePatternItemsHasRest &> (*items);
+       size_t bound_patterns_count
+         = items_has_rest.get_lower_patterns ().size ()
+           + items_has_rest.get_upper_patterns ().size ();
+       auto *capacity_ty = arr.get_capacity ();
+       rust_assert (capacity_ty->get_kind () == TyTy::TypeKind::CONST);
+       auto *capacity_const = capacity_ty->as_const_type ();
+       rust_assert (capacity_const->const_kind ()
+                    == TyTy::BaseConstType::ConstKind::Value);
+       auto &capacity_value
+         = *static_cast<const TyTy::ConstValueType *> (capacity_const);
+       auto cap_tree = capacity_value.get_value ();
+       rust_assert (!error_operand_p (cap_tree));
+       size_t cap = (size_t) wi::to_wide (cap_tree).to_uhwi ();
+
+       if (bound_patterns_count > cap)
+         return true;
+       for (const auto &pattern : items_has_rest.get_lower_patterns ())
+         if (is_refutable_with_lookup (*pattern))
+           {
+             return true;
+           }
+       for (const auto &pattern : items_has_rest.get_upper_patterns ())
+         if (is_refutable_with_lookup (*pattern))
+           {
+             return true;
+           }
+       break;
+      }
+    }
+  return false;
+}
+
+bool
+TupleStructPattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  // A tuple struct pattern corresponding to an enum with multiple variants is
+  // always refutable
+  if (scrutinee.get_kind () == TyTy::TypeKind::ADT)
+    {
+      const auto &adt = static_cast<const TyTy::ADTType &> (scrutinee);
+      if (adt.is_enum () && adt.number_of_variants () > 1)
+       {
+         return true;
+       }
+    }
+  // We need to also check the refutability of each item's pattern in the tuple
+  // struct
+  switch (items->get_item_type ())
+    {
+    case TupleStructItems::ItemType::NO_REST:
+      for (const auto &pattern :
+          static_cast<TupleStructItemsNoRest &> (*items).get_patterns ())
+       if (is_refutable_with_lookup (*pattern))
+         {
+           return true;
+         }
+      break;
+    case TupleStructItems::ItemType::HAS_REST:
+      auto &items_has_rest = static_cast<TupleStructItemsHasRest &> (*items);
+      for (const auto &pattern : items_has_rest.get_lower_patterns ())
+       if (is_refutable_with_lookup (*pattern))
+         {
+           return true;
+         }
+      for (const auto &pattern : items_has_rest.get_upper_patterns ())
+       if (is_refutable_with_lookup (*pattern))
+         {
+           return true;
+         }
+      break;
+    }
+  return false;
+}
+
+bool
+TuplePattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  const auto *destructured = scrutinee.destructure ();
+  rust_assert (destructured->get_kind () == TyTy::TypeKind::TUPLE);
+  const auto &tup = static_cast<const TyTy::TupleType &> (*destructured);
+
+  switch (items->get_item_type ())
+    {
+    case TuplePatternItems::ItemType::NO_REST:
+      {
+       auto &no_rest = static_cast<TuplePatternItemsNoRest &> (*items);
+       const auto &patterns = no_rest.get_patterns ();
+       for (size_t i = 0; i < patterns.size (); i++)
+         if (patterns[i]->is_refutable (*tup.get_field (i)))
+           return true;
+       break;
+      }
+    case TuplePatternItems::ItemType::HAS_REST:
+      {
+       auto &has_rest = static_cast<TuplePatternItemsHasRest &> (*items);
+       const auto &lower = has_rest.get_lower_patterns ();
+       const auto &upper = has_rest.get_upper_patterns ();
+       for (size_t i = 0; i < lower.size (); i++)
+         if (lower[i]->is_refutable (*tup.get_field (i)))
+           return true;
+       size_t base = tup.get_fields ().size () - upper.size ();
+       for (size_t i = 0; i < upper.size (); i++)
+         if (upper[i]->is_refutable (*tup.get_field (base + i)))
+           return true;
+       break;
+      }
+    }
+  return false;
+}
+bool
+IdentifierPattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  if (has_subpattern ())
+    {
+      auto context = Resolver::TypeCheckContext::get ();
+      HirId hir_id = subpattern->get_mappings ().get_hirid ();
+      if (hir_id)
+       {
+         TyTy::BaseType *ty = nullptr;
+         if (context->lookup_type (hir_id, &ty))
+           {
+             return subpattern->is_refutable (*ty);
+           }
+         else
+           {
+             rust_internal_error_at (
+               get_locus (),
+               "failed to lookup hir item during refutability checks");
+             return true;
+           }
+       }
+    }
+
+  return false;
+}
+
+bool
+ReferencePattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  const auto *inner = scrutinee.destructure ();
+
+  rust_assert (inner->get_kind () == TyTy::TypeKind::REF);
+  const auto &ref = static_cast<const TyTy::ReferenceType &> (*inner);
+  return pattern->is_refutable (*ref.get_base ());
+}
+
+bool
+StructPattern::is_refutable (const TyTy::BaseType &scrutinee) const
+{
+  const auto *inner = scrutinee.destructure ();
+  rust_assert (inner->get_kind () == TyTy::TypeKind::ADT);
+  const auto &adt = static_cast<const TyTy::ADTType &> (*inner);
+  rust_assert (!adt.is_enum ());
+  TyTy::VariantDef *variant = adt.get_variants ().at (0);
+
+  for (const auto &field : elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+       {
+       case StructPatternField::ItemType::TUPLE_PAT:
+         {
+           const auto &tuple_field
+             = static_cast<const StructPatternFieldTuplePat &> (*field);
+           TyTy::StructFieldType *field_ty
+             = variant->get_field_at_index (tuple_field.get_index ());
+           if (tuple_field.get_tuple_pattern ().is_refutable (
+                 *field_ty->get_field_type ()))
+             return true;
+           break;
+         }
+       case StructPatternField::ItemType::IDENT_PAT:
+         {
+           const auto &ident_field
+             = static_cast<const StructPatternFieldIdentPat &> (*field);
+           TyTy::StructFieldType *field_ty = nullptr;
+           bool found = variant->lookup_field (
+             ident_field.get_identifier ().as_string (), &field_ty, nullptr);
+           rust_assert (found);
+           if (ident_field.get_pattern ().is_refutable (
+                 *field_ty->get_field_type ()))
+             return true;
+           break;
+         }
+       case StructPatternField::ItemType::IDENT:
+         break;
+       }
+    }
+  return false;
+}
+} // namespace HIR
+
+} // namespace Rust
diff --git a/gcc/rust/hir/tree/rust-hir-pattern-abstract.h 
b/gcc/rust/hir/tree/rust-hir-pattern-abstract.h
index ee1b46615..7195115b1 100644
--- a/gcc/rust/hir/tree/rust-hir-pattern-abstract.h
+++ b/gcc/rust/hir/tree/rust-hir-pattern-abstract.h
@@ -25,8 +25,10 @@
 #include "rust-system.h"
 
 namespace Rust {
+namespace TyTy {
+class BaseType;
+}
 namespace HIR {
-
 // Pattern base HIR node
 class Pattern : public Node, virtual public FullVisitable
 {
@@ -57,10 +59,22 @@ public:
     return std::unique_ptr<Pattern> (clone_pattern_impl ());
   }
 
-  // possible virtual methods: is_refutable()
-
   virtual ~Pattern () {}
 
+  // Syntactic refutability. ICEs for patterns whose refutability depends
+  // on type context (Path, Range, Slice, Alt). Callers that might
+  // encounter such patterns must use the typed overload.
+  virtual bool is_refutable () const = 0;
+  // Type-aware refutability. Defaults to the syntactic answer for
+  // patterns whose refutability is independent of the scrutinee type.
+
+  // Virtual method overriden by classes that enable this.
+  virtual bool
+  is_refutable (const TyTy::BaseType &scrutinee ATTRIBUTE_UNUSED) const
+  {
+    return is_refutable ();
+  }
+
   virtual std::string to_string () const = 0;
 
   std::string to_debug_string () const
diff --git a/gcc/rust/hir/tree/rust-hir-pattern.h 
b/gcc/rust/hir/tree/rust-hir-pattern.h
index bc9c63fae..90f8f5792 100644
--- a/gcc/rust/hir/tree/rust-hir-pattern.h
+++ b/gcc/rust/hir/tree/rust-hir-pattern.h
@@ -35,6 +35,9 @@ class LiteralPattern : public Pattern
   bool has_minus;
 
 public:
+  using Pattern::is_refutable;
+  bool is_refutable () const override { return true; }
+
   std::string to_string () const override;
 
   // Constructor for a literal pattern
@@ -95,6 +98,14 @@ class IdentifierPattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  bool is_refutable () const override
+  {
+    // Needs to be called with the other overload
+    rust_unreachable ();
+  }
+
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+
   std::string to_string () const override;
 
   // Returns whether the IdentifierPattern has a pattern to bind.
@@ -176,6 +187,9 @@ class WildcardPattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  using Pattern::is_refutable;
+  bool is_refutable () const override { return false; }
+
   std::string to_string () const override { return "_"; }
 
   WildcardPattern (Analysis::NodeMapping mappings, location_t locus)
@@ -364,6 +378,7 @@ class RangePattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  using Pattern::is_refutable;
   std::string to_string () const override;
 
   // Constructor
@@ -401,6 +416,15 @@ public:
   RangePattern (RangePattern &&other) = default;
   RangePattern &operator= (RangePattern &&other) = default;
 
+  bool is_refutable () const override
+  {
+    // TODO This needs to use exhaustiveness of ranges to determine
+    // refutability.
+    rust_sorry_at (get_locus (),
+                  "range pattern refutability is not yet implemented");
+    rust_unreachable ();
+  };
+
   location_t get_locus () const override { return locus; }
 
   void accept_vis (HIRFullVisitor &vis) override;
@@ -443,6 +467,13 @@ class ReferencePattern : public Pattern
 public:
   std::string to_string () const override;
 
+  bool is_refutable () const override
+  {
+    // Needs to be called with the other overload
+    rust_unreachable ();
+  }
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+
   ReferencePattern (Analysis::NodeMapping mappings,
                    std::unique_ptr<Pattern> pattern, Mutability reference_mut,
                    location_t locus)
@@ -776,6 +807,32 @@ class StructPattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  bool is_refutable () const override
+  {
+    for (const auto &field : elems.get_struct_pattern_fields ())
+      {
+       switch (field->get_item_type ())
+         {
+         case StructPatternField::ItemType::TUPLE_PAT:
+           if (static_cast<StructPatternFieldTuplePat &> (*field)
+                 .get_tuple_pattern ()
+                 .is_refutable ())
+             return true;
+           break;
+         case StructPatternField::ItemType::IDENT_PAT:
+           if (static_cast<StructPatternFieldIdentPat &> (*field)
+                 .get_pattern ()
+                 .is_refutable ())
+             return true;
+           break;
+         case StructPatternField::ItemType::IDENT:
+           break;
+         }
+      }
+    return false;
+  }
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+
   std::string to_string () const override;
 
   StructPattern (Analysis::NodeMapping mappings, PathInExpression struct_path,
@@ -1004,6 +1061,12 @@ class TupleStructPattern : public Pattern
    * data */
 
 public:
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+  bool is_refutable () const override
+  {
+    // Needs to be called with the other overload
+    rust_unreachable ();
+  }
   std::string to_string () const override;
 
   TupleStructPattern (Analysis::NodeMapping mappings,
@@ -1220,6 +1283,30 @@ class TuplePattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  bool is_refutable () const override
+  {
+    switch (items->get_item_type ())
+      {
+      case TuplePatternItems::ItemType::NO_REST:
+       for (const auto &pattern :
+            static_cast<TuplePatternItemsNoRest &> (*items).get_patterns ())
+         if (pattern->is_refutable ())
+           return true;
+       break;
+      case TuplePatternItems::ItemType::HAS_REST:
+       auto &items_has_rest = static_cast<TuplePatternItemsHasRest &> (*items);
+       for (const auto &pattern : items_has_rest.get_lower_patterns ())
+         if (pattern->is_refutable ())
+           return true;
+       for (const auto &pattern : items_has_rest.get_upper_patterns ())
+         if (pattern->is_refutable ())
+           return true;
+       break;
+      }
+    return false;
+  }
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+
   std::string to_string () const override;
 
   // Returns true if the tuple pattern has items
@@ -1432,6 +1519,12 @@ class SlicePattern : public Pattern
   Analysis::NodeMapping mappings;
 
 public:
+  bool is_refutable (const TyTy::BaseType &scrutinee) const override;
+  bool is_refutable () const override
+  {
+    // Needs to be called with the other overload
+    rust_unreachable ();
+  }
   std::string to_string () const override;
 
   SlicePattern (Analysis::NodeMapping mappings,
@@ -1528,6 +1621,16 @@ public:
   AltPattern (AltPattern &&other) = default;
   AltPattern &operator= (AltPattern &&other) = default;
 
+  using Pattern::is_refutable;
+  bool is_refutable () const override
+  {
+    // TODO We need exhaustiveness checks of the type being matched on to
+    // correctly determine refutability, so we conservatively return true
+    rust_sorry_at (get_locus (),
+                  "alt pattern refutability is not yet implemented");
+    rust_unreachable ();
+  }
+
   std::vector<std::unique_ptr<Pattern>> &get_alts () { return alts; }
   const std::vector<std::unique_ptr<Pattern>> &get_alts () const
   {
-- 
2.54.0

Reply via email to