[COMMITTED 46/77] gccrs: rust: Add is_refutable methods to HIR pattern nodes

[email protected]
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.rust
Message-ID <[email protected]>
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]>
---
 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 443c441df53..9103fbe234a 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 b5bed4eaa13..c6b3cbfab37 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 00000000000..f33af84f349
--- /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 ee1b4661518..7195115b14b 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 bc9c63fae0a..90f8f57921a 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.50.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.