diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-09-17 17:41:01 +0100 |
---|---|---|
committer | Philip Herron <philip.herron@embecosm.com> | 2021-09-17 17:41:01 +0100 |
commit | c674e168ebc29c061a8a936e064c2e3d556ab326 (patch) | |
tree | 0cbce4d44d5069009fad649da7eb9be83dd07699 /gcc | |
parent | aa019b251e9900e81e8b3ec258a4f1f340a91296 (diff) | |
download | gcc-c674e168ebc29c061a8a936e064c2e3d556ab326.zip gcc-c674e168ebc29c061a8a936e064c2e3d556ab326.tar.gz gcc-c674e168ebc29c061a8a936e064c2e3d556ab326.tar.bz2 |
Add object safety checks for dynamic objects
You cannot create dynamic objects that contain non object safe trait items.
This adds checks to ensure that all items are object safe so code
generation does not need to care.
see: https://doc.rust-lang.org/reference/items/traits.html#object-safety
Addresses: #197
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/hir/tree/rust-hir-item.h | 2 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-ref.h | 49 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-resolve.cc | 30 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-trait-resolve.h | 5 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check-type.cc | 11 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-tyty-bounds.cc | 8 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-tyty.h | 4 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits10.rs | 16 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/traits11.rs | 20 |
9 files changed, 137 insertions, 8 deletions
diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h index 0b2cf10..8cd7a01 100644 --- a/gcc/rust/hir/tree/rust-hir-item.h +++ b/gcc/rust/hir/tree/rust-hir-item.h @@ -2264,6 +2264,8 @@ public: TraitFunctionDecl &get_decl () { return decl; } + const TraitFunctionDecl &get_decl () const { return decl; } + bool has_block_defined () const { return block_expr != nullptr; } std::unique_ptr<BlockExpr> &get_block_expr () diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h index f05ff0c..2b5b78c 100644 --- a/gcc/rust/typecheck/rust-hir-trait-ref.h +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -143,6 +143,8 @@ public: void associated_type_reset (); + bool is_object_safe () const; + private: TyTy::ErrorType *get_error () const { @@ -179,8 +181,10 @@ class TraitReference { public: TraitReference (const HIR::Trait *hir_trait_ref, - std::vector<TraitItemReference> item_refs) - : hir_trait_ref (hir_trait_ref), item_refs (item_refs) + std::vector<TraitItemReference> item_refs, + std::vector<const TraitReference *> super_traits) + : hir_trait_ref (hir_trait_ref), item_refs (item_refs), + super_traits (super_traits) {} TraitReference (TraitReference const &other) @@ -198,7 +202,7 @@ public: TraitReference (TraitReference &&other) = default; TraitReference &operator= (TraitReference &&other) = default; - static TraitReference error () { return TraitReference (nullptr, {}); } + static TraitReference error () { return TraitReference (nullptr, {}, {}); } bool is_error () const { return hir_trait_ref == nullptr; } @@ -323,9 +327,48 @@ public: return this_id == other_id; } + const std::vector<const TraitReference *> get_super_traits () const + { + return super_traits; + } + + bool is_object_safe (bool emit_error, Location locus) const + { + // https: // doc.rust-lang.org/reference/items/traits.html#object-safety + std::vector<const TraitReference *> non_object_super_traits; + for (auto &item : super_traits) + { + if (!item->is_object_safe (false, Location ())) + non_object_super_traits.push_back (item); + } + + std::vector<const Resolver::TraitItemReference *> non_object_safe_items; + for (auto &item : get_trait_items ()) + { + if (!item.is_object_safe ()) + non_object_safe_items.push_back (&item); + } + + bool is_safe + = non_object_super_traits.empty () && non_object_safe_items.empty (); + if (emit_error && !is_safe) + { + RichLocation r (locus); + for (auto &item : non_object_super_traits) + r.add_range (item->get_locus ()); + for (auto &item : non_object_safe_items) + r.add_range (item->get_locus ()); + + rust_error_at (r, "trait bound is not object safe"); + } + + return is_safe; + } + private: const HIR::Trait *hir_trait_ref; std::vector<TraitItemReference> item_refs; + std::vector<const TraitReference *> super_traits; }; class AssociatedImplTrait diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc index be58840..cee6999 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.cc +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc @@ -193,6 +193,36 @@ TraitItemReference::get_parent_trait_mappings () const return trait->get_mappings (); } +bool +TraitItemReference::is_object_safe () const +{ + // https://doc.rust-lang.org/reference/items/traits.html#object-safety + switch (get_trait_item_type ()) + { + case TraitItemReference::TraitItemType::FN: { + // lets be boring and just check that this is indeed a method will do + // for now + const HIR::TraitItem *item = get_hir_trait_item (); + const HIR::TraitItemFunc *fn + = static_cast<const HIR::TraitItemFunc *> (item); + return fn->get_decl ().is_method (); + } + + // constants are not available via dyn dispatch and so is not object safe + case TraitItemReference::TraitItemType::CONST: + return false; + + // types are object safe since they are not available via dyn dispatch + case TraitItemReference::TraitItemType::TYPE: + return true; + + // this is just an error so lets just fail it + case TraitItemReference::TraitItemType::ERROR: + return false; + } + return false; +} + TyTy::BaseType * AssociatedImplTrait::get_projected_type ( const TraitItemReference *trait_item_ref, TyTy::BaseType *receiver, HirId ref, diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h index b0e2c0d..a32b4da 100644 --- a/gcc/rust/typecheck/rust-hir-trait-resolve.h +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -153,6 +153,7 @@ private: TyTy::TypeBoundPredicate (trait_reference->get_mappings ().get_defid (), trait_reference->get_locus ())); + std::vector<const TraitReference *> super_traits; if (trait_reference->has_type_param_bounds ()) { for (auto &bound : trait_reference->get_type_param_bounds ()) @@ -170,6 +171,7 @@ private: trait->get_mappings ().get_defid (), bound->get_locus ()); specified_bounds.push_back (std::move (predicate)); + super_traits.push_back (predicate.get ()); } } } @@ -189,7 +191,8 @@ private: item_refs.push_back (std::move (trait_item_ref)); } - TraitReference trait_object (trait_reference, item_refs); + TraitReference trait_object (trait_reference, item_refs, + std::move (super_traits)); context->insert_trait_reference ( trait_reference->get_mappings ().get_defid (), std::move (trait_object)); diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc index c660521..7ad6d03 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc @@ -231,10 +231,13 @@ TypeCheckType::visit (HIR::TraitObjectTypeOneBound &type) TyTy::TypeBoundPredicate predicate (trait->get_mappings ().get_defid (), trait_bound.get_locus ()); - specified_bounds.push_back (std::move (predicate)); - - translated = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), - std::move (specified_bounds)); + if (predicate.is_object_safe (true, type.get_locus ())) + { + specified_bounds.push_back (std::move (predicate)); + translated + = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), + std::move (specified_bounds)); + } } } // namespace Resolver diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc index 1bd7865..196a6ca 100644 --- a/gcc/rust/typecheck/rust-tyty-bounds.cc +++ b/gcc/rust/typecheck/rust-tyty-bounds.cc @@ -91,5 +91,13 @@ TypeBoundPredicate::get_name () const return get ()->get_name (); } +bool +TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const +{ + const Resolver::TraitReference *trait = get (); + rust_assert (trait != nullptr); + return trait->is_object_safe (emit_error, locus); +} + } // namespace TyTy } // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index 90057de..040831e 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -154,6 +154,10 @@ public: std::string get_name () const; + // check that this predicate is object-safe see: + // https://doc.rust-lang.org/reference/items/traits.html#object-safety + bool is_object_safe (bool emit_error, Location locus) const; + private: DefId reference; Location locus; diff --git a/gcc/testsuite/rust/compile/traits10.rs b/gcc/testsuite/rust/compile/traits10.rs new file mode 100644 index 0000000..a4622b2 --- /dev/null +++ b/gcc/testsuite/rust/compile/traits10.rs @@ -0,0 +1,16 @@ +struct Foo(i32); + +trait Bar { + const A: i32 = 123; + fn B(); + fn C(&self); +} + +pub fn main() { + let a; + a = Foo(123); + + let b: &dyn Bar = &a; + // { dg-error "trait bound is not object safe" "" { target *-*-* } .-1 } + // { dg-error "expected" "" { target *-*-* } .-2 } +} diff --git a/gcc/testsuite/rust/compile/traits11.rs b/gcc/testsuite/rust/compile/traits11.rs new file mode 100644 index 0000000..bf69ff0 --- /dev/null +++ b/gcc/testsuite/rust/compile/traits11.rs @@ -0,0 +1,20 @@ +struct Foo(i32); + +trait A { + const A: i32 = 123; + fn B(); + fn C(&self); +} + +trait B: A { + fn test(&self); +} + +pub fn main() { + let a; + a = Foo(123); + + let b: &dyn B = &a; + // { dg-error "trait bound is not object safe" "" { target *-*-* } .-1 } + // { dg-error "expected" "" { target *-*-* } .-2 } +} |