https://gcc.gnu.org/g:b44ed1c5e770490c0d73c648a741263c12be6328
commit r17-3105-gb44ed1c5e770490c0d73c648a741263c12be6328 Author: Egas Ribeiro <[email protected]> Date: Tue May 19 18:06:51 2026 +0100 gccrs: rust: Add is_refutable methods to HIR pattern nodes 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]> Diff: --- gcc/rust/Make-lang.in | 1 + gcc/rust/hir/tree/rust-hir-path.h | 6 + gcc/rust/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(-) diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 443c441df534..9103fbe234af 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 b5bed4eaa135..c6b3cbfab376 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 000000000000..f33af84f3497 --- /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 ee1b46615184..7195115b14b7 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 bc9c63fae0a1..90f8f57921a8 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 {
