From: Enes Cevik <[email protected]>
During vtable generation in `compute_address_for_trait_item`, the
compiler was picking the first `impl` block that satisfied the trait
predicate, without verifying if the `impl` block actually belonged to
the target receiver type. This caused dynamic dispatch to call the
wrong methods when multiple types implemented the same trait in the
same scope.
This patch adds a comparison between the impl block's resolved type and
the `receiver` type to ensure we compute the address of the correct
associated function.
gcc/rust/ChangeLog:
* backend/rust-compile.cc
(HIRCompileBase::compute_address_for_trait_item): Ensure the
impl block type matches the receiver type.
gcc/testsuite/ChangeLog:
* rust/execute/trait-multi-impl.rs: New test.
* rust/execute/trait-multi-impl-generic.rs: New test.
* rust/execute/trait-multi-impl-generic2.rs: New test.
Signed-off-by: Enes Cevik <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/b63444666d45969e518b67677f522a85cbcaa22e
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4660
gcc/rust/backend/rust-compile.cc | 9 +++
.../rust/execute/trait-multi-impl-generic.rs | 46 +++++++++++++
.../rust/execute/trait-multi-impl-generic2.rs | 69 +++++++++++++++++++
.../rust/execute/trait-multi-impl.rs | 51 ++++++++++++++
4 files changed, 175 insertions(+)
create mode 100644 gcc/testsuite/rust/execute/trait-multi-impl-generic.rs
create mode 100644 gcc/testsuite/rust/execute/trait-multi-impl-generic2.rs
create mode 100644 gcc/testsuite/rust/execute/trait-multi-impl.rs
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index c2ef00b8b..b57ba966f 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -314,6 +314,15 @@ HIRCompileBase::compute_address_for_trait_item (
rust_assert (ok);
+ TyTy::BaseType *mut_receiver = const_cast<TyTy::BaseType *> (receiver);
+ bool receiver_matches = Resolver::types_compatable (
+ TyTy::TyWithLocation (self, self->get_locus ()),
+ TyTy::TyWithLocation (mut_receiver, mut_receiver->get_locus ()),
+ UNDEF_LOCATION, false);
+
+ if (!receiver_matches)
+ continue;
+
// Look through the relevant bounds on our type, and find which one our
// impl block satisfies
TyTy::TypeBoundPredicate *self_bound = nullptr;
diff --git a/gcc/testsuite/rust/execute/trait-multi-impl-generic.rs
b/gcc/testsuite/rust/execute/trait-multi-impl-generic.rs
new file mode 100644
index 000000000..7be5eb29b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/trait-multi-impl-generic.rs
@@ -0,0 +1,46 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+trait TraitA {
+ fn do_a(&self) -> i32;
+}
+
+pub struct Container<T> {
+ pub val: i32,
+ pub _marker: T,
+}
+
+impl TraitA for Container<i32> {
+ fn do_a(&self) -> i32 {
+ self.val * 10
+ }
+}
+
+impl TraitA for Container<u32> {
+ fn do_a(&self) -> i32 {
+ self.val * 20
+ }
+}
+
+fn main() -> i32 {
+ let a = Container {
+ val: 10,
+ _marker: 0_i32,
+ };
+ let b = Container {
+ val: 20,
+ _marker: 0_u32,
+ };
+
+ let dyn_a = &a as &dyn TraitA;
+ let dyn_b = &b as &dyn TraitA;
+
+ if dyn_a.do_a() == 100 && dyn_b.do_a() == 400 {
+ 0
+ } else {
+ 1
+ }
+}
diff --git a/gcc/testsuite/rust/execute/trait-multi-impl-generic2.rs
b/gcc/testsuite/rust/execute/trait-multi-impl-generic2.rs
new file mode 100644
index 000000000..2b42668f3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/trait-multi-impl-generic2.rs
@@ -0,0 +1,69 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+trait TraitA {
+ fn do_a(&self) -> i32;
+}
+
+mod mod_a {
+ use super::TraitA;
+
+ pub struct StructX<T> {
+ pub val: i32,
+ _marker: T,
+ }
+
+ impl TraitA for StructX<i32> {
+ fn do_a(&self) -> i32 {
+ self.val * 10
+ }
+ }
+ impl TraitA for StructX<u32> {
+ fn do_a(&self) -> i32 {
+ self.val * 1000
+ }
+ }
+}
+
+mod mod_b {
+ use super::TraitA;
+
+ pub struct StructX<T> {
+ pub val: i32,
+ _marker: T,
+ }
+
+ impl TraitA for StructX<u32> {
+ fn do_a(&self) -> i32 {
+ self.val * 20
+ }
+ }
+ impl TraitA for StructX<i32> {
+ fn do_a(&self) -> i32 {
+ self.val * 2000
+ }
+ }
+}
+
+fn main() -> i32 {
+ let a = mod_a::StructX {
+ val: 10,
+ _marker: 0_i32,
+ };
+ let b = mod_b::StructX {
+ val: 20,
+ _marker: 0_u32,
+ };
+
+ let dyn_a = &a as &dyn TraitA;
+ let dyn_b = &b as &dyn TraitA;
+
+ if dyn_a.do_a() == 100 && dyn_b.do_a() == 400 {
+ 0
+ } else {
+ 1
+ }
+}
diff --git a/gcc/testsuite/rust/execute/trait-multi-impl.rs
b/gcc/testsuite/rust/execute/trait-multi-impl.rs
new file mode 100644
index 000000000..cd507c0d7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/trait-multi-impl.rs
@@ -0,0 +1,51 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+trait TraitA {
+ fn do_a(&self) -> i32;
+}
+
+mod mod_a {
+ use super::TraitA;
+
+ pub struct StructX {
+ pub val: i32,
+ }
+
+ impl TraitA for StructX {
+ fn do_a(&self) -> i32 {
+ self.val * 10
+ }
+ }
+}
+
+mod mod_b {
+ use super::TraitA;
+
+ pub struct StructX {
+ pub val: i32,
+ }
+
+ impl TraitA for StructX {
+ fn do_a(&self) -> i32 {
+ self.val * 20
+ }
+ }
+}
+
+fn main() -> i32 {
+ let a = mod_a::StructX { val: 10 };
+ let b = mod_b::StructX { val: 20 };
+
+ let dyn_a = &a as &dyn TraitA;
+ let dyn_b = &b as &dyn TraitA;
+
+ if dyn_a.do_a() == 100 && dyn_b.do_a() == 400 {
+ 0
+ } else {
+ 1
+ }
+}
base-commit: 1ce8d91871c231c695589197141184200519852f
--
2.54.0