From 60b1209d64cee3b47cf9b3f5b9da4f74f03a25bb Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Tue, 13 Jul 2021 20:34:04 +0100 Subject: Fix Placeholder type checking When we are checking a trait impl block, associated types can equal any type this ensures we update the traits associated types inline with respective trait-impl-block that implements this trait. Fixes #456 --- gcc/rust/typecheck/rust-hir-trait-ref.h | 83 ++++++++++++--- gcc/rust/typecheck/rust-hir-trait-resolve.h | 103 +----------------- gcc/rust/typecheck/rust-hir-type-check-implitem.h | 1 + gcc/rust/typecheck/rust-hir-type-check.cc | 123 ++++++++++++++++++++++ gcc/testsuite/rust/compile/torture/traits6.rs | 23 ++++ gcc/testsuite/rust/compile/traits3.rs | 22 ++++ 6 files changed, 238 insertions(+), 117 deletions(-) create mode 100644 gcc/testsuite/rust/compile/torture/traits6.rs create mode 100644 gcc/testsuite/rust/compile/traits3.rs (limited to 'gcc') diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index 96498a5..e255fc4 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -27,6 +27,7 @@ namespace Resolver { // Data Objects for the associated trait items in a structure we can work with // https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html +class TypeCheckContext; class TraitItemReference { public: @@ -39,17 +40,11 @@ public: }; TraitItemReference (std::string identifier, bool optional, TraitItemType type, - const HIR::TraitItem *hir_trait_item, TyTy::BaseType *ty, - Location locus) - : identifier (identifier), optional_flag (optional), type (type), - hir_trait_item (hir_trait_item), ty (ty), locus (locus) - {} + HIR::TraitItem *hir_trait_item, TyTy::BaseType *self, + std::vector substitutions, + Location locus); - TraitItemReference (TraitItemReference const &other) - : identifier (other.identifier), optional_flag (other.optional_flag), - type (other.type), hir_trait_item (other.hir_trait_item), ty (other.ty), - locus (other.locus) - {} + TraitItemReference (TraitItemReference const &other); TraitItemReference &operator= (TraitItemReference const &other) { @@ -57,8 +52,13 @@ public: optional_flag = other.optional_flag; type = other.type; hir_trait_item = other.hir_trait_item; - ty = other.ty; + self = other.self; locus = other.locus; + context = other.context; + + inherited_substitutions.reserve (other.inherited_substitutions.size ()); + for (size_t i = 0; i < other.inherited_substitutions.size (); i++) + inherited_substitutions.push_back (other.inherited_substitutions.at (i)); return *this; } @@ -68,7 +68,8 @@ public: static TraitItemReference error () { - return TraitItemReference ("", false, ERROR, nullptr, nullptr, Location ()); + return TraitItemReference ("", false, ERROR, nullptr, nullptr, {}, + Location ()); } static TraitItemReference &error_node () @@ -82,7 +83,7 @@ public: std::string as_string () const { return "(" + trait_item_type_as_string (type) + " " + identifier + " " - + ty->as_string () + ")"; + + ")"; } static std::string trait_item_type_as_string (TraitItemType ty) @@ -109,17 +110,65 @@ public: const HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; } - TyTy::BaseType *get_tyty () const { return ty; } - Location get_locus () const { return locus; } + const Analysis::NodeMapping &get_mappings () const + { + return hir_trait_item->get_mappings (); + } + + TyTy::BaseType *get_tyty () const + { + rust_assert (hir_trait_item != nullptr); + + switch (type) + { + case CONST: + return get_type_from_constant ( + static_cast (*hir_trait_item)); + break; + + case TYPE: + return get_type_from_typealias ( + static_cast (*hir_trait_item)); + + case FN: + return get_type_from_fn ( + static_cast (*hir_trait_item)); + break; + + default: + return get_error (); + } + + gcc_unreachable (); + return get_error (); + } + private: + TyTy::ErrorType *get_error () const + { + return new TyTy::ErrorType (get_mappings ().get_hirid ()); + } + + TyTy::BaseType *get_type_from_typealias (/*const*/ + HIR::TraitItemType &type) const; + + TyTy::BaseType * + get_type_from_constant (/*const*/ HIR::TraitItemConst &constant) const; + + TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const; + std::string identifier; bool optional_flag; TraitItemType type; - const HIR::TraitItem *hir_trait_item; - TyTy::BaseType *ty; + HIR::TraitItem *hir_trait_item; + std::vector inherited_substitutions; Location locus; + + TyTy::BaseType + *self; // this is the implict Self TypeParam required for methods + Resolver::TypeCheckContext *context; }; class TraitReference diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h index 1b83be3..3de68c8 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.h +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -55,24 +55,11 @@ public: resolved = TraitItemReference (identifier, is_optional, TraitItemReference::TraitItemType::TYPE, - &type, ty, locus); + &type, self, substitutions, locus); } void visit (HIR::TraitItemConst &cst) override { - // attempt to lookup the type of the trait item function - TyTy::BaseType *ty = nullptr; - if (!context->lookup_type (cst.get_mappings ().get_hirid (), &ty)) - { - auto resolved = TypeCheckType::Resolve (cst.get_type ().get ()); - if (resolved->get_kind () == TyTy::TypeKind::ERROR) - { - rust_error_at (cst.get_locus (), - "failed to resolve trait constant type"); - return; - } - } - // create trait-item-ref Location locus = cst.get_locus (); bool is_optional = cst.has_expr (); @@ -80,95 +67,11 @@ public: resolved = TraitItemReference (identifier, is_optional, TraitItemReference::TraitItemType::CONST, - &cst, ty, locus); + &cst, self, substitutions, locus); } void visit (HIR::TraitItemFunc &fn) override { - // FIXME this is duplicated in a few places and could be refactored - - // attempt to lookup the type of the trait item function - TyTy::BaseType *ty = nullptr; - if (!context->lookup_type (fn.get_mappings ().get_hirid (), &ty)) - { - HIR::TraitFunctionDecl &function = fn.get_decl (); - if (function.has_generics ()) - { - for (auto &generic_param : function.get_generic_params ()) - { - switch (generic_param.get ()->get_kind ()) - { - case HIR::GenericParam::GenericKind::LIFETIME: - // Skipping Lifetime completely until better handling. - break; - - case HIR::GenericParam::GenericKind::TYPE: { - auto param_type = TypeResolveGenericParam::Resolve ( - generic_param.get ()); - context->insert_type (generic_param->get_mappings (), - param_type); - - substitutions.push_back (TyTy::SubstitutionParamMapping ( - static_cast (*generic_param), - param_type)); - } - break; - } - } - } - - TyTy::BaseType *ret_type = nullptr; - if (!function.has_return_type ()) - ret_type = new TyTy::TupleType (fn.get_mappings ().get_hirid ()); - else - { - auto resolved - = TypeCheckType::Resolve (function.get_return_type ().get ()); - if (resolved->get_kind () == TyTy::TypeKind::ERROR) - { - rust_error_at (fn.get_locus (), - "failed to resolve return type"); - return; - } - - ret_type = resolved->clone (); - ret_type->set_ref ( - function.get_return_type ()->get_mappings ().get_hirid ()); - } - - std::vector > params; - if (function.is_method ()) - { - // add the synthetic self param at the front, this is a placeholder - // for compilation to know parameter names. The types are ignored - // but we reuse the HIR identifier pattern which requires it - HIR::SelfParam &self_param = function.get_self (); - HIR::IdentifierPattern *self_pattern = new HIR::IdentifierPattern ( - "self", self_param.get_locus (), self_param.is_ref (), - self_param.is_mut (), std::unique_ptr (nullptr)); - context->insert_type (self_param.get_mappings (), self->clone ()); - params.push_back ( - std::pair (self_pattern, - self->clone ())); - } - - for (auto ¶m : function.get_function_params ()) - { - // get the name as well required for later on - auto param_tyty = TypeCheckType::Resolve (param.get_type ()); - params.push_back (std::pair ( - param.get_param_name (), param_tyty)); - - context->insert_type (param.get_mappings (), param_tyty); - } - - ty = new TyTy::FnType (fn.get_mappings ().get_hirid (), - function.get_function_name (), - function.is_method (), std::move (params), - ret_type, std::move (substitutions)); - context->insert_type (fn.get_mappings (), ty); - } - // create trait-item-ref Location locus = fn.get_locus (); bool is_optional = fn.has_block_defined (); @@ -176,7 +79,7 @@ public: resolved = TraitItemReference (identifier, is_optional, TraitItemReference::TraitItemType::FN, &fn, - ty, locus); + self, substitutions, locus); } private: diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h index b0264e5..0f704ae 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-implitem.h +++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h @@ -289,6 +289,7 @@ public: return; } + context->insert_type (trait_item_ref.get_mappings (), lookup->clone ()); resolved_trait_item = trait_item_ref; } diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc index d21fb3d..1d97deb 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.cc +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -375,5 +375,128 @@ TypeCheckType::visit (HIR::ArrayType &type) TyTy::TyVar (base->get_ref ())); } +// rust-hir-trait-ref.h + +TraitItemReference::TraitItemReference ( + std::string identifier, bool optional, TraitItemType type, + HIR::TraitItem *hir_trait_item, TyTy::BaseType *self, + std::vector substitutions, Location locus) + : identifier (identifier), optional_flag (optional), type (type), + hir_trait_item (hir_trait_item), inherited_substitutions (substitutions), + locus (locus), self (self), context (TypeCheckContext::get ()) +{} + +TraitItemReference::TraitItemReference (TraitItemReference const &other) + : identifier (other.identifier), optional_flag (other.optional_flag), + type (other.type), hir_trait_item (other.hir_trait_item), + locus (other.locus), self (other.self), context (TypeCheckContext::get ()) +{ + inherited_substitutions.reserve (other.inherited_substitutions.size ()); + for (size_t i = 0; i < other.inherited_substitutions.size (); i++) + inherited_substitutions.push_back (other.inherited_substitutions.at (i)); +} + +TyTy::BaseType * +TraitItemReference::get_type_from_typealias (/*const*/ + HIR::TraitItemType &type) const +{ + TyTy::TyVar var (get_mappings ().get_hirid ()); + return var.get_tyty (); +} + +TyTy::BaseType * +TraitItemReference::get_type_from_constant ( + /*const*/ HIR::TraitItemConst &constant) const +{ + TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ().get ()); + TyTy::BaseType *expr + = TypeCheckExpr::Resolve (constant.get_expr ().get (), false); + + return type->unify (expr); +} + +TyTy::BaseType * +TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const +{ + std::vector substitutions + = inherited_substitutions; + + HIR::TraitFunctionDecl &function = fn.get_decl (); + if (function.has_generics ()) + { + for (auto &generic_param : function.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + // Skipping Lifetime completely until better handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_return_type ()) + ret_type = new TyTy::TupleType (fn.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (fn.get_locus (), "failed to resolve return type"); + return get_error (); + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector > params; + if (function.is_method ()) + { + // add the synthetic self param at the front, this is a placeholder + // for compilation to know parameter names. The types are ignored + // but we reuse the HIR identifier pattern which requires it + HIR::SelfParam &self_param = function.get_self (); + HIR::IdentifierPattern *self_pattern + = new HIR::IdentifierPattern ("self", self_param.get_locus (), + self_param.is_ref (), + self_param.is_mut (), + std::unique_ptr (nullptr)); + context->insert_type (self_param.get_mappings (), self->clone ()); + params.push_back ( + std::pair (self_pattern, + self->clone ())); + } + + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ()); + params.push_back ( + std::pair (param.get_param_name (), + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + } + + return new TyTy::FnType (fn.get_mappings ().get_hirid (), + function.get_function_name (), function.is_method (), + std::move (params), ret_type, substitutions); +} + } // namespace Resolver } // namespace Rust diff --git a/gcc/testsuite/rust/compile/torture/traits6.rs b/gcc/testsuite/rust/compile/torture/traits6.rs new file mode 100644 index 0000000..5aae2e3 --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/traits6.rs @@ -0,0 +1,23 @@ +trait Foo { + type A; + // { dg-warning "unused name .Foo::A." "" { target *-*-* } .-1 } + + fn baz(a: Self::A) -> Self::A; + // { dg-warning "unused name .a." "" { target *-*-* } .-1 } + // { dg-warning "unused name .Foo::baz." "" { target *-*-* } .-2 } +} + +struct Bar(T); + +impl Foo for Bar { + type A = T; + + fn baz(a: Self::A) -> T { + a + } +} + +fn main() { + let a; + a = Bar::::baz(123); +} diff --git a/gcc/testsuite/rust/compile/traits3.rs b/gcc/testsuite/rust/compile/traits3.rs new file mode 100644 index 0000000..c971a11 --- /dev/null +++ b/gcc/testsuite/rust/compile/traits3.rs @@ -0,0 +1,22 @@ +trait Foo { + type A; + + fn baz(a: Self::A) -> Self::A; +} + +struct Bar(T); + +impl Foo for Bar { + type A = i32; + + fn baz(a: f32) -> f32 { + // { dg-error "expected .i32. got .f32." "" { target *-*-* } .-1 } + // { dg-error "method .baz. has an incompatible type for trait .Foo." "" { target *-*-* } .-2 } + a + } +} + +fn main() { + let a; + a = Bar::::baz(123f32); +} -- cgit v1.1 From 6e0c46b86aaa38f18e2c0af2a4711bfeaa242498 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 14 Jul 2021 17:56:29 +0100 Subject: Trait item consts can have an optional expr When we are checking for trait constants their expression can be optional which will cause an ICE when ignored. Fixes #471 --- gcc/rust/ast/rust-item.h | 4 +++- gcc/rust/resolve/rust-ast-resolve-item.h | 4 +++- gcc/rust/typecheck/rust-hir-type-check.cc | 10 +++++++--- gcc/testsuite/rust/compile/torture/traits7.rs | 22 ++++++++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/rust/compile/torture/traits7.rs (limited to 'gcc') diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h index 37d087c..30cab0e 100644 --- a/gcc/rust/ast/rust-item.h +++ b/gcc/rust/ast/rust-item.h @@ -3265,10 +3265,12 @@ public: std::vector &get_outer_attrs () { return outer_attrs; } const std::vector &get_outer_attrs () const { return outer_attrs; } + bool has_expr () const { return expr != nullptr; } + // TODO: is this better? Or is a "vis_block" better? std::unique_ptr &get_expr () { - rust_assert (expr != nullptr); + rust_assert (has_expr ()); return expr; } diff --git a/gcc/rust/resolve/rust-ast-resolve-item.h b/gcc/rust/resolve/rust-ast-resolve-item.h index 1f1ff30..0714f5d 100644 --- a/gcc/rust/resolve/rust-ast-resolve-item.h +++ b/gcc/rust/resolve/rust-ast-resolve-item.h @@ -168,7 +168,9 @@ public: void visit (AST::TraitItemConst &constant) override { ResolveType::go (constant.get_type ().get (), constant.get_node_id ()); - ResolveExpr::go (constant.get_expr ().get (), constant.get_node_id ()); + + if (constant.has_expr ()) + ResolveExpr::go (constant.get_expr ().get (), constant.get_node_id ()); // the mutability checker needs to verify for immutable decls the number // of assignments are <1. This marks an implicit assignment diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc index 1d97deb..acfc022 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.cc +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -409,10 +409,14 @@ TraitItemReference::get_type_from_constant ( /*const*/ HIR::TraitItemConst &constant) const { TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ().get ()); - TyTy::BaseType *expr - = TypeCheckExpr::Resolve (constant.get_expr ().get (), false); + if (constant.has_expr ()) + { + TyTy::BaseType *expr + = TypeCheckExpr::Resolve (constant.get_expr ().get (), false); - return type->unify (expr); + return type->unify (expr); + } + return type; } TyTy::BaseType * diff --git a/gcc/testsuite/rust/compile/torture/traits7.rs b/gcc/testsuite/rust/compile/torture/traits7.rs new file mode 100644 index 0000000..a6fe5a3 --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/traits7.rs @@ -0,0 +1,22 @@ +trait Foo { + const A: i32; + // { dg-warning "unused name .Foo::A." "" { target *-*-* } .-1 } + + fn test(self); + // { dg-warning "unused name .self." "" { target *-*-* } .-1 } + // { dg-warning "unused name .Foo::test." "" { target *-*-* } .-2 } +} + +struct Bar; +impl Foo for Bar { + const A: i32 = 123; + // { dg-warning "unused name" "" { target *-*-* } .-1 } + + fn test(self) {} + // { dg-warning "unused name" "" { target *-*-* } .-1 } +} + +fn main() { + let a = Bar; + a.test(); +} -- cgit v1.1 From 05b5eec7ab095d2d80a4fb8262c8985ad03550b6 Mon Sep 17 00:00:00 2001 From: Philip Herron Date: Wed, 14 Jul 2021 18:32:24 +0100 Subject: Add check for missing items in impl of trait If an impl block does not implement all trait-items it is an error. This also takes into account that functions and constants may actually have values and may not need to be implemented by the impl block. Addresses #542 --- gcc/rust/typecheck/rust-hir-trait-ref.h | 7 ++ gcc/rust/typecheck/rust-hir-type-check-implitem.h | 103 ++++++++++------------ gcc/rust/typecheck/rust-hir-type-check-item.h | 58 +++++++++++- gcc/testsuite/rust/compile/traits4.rs | 16 ++++ gcc/testsuite/rust/compile/traits5.rs | 9 ++ 5 files changed, 133 insertions(+), 60 deletions(-) create mode 100644 gcc/testsuite/rust/compile/traits4.rs create mode 100644 gcc/testsuite/rust/compile/traits5.rs (limited to 'gcc') diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index e255fc4..35c0fef 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -236,6 +236,13 @@ public: return TraitItemReference::error_node (); } + size_t size () const { return item_refs.size (); } + + const std::vector &get_trait_items () const + { + return item_refs; + } + private: const HIR::Trait *hir_trait_ref; std::vector item_refs; diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h index 0f704ae..d6c3aed 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-implitem.h +++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h @@ -214,117 +214,109 @@ public: void visit (HIR::ConstantItem &constant) override { - TypeCheckImplItem::visit (constant); - - // we get the error checking from the base method here - TyTy::BaseType *lookup; - if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup)) - return; - - const TraitItemReference &trait_item_ref - = trait_reference.lookup_trait_item ( - constant.get_identifier (), TraitItemReference::TraitItemType::CONST); + resolved_trait_item = trait_reference.lookup_trait_item ( + constant.get_identifier (), TraitItemReference::TraitItemType::CONST); // unknown trait item - if (trait_item_ref.is_error ()) + if (resolved_trait_item.is_error ()) { RichLocation r (constant.get_locus ()); r.add_range (trait_reference.get_locus ()); rust_error_at (r, "constant %<%s%> is not a member of trait %<%s%>", constant.get_identifier ().c_str (), trait_reference.get_name ().c_str ()); - return; } + // normal resolution of the item + TypeCheckImplItem::visit (constant); + TyTy::BaseType *lookup; + if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup)) + return; + if (resolved_trait_item.is_error ()) + return; + // check the types are compatible - if (!trait_item_ref.get_tyty ()->can_eq (lookup, true)) + if (!resolved_trait_item.get_tyty ()->can_eq (lookup, true)) { RichLocation r (constant.get_locus ()); - r.add_range (trait_item_ref.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); rust_error_at ( r, "constant %<%s%> has an incompatible type for trait %<%s%>", constant.get_identifier ().c_str (), trait_reference.get_name ().c_str ()); - return; } - - resolved_trait_item = trait_item_ref; } void visit (HIR::TypeAlias &type) override { - TypeCheckImplItem::visit (type); - - // we get the error checking from the base method here - TyTy::BaseType *lookup; - if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup)) - return; - - const TraitItemReference &trait_item_ref - = trait_reference.lookup_trait_item ( - type.get_new_type_name (), TraitItemReference::TraitItemType::TYPE); + resolved_trait_item = trait_reference.lookup_trait_item ( + type.get_new_type_name (), TraitItemReference::TraitItemType::TYPE); // unknown trait item - if (trait_item_ref.is_error ()) + if (resolved_trait_item.is_error ()) { RichLocation r (type.get_locus ()); r.add_range (trait_reference.get_locus ()); rust_error_at (r, "type alias %<%s%> is not a member of trait %<%s%>", type.get_new_type_name ().c_str (), trait_reference.get_name ().c_str ()); - return; } + // normal resolution of the item + TypeCheckImplItem::visit (type); + TyTy::BaseType *lookup; + if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup)) + return; + if (resolved_trait_item.is_error ()) + return; + // check the types are compatible - if (!trait_item_ref.get_tyty ()->can_eq (lookup, true)) + if (!resolved_trait_item.get_tyty ()->can_eq (lookup, true)) { RichLocation r (type.get_locus ()); - r.add_range (trait_item_ref.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); rust_error_at ( r, "type alias %<%s%> has an incompatible type for trait %<%s%>", type.get_new_type_name ().c_str (), trait_reference.get_name ().c_str ()); - return; } - context->insert_type (trait_item_ref.get_mappings (), lookup->clone ()); - resolved_trait_item = trait_item_ref; + context->insert_type (resolved_trait_item.get_mappings (), + lookup->clone ()); } void visit (HIR::Function &function) override { - TypeCheckImplItem::visit (function); - - // we get the error checking from the base method here - TyTy::BaseType *lookup; - if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup)) - return; - - if (lookup->get_kind () != TyTy::TypeKind::FNDEF) - return; - - TyTy::FnType *fntype = static_cast (lookup); - const TraitItemReference &trait_item_ref - = trait_reference.lookup_trait_item ( - fntype->get_identifier (), TraitItemReference::TraitItemType::FN); + resolved_trait_item = trait_reference.lookup_trait_item ( + function.get_function_name (), TraitItemReference::TraitItemType::FN); // unknown trait item - if (trait_item_ref.is_error ()) + if (resolved_trait_item.is_error ()) { RichLocation r (function.get_locus ()); r.add_range (trait_reference.get_locus ()); rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>", - fntype->get_identifier ().c_str (), + function.get_function_name ().c_str (), trait_reference.get_name ().c_str ()); - return; } - rust_assert (trait_item_ref.get_tyty ()->get_kind () + // we get the error checking from the base method here + TypeCheckImplItem::visit (function); + TyTy::BaseType *lookup; + if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup)) + return; + if (resolved_trait_item.is_error ()) + return; + + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); + rust_assert (resolved_trait_item.get_tyty ()->get_kind () == TyTy::TypeKind::FNDEF); + + TyTy::FnType *fntype = static_cast (lookup); TyTy::FnType *trait_item_fntype - = static_cast (trait_item_ref.get_tyty ()); + = static_cast (resolved_trait_item.get_tyty ()); // sets substitute self into the trait_item_ref->tyty TyTy::SubstitutionParamMapping *self_mapping = nullptr; @@ -351,16 +343,13 @@ public: if (!trait_item_fntype->can_eq (fntype, true)) { RichLocation r (function.get_locus ()); - r.add_range (trait_item_ref.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); rust_error_at ( r, "method %<%s%> has an incompatible type for trait %<%s%>", fntype->get_identifier ().c_str (), trait_reference.get_name ().c_str ()); - return; } - - resolved_trait_item = trait_item_ref; } private: diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h index 0126ded..f1ae82d 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-item.h +++ b/gcc/rust/typecheck/rust-hir-type-check-item.h @@ -60,13 +60,65 @@ public: return; } + bool is_trait_impl_block = !trait_reference.is_error (); + + std::vector> + trait_item_refs; for (auto &impl_item : impl_block.get_impl_items ()) { - if (trait_reference.is_error ()) + if (!is_trait_impl_block) TypeCheckImplItem::Resolve (impl_item.get (), self); else - TypeCheckImplItemWithTrait::Resolve (impl_item.get (), self, - trait_reference); + { + auto &trait_item_ref + = TypeCheckImplItemWithTrait::Resolve (impl_item.get (), self, + trait_reference); + trait_item_refs.push_back (trait_item_ref); + } + } + + bool impl_block_missing_trait_items + = is_trait_impl_block + && trait_reference.size () != trait_item_refs.size (); + if (impl_block_missing_trait_items) + { + // filter the missing impl_items + std::vector> + missing_trait_items; + for (auto &trait_item_ref : trait_reference.get_trait_items ()) + { + bool found = false; + for (const TraitItemReference &implemented_trait_item : + trait_item_refs) + { + std::string trait_item_name = trait_item_ref.get_identifier (); + std::string impl_item_name + = implemented_trait_item.get_identifier (); + found = trait_item_name.compare (impl_item_name) == 0; + if (found) + break; + } + + bool is_required_trait_item = !trait_item_ref.is_optional (); + if (!found && is_required_trait_item) + missing_trait_items.push_back (trait_item_ref); + } + + std::string missing_items_buf; + RichLocation r (impl_block.get_locus ()); + for (size_t i = 0; i < missing_trait_items.size (); i++) + { + bool has_more = (i + 1) < missing_trait_items.size (); + const TraitItemReference &missing_trait_item + = missing_trait_items.at (i); + missing_items_buf + += missing_trait_item.get_identifier () + (has_more ? ", " : ""); + r.add_range (missing_trait_item.get_locus ()); + } + + rust_error_at (r, "missing %s in implementation of trait %<%s%>", + missing_items_buf.c_str (), + trait_reference.get_name ().c_str ()); } } diff --git a/gcc/testsuite/rust/compile/traits4.rs b/gcc/testsuite/rust/compile/traits4.rs new file mode 100644 index 0000000..486301d --- /dev/null +++ b/gcc/testsuite/rust/compile/traits4.rs @@ -0,0 +1,16 @@ +trait Foo { + const A: i32; + + fn test(self); +} + +struct Bar; +impl Foo for Bar { + // { dg-error "missing A in implementation of trait .Foo." "" { target *-*-* } .-1 } + fn test(self) {} +} + +fn main() { + let a = Bar; + a.test(); +} diff --git a/gcc/testsuite/rust/compile/traits5.rs b/gcc/testsuite/rust/compile/traits5.rs new file mode 100644 index 0000000..8b2fb9b --- /dev/null +++ b/gcc/testsuite/rust/compile/traits5.rs @@ -0,0 +1,9 @@ +trait Foo { + const A: i32; + + fn test(self); +} + +struct Bar; +impl Foo for Bar {} +// { dg-error "missing A, test in implementation of trait .Foo." "" { target *-*-* } .-1 } -- cgit v1.1