diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc index 149f6b074113..ab12b90a8435 100644 --- a/gcc/rust/backend/rust-compile-pattern.cc +++ b/gcc/rust/backend/rust-compile-pattern.cc @@ -358,8 +358,42 @@ CompilePatternCheckExpr::visit (HIR::TupleStructPattern &pattern) { case HIR::TupleStructItems::HAS_REST: { - // TODO - rust_unreachable (); + HIR::TupleStructItemsHasRest &items_has_rest + = static_cast (items); + size_t num_patterns = items_has_rest.get_lower_patterns ().size () + + items_has_rest.get_upper_patterns ().size (); + + // enums cases shouldn't reach here + rust_assert (num_patterns <= variant->num_fields () + && (!adt->is_enum ())); + + size_t tuple_field_index = 0; + for (auto &pattern : items_has_rest.get_lower_patterns ()) + { + tree field_expr + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pattern->get_locus ()); + tree check_expr_sub + = CompilePatternCheckExpr::Compile (*pattern, field_expr, ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_expr, + check_expr_sub, pattern->get_locus ()); + } + tuple_field_index = variant->num_fields () + - items_has_rest.get_upper_patterns ().size (); + for (auto &pattern : items_has_rest.get_upper_patterns ()) + { + tree field_expr + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pattern->get_locus ()); + tree check_expr_sub + = CompilePatternCheckExpr::Compile (*pattern, field_expr, ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_expr, + check_expr_sub, pattern->get_locus ()); + } } break; @@ -738,23 +772,52 @@ CompilePatternBindings::visit (HIR::TupleStructPattern &pattern) { case HIR::TupleStructItems::HAS_REST: { - // TODO - rust_unreachable (); + HIR::TupleStructItemsHasRest &items_has_rest + = static_cast (items); + size_t num_patterns = items_has_rest.get_lower_patterns ().size () + + items_has_rest.get_upper_patterns ().size (); + + // enums cases shouldn't reach here + rust_assert (num_patterns <= variant->num_fields () + && (!adt->is_enum ())); + + size_t tuple_field_index = 0; + for (auto &pattern : items_has_rest.get_lower_patterns ()) + { + tree binding + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pattern->get_locus ()); + + CompilePatternBindings::Compile (*pattern, binding, ctx); + } + + tuple_field_index = variant->num_fields () + - items_has_rest.get_upper_patterns ().size (); + + for (auto &pattern : items_has_rest.get_upper_patterns ()) + { + tree binding + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pattern->get_locus ()); + + CompilePatternBindings::Compile (*pattern, binding, ctx); + } } break; case HIR::TupleStructItems::NO_REST: { - HIR::TupleStructItemsNoRest &items_no_range + HIR::TupleStructItemsNoRest &items_no_rest = static_cast (items); - - rust_assert (items_no_range.get_patterns ().size () + rust_assert (items_no_rest.get_patterns ().size () == variant->num_fields ()); if (adt->is_enum ()) { size_t tuple_field_index = 0; - for (auto &pattern : items_no_range.get_patterns ()) + for (auto &pattern : items_no_rest.get_patterns ()) { tree payload_accessor_union = Backend::struct_field_expression (match_scrutinee_expr, 1, @@ -776,12 +839,10 @@ CompilePatternBindings::visit (HIR::TupleStructPattern &pattern) else { size_t tuple_field_index = 0; - for (auto &pattern : items_no_range.get_patterns ()) + for (auto &pattern : items_no_rest.get_patterns ()) { - tree variant_accessor = match_scrutinee_expr; - tree binding - = Backend::struct_field_expression (variant_accessor, + = Backend::struct_field_expression (match_scrutinee_expr, tuple_field_index++, pattern->get_locus ()); diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc index 13c54e34ccee..f132e04ef7b7 100644 --- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc +++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc @@ -1212,16 +1212,16 @@ lower_tuple_pattern (Resolver::TypeCheckContext *ctx, { case HIR::TupleStructItems::ItemType::NO_REST: { - HIR::TupleStructItemsNoRest &multiple + HIR::TupleStructItemsNoRest &items_no_rest = static_cast (elems); rust_assert (variant->get_fields ().size () - == multiple.get_patterns ().size ()); + == items_no_rest.get_patterns ().size ()); - for (size_t i = 0; i < multiple.get_patterns ().size (); i++) + for (size_t i = 0; i < items_no_rest.get_patterns ().size (); i++) { fields.push_back ( - lower_pattern (ctx, *multiple.get_patterns ().at (i), + lower_pattern (ctx, *items_no_rest.get_patterns ().at (i), variant->get_fields ().at (i)->get_field_type ())); } return DeconstructedPat (ctor, arity, fields, pattern.get_locus ()); @@ -1229,8 +1229,35 @@ lower_tuple_pattern (Resolver::TypeCheckContext *ctx, break; case HIR::TupleStructItems::ItemType::HAS_REST: { - // TODO: ranged tuple struct items - rust_unreachable (); + HIR::TupleStructItemsHasRest &items_has_rest + = static_cast (elems); + + size_t num_patterns = items_has_rest.get_lower_patterns ().size () + + items_has_rest.get_upper_patterns ().size (); + + rust_assert (num_patterns <= variant->num_fields ()); + + size_t i = 0; + for (auto &pattern_member : items_has_rest.get_lower_patterns ()) + { + fields.push_back (lower_pattern ( + ctx, *pattern_member, + variant->get_fields ().at (i++)->get_field_type ())); + } + while (i < variant->num_fields () + - items_has_rest.get_upper_patterns ().size ()) + { + fields.push_back ( + DeconstructedPat::make_wildcard (pattern.get_locus ())); + i++; + } + for (auto &pattern_member : items_has_rest.get_upper_patterns ()) + { + fields.push_back (lower_pattern ( + ctx, *pattern_member, + variant->get_fields ().at (i++)->get_field_type ())); + } + return DeconstructedPat (ctor, arity, fields, pattern.get_locus ()); } break; default: diff --git a/gcc/rust/hir/rust-ast-lower-pattern.cc b/gcc/rust/hir/rust-ast-lower-pattern.cc index a209f80f4dad..ee36a60a51f4 100644 --- a/gcc/rust/hir/rust-ast-lower-pattern.cc +++ b/gcc/rust/hir/rust-ast-lower-pattern.cc @@ -83,20 +83,39 @@ ASTLoweringPattern::visit (AST::TupleStructPattern &pattern) { case AST::TupleStructItems::HAS_REST: { - // TODO - rust_unreachable (); + AST::TupleStructItemsHasRest &items_has_rest + = static_cast (items); + + std::vector> lower_patterns; + lower_patterns.reserve (items_has_rest.get_lower_patterns ().size ()); + for (auto &pattern_member : items_has_rest.get_lower_patterns ()) + { + lower_patterns.emplace_back ( + ASTLoweringPattern::translate (*pattern_member)); + } + + std::vector> upper_patterns; + upper_patterns.reserve (items_has_rest.get_upper_patterns ().size ()); + for (auto &pattern_member : items_has_rest.get_upper_patterns ()) + { + upper_patterns.emplace_back ( + ASTLoweringPattern::translate (*pattern_member)); + } + + lowered = new HIR::TupleStructItemsHasRest (std::move (lower_patterns), + std::move (upper_patterns)); } break; case AST::TupleStructItems::NO_REST: { - AST::TupleStructItemsNoRest &items_no_range + AST::TupleStructItemsNoRest &items_no_rest = static_cast (items); std::vector> patterns; - patterns.reserve (items_no_range.get_patterns ().size ()); + patterns.reserve (items_no_rest.get_patterns ().size ()); - for (auto &inner_pattern : items_no_range.get_patterns ()) + for (auto &inner_pattern : items_no_rest.get_patterns ()) patterns.emplace_back ( ASTLoweringPattern::translate (*inner_pattern)); diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc index 5a2dde85e18a..48d7e76a5d3d 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc @@ -202,31 +202,142 @@ TypeCheckPattern::visit (HIR::TupleStructPattern &pattern) { case HIR::TupleStructItems::HAS_REST: { - // TODO - rust_unreachable (); + HIR::TupleStructItemsHasRest &items_has_rest + = static_cast (items); + auto &lower_patterns = items_has_rest.get_lower_patterns (); + auto &upper_patterns = items_has_rest.get_upper_patterns (); + size_t pattern_min_cap + = lower_patterns.size () + upper_patterns.size (); + if (variant->num_fields () < pattern_min_cap) + { + if (!lower_patterns.empty ()) + { + // TODO initialize rich_locus with loc of ADT definition instead + rich_location rich_locus (line_table, + lower_patterns[0]->get_locus ()); + for (auto &pattern : lower_patterns) + { + if (pattern == lower_patterns[0]) + continue; + rich_locus.add_range (pattern->get_locus (), + SHOW_RANGE_WITH_CARET); + } + for (auto &pattern : upper_patterns) + { + rich_locus.add_range (pattern->get_locus (), + SHOW_RANGE_WITH_CARET); + } + rust_error_at (rich_locus, ErrorCode::E0023, + "this pattern has %lu %s but the corresponding " + "tuple variant has %lu %s", + (unsigned long) (pattern_min_cap), + pattern_min_cap == 1 ? "field" : "fields", + (unsigned long) variant->num_fields (), + variant->num_fields () == 1 ? "field" + : "fields"); + } + else + { + // TODO initialize rich_locus with loc of ADT definition instead + rich_location rich_locus (line_table, + upper_patterns[0]->get_locus ()); + for (auto &pattern : upper_patterns) + { + if (pattern == upper_patterns[0]) + continue; + rich_locus.add_range (pattern->get_locus (), + SHOW_RANGE_WITH_CARET); + } + rust_error_at (rich_locus, ErrorCode::E0023, + "this pattern has %lu %s but the corresponding " + "tuple variant has %lu %s", + (unsigned long) (pattern_min_cap), + pattern_min_cap == 1 ? "field" : "fields", + (unsigned long) variant->num_fields (), + variant->num_fields () == 1 ? "field" + : "fields"); + } + // we continue on to try and setup the types as best we can for + // type checking + } + + // iterate the fields manually to set them up + size_t i = 0; + for (auto &pattern : lower_patterns) + { + if (i >= variant->num_fields ()) + break; + + TyTy::StructFieldType *field = variant->get_field_at_index (i++); + TyTy::BaseType *fty = field->get_field_type (); + + // setup the type on this pattern type + context->insert_type (pattern->get_mappings (), fty); + TypeCheckPattern::Resolve (*pattern, fty); + } + + i = variant->num_fields () - upper_patterns.size (); + for (auto &pattern : upper_patterns) + { + if (i >= variant->num_fields ()) + break; + + TyTy::StructFieldType *field = variant->get_field_at_index (i++); + TyTy::BaseType *fty = field->get_field_type (); + + // setup the type on this pattern type + context->insert_type (pattern->get_mappings (), fty); + TypeCheckPattern::Resolve (*pattern, fty); + } } break; case HIR::TupleStructItems::NO_REST: { - HIR::TupleStructItemsNoRest &items_no_range + HIR::TupleStructItemsNoRest &items_no_rest = static_cast (items); + auto &patterns = items_no_rest.get_patterns (); - if (items_no_range.get_patterns ().size () != variant->num_fields ()) + if (patterns.size () != variant->num_fields ()) { - rust_error_at ( - pattern.get_locus (), ErrorCode::E0023, - "this pattern has %lu fields but the corresponding " - "tuple variant has %lu field", - (unsigned long) items_no_range.get_patterns ().size (), - (unsigned long) variant->num_fields ()); + if (patterns.empty ()) + { + rust_error_at (pattern.get_locus (), ErrorCode::E0023, + "this pattern has %lu %s but the corresponding " + "tuple variant has %lu %s", + (unsigned long) patterns.size (), + patterns.size () == 1 ? "field" : "fields", + (unsigned long) variant->num_fields (), + variant->num_fields () == 1 ? "field" + : "fields"); + } + else + { + rich_location rich_locus (line_table, + patterns[0]->get_locus ()); + for (auto &pattern : items_no_rest.get_patterns ()) + { + if (pattern == patterns[0]) + continue; + rich_locus.add_range (pattern->get_locus (), + SHOW_RANGE_WITH_CARET); + } + rust_error_at (rich_locus, ErrorCode::E0023, + "this pattern has %lu %s but the corresponding " + "tuple variant has %lu %s", + (unsigned long) patterns.size (), + patterns.size () == 1 ? "field" : "fields", + (unsigned long) variant->num_fields (), + variant->num_fields () == 1 ? "field" + : "fields"); + } // we continue on to try and setup the types as best we can for // type checking } // iterate the fields and set them up, I wish we had ZIP size_t i = 0; - for (auto &pattern : items_no_range.get_patterns ()) + for (auto &pattern : items_no_rest.get_patterns ()) { if (i >= variant->num_fields ()) break; diff --git a/gcc/testsuite/rust/compile/match-tuplestructpattern-err.rs b/gcc/testsuite/rust/compile/match-tuplestructpattern-err.rs new file mode 100644 index 000000000000..efd1a8969416 --- /dev/null +++ b/gcc/testsuite/rust/compile/match-tuplestructpattern-err.rs @@ -0,0 +1,14 @@ +fn main() { + struct A (i32, i32); + let a = A (0, 1); + + match a { + A (1, 2, 3, 4) => {}, + // { dg-error "this pattern has 4 fields but the corresponding tuple variant has 2 fields .E0023." "" { target *-*-* } .-1 } + A (1, 2, .., 3, 4) => {}, + // { dg-error "this pattern has 4 fields but the corresponding tuple variant has 2 fields .E0023." "" { target *-*-* } .-1 } + A (.., 3, 4, 5) => {}, + // { dg-error "this pattern has 3 fields but the corresponding tuple variant has 2 fields .E0023." "" { target *-*-* } .-1 } + _ => {} + } +} \ No newline at end of file diff --git a/gcc/testsuite/rust/compile/match-tuplestructpattern-rest.rs b/gcc/testsuite/rust/compile/match-tuplestructpattern-rest.rs new file mode 100644 index 000000000000..4681acbfcb1b --- /dev/null +++ b/gcc/testsuite/rust/compile/match-tuplestructpattern-rest.rs @@ -0,0 +1,9 @@ +fn main() { + struct A (i32, i32); + let a = A (0, 1); + + match a { + A (0, ..) => {}, + _ => {} + } +} diff --git a/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-1.rs b/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-1.rs new file mode 100644 index 000000000000..8d7446d8e5f4 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-1.rs @@ -0,0 +1,24 @@ +// { dg-output "correct\r*" } +extern "C" { + fn puts(s: *const i8); +} + +fn main() -> i32 { + struct A (i32, i32, i32); + let a = A (0, 1, 2); + let mut ret = 1; + + match a { + A (1, ..) => { + /* should not take this path */ + unsafe { puts("wrong\0" as *const str as *const i8) } + } + A (0, b, ..) => { + ret -= b; + unsafe { puts("correct\0" as *const str as *const i8) } + }, + _ => {} + } + + ret +} diff --git a/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-2.rs b/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-2.rs new file mode 100644 index 000000000000..f433be91b4a9 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/match-tuplestructpattern-rest-2.rs @@ -0,0 +1,28 @@ +// { dg-output "correct\r*" } +extern "C" { + fn puts(s: *const i8); +} + +fn main() -> i32 { + struct A (i32, i32, i32); + let a = A (0, 3, 1); + let mut ret = 1; + + match a { + A (1, ..) => { + /* should not take this path */ + unsafe { puts("wrong\0" as *const str as *const i8) } + } + A (.., 3) => { + /* should not take this path */ + unsafe { puts("wrong\0" as *const str as *const i8) } + } + A (.., b) => { + ret -= b; + unsafe { puts("correct\0" as *const str as *const i8) } + }, + _ => {} + } + + ret +}