From: Philip Herron <[email protected]>

Before this require_b<T: B> (T) only checked for S<Bad> implement B and
it early accepted the

  impl<T: A> B for S<T>

But we need to validate the impl's bound after we bind the arguments being
used on the generic impl. So when we do a call-expr/method-call expr we are
binding to that impl and we need to validate. When we do that we know that
impl T = Bad and we walk the impl generic params T: A apply the subst for
Bad: A where its is not implemented and we fail and return.

Fixes Rust-GCC/gccrs#4678

gcc/rust/ChangeLog:

        * typecheck/rust-hir-trait-reference.h: add emit_errors option
        * typecheck/rust-hir-trait-resolve.cc 
(AssociatedImplTrait::bind_impl_for_projection): new
        (AssociatedImplTrait::bind_impl_for_bound): call new validate function
        * typecheck/rust-type-util.cc (lookup_associated_impl_block): make this 
more generic
        * typecheck/rust-tyty-call.cc 
(validate_call_argument_associated_impl_bounds): new
        (TypeCheckCallExpr::visit): for each call arg validate and bind
        (TypeCheckMethodCallExpr::check): likewise

gcc/testsuite/ChangeLog:

        * rust/compile/issue-4678.rs: New test.

Signed-off-by: Philip Herron <[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/fde042ef7c7241b40e12b3dabaea90d4298c6fa5

The commit has been mentioned in the following issue(s):
 - Rust-GCC/gccrs#4678: https://github.com/Rust-GCC/gccrs/issues/4678

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4787

 gcc/rust/typecheck/rust-hir-trait-reference.h |  3 +-
 gcc/rust/typecheck/rust-hir-trait-resolve.cc  | 93 ++++++++++++++++++-
 gcc/rust/typecheck/rust-type-util.cc          |  2 +-
 gcc/rust/typecheck/rust-tyty-call.cc          | 46 +++++++++
 gcc/testsuite/rust/compile/issue-4678.rs      | 23 +++++
 5 files changed, 164 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issue-4678.rs

diff --git a/gcc/rust/typecheck/rust-hir-trait-reference.h 
b/gcc/rust/typecheck/rust-hir-trait-reference.h
index a8f923826..b26ba0ad5 100644
--- a/gcc/rust/typecheck/rust-hir-trait-reference.h
+++ b/gcc/rust/typecheck/rust-hir-trait-reference.h
@@ -253,7 +253,8 @@ public:
 
   TyTy::SubstitutionArgumentMappings
   bind_impl_for_bound (TyTy::BaseType *receiver,
-                      const TyTy::TypeBoundPredicate &bound, location_t locus);
+                      const TyTy::TypeBoundPredicate &bound, location_t locus,
+                      bool emit_error = false);
 
 private:
   TraitReference *trait;
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc 
b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
index b036c634f..3ed46e0f0 100644
--- a/gcc/rust/typecheck/rust-hir-trait-resolve.cc
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
@@ -17,16 +17,100 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-hir-trait-resolve.h"
+#include "rich-location.h"
 #include "rust-hir-trait-reference.h"
 #include "rust-hir-type-check-expr.h"
 #include "rust-rib.h"
 #include "rust-substitution-mapper.h"
+#include "text-range-label.h"
 #include "rust-type-util.h"
 #include "rust-finalized-name-resolution-context.h"
 
 namespace Rust {
 namespace Resolver {
 
+static bool
+validate_impl_substitution_bounds (
+  const std::vector<TyTy::SubstitutionArg> &resolved_args, location_t locus,
+  bool emit_error)
+{
+  auto &mctx = Analysis::Mappings::get ();
+
+  std::vector<TyTy::SubstitutionArg> args;
+  for (const auto &arg : resolved_args)
+    args.push_back (arg);
+
+  TyTy::SubstitutionArgumentMappings mappings (std::move (args),
+                                              {} /*binding_args*/,
+                                              TyTy::RegionParamList (0),
+                                              locus);
+
+  for (const auto &arg : resolved_args)
+    {
+      TyTy::BaseGeneric *param
+       = const_cast<TyTy::BaseGeneric *> (arg.get_param_ty ());
+      if (param == nullptr)
+       continue;
+
+      TyTy::BaseType *resolved_arg = arg.get_tyty ();
+      if (resolved_arg->get_kind () == TyTy::TypeKind::PARAM)
+       resolved_arg
+         = static_cast<TyTy::ParamType *> (resolved_arg)->resolve ();
+
+      if (resolved_arg->get_kind () == TyTy::TypeKind::PARAM
+         || resolved_arg->get_kind () == TyTy::TypeKind::INFER)
+       continue;
+
+      auto arg_type_locus
+       = mctx.lookup_location (arg.get_tyty ()->get_ty_ref ());
+      for (auto bound : param->get_specified_bounds ())
+       {
+         auto bound_locus = bound.get_locus ();
+         auto trait_locus = bound.get ()->get_locus ();
+         bound.apply_argument_mappings (mappings, false /*is_super_trait*/);
+
+         if (!resolved_arg->satisfies_bound (bound, false /*emit_error*/))
+           {
+             if (emit_error)
+               {
+                 rich_location r (line_table, locus);
+
+                 std::string arg_label_text = "the trait " + bound.get_name ()
+                                              + " is not implemented for "
+                                              + resolved_arg->get_name ();
+
+                 text_range_label arg_label (arg_label_text.c_str ());
+                 r.add_range (arg_type_locus, SHOW_RANGE_WITHOUT_CARET,
+                              &arg_label);
+
+                 bool ambiguous = false;
+                 auto *trait_impl
+                   = lookup_associated_impl_block (bound, resolved_arg,
+                                                   &ambiguous);
+                 text_range_label trait_label (
+                   "this trait has no implementations, consider adding one");
+                 if (trait_impl == nullptr)
+                   r.add_range (trait_locus, SHOW_RANGE_WITHOUT_CARET,
+                                &trait_label);
+
+                 text_range_label bound_label (
+                   "unsatisfied trait bound introduced here");
+                 r.add_range (bound_locus, SHOW_RANGE_WITHOUT_CARET,
+                              &bound_label);
+
+                 rust_error_at (r, ErrorCode::E0277,
+                                "the trait bound %<%s: %s%> is not satisfied",
+                                resolved_arg->get_name ().c_str (),
+                                bound.get_name ().c_str ());
+               }
+             return false;
+           }
+       }
+    }
+
+  return true;
+}
+
 TraitItemReference
 ResolveTraitItemToRef::Resolve (
   HIR::TraitItem &item, TyTy::BaseType *self,
@@ -598,6 +682,10 @@ AssociatedImplTrait::bind_impl_for_projection 
(TyTy::ProjectionType &proj,
       resolved_args.emplace_back (&p, r);
     }
 
+  if (!validate_impl_substitution_bounds (resolved_args, locus,
+                                         false /*emit_error*/))
+    return TyTy::SubstitutionArgumentMappings::error ();
+
   return TyTy::SubstitutionArgumentMappings (std::move (resolved_args),
                                             {} /*binding_args*/,
                                             TyTy::RegionParamList (0)
@@ -608,7 +696,7 @@ AssociatedImplTrait::bind_impl_for_projection 
(TyTy::ProjectionType &proj,
 TyTy::SubstitutionArgumentMappings
 AssociatedImplTrait::bind_impl_for_bound (TyTy::BaseType *receiver,
                                          const TyTy::TypeBoundPredicate &bound,
-                                         location_t locus)
+                                         location_t locus, bool emit_error)
 {
   // Same shape as bind_impl_for_projection but the receiver/trait-args are
   // taken from the (binding, bound) pair instead of a ProjectionType.
@@ -711,6 +799,9 @@ AssociatedImplTrait::bind_impl_for_bound (TyTy::BaseType 
*receiver,
       resolved_args.emplace_back (&p, r);
     }
 
+  if (!validate_impl_substitution_bounds (resolved_args, locus, emit_error))
+    return TyTy::SubstitutionArgumentMappings::error ();
+
   return TyTy::SubstitutionArgumentMappings (std::move (resolved_args),
                                             {} /*binding_args*/,
                                             TyTy::RegionParamList (0)
diff --git a/gcc/rust/typecheck/rust-type-util.cc 
b/gcc/rust/typecheck/rust-type-util.cc
index 5fcea2c8b..5f43bbdbe 100644
--- a/gcc/rust/typecheck/rust-type-util.cc
+++ b/gcc/rust/typecheck/rust-type-util.cc
@@ -395,7 +395,7 @@ lookup_associated_impl_block (const 
TyTy::TypeBoundPredicate &bound,
       if (found_impl_trait)
        {
          // compare the bounds from here i think is what we can do:
-         if (bound.is_equal (associated->get_predicate ()))
+         if (bound.get ()->is_equal (*associated->get_predicate ().get ()))
            {
              associated_impl_traits.push_back (associated);
            }
diff --git a/gcc/rust/typecheck/rust-tyty-call.cc 
b/gcc/rust/typecheck/rust-tyty-call.cc
index 5a2b105e2..7ec23281a 100644
--- a/gcc/rust/typecheck/rust-tyty-call.cc
+++ b/gcc/rust/typecheck/rust-tyty-call.cc
@@ -20,6 +20,7 @@
 #include "rust-hir-type-check-expr.h"
 #include "rust-hir-type-check.h"
 #include "rust-type-util.h"
+#include "rust-hir-trait-reference.h"
 
 namespace Rust {
 namespace TyTy {
@@ -53,6 +54,43 @@ emit_unexpected_argument_error (location_t loc,
                 unexpected_arg_count);
 }
 
+static bool
+validate_call_argument_associated_impl_bounds (BaseType *param_ty,
+                                              BaseType *argument_ty,
+                                              location_t locus)
+{
+  auto *context = Resolver::TypeCheckContext::get ();
+
+  // impl bodies are checked generically
+  if (context->have_function_context ()
+      && context->peek_context ().get_type ()
+          == Resolver::TypeCheckContextItem::IMPL_ITEM)
+    return true;
+
+  auto *resolved_argument_ty = argument_ty->destructure ();
+  if (resolved_argument_ty->get_kind () == TypeKind::PARAM
+      || resolved_argument_ty->get_kind () == TypeKind::INFER
+      || resolved_argument_ty->get_kind () == TypeKind::PROJECTION)
+    return true;
+
+  for (auto bound : param_ty->get_specified_bounds ())
+    {
+      bool ambigious = false;
+      auto associated
+       = Resolver::lookup_associated_impl_block (bound, argument_ty,
+                                                 &ambigious);
+      if (associated == nullptr)
+       continue;
+
+      auto mapping = associated->bind_impl_for_bound (argument_ty, bound, 
locus,
+                                                     true /*emit_error*/);
+      if (mapping.is_error ())
+       return false;
+    }
+
+  return true;
+}
+
 void
 TypeCheckCallExpr::visit (ADTType &type)
 {
@@ -192,6 +230,10 @@ TypeCheckCallExpr::visit (FnType &type)
            {
              return;
            }
+
+         if (!validate_call_argument_associated_impl_bounds (
+               param_ty, argument_expr_tyty, argument->get_locus ()))
+           return;
        }
       else
        {
@@ -420,6 +462,10 @@ TypeCheckMethodCallExpr::check (FnType &type)
          return new ErrorType (type.get_ref ());
        }
 
+      if (!validate_call_argument_associated_impl_bounds (
+           param_ty, argument_expr_tyty, argument.get_locus ()))
+       return new ErrorType (type.get_ref ());
+
       i++;
     }
 
diff --git a/gcc/testsuite/rust/compile/issue-4678.rs 
b/gcc/testsuite/rust/compile/issue-4678.rs
new file mode 100644
index 000000000..a088edb80
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-4678.rs
@@ -0,0 +1,23 @@
+#![feature(no_core)]
+#![no_core]
+#![feature(lang_items)]
+
+#[lang = "sized"]
+trait Sized {}
+
+trait A {}
+trait B {}
+
+struct S<T>(T);
+
+impl<T: A> B for S<T> {}
+
+struct Bad;
+
+fn require_b<T: B>(_: T) {}
+
+fn main() {
+    let b = S(Bad);
+    require_b(b);
+    // { dg-error "the trait bound .Bad: A. is not satisfied .E0277." "" { 
target *-*-* } .-1 }
+}

base-commit: 79c81d0527fe0527084fc831c5ecebe5a39d30c3
-- 
2.54.0

Reply via email to