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