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

arthur.cohen@embecosm.com arthur.cohen@embecosm.com
Fri Aug 7 12:39:37 GMT 2026


From: Egas Ribeiro <egas.g.ribeiro@tecnico.ulisboa.pt>

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 <joao.c.novo@tecnico.ulisboa.pt>
Signed-off-by: Egas Ribeiro <egas.g.ribeiro@tecnico.ulisboa.pt>
---
 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



More information about the Gcc-rust mailing list