aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-07-14 18:32:24 +0100
committerPhilip Herron <philip.herron@embecosm.com>2021-07-14 20:18:09 +0100
commit05b5eec7ab095d2d80a4fb8262c8985ad03550b6 (patch)
tree29194600c60c434f6af98f7ac96fea8863830d68
parent6e0c46b86aaa38f18e2c0af2a4711bfeaa242498 (diff)
downloadgcc-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.h7
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-implitem.h103
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-item.h58
-rw-r--r--gcc/testsuite/rust/compile/traits4.rs16
-rw-r--r--gcc/testsuite/rust/compile/traits5.rs9
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 }