Skip to content

gccrs: Implement rest pattern support for slice patterns #4062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 237 additions & 53 deletions gcc/rust/backend/rust-compile-pattern.cc
Original file line number Diff line number Diff line change
Expand Up @@ -534,25 +534,14 @@ CompilePatternCheckExpr::visit (HIR::SlicePattern &pattern)
|| lookup->get_kind () == TyTy::TypeKind::SLICE
|| lookup->get_kind () == TyTy::REF);

size_t array_element_index = 0;
// function ptr that points to either array_index_expression or
// slice_index_expression depending on the scrutinee's type
tree (*scrutinee_index_expr_func) (tree, tree, location_t) = nullptr;

switch (lookup->get_kind ())
{
case TyTy::TypeKind::ARRAY:
for (auto &pattern_member : pattern.get_items ())
{
tree array_index_tree
= Backend::size_constant_expression (array_element_index++);
tree element_expr
= Backend::array_index_expression (match_scrutinee_expr,
array_index_tree,
pattern.get_locus ());
tree check_expr_sub
= CompilePatternCheckExpr::Compile (*pattern_member, element_expr,
ctx);
check_expr = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::BITWISE_AND, check_expr,
check_expr_sub, pattern.get_locus ());
}
scrutinee_index_expr_func = Backend::array_index_expression;
break;
case TyTy::TypeKind::SLICE:
rust_sorry_at (
Expand All @@ -562,36 +551,153 @@ CompilePatternCheckExpr::visit (HIR::SlicePattern &pattern)
case TyTy::TypeKind::REF:
{
rust_assert (RS_DST_FLAG_P (TREE_TYPE (match_scrutinee_expr)));
scrutinee_index_expr_func = Backend::slice_index_expression;
tree size_field
= Backend::struct_field_expression (match_scrutinee_expr, 1,
pattern.get_locus ());

// First compare the size
check_expr = Backend::comparison_expression (
ComparisonOperator::EQUAL, size_field,
build_int_cst (size_type_node, pattern.get_items ().size ()),
pattern.get_locus ());
// for slices, generate a dynamic size comparison expression tree
// because size checking is done at runtime.
switch (pattern.get_items ().get_item_type ())
{
case HIR::SlicePatternItems::ItemType::NO_REST:
{
auto &items = static_cast<HIR::SlicePatternItemsNoRest &> (
pattern.get_items ());
check_expr = Backend::comparison_expression (
ComparisonOperator::EQUAL, size_field,
build_int_cst (size_type_node, items.get_patterns ().size ()),
pattern.get_locus ());
}
break;
case HIR::SlicePatternItems::ItemType::HAS_REST:
{
auto &items = static_cast<HIR::SlicePatternItemsHasRest &> (
pattern.get_items ());
auto pattern_min_cap = items.get_lower_patterns ().size ()
+ items.get_upper_patterns ().size ();
check_expr = Backend::comparison_expression (
ComparisonOperator::GREATER_OR_EQUAL, size_field,
build_int_cst (size_type_node, pattern_min_cap),
pattern.get_locus ());
}
break;
}
}
break;
default:
rust_unreachable ();
}

rust_assert (scrutinee_index_expr_func != nullptr);

// Then compare each element in the slice pattern
for (auto &pattern_member : pattern.get_items ())
// Generate tree to compare every element within array/slice
size_t element_index = 0;
switch (pattern.get_items ().get_item_type ())
{
case HIR::SlicePatternItems::ItemType::NO_REST:
{
auto &items
= static_cast<HIR::SlicePatternItemsNoRest &> (pattern.get_items ());
for (auto &pattern_member : items.get_patterns ())
{
tree slice_index_tree
= Backend::size_constant_expression (array_element_index++);
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= Backend::slice_index_expression (match_scrutinee_expr,
slice_index_tree,
pattern.get_locus ());
= scrutinee_index_expr_func (match_scrutinee_expr, index_tree,
pattern.get_locus ());
tree check_expr_sub
= CompilePatternCheckExpr::Compile (*pattern_member, element_expr,
ctx);
check_expr = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::BITWISE_AND, check_expr,
check_expr_sub, pattern.get_locus ());
}
break;
}
case HIR::SlicePatternItems::ItemType::HAS_REST:
{
auto &items
= static_cast<HIR::SlicePatternItemsHasRest &> (pattern.get_items ());
for (auto &pattern_member : items.get_lower_patterns ())
{
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr, index_tree,
pattern.get_locus ());
tree check_expr_sub
= CompilePatternCheckExpr::Compile (*pattern_member, element_expr,
ctx);
check_expr = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::BITWISE_AND, check_expr,
check_expr_sub, pattern.get_locus ());
}

// handle codegen for upper patterns differently for both types
switch (lookup->get_kind ())
{
case TyTy::TypeKind::ARRAY:
{
// for array type scrutinee, we can simply get the capacity as a
// const and calculate how many elements to skip
auto array_ty = static_cast<TyTy::ArrayType *> (lookup);
auto cap_tree = array_ty->get_capacity ()->get_value ();
size_t cap_wi = (size_t) wi::to_wide (cap_tree).to_uhwi ();
element_index = cap_wi - items.get_upper_patterns ().size ();
for (auto &pattern_member : items.get_upper_patterns ())
{
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr,
index_tree,
pattern.get_locus ());
tree check_expr_sub
= CompilePatternCheckExpr::Compile (*pattern_member,
element_expr, ctx);
check_expr = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::BITWISE_AND, check_expr,
check_expr_sub, pattern.get_locus ());
}
}
break;
case TyTy::TypeKind::REF:
{
// for slice type scrutinee, size is dyanamic, so number of
// elements to skip is calculated during runtime
tree slice_size
= Backend::struct_field_expression (match_scrutinee_expr, 1,
pattern.get_locus ());
tree upper_patterns_size = Backend::size_constant_expression (
items.get_upper_patterns ().size ());
tree index_tree = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::SUBTRACT, slice_size,
upper_patterns_size, pattern.get_locus ());
for (auto &pattern_member : items.get_upper_patterns ())
{
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr,
index_tree,
pattern.get_locus ());
tree check_expr_sub
= CompilePatternCheckExpr::Compile (*pattern_member,
element_expr, ctx);
check_expr = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::BITWISE_AND, check_expr,
check_expr_sub, pattern.get_locus ());
index_tree = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::ADD, index_tree,
Backend::size_constant_expression (1),
pattern.get_locus ());
}
}
break;
default:
rust_unreachable ();
}
}
break;
default:
rust_unreachable ();
}
}

Expand Down Expand Up @@ -932,43 +1038,121 @@ CompilePatternBindings::visit (HIR::SlicePattern &pattern)
|| lookup->get_kind () == TyTy::TypeKind::SLICE
|| lookup->get_kind () == TyTy::REF);

size_t array_element_index = 0;
// function ptr that points to either array_index_expression or
// slice_index_expression depending on the scrutinee's type
tree (*scrutinee_index_expr_func) (tree, tree, location_t) = nullptr;

switch (lookup->get_kind ())
{
case TyTy::TypeKind::ARRAY:
for (auto &pattern_member : pattern.get_items ())
{
tree array_index_tree
= Backend::size_constant_expression (array_element_index++);
tree element_expr
= Backend::array_index_expression (match_scrutinee_expr,
array_index_tree,
pattern.get_locus ());
CompilePatternBindings::Compile (*pattern_member, element_expr, ctx);
}
scrutinee_index_expr_func = Backend::array_index_expression;
break;
case TyTy::TypeKind::SLICE:
rust_sorry_at (
pattern.get_locus (),
"SlicePattern matching against non-ref slices are not yet supported");
rust_sorry_at (pattern.get_locus (),
"SlicePattern matching against non-ref slices are "
"not yet supported");
break;
case TyTy::TypeKind::REF:
scrutinee_index_expr_func = Backend::slice_index_expression;
break;
default:
rust_unreachable ();
}

rust_assert (scrutinee_index_expr_func != nullptr);

size_t element_index = 0;

switch (pattern.get_items ().get_item_type ())
{
case HIR::SlicePatternItems::ItemType::NO_REST:
{
for (auto &pattern_member : pattern.get_items ())
auto &items
= static_cast<HIR::SlicePatternItemsNoRest &> (pattern.get_items ());
for (auto &pattern_member : items.get_patterns ())
{
tree slice_index_tree
= Backend::size_constant_expression (array_element_index++);
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= Backend::slice_index_expression (match_scrutinee_expr,
slice_index_tree,
pattern.get_locus ());
= scrutinee_index_expr_func (match_scrutinee_expr, index_tree,
pattern.get_locus ());
CompilePatternBindings::Compile (*pattern_member, element_expr,
ctx);
}
break;
}
default:
rust_unreachable ();
break;
case HIR::SlicePatternItems::ItemType::HAS_REST:
{
auto &items
= static_cast<HIR::SlicePatternItemsHasRest &> (pattern.get_items ());
for (auto &pattern_member : items.get_lower_patterns ())
{
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr, index_tree,
pattern.get_locus ());
CompilePatternBindings::Compile (*pattern_member, element_expr,
ctx);
}

// handle codegen for upper patterns differently for both types
switch (lookup->get_kind ())
{
case TyTy::TypeKind::ARRAY:
{
auto array_ty = static_cast<TyTy::ArrayType *> (lookup);
auto cap_tree = array_ty->get_capacity ()->get_value ();
size_t cap_wi = (size_t) wi::to_wide (cap_tree).to_uhwi ();
element_index = cap_wi - items.get_upper_patterns ().size ();
for (auto &pattern_member : items.get_upper_patterns ())
{
tree index_tree
= Backend::size_constant_expression (element_index++);
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr,
index_tree,
pattern.get_locus ());
CompilePatternBindings::Compile (*pattern_member,
element_expr, ctx);
}
}
break;
case TyTy::TypeKind::SLICE:
rust_sorry_at (pattern.get_locus (),
"SlicePattern matching against non-ref slices are "
"not yet supported");
break;
case TyTy::TypeKind::REF:
{
tree slice_size
= Backend::struct_field_expression (match_scrutinee_expr, 1,
pattern.get_locus ());
tree upper_patterns_size = Backend::size_constant_expression (
items.get_upper_patterns ().size ());
tree index_tree = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::SUBTRACT, slice_size,
upper_patterns_size, pattern.get_locus ());
for (auto &pattern_member : items.get_upper_patterns ())
{
tree element_expr
= scrutinee_index_expr_func (match_scrutinee_expr,
index_tree,
pattern.get_locus ());
CompilePatternBindings::Compile (*pattern_member,
element_expr, ctx);
index_tree = Backend::arithmetic_or_logical_expression (
ArithmeticOrLogicalOperator::ADD, index_tree,
Backend::size_constant_expression (1),
pattern.get_locus ());
}
}
break;
default:
rust_unreachable ();
}
}
break;
}
}

Expand Down
29 changes: 26 additions & 3 deletions gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,34 @@ PatternBindingBuilder::visit (HIR::SlicePattern &pattern)
return ty->as<TyTy::SliceType> ()->get_element_type ();
});

// Regions are unchnaged.
// Regions are unchanged.

for (auto &item : pattern.get_items ())
switch (pattern.get_items ().get_item_type ())
{
item->accept_vis (*this);
case HIR::SlicePatternItems::NO_REST:
{
auto &items
= static_cast<HIR::SlicePatternItemsNoRest &> (pattern.get_items ());
for (auto &member : items.get_patterns ())
{
member->accept_vis (*this);
}
break;
}
case HIR::SlicePatternItems::HAS_REST:
{
auto &items
= static_cast<HIR::SlicePatternItemsHasRest &> (pattern.get_items ());
for (auto &member : items.get_lower_patterns ())
{
member->accept_vis (*this);
}
for (auto &member : items.get_upper_patterns ())
{
member->accept_vis (*this);
}
break;
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,14 @@ class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor
rust_unreachable ();
}
void visit (HIR::TuplePattern &pattern) override { rust_unreachable (); }
void visit (HIR::SlicePatternItemsNoRest &tuple_items) override
{
rust_unreachable ();
}
void visit (HIR::SlicePatternItemsHasRest &tuple_items) override
{
rust_unreachable ();
}
void visit (HIR::SlicePattern &pattern) override { rust_unreachable (); }
void visit (HIR::AltPattern &pattern) override { rust_unreachable (); }
void visit (HIR::EmptyStmt &stmt) override { rust_unreachable (); }
Expand Down
Loading
Loading