From 5da381ffd112693836b4532f680a5bd512552837 Mon Sep 17 00:00:00 2001 From: Raiki Tamura Date: Fri, 9 Aug 2024 23:56:55 +0900 Subject: [PATCH] gccrs: Add typecheck for path patterns. gcc/rust/ChangeLog: * hir/tree/rust-hir.cc (Item::item_kind_string): New function. * hir/tree/rust-hir.h: New function. * typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit): Modify to check all arms in match expressions even if some of them has errors. * typecheck/rust-hir-type-check-pattern.cc (TypeCheckPattern::visit): Add and fix check for path patterns. gcc/testsuite/ChangeLog: * rust/compile/issue-2324-2.rs: Fix error message. * rust/compile/match9.rs: New test. Signed-off-by: Raiki Tamura --- gcc/rust/hir/tree/rust-hir.cc | 38 ++++++ gcc/rust/hir/tree/rust-hir.h | 2 + .../typecheck/rust-hir-type-check-expr.cc | 13 +- .../typecheck/rust-hir-type-check-pattern.cc | 118 ++++++++++++++---- gcc/testsuite/rust/compile/issue-2324-2.rs | 2 +- gcc/testsuite/rust/compile/match9.rs | 30 +++++ 6 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 gcc/testsuite/rust/compile/match9.rs diff --git a/gcc/rust/hir/tree/rust-hir.cc b/gcc/rust/hir/tree/rust-hir.cc index 35aa5851f69d..797a7fbe3410 100644 --- a/gcc/rust/hir/tree/rust-hir.cc +++ b/gcc/rust/hir/tree/rust-hir.cc @@ -212,6 +212,44 @@ Module::as_string () const return str + "\n"; } +std::string +Item::item_kind_string (Item::ItemKind kind) +{ + switch (kind) + { + case Item::ItemKind::Static: + return "static"; + case Item::ItemKind::Constant: + return "constant"; + case Item::ItemKind::TypeAlias: + return "type alias"; + case Item::ItemKind::Function: + return "function"; + case Item::ItemKind::UseDeclaration: + return "use declaration"; + case Item::ItemKind::ExternBlock: + return "extern block"; + case Item::ItemKind::ExternCrate: + return "extern crate"; + case Item::ItemKind::Struct: + return "struct"; + case Item::ItemKind::Union: + return "union"; + case Item::ItemKind::Enum: + return "enum"; + case Item::ItemKind::EnumItem: + return "enum item"; + case Item::ItemKind::Trait: + return "trait"; + case Item::ItemKind::Impl: + return "impl"; + case Item::ItemKind::Module: + return "module"; + default: + rust_unreachable (); + } +} + std::string StaticItem::as_string () const { diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h index 3fb97b2f1218..e61249987a2c 100644 --- a/gcc/rust/hir/tree/rust-hir.h +++ b/gcc/rust/hir/tree/rust-hir.h @@ -220,6 +220,8 @@ class Item : public Stmt, public WithOuterAttrs Module, }; + static std::string item_kind_string (ItemKind kind); + virtual ItemKind get_item_kind () const = 0; // Unique pointer custom clone function diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc index 8a1460b11bc8..12a8b65c5ff5 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc @@ -1465,6 +1465,7 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr) TyTy::BaseType *scrutinee_tyty = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ()); + bool saw_error = false; std::vector kase_block_tys; for (auto &kase : expr.get_match_cases ()) { @@ -1475,7 +1476,10 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr) TyTy::BaseType *kase_arm_ty = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty); if (kase_arm_ty->get_kind () == TyTy ::TypeKind::ERROR) - return; + { + saw_error = true; + continue; + } TyTy::BaseType *checked_kase = unify_site ( expr.get_mappings ().get_hirid (), @@ -1484,7 +1488,10 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr) TyTy::TyWithLocation (kase_arm_ty, pattern->get_locus ()), expr.get_locus ()); if (checked_kase->get_kind () == TyTy::TypeKind::ERROR) - return; + { + saw_error = true; + continue; + } } // check the kase type @@ -1492,6 +1499,8 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr) = TypeCheckExpr::Resolve (kase.get_expr ().get ()); kase_block_tys.push_back (kase_block_ty); } + if (saw_error) + return; if (kase_block_tys.size () == 0) { diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc index 811e329ba2b4..b0e4ca52a93d 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc @@ -43,32 +43,100 @@ TypeCheckPattern::Resolve (HIR::Pattern *pattern, TyTy::BaseType *parent) void TypeCheckPattern::visit (HIR::PathInExpression &pattern) { - infered = TypeCheckExpr::Resolve (&pattern); - - /* - * We are compiling a PathInExpression, which can't be a Struct or Tuple - * pattern. We should check that the declaration we are referencing IS NOT a - * struct pattern or tuple with values. - */ + // Pattern must be enum variants, sturcts, constants, or associated constansts + TyTy::BaseType *pattern_ty = TypeCheckExpr::Resolve (&pattern); + + NodeId ref_node_id = UNKNOWN_NODEID; + bool maybe_item = false; + maybe_item + |= resolver->lookup_resolved_name (pattern.get_mappings ().get_nodeid (), + &ref_node_id); + maybe_item + |= resolver->lookup_resolved_type (pattern.get_mappings ().get_nodeid (), + &ref_node_id); + bool path_is_const_item = false; + + if (maybe_item) + { + tl::optional definition_id + = mappings.lookup_node_to_hir (ref_node_id); + rust_assert (definition_id.has_value ()); + HirId def_id = definition_id.value (); + + tl::optional hir_item = mappings.lookup_hir_item (def_id); + // If the path refrerences an item, it must be constants or structs. + if (hir_item.has_value ()) + { + HIR::Item *item = hir_item.value (); + if (item->get_item_kind () == HIR::Item::ItemKind::Constant) + { + path_is_const_item = true; + } + else if (item->get_item_kind () != HIR::Item::ItemKind::Struct) + { + HIR::Item *item = hir_item.value (); + std::string item_kind + = HIR::Item::item_kind_string (item->get_item_kind ()); + + std::string path_buf; + for (size_t i = 0; i < pattern.get_segments ().size (); i++) + { + HIR::PathExprSegment &seg = pattern.get_segments ().at (i); + path_buf += seg.as_string (); + if (i != pattern.get_segments ().size () - 1) + path_buf += "::"; + } + + rich_location rich_locus ( + line_table, pattern.get_final_segment ().get_locus ()); + rich_locus.add_fixit_replace ( + "not a unit struct, unit variant or constatnt"); + rust_error_at (rich_locus, ErrorCode::E0532, + "expected unit struct, unit variant or constant, " + "found %s %<%s%>", + item_kind.c_str (), path_buf.c_str ()); + return; + } + } + } - rust_assert (infered->get_kind () == TyTy::TypeKind::ADT); - TyTy::ADTType *adt = static_cast (infered); + // If the path is a constructor, it must be a unit struct or unit variants. + if (!path_is_const_item && pattern_ty->get_kind () == TyTy::TypeKind::ADT) + { + TyTy::ADTType *adt = static_cast (pattern_ty); + rust_assert (adt->get_variants ().size () > 0); - HirId variant_id = UNKNOWN_HIRID; - bool ok - = context->lookup_variant_definition (pattern.get_mappings ().get_hirid (), - &variant_id); - rust_assert (ok); + TyTy::VariantDef *variant = adt->get_variants ().at (0); + if (adt->is_enum ()) + { + HirId variant_id = UNKNOWN_HIRID; + bool ok = context->lookup_variant_definition ( + pattern.get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); - TyTy::VariantDef *variant = nullptr; - ok = adt->lookup_variant_by_id (variant_id, &variant); + ok = adt->lookup_variant_by_id (variant_id, &variant); + rust_assert (ok); + } - TyTy::VariantDef::VariantType vty = variant->get_variant_type (); + if (variant->get_variant_type () != TyTy::VariantDef::VariantType::NUM) + { + std::string variant_type = TyTy::VariantDef::variant_type_string ( + variant->get_variant_type ()); + + rich_location rich_locus (line_table, + pattern.get_final_segment ().get_locus ()); + rich_locus.add_fixit_replace ( + "not a unit struct, unit variant or constatnt"); + rust_error_at (rich_locus, ErrorCode::E0532, + "expected unit struct, unit variant or constant, " + "found %s variant %<%s::%s%>", + variant_type.c_str (), adt->get_name ().c_str (), + variant->get_identifier ().c_str ()); + return; + } - if (vty != TyTy::VariantDef::VariantType::NUM) - rust_error_at ( - pattern.get_final_segment ().get_locus (), ErrorCode::E0532, - "expected unit struct, unit variant or constant, found tuple variant"); + infered = pattern_ty; + } } void @@ -100,8 +168,8 @@ TypeCheckPattern::visit (HIR::TupleStructPattern &pattern) rust_assert (ok); } - // error[E0532]: expected tuple struct or tuple variant, found struct variant - // `Foo::D`, E0532 by rustc 1.49.0 , E0164 by rustc 1.71.0 + // error[E0532]: expected tuple struct or tuple variant, found struct + // variant `Foo::D`, E0532 by rustc 1.49.0 , E0164 by rustc 1.71.0 if (variant->get_variant_type () != TyTy::VariantDef::VariantType::TUPLE) { std::string variant_type @@ -203,8 +271,8 @@ TypeCheckPattern::visit (HIR::StructPattern &pattern) rust_assert (ok); } - // error[E0532]: expected tuple struct or tuple variant, found struct variant - // `Foo::D` + // error[E0532]: expected tuple struct or tuple variant, found struct + // variant `Foo::D` if (variant->get_variant_type () != TyTy::VariantDef::VariantType::STRUCT) { std::string variant_type diff --git a/gcc/testsuite/rust/compile/issue-2324-2.rs b/gcc/testsuite/rust/compile/issue-2324-2.rs index 5ef40147961a..1530b002365c 100644 --- a/gcc/testsuite/rust/compile/issue-2324-2.rs +++ b/gcc/testsuite/rust/compile/issue-2324-2.rs @@ -6,7 +6,7 @@ enum State { fn print_on_failure(state: &State) { match *state { State::Succeeded => (), - State::Failed => (), // { dg-error "expected unit struct, unit variant or constant, found tuple variant" } + State::Failed => (), // { dg-error "expected unit struct, unit variant or constant, found struct variant" } _ => () } } diff --git a/gcc/testsuite/rust/compile/match9.rs b/gcc/testsuite/rust/compile/match9.rs new file mode 100644 index 000000000000..115d458e266e --- /dev/null +++ b/gcc/testsuite/rust/compile/match9.rs @@ -0,0 +1,30 @@ +enum E { + A(), + B, +} + +const CONST_E: E = E::A(); + +static static_e: E = E::A(); + +type type_alias = E; + +fn f(e: E) { + match e { + E::A => {} + // { dg-error "expected unit struct, unit variant or constant, found tuple variant .E::A." "" { target *-*-* } .-1 } + E::B => {} + crate::CONST_E => {} + crate::type_alias => {} + // { dg-error "expected unit struct, unit variant or constant, found type alias .crate::type_alias." "" { target *-*-* } .-1 } + crate::E => {} + // { dg-error "expected unit struct, unit variant or constant, found enum .crate::E." "" { target *-*-* } .-1 } + crate::static_e => {} + // { dg-error "expected unit struct, unit variant or constant, found static .crate::static_e." "" { target *-*-* } .-1 } + crate::f => {} + // { dg-error "expected unit struct, unit variant or constant, found function .crate::f." "" { target *-*-* } .-1 } + _ => {} + } +} + +fn main() {}