[committed,75/88] gccrs: Support for Sized builtin marker trait

Message ID 20230405140411.3016563-76-arthur.cohen@embecosm.com
State Accepted
Headers
Series [committed,01/88] gccrs: fatal_error_flag: Fix typo in error message |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Arthur Cohen April 5, 2023, 2:03 p.m. UTC
  From: Philip Herron <herron.philip@googlemail.com>

When implementing general bounds checking as part of unify calls, we did
not check associated types on bounds which lead to alot of missed error
checking. This now recursively checks the bounds and the associated types
with a decent error message. This also required us to implement the Sized
marker trait to keep existing test-cases happy.

Fixes #1725

Signed-off-by: Philip Herron <herron.philip@googlemail.com>

gcc/rust/ChangeLog:

	* typecheck/rust-hir-trait-reference.cc (TraitReference::clear_associated_types): make const
	(TraitReference::clear_associated_type_projections): new interface
	* typecheck/rust-hir-trait-reference.h:
	* typecheck/rust-hir-trait-resolve.cc (TraitResolver::resolve_trait): refactor
	(TraitItemReference::associated_type_reset): reset projections
	* typecheck/rust-hir-type-bounds.h:
	* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit): fix bounds
	* typecheck/rust-tyty-bounds.cc (TypeBoundsProbe::TypeBoundsProbe): refactor into cc file
	(TypeBoundsProbe::Probe): refactor
	(TypeBoundsProbe::is_bound_satisfied_for_type): likewise
	(TypeBoundsProbe::assemble_sized_builtin): add builtin for Sized
	(TypeCheckBase::get_predicate_from_bound): refactor
	(TypeBoundPredicate::lookup_associated_type): refactor
	* typecheck/rust-tyty-subst.cc (SubstitutionRef::lookup_associated_impl)
	(SubstitutionRef::prepare_higher_ranked_bounds): new interface to clear hanging bounds
	(SubstitutionRef::monomorphize): refactor
	* typecheck/rust-tyty-subst.h:
	* typecheck/rust-tyty.cc (BaseType::get_locus): helper
	(BaseType::satisfies_bound): ensure bounds are satisfied and assoicated types
	(ParamType::ParamType): new field in constructor
	(ParamType::clone): update clone
	(ParamType::set_implicit_self_trait): new interface
	(ParamType::is_implicit_self_trait): likewise
	* typecheck/rust-tyty.h: cleanup
	* util/rust-hir-map.cc (Mappings::Mappings): builtin marker
	(Mappings::~Mappings): delete marker
	(Mappings::lookup_builtin_marker): lookup
	* util/rust-hir-map.h: update header

gcc/testsuite/ChangeLog:

	* rust/compile/issue-1725-1.rs: New test.
	* rust/compile/issue-1725-2.rs: New test.
---
 .../typecheck/rust-hir-trait-reference.cc     |  18 +-
 gcc/rust/typecheck/rust-hir-trait-reference.h |   6 +-
 gcc/rust/typecheck/rust-hir-trait-resolve.cc  |  27 +-
 gcc/rust/typecheck/rust-hir-type-bounds.h     |  34 +-
 .../typecheck/rust-hir-type-check-expr.cc     |   6 +
 gcc/rust/typecheck/rust-tyty-bounds.cc        | 111 ++++++-
 gcc/rust/typecheck/rust-tyty-subst.cc         | 291 ++++++++++--------
 gcc/rust/typecheck/rust-tyty-subst.h          |  12 +-
 gcc/rust/typecheck/rust-tyty.cc               |  82 ++++-
 gcc/rust/typecheck/rust-tyty.h                |   8 +-
 gcc/rust/util/rust-hir-map.cc                 |  17 +-
 gcc/rust/util/rust-hir-map.h                  |   3 +
 gcc/testsuite/rust/compile/issue-1725-1.rs    |  19 ++
 gcc/testsuite/rust/compile/issue-1725-2.rs    |  28 ++
 14 files changed, 476 insertions(+), 186 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issue-1725-1.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1725-2.rs
  

Patch

diff --git a/gcc/rust/typecheck/rust-hir-trait-reference.cc b/gcc/rust/typecheck/rust-hir-trait-reference.cc
index 8574988fb0b..a1229adc06c 100644
--- a/gcc/rust/typecheck/rust-hir-trait-reference.cc
+++ b/gcc/rust/typecheck/rust-hir-trait-reference.cc
@@ -343,14 +343,26 @@  TraitReference::on_resolved ()
 }
 
 void
-TraitReference::clear_associated_types ()
+TraitReference::clear_associated_types () const
 {
-  for (auto &item : item_refs)
+  for (const auto &item : item_refs)
+    {
+      bool is_assoc_type = item.get_trait_item_type ()
+			   == TraitItemReference::TraitItemType::TYPE;
+      if (is_assoc_type)
+	item.associated_type_reset (false);
+    }
+}
+
+void
+TraitReference::clear_associated_type_projections () const
+{
+  for (const auto &item : item_refs)
     {
       bool is_assoc_type = item.get_trait_item_type ()
 			   == TraitItemReference::TraitItemType::TYPE;
       if (is_assoc_type)
-	item.associated_type_reset ();
+	item.associated_type_reset (true);
     }
 }
 
diff --git a/gcc/rust/typecheck/rust-hir-trait-reference.h b/gcc/rust/typecheck/rust-hir-trait-reference.h
index f3703efcced..d20b2952e5b 100644
--- a/gcc/rust/typecheck/rust-hir-trait-reference.h
+++ b/gcc/rust/typecheck/rust-hir-trait-reference.h
@@ -106,7 +106,7 @@  public:
 
   void associated_type_set (TyTy::BaseType *ty) const;
 
-  void associated_type_reset () const;
+  void associated_type_reset (bool only_projections) const;
 
   bool is_object_safe () const;
 
@@ -212,7 +212,9 @@  public:
 
   void on_resolved ();
 
-  void clear_associated_types ();
+  void clear_associated_types () const;
+
+  void clear_associated_type_projections () const;
 
   bool is_equal (const TraitReference &other) const;
 
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
index 6e23093eceb..2d7985703cf 100644
--- a/gcc/rust/typecheck/rust-hir-trait-resolve.cc
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
@@ -161,6 +161,9 @@  TraitResolver::resolve_trait (HIR::Trait *trait_reference)
   TraitQueryGuard guard (trait_id);
   TyTy::BaseType *self = nullptr;
   std::vector<TyTy::SubstitutionParamMapping> substitutions;
+
+  // FIXME
+  // this should use the resolve_generic_params like everywhere else
   for (auto &generic_param : trait_reference->get_generic_params ())
     {
       switch (generic_param.get ()->get_kind ())
@@ -182,7 +185,11 @@  TraitResolver::resolve_trait (HIR::Trait *trait_reference)
 
 	    if (typaram.get_type_representation ().compare ("Self") == 0)
 	      {
-		self = param_type;
+		rust_assert (param_type->get_kind () == TyTy::TypeKind::PARAM);
+		TyTy::ParamType *p
+		  = static_cast<TyTy::ParamType *> (param_type);
+		p->set_implicit_self_trait ();
+		self = p;
 	      }
 	  }
 	  break;
@@ -365,7 +372,7 @@  TraitItemReference::associated_type_set (TyTy::BaseType *ty) const
 }
 
 void
-TraitItemReference::associated_type_reset () const
+TraitItemReference::associated_type_reset (bool only_projections) const
 {
   rust_assert (get_trait_item_type () == TraitItemType::TYPE);
 
@@ -374,7 +381,21 @@  TraitItemReference::associated_type_reset () const
   TyTy::PlaceholderType *placeholder
     = static_cast<TyTy::PlaceholderType *> (item_ty);
 
-  placeholder->clear_associated_type ();
+  if (!only_projections)
+    {
+      placeholder->clear_associated_type ();
+    }
+  else
+    {
+      if (!placeholder->can_resolve ())
+	return;
+
+      const TyTy::BaseType *r = placeholder->resolve ();
+      if (r->get_kind () == TyTy::TypeKind::PROJECTION)
+	{
+	  placeholder->clear_associated_type ();
+	}
+    }
 }
 
 TyTy::BaseType *
diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h
index 4e8c58344de..628bba5da20 100644
--- a/gcc/rust/typecheck/rust-hir-type-bounds.h
+++ b/gcc/rust/typecheck/rust-hir-type-bounds.h
@@ -30,42 +30,18 @@  class TypeBoundsProbe : public TypeCheckBase
 {
 public:
   static std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
-  Probe (const TyTy::BaseType *receiver)
-  {
-    TypeBoundsProbe probe (receiver);
-    probe.scan ();
-    return probe.trait_references;
-  }
+  Probe (const TyTy::BaseType *receiver);
 
   static bool is_bound_satisfied_for_type (TyTy::BaseType *receiver,
-					   TraitReference *ref)
-  {
-    for (auto &bound : receiver->get_specified_bounds ())
-      {
-	const TraitReference *b = bound.get ();
-	if (b->is_equal (*ref))
-	  return true;
-      }
-
-    std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
-      = Probe (receiver);
-    for (auto &bound : bounds)
-      {
-	const TraitReference *b = bound.first;
-	if (b->is_equal (*ref))
-	  return true;
-      }
-
-    return false;
-  }
+					   TraitReference *ref);
 
 private:
   void scan ();
+  void assemble_sized_builtin ();
+  void assemble_builtin_candidate (Analysis::RustLangItem::ItemType item);
 
 private:
-  TypeBoundsProbe (const TyTy::BaseType *receiver)
-    : TypeCheckBase (), receiver (receiver)
-  {}
+  TypeBoundsProbe (const TyTy::BaseType *receiver);
 
   const TyTy::BaseType *receiver;
   std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> trait_references;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index 46a14eb6e92..d4eea7ae954 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -1095,6 +1095,7 @@  TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
       return;
     }
 
+  fn->prepare_higher_ranked_bounds ();
   auto root = receiver_tyty->get_root ();
   if (root->get_kind () == TyTy::TypeKind::ADT)
     {
@@ -1659,6 +1660,11 @@  TypeCheckExpr::resolve_operator_overload (
   TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
   rust_assert (fn->is_method ());
 
+  fn->prepare_higher_ranked_bounds ();
+  rust_debug_loc (expr.get_locus (), "resolved operator overload to: {%u} {%s}",
+		  candidate.candidate.ty->get_ref (),
+		  candidate.candidate.ty->debug_str ().c_str ());
+
   auto root = lhs->get_root ();
   if (root->get_kind () == TyTy::TypeKind::ADT)
     {
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
index b14e0c68e5a..76d2eeff8ef 100644
--- a/gcc/rust/typecheck/rust-tyty-bounds.cc
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -23,6 +23,41 @@ 
 namespace Rust {
 namespace Resolver {
 
+TypeBoundsProbe::TypeBoundsProbe (const TyTy::BaseType *receiver)
+  : TypeCheckBase (), receiver (receiver)
+{}
+
+std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
+TypeBoundsProbe::Probe (const TyTy::BaseType *receiver)
+{
+  TypeBoundsProbe probe (receiver);
+  probe.scan ();
+  return probe.trait_references;
+}
+
+bool
+TypeBoundsProbe::is_bound_satisfied_for_type (TyTy::BaseType *receiver,
+					      TraitReference *ref)
+{
+  for (auto &bound : receiver->get_specified_bounds ())
+    {
+      const TraitReference *b = bound.get ();
+      if (b->is_equal (*ref))
+	return true;
+    }
+
+  std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
+    = Probe (receiver);
+  for (auto &bound : bounds)
+    {
+      const TraitReference *b = bound.first;
+      if (b->is_equal (*ref))
+	return true;
+    }
+
+  return false;
+}
+
 void
 TypeBoundsProbe::scan ()
 {
@@ -57,6 +92,75 @@  TypeBoundsProbe::scan ()
       if (!trait_ref->is_error ())
 	trait_references.push_back ({trait_ref, path.second});
     }
+
+  // marker traits...
+  assemble_sized_builtin ();
+}
+
+void
+TypeBoundsProbe::assemble_sized_builtin ()
+{
+  const TyTy::BaseType *raw = receiver->destructure ();
+
+  // does this thing actually implement sized?
+  switch (raw->get_kind ())
+    {
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+      assemble_builtin_candidate (Analysis::RustLangItem::SIZED);
+      break;
+
+      // not-sure about this.... FIXME
+    case TyTy::INFER:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      break;
+    }
+}
+
+void
+TypeBoundsProbe::assemble_builtin_candidate (
+  Analysis::RustLangItem::ItemType lang_item)
+{
+  DefId id;
+  bool found_lang_item = mappings->lookup_lang_item (lang_item, &id);
+  if (!found_lang_item)
+    return;
+
+  HIR::Item *item = mappings->lookup_defid (id);
+  if (item == nullptr)
+    return;
+
+  rust_assert (item->get_item_kind () == HIR::Item::ItemKind::Trait);
+  HIR::Trait *trait = static_cast<HIR::Trait *> (item);
+  const TyTy::BaseType *raw = receiver->destructure ();
+
+  // assemble the reference
+  TraitReference *trait_ref = TraitResolver::Resolve (*trait);
+  trait_references.push_back ({trait_ref, mappings->lookup_builtin_marker ()});
+
+  rust_debug ("Added builtin lang_item: %s for %s",
+	      Analysis::RustLangItem::ToString (lang_item).c_str (),
+	      raw->get_name ().c_str ());
 }
 
 TraitReference *
@@ -101,7 +205,8 @@  TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path)
 	  = static_cast<HIR::TypePathSegmentFunction *> (final_seg.get ());
 	auto &fn = final_function_seg->get_function_path ();
 
-	// we need to make implicit generic args which must be an implicit Tuple
+	// we need to make implicit generic args which must be an implicit
+	// Tuple
 	auto crate_num = mappings->get_current_crate ();
 	HirId implicit_args_id = mappings->get_next_hir_id ();
 	Analysis::NodeMapping mapping (crate_num,
@@ -514,8 +619,8 @@  TypeBoundPredicate::lookup_associated_type (const std::string &search)
 {
   TypeBoundPredicateItem item = lookup_associated_item (search);
 
-  // only need to check that it is infact an associated type because other wise
-  // if it was not found it will just be an error node anyway
+  // only need to check that it is infact an associated type because other
+  // wise if it was not found it will just be an error node anyway
   if (!item.is_error ())
     {
       const auto raw = item.get_raw_item ();
diff --git a/gcc/rust/typecheck/rust-tyty-subst.cc b/gcc/rust/typecheck/rust-tyty-subst.cc
index e4fe30e00ea..d2f6cf607d1 100644
--- a/gcc/rust/typecheck/rust-tyty-subst.cc
+++ b/gcc/rust/typecheck/rust-tyty-subst.cc
@@ -119,11 +119,6 @@  SubstitutionParamMapping::fill_param_ty (
     {
       type.inherit_bounds (*param);
     }
-  else
-    {
-      if (!param->bounds_compatible (type, locus, true))
-	return false;
-    }
 
   if (type.get_kind () == TypeKind::PARAM)
     {
@@ -133,8 +128,15 @@  SubstitutionParamMapping::fill_param_ty (
   else
     {
       // check the substitution is compatible with bounds
-      if (!param->bounds_compatible (type, locus, true))
-	return false;
+      rust_debug_loc (locus,
+		      "fill_param_ty bounds_compatible: param %s type %s",
+		      param->get_name ().c_str (), type.get_name ().c_str ());
+
+      if (!param->is_implicit_self_trait ())
+	{
+	  if (!param->bounds_compatible (type, locus, true))
+	    return false;
+	}
 
       // recursively pass this down to all HRTB's
       for (auto &bound : param->get_specified_bounds ())
@@ -870,10 +872,149 @@  SubstitutionRef::solve_missing_mappings_from_this (SubstitutionRef &ref,
   return SubstitutionArgumentMappings (resolved_mappings, {}, locus);
 }
 
+Resolver::AssociatedImplTrait *
+SubstitutionRef::lookup_associated_impl (const SubstitutionParamMapping &subst,
+					 const TypeBoundPredicate &bound,
+					 const TyTy::BaseType *binding,
+					 bool *error_flag) const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  const Resolver::TraitReference *specified_bound_ref = bound.get ();
+
+  // setup any associated type mappings for the specified bonds and this
+  // type
+  auto candidates = Resolver::TypeBoundsProbe::Probe (binding);
+  std::vector<Resolver::AssociatedImplTrait *> associated_impl_traits;
+  for (auto &probed_bound : candidates)
+    {
+      const Resolver::TraitReference *bound_trait_ref = probed_bound.first;
+      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+      HirId impl_block_id = associated_impl->get_mappings ().get_hirid ();
+      Resolver::AssociatedImplTrait *associated = nullptr;
+      bool found_impl_trait
+	= context->lookup_associated_trait_impl (impl_block_id, &associated);
+      if (found_impl_trait)
+	{
+	  bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref);
+	  bool found_self = associated->get_self ()->can_eq (binding, false);
+	  if (found_trait && found_self)
+	    {
+	      associated_impl_traits.push_back (associated);
+	    }
+	}
+    }
+
+  if (associated_impl_traits.empty ())
+    return nullptr;
+
+  // This code is important when you look at slices for example when
+  // you have a slice such as:
+  //
+  // let slice = &array[1..3]
+  //
+  // the higher ranked bounds will end up having an Index trait
+  // implementation for Range<usize> so we need this code to resolve
+  // that we have an integer inference variable that needs to become
+  // a usize
+  //
+  // The other complicated issue is that we might have an intrinsic
+  // which requires the :Clone or Copy bound but the libcore adds
+  // implementations for all the integral types so when there are
+  // multiple candidates we need to resolve to the default
+  // implementation for that type otherwise its an error for
+  // ambiguous type bounds
+
+  // if we have a non-general inference variable we need to be
+  // careful about the selection here
+  bool is_infer_var = binding->get_kind () == TyTy::TypeKind::INFER;
+  bool is_integer_infervar
+    = is_infer_var
+      && static_cast<const TyTy::InferType *> (binding)->get_infer_kind ()
+	   == TyTy::InferType::InferTypeKind::INTEGRAL;
+  bool is_float_infervar
+    = is_infer_var
+      && static_cast<const TyTy::InferType *> (binding)->get_infer_kind ()
+	   == TyTy::InferType::InferTypeKind::FLOAT;
+
+  Resolver::AssociatedImplTrait *associate_impl_trait = nullptr;
+  if (associated_impl_traits.size () == 1)
+    {
+      // just go for it
+      associate_impl_trait = associated_impl_traits.at (0);
+    }
+  else if (is_integer_infervar)
+    {
+      TyTy::BaseType *type = nullptr;
+      bool ok = context->lookup_builtin ("i32", &type);
+      rust_assert (ok);
+
+      for (auto &impl : associated_impl_traits)
+	{
+	  bool found = impl->get_self ()->is_equal (*type);
+	  if (found)
+	    {
+	      associate_impl_trait = impl;
+	      break;
+	    }
+	}
+    }
+  else if (is_float_infervar)
+    {
+      TyTy::BaseType *type = nullptr;
+      bool ok = context->lookup_builtin ("f64", &type);
+      rust_assert (ok);
+
+      for (auto &impl : associated_impl_traits)
+	{
+	  bool found = impl->get_self ()->is_equal (*type);
+	  if (found)
+	    {
+	      associate_impl_trait = impl;
+	      break;
+	    }
+	}
+    }
+
+  if (associate_impl_trait == nullptr)
+    {
+      // go for the first one? or error out?
+      auto &mappings = *Analysis::Mappings::get ();
+      const auto &type_param = subst.get_generic_param ();
+      const auto *trait_ref = bound.get ();
+
+      RichLocation r (type_param.get_locus ());
+      r.add_range (bound.get_locus ());
+      r.add_range (mappings.lookup_location (binding->get_ref ()));
+
+      rust_error_at (r, "ambiguous type bound for trait %s and type %s",
+		     trait_ref->get_name ().c_str (),
+		     binding->get_name ().c_str ());
+
+      *error_flag = true;
+      return nullptr;
+    }
+
+  return associate_impl_trait;
+}
+
+void
+SubstitutionRef::prepare_higher_ranked_bounds ()
+{
+  for (const auto &subst : get_substs ())
+    {
+      const TyTy::ParamType *pty = subst.get_param_ty ();
+      for (const auto &bound : pty->get_specified_bounds ())
+	{
+	  const auto ref = bound.get ();
+	  ref->clear_associated_type_projections ();
+	}
+    }
+}
+
 bool
 SubstitutionRef::monomorphize ()
 {
-  auto context = Resolver::TypeCheckContext::get ();
   for (const auto &subst : get_substs ())
     {
       const TyTy::ParamType *pty = subst.get_param_ty ();
@@ -887,136 +1028,16 @@  SubstitutionRef::monomorphize ()
 
       for (const auto &bound : pty->get_specified_bounds ())
 	{
-	  const Resolver::TraitReference *specified_bound_ref = bound.get ();
-
-	  // setup any associated type mappings for the specified bonds and this
-	  // type
-	  auto candidates = Resolver::TypeBoundsProbe::Probe (binding);
-	  std::vector<Resolver::AssociatedImplTrait *> associated_impl_traits;
-	  for (auto &probed_bound : candidates)
+	  bool error_flag = false;
+	  auto associated
+	    = lookup_associated_impl (subst, bound, binding, &error_flag);
+	  if (associated != nullptr)
 	    {
-	      const Resolver::TraitReference *bound_trait_ref
-		= probed_bound.first;
-	      const HIR::ImplBlock *associated_impl = probed_bound.second;
-
-	      HirId impl_block_id
-		= associated_impl->get_mappings ().get_hirid ();
-	      Resolver::AssociatedImplTrait *associated = nullptr;
-	      bool found_impl_trait
-		= context->lookup_associated_trait_impl (impl_block_id,
-							 &associated);
-	      if (found_impl_trait)
-		{
-		  bool found_trait
-		    = specified_bound_ref->is_equal (*bound_trait_ref);
-		  bool found_self
-		    = associated->get_self ()->can_eq (binding, false);
-		  if (found_trait && found_self)
-		    {
-		      associated_impl_traits.push_back (associated);
-		    }
-		}
+	      associated->setup_associated_types (binding, bound);
 	    }
 
-	  if (!associated_impl_traits.empty ())
-	    {
-	      // This code is important when you look at slices for example when
-	      // you have a slice such as:
-	      //
-	      // let slice = &array[1..3]
-	      //
-	      // the higher ranked bounds will end up having an Index trait
-	      // implementation for Range<usize> so we need this code to resolve
-	      // that we have an integer inference variable that needs to become
-	      // a usize
-	      //
-	      // The other complicated issue is that we might have an intrinsic
-	      // which requires the :Clone or Copy bound but the libcore adds
-	      // implementations for all the integral types so when there are
-	      // multiple candidates we need to resolve to the default
-	      // implementation for that type otherwise its an error for
-	      // ambiguous type bounds
-
-	      if (associated_impl_traits.size () == 1)
-		{
-		  Resolver::AssociatedImplTrait *associate_impl_trait
-		    = associated_impl_traits.at (0);
-		  associate_impl_trait->setup_associated_types (binding, bound);
-		}
-	      else
-		{
-		  // if we have a non-general inference variable we need to be
-		  // careful about the selection here
-		  bool is_infer_var
-		    = binding->get_kind () == TyTy::TypeKind::INFER;
-		  bool is_integer_infervar
-		    = is_infer_var
-		      && static_cast<const TyTy::InferType *> (binding)
-			     ->get_infer_kind ()
-			   == TyTy::InferType::InferTypeKind::INTEGRAL;
-		  bool is_float_infervar
-		    = is_infer_var
-		      && static_cast<const TyTy::InferType *> (binding)
-			     ->get_infer_kind ()
-			   == TyTy::InferType::InferTypeKind::FLOAT;
-
-		  Resolver::AssociatedImplTrait *associate_impl_trait = nullptr;
-		  if (is_integer_infervar)
-		    {
-		      TyTy::BaseType *type = nullptr;
-		      bool ok = context->lookup_builtin ("i32", &type);
-		      rust_assert (ok);
-
-		      for (auto &impl : associated_impl_traits)
-			{
-			  bool found = impl->get_self ()->is_equal (*type);
-			  if (found)
-			    {
-			      associate_impl_trait = impl;
-			      break;
-			    }
-			}
-		    }
-		  else if (is_float_infervar)
-		    {
-		      TyTy::BaseType *type = nullptr;
-		      bool ok = context->lookup_builtin ("f64", &type);
-		      rust_assert (ok);
-
-		      for (auto &impl : associated_impl_traits)
-			{
-			  bool found = impl->get_self ()->is_equal (*type);
-			  if (found)
-			    {
-			      associate_impl_trait = impl;
-			      break;
-			    }
-			}
-		    }
-
-		  if (associate_impl_trait == nullptr)
-		    {
-		      // go for the first one? or error out?
-		      auto &mappings = *Analysis::Mappings::get ();
-		      const auto &type_param = subst.get_generic_param ();
-		      const auto *trait_ref = bound.get ();
-
-		      RichLocation r (type_param.get_locus ());
-		      r.add_range (bound.get_locus ());
-		      r.add_range (
-			mappings.lookup_location (binding->get_ref ()));
-
-		      rust_error_at (
-			r, "ambiguous type bound for trait %s and type %s",
-			trait_ref->get_name ().c_str (),
-			binding->get_name ().c_str ());
-
-		      return false;
-		    }
-
-		  associate_impl_trait->setup_associated_types (binding, bound);
-		}
-	    }
+	  if (error_flag)
+	    return false;
 	}
     }
 
diff --git a/gcc/rust/typecheck/rust-tyty-subst.h b/gcc/rust/typecheck/rust-tyty-subst.h
index fd5826102b3..365fdb6c042 100644
--- a/gcc/rust/typecheck/rust-tyty-subst.h
+++ b/gcc/rust/typecheck/rust-tyty-subst.h
@@ -298,7 +298,13 @@  public:
   // TODO comment
   BaseType *infer_substitions (Location locus);
 
-  // TODO comment
+  // this clears any possible projections from higher ranked trait bounds which
+  // could be hanging around from a previous resolution
+  void prepare_higher_ranked_bounds ();
+
+  // FIXME
+  // this is bad name for this, i think it should be something like
+  // compute-higher-ranked-bounds
   bool monomorphize ();
 
   // TODO comment
@@ -308,6 +314,10 @@  public:
   SubstitutionArgumentMappings get_used_arguments () const;
 
 protected:
+  Resolver::AssociatedImplTrait *lookup_associated_impl (
+    const SubstitutionParamMapping &subst, const TypeBoundPredicate &bound,
+    const TyTy::BaseType *binding, bool *error_flag) const;
+
   std::vector<SubstitutionParamMapping> substitutions;
   SubstitutionArgumentMappings used_arguments;
 };
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index fe5aa2b059c..d0d36ac0a11 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -31,6 +31,7 @@ 
 #include "rust-hir-type-bounds.h"
 #include "rust-hir-trait-resolve.h"
 #include "rust-tyty-cmp.h"
+#include "rust-type-util.h"
 
 #include "options.h"
 
@@ -266,6 +267,7 @@  BaseType::get_locus () const
   return ident.locus;
 }
 
+// FIXME this is missing locus
 bool
 BaseType::satisfies_bound (const TypeBoundPredicate &predicate) const
 {
@@ -277,12 +279,67 @@  BaseType::satisfies_bound (const TypeBoundPredicate &predicate) const
 	return true;
     }
 
+  bool satisfied = false;
   auto probed = Resolver::TypeBoundsProbe::Probe (this);
   for (const auto &b : probed)
     {
       const Resolver::TraitReference *bound = b.first;
       if (bound->satisfies_bound (*query))
+	{
+	  satisfied = true;
+	  break;
+	}
+    }
+
+  if (!satisfied)
+    return false;
+
+  for (const auto &b : probed)
+    {
+      const Resolver::TraitReference *bound = b.first;
+      if (!bound->is_equal (*query))
+	continue;
+
+      // builtin ones have no impl-block this needs fixed and use a builtin node
+      // of somekind
+      if (b.second == nullptr)
 	return true;
+
+      // need to check that associated types can match as well
+      const HIR::ImplBlock &impl = *(b.second);
+      for (const auto &item : impl.get_impl_items ())
+	{
+	  TyTy::BaseType *impl_item_ty = nullptr;
+	  Analysis::NodeMapping i = item->get_impl_mappings ();
+	  bool query_ok = Resolver::query_type (i.get_hirid (), &impl_item_ty);
+	  if (!query_ok)
+	    return false;
+
+	  std::string item_name = item->get_impl_item_name ();
+	  TypeBoundPredicateItem lookup
+	    = predicate.lookup_associated_item (item_name);
+	  if (lookup.is_error ())
+	    return false;
+
+	  const auto *item_ref = lookup.get_raw_item ();
+	  TyTy::BaseType *bound_ty = item_ref->get_tyty ();
+
+	  // compare the types
+	  if (!bound_ty->can_eq (impl_item_ty, false))
+	    {
+	      RichLocation r (mappings->lookup_location (get_ref ()));
+	      r.add_range (predicate.get_locus ());
+	      r.add_range (mappings->lookup_location (i.get_hirid ()));
+
+	      rust_error_at (
+		r, "expected %<%s%> got %<%s%>",
+		bound_ty->destructure ()->get_name ().c_str (),
+		impl_item_ty->destructure ()->get_name ().c_str ());
+	      return false;
+	    }
+	}
+
+      return true;
     }
 
   return false;
@@ -2827,18 +2884,18 @@  ParamType::ParamType (std::string symbol, Location locus, HirId ref,
 	      {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
 	       locus},
 	      specified_bounds, refs),
-    symbol (symbol), param (param)
+    is_trait_self (false), symbol (symbol), param (param)
 {}
 
-ParamType::ParamType (std::string symbol, Location locus, HirId ref,
-		      HirId ty_ref, HIR::GenericParam &param,
+ParamType::ParamType (bool is_trait_self, std::string symbol, Location locus,
+		      HirId ref, HirId ty_ref, HIR::GenericParam &param,
 		      std::vector<TypeBoundPredicate> specified_bounds,
 		      std::set<HirId> refs)
   : BaseType (ref, ty_ref, TypeKind::PARAM,
 	      {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
 	       locus},
 	      specified_bounds, refs),
-    symbol (symbol), param (param)
+    is_trait_self (is_trait_self), symbol (symbol), param (param)
 {}
 
 HIR::GenericParam &
@@ -2906,8 +2963,9 @@  ParamType::can_eq (const BaseType *other, bool emit_errors) const
 BaseType *
 ParamType::clone () const
 {
-  return new ParamType (get_symbol (), ident.locus, get_ref (), get_ty_ref (),
-			param, get_specified_bounds (), get_combined_refs ());
+  return new ParamType (is_trait_self, get_symbol (), ident.locus, get_ref (),
+			get_ty_ref (), param, get_specified_bounds (),
+			get_combined_refs ());
 }
 
 BaseType *
@@ -2997,6 +3055,18 @@  ParamType::handle_substitions (SubstitutionArgumentMappings &subst_mappings)
   return p;
 }
 
+void
+ParamType::set_implicit_self_trait ()
+{
+  is_trait_self = true;
+}
+
+bool
+ParamType::is_implicit_self_trait () const
+{
+  return is_trait_self;
+}
+
 // StrType
 
 StrType::StrType (HirId ref, std::set<HirId> refs)
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index d2cf5b07fc1..64b9379a1c0 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -267,8 +267,8 @@  public:
 	     std::vector<TypeBoundPredicate> specified_bounds,
 	     std::set<HirId> refs = std::set<HirId> ());
 
-  ParamType (std::string symbol, Location locus, HirId ref, HirId ty_ref,
-	     HIR::GenericParam &param,
+  ParamType (bool is_trait_self, std::string symbol, Location locus, HirId ref,
+	     HirId ty_ref, HIR::GenericParam &param,
 	     std::vector<TypeBoundPredicate> specified_bounds,
 	     std::set<HirId> refs = std::set<HirId> ());
 
@@ -298,7 +298,11 @@  public:
 
   ParamType *handle_substitions (SubstitutionArgumentMappings &mappings);
 
+  void set_implicit_self_trait ();
+  bool is_implicit_self_trait () const;
+
 private:
+  bool is_trait_self;
   std::string symbol;
   HIR::GenericParam &param;
 };
diff --git a/gcc/rust/util/rust-hir-map.cc b/gcc/rust/util/rust-hir-map.cc
index 1fc32038d19..a9687040144 100644
--- a/gcc/rust/util/rust-hir-map.cc
+++ b/gcc/rust/util/rust-hir-map.cc
@@ -96,9 +96,16 @@  static const HirId kDefaultCrateNumBegin = 0;
 Mappings::Mappings ()
   : crateNumItr (kDefaultCrateNumBegin), currentCrateNum (UNKNOWN_CREATENUM),
     hirIdIter (kDefaultHirIdBegin), nodeIdIter (kDefaultNodeIdBegin)
-{}
+{
+  Analysis::NodeMapping node (0, 0, 0, 0);
+  builtinMarker
+    = new HIR::ImplBlock (node, {}, {}, nullptr, nullptr, HIR::WhereClause ({}),
+			  Positive,
+			  HIR::Visibility (HIR::Visibility::VisType::PUBLIC),
+			  {}, {}, Location ());
+}
 
-Mappings::~Mappings () {}
+Mappings::~Mappings () { delete builtinMarker; }
 
 Mappings *
 Mappings::get ()
@@ -1035,5 +1042,11 @@  Mappings::lookup_ast_item (NodeId id, AST::Item **result)
   return true;
 }
 
+HIR::ImplBlock *
+Mappings::lookup_builtin_marker ()
+{
+  return builtinMarker;
+}
+
 } // namespace Analysis
 } // namespace Rust
diff --git a/gcc/rust/util/rust-hir-map.h b/gcc/rust/util/rust-hir-map.h
index 13cae717031..9d6affa27e0 100644
--- a/gcc/rust/util/rust-hir-map.h
+++ b/gcc/rust/util/rust-hir-map.h
@@ -296,6 +296,8 @@  public:
   void insert_ast_item (AST::Item *item);
   bool lookup_ast_item (NodeId id, AST::Item **result);
 
+  HIR::ImplBlock *lookup_builtin_marker ();
+
 private:
   Mappings ();
 
@@ -304,6 +306,7 @@  private:
   HirId hirIdIter;
   NodeId nodeIdIter;
   std::map<CrateNum, LocalDefId> localIdIter;
+  HIR::ImplBlock *builtinMarker;
 
   std::map<NodeId, CrateNum> crate_node_to_crate_num;
   std::map<CrateNum, AST::Crate *> ast_crate_mappings;
diff --git a/gcc/testsuite/rust/compile/issue-1725-1.rs b/gcc/testsuite/rust/compile/issue-1725-1.rs
new file mode 100644
index 00000000000..1ace9fbbf30
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1725-1.rs
@@ -0,0 +1,19 @@ 
+mod core {
+    mod ops {
+        #[lang = "add"]
+        pub trait Add<Rhs = Self> {
+            type Output;
+
+            fn add(self, rhs: Rhs) -> Self::Output;
+        }
+    }
+}
+
+pub fn foo<T: core::ops::Add<Output = i32>>(a: T) -> i32 {
+    a + a
+}
+
+pub fn main() {
+    foo(123f32);
+    // { dg-error "bounds not satisfied for f32 .Add. is not satisfied" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1725-2.rs b/gcc/testsuite/rust/compile/issue-1725-2.rs
new file mode 100644
index 00000000000..8bfd0bbd924
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1725-2.rs
@@ -0,0 +1,28 @@ 
+mod core {
+    mod ops {
+        #[lang = "add"]
+        pub trait Add<Rhs = Self> {
+            type Output;
+
+            fn add(self, rhs: Rhs) -> Self::Output;
+        }
+    }
+}
+
+impl core::ops::Add for f32 {
+    type Output = f32;
+
+    fn add(self, rhs: Self) -> Self::Output {
+        self + rhs
+    }
+}
+
+pub fn foo<T: core::ops::Add<Output = i32>>(a: T) -> i32 {
+    a + a
+}
+
+pub fn main() {
+    foo(123f32);
+    // { dg-error "expected .i32. got .f32." "" { target *-*-* } .-1 }
+    // { dg-error "bounds not satisfied for f32 .Add. is not satisfied" "" { target *-*-* } .-2 }
+}