diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-07-14 18:32:24 +0100 |
---|---|---|
committer | Philip Herron <philip.herron@embecosm.com> | 2021-07-14 20:18:09 +0100 |
commit | 05b5eec7ab095d2d80a4fb8262c8985ad03550b6 (patch) | |
tree | 29194600c60c434f6af98f7ac96fea8863830d68 | |
parent | 6e0c46b86aaa38f18e2c0af2a4711bfeaa242498 (diff) | |
download | gcc-05b5eec7ab095d2d80a4fb8262c8985ad03550b6.zip gcc-05b5eec7ab095d2d80a4fb8262c8985ad03550b6.tar.gz gcc-05b5eec7ab095d2d80a4fb8262c8985ad03550b6.tar.bz2 |
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
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-ref.h | 7 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check-implitem.h | 103 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check-item.h | 58 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits4.rs | 16 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits5.rs | 9 |
5 files changed, 133 insertions, 60 deletions
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<TraitItemReference> &get_trait_items () const + { + return item_refs; + } + private: const HIR::Trait *hir_trait_ref; std::vector<TraitItemReference> 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<TyTy::FnType *> (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<TyTy::FnType *> (lookup); TyTy::FnType *trait_item_fntype - = static_cast<TyTy::FnType *> (trait_item_ref.get_tyty ()); + = static_cast<TyTy::FnType *> (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<std::reference_wrapper<const TraitItemReference>> + 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<std::reference_wrapper<const TraitItemReference>> + 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 } |