blob: 146208c4751ac2dbc59ed113636fcad956f61162 [file]
// 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.hir.items.lookup (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