aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2021-10-20 17:47:17 +0100
committerPhilip Herron <philip.herron@embecosm.com>2021-10-22 11:48:42 +0100
commitfe77d2d5747b0c9f44c01eba4f6898fbb2790f23 (patch)
treebe68921b507eb448d0573d9071e1daf628653214
parent86ec0383d45b3339edd0583452d8bc3a0a3cddca (diff)
downloadgcc-fe77d2d5747b0c9f44c01eba4f6898fbb2790f23.zip
gcc-fe77d2d5747b0c9f44c01eba4f6898fbb2790f23.tar.gz
gcc-fe77d2d5747b0c9f44c01eba4f6898fbb2790f23.tar.bz2
Fix ICE when trying to resolve associated type from type bounds
When we have a generic function with specified type bounds this allows rust code to actually then look up the relevant bounds for associated types, functions or constants. This means at code generation the relevant associated mappings msut be setup such that we compile to the implementaiton if available or the default implementation if possible. The bug here was that the resolution for associated types were auto setting up the associated type mappings even when the receiver was generic. So for example: ```rust pub trait Foo { type A; fn bar(self) -> Self::A; } struct S(i32); impl Foo for S { type A = i32; fn bar(self) -> Self::A { self.0 } } fn test_bar<T: Foo>(x: T) -> T::A { x.bar() } ``` The generic function of test_bar was auto resolving the T::A to A=i32 since it found the type-bound Foo was specified and the only implementation of Foo is on S. This is usually the correct flow to auto setup these mappings but in the conjtext of a generic receiver this needs to still result in a Placeholder type of the bound Foo to type check correctly. Fixes: #744 #741
-rw-r--r--gcc/rust/typecheck/rust-hir-path-probe.h11
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-type.cc2
-rw-r--r--gcc/rust/typecheck/rust-tyty.cc50
-rw-r--r--gcc/rust/typecheck/rust-tyty.h6
-rw-r--r--gcc/testsuite/rust/execute/torture/trait6.rs41
-rw-r--r--gcc/testsuite/rust/execute/torture/trait7.rs41
6 files changed, 145 insertions, 6 deletions
diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h
index dd51f83..d2b5f5b 100644
--- a/gcc/rust/typecheck/rust-hir-path-probe.h
+++ b/gcc/rust/typecheck/rust-hir-path-probe.h
@@ -276,7 +276,16 @@ protected:
TyTy::BaseType *trait_item_tyty = trait_item_ref->get_tyty ();
- if (impl != nullptr)
+ // we cannot auto setup associated type mappings when our receiver is a
+ // generic type bound
+ const TyTy::BaseType *root = receiver->get_root ();
+ bool receiver_is_type_param
+ = root->get_kind () == TyTy::TypeKind::PARAM;
+ bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC;
+ bool receiver_is_generic = receiver_is_type_param || receiver_is_dyn;
+
+ if (impl != nullptr && !receiver_is_generic)
+
{
HirId impl_block_id = impl->get_mappings ().get_hirid ();
AssociatedImplTrait *lookup_associated = nullptr;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
index a479581..ed21d53 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -445,7 +445,7 @@ TypeCheckType::resolve_segments (
// lookup the associated-impl-trait
HIR::ImplBlock *impl = candidate.item.trait.impl;
- if (impl != nullptr)
+ if (impl != nullptr && !reciever_is_generic)
{
AssociatedImplTrait *lookup_associated = nullptr;
bool found_impl_trait = context->lookup_associated_trait_impl (
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index f2216f9..1e7c87e 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -331,9 +331,11 @@ StructFieldType::clone () const
get_field_type ()->clone ());
}
-void
+bool
SubstitutionParamMapping::fill_param_ty (BaseType &type, Location locus)
{
+ auto context = Resolver::TypeCheckContext::get ();
+
if (type.get_kind () == TyTy::TypeKind::INFER)
{
type.inherit_bounds (*param);
@@ -341,7 +343,7 @@ SubstitutionParamMapping::fill_param_ty (BaseType &type, Location locus)
else
{
if (!param->bounds_compatible (type, locus, true))
- return;
+ return false;
}
if (type.get_kind () == TypeKind::PARAM)
@@ -351,8 +353,52 @@ SubstitutionParamMapping::fill_param_ty (BaseType &type, Location locus)
}
else
{
+ // check the substitution is compatible with bounds
+ if (!param->bounds_compatible (type, locus, true))
+ return false;
+
+ // setup any associated type mappings for the specified bonds and this
+ // type
+ auto candidates = Resolver::TypeBoundsProbe::Probe (&type);
+ for (auto &specified_bound : param->get_specified_bounds ())
+ {
+ const Resolver::TraitReference *specified_bound_ref
+ = specified_bound.get ();
+
+ // since the bounds_compatible check has occurred we should be able to
+ // assert on finding the trait references
+ HirId associated_impl_block_id = UNKNOWN_HIRID;
+ bool found = false;
+ for (auto &bound : candidates)
+ {
+ const Resolver::TraitReference *bound_trait_ref = bound.first;
+ const HIR::ImplBlock *associated_impl = bound.second;
+
+ found = specified_bound_ref->is_equal (*bound_trait_ref);
+ if (found)
+ {
+ rust_assert (associated_impl != nullptr);
+ associated_impl_block_id
+ = associated_impl->get_mappings ().get_hirid ();
+ break;
+ }
+ }
+
+ if (found && associated_impl_block_id != UNKNOWN_HIRID)
+ {
+ Resolver::AssociatedImplTrait *lookup_associated = nullptr;
+ bool found_impl_trait = context->lookup_associated_trait_impl (
+ associated_impl_block_id, &lookup_associated);
+
+ if (found_impl_trait)
+ lookup_associated->setup_associated_types ();
+ }
+ }
+
param->set_ty_ref (type.get_ref ());
}
+
+ return true;
}
void
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 054e327..f6a2797 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -626,7 +626,7 @@ public:
std::string as_string () const { return param->as_string (); }
- void fill_param_ty (BaseType &type, Location locus);
+ bool fill_param_ty (BaseType &type, Location locus);
SubstitutionParamMapping clone () const
{
@@ -1812,7 +1812,9 @@ public:
bool contains_type_parameters () const override
{
- rust_assert (can_resolve ());
+ if (!can_resolve ())
+ return false;
+
return resolve ()->contains_type_parameters ();
}
diff --git a/gcc/testsuite/rust/execute/torture/trait6.rs b/gcc/testsuite/rust/execute/torture/trait6.rs
new file mode 100644
index 0000000..54023d2
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait6.rs
@@ -0,0 +1,41 @@
+/* { dg-output "123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+pub trait Foo {
+ type A;
+
+ fn bar(self) -> Self::A;
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+struct S(i32);
+impl Foo for S {
+ type A = i32;
+
+ fn bar(self) -> Self::A {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ self.0
+ }
+}
+
+fn test_bar<T: Foo>(x: T) -> T::A {
+ x.bar()
+}
+
+fn main() -> i32 {
+ let a;
+ a = S(123);
+
+ let bar: i32 = test_bar::<S>(a);
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, bar);
+ }
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait7.rs b/gcc/testsuite/rust/execute/torture/trait7.rs
new file mode 100644
index 0000000..059ba15
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait7.rs
@@ -0,0 +1,41 @@
+/* { dg-output "123\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+pub trait Foo {
+ type A;
+
+ fn bar(self) -> Self::A;
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+struct S(i32);
+impl Foo for S {
+ type A = i32;
+
+ fn bar(self) -> Self::A {
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+ self.0
+ }
+}
+
+fn test_bar<T: Foo>(x: T) -> T::A {
+ x.bar()
+}
+
+fn main() -> i32 {
+ let a;
+ a = S(123);
+
+ let bar: i32 = test_bar(a);
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, bar);
+ }
+
+ 0
+}