diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-10-20 17:47:17 +0100 |
---|---|---|
committer | Philip Herron <philip.herron@embecosm.com> | 2021-10-22 11:48:42 +0100 |
commit | fe77d2d5747b0c9f44c01eba4f6898fbb2790f23 (patch) | |
tree | be68921b507eb448d0573d9071e1daf628653214 | |
parent | 86ec0383d45b3339edd0583452d8bc3a0a3cddca (diff) | |
download | gcc-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.h | 11 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check-type.cc | 2 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-tyty.cc | 50 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-tyty.h | 6 | ||||
-rw-r--r-- | gcc/testsuite/rust/execute/torture/trait6.rs | 41 | ||||
-rw-r--r-- | gcc/testsuite/rust/execute/torture/trait7.rs | 41 |
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 +} |