Skip to content

Commit fde042e

Browse files
committed
gccrs: Add missing bounds checks on the associated impl
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 #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 <herron.philip@googlemail.com>
1 parent 79c81d0 commit fde042e

5 files changed

Lines changed: 164 additions & 3 deletions

File tree

gcc/rust/typecheck/rust-hir-trait-reference.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ class AssociatedImplTrait
253253

254254
TyTy::SubstitutionArgumentMappings
255255
bind_impl_for_bound (TyTy::BaseType *receiver,
256-
const TyTy::TypeBoundPredicate &bound, location_t locus);
256+
const TyTy::TypeBoundPredicate &bound, location_t locus,
257+
bool emit_error = false);
257258

258259
private:
259260
TraitReference *trait;

gcc/rust/typecheck/rust-hir-trait-resolve.cc

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,100 @@
1717
// <http://www.gnu.org/licenses/>.
1818

1919
#include "rust-hir-trait-resolve.h"
20+
#include "rich-location.h"
2021
#include "rust-hir-trait-reference.h"
2122
#include "rust-hir-type-check-expr.h"
2223
#include "rust-rib.h"
2324
#include "rust-substitution-mapper.h"
25+
#include "text-range-label.h"
2426
#include "rust-type-util.h"
2527
#include "rust-finalized-name-resolution-context.h"
2628

2729
namespace Rust {
2830
namespace Resolver {
2931

32+
static bool
33+
validate_impl_substitution_bounds (
34+
const std::vector<TyTy::SubstitutionArg> &resolved_args, location_t locus,
35+
bool emit_error)
36+
{
37+
auto &mctx = Analysis::Mappings::get ();
38+
39+
std::vector<TyTy::SubstitutionArg> args;
40+
for (const auto &arg : resolved_args)
41+
args.push_back (arg);
42+
43+
TyTy::SubstitutionArgumentMappings mappings (std::move (args),
44+
{} /*binding_args*/,
45+
TyTy::RegionParamList (0),
46+
locus);
47+
48+
for (const auto &arg : resolved_args)
49+
{
50+
TyTy::BaseGeneric *param
51+
= const_cast<TyTy::BaseGeneric *> (arg.get_param_ty ());
52+
if (param == nullptr)
53+
continue;
54+
55+
TyTy::BaseType *resolved_arg = arg.get_tyty ();
56+
if (resolved_arg->get_kind () == TyTy::TypeKind::PARAM)
57+
resolved_arg
58+
= static_cast<TyTy::ParamType *> (resolved_arg)->resolve ();
59+
60+
if (resolved_arg->get_kind () == TyTy::TypeKind::PARAM
61+
|| resolved_arg->get_kind () == TyTy::TypeKind::INFER)
62+
continue;
63+
64+
auto arg_type_locus
65+
= mctx.lookup_location (arg.get_tyty ()->get_ty_ref ());
66+
for (auto bound : param->get_specified_bounds ())
67+
{
68+
auto bound_locus = bound.get_locus ();
69+
auto trait_locus = bound.get ()->get_locus ();
70+
bound.apply_argument_mappings (mappings, false /*is_super_trait*/);
71+
72+
if (!resolved_arg->satisfies_bound (bound, false /*emit_error*/))
73+
{
74+
if (emit_error)
75+
{
76+
rich_location r (line_table, locus);
77+
78+
std::string arg_label_text = "the trait " + bound.get_name ()
79+
+ " is not implemented for "
80+
+ resolved_arg->get_name ();
81+
82+
text_range_label arg_label (arg_label_text.c_str ());
83+
r.add_range (arg_type_locus, SHOW_RANGE_WITHOUT_CARET,
84+
&arg_label);
85+
86+
bool ambiguous = false;
87+
auto *trait_impl
88+
= lookup_associated_impl_block (bound, resolved_arg,
89+
&ambiguous);
90+
text_range_label trait_label (
91+
"this trait has no implementations, consider adding one");
92+
if (trait_impl == nullptr)
93+
r.add_range (trait_locus, SHOW_RANGE_WITHOUT_CARET,
94+
&trait_label);
95+
96+
text_range_label bound_label (
97+
"unsatisfied trait bound introduced here");
98+
r.add_range (bound_locus, SHOW_RANGE_WITHOUT_CARET,
99+
&bound_label);
100+
101+
rust_error_at (r, ErrorCode::E0277,
102+
"the trait bound %<%s: %s%> is not satisfied",
103+
resolved_arg->get_name ().c_str (),
104+
bound.get_name ().c_str ());
105+
}
106+
return false;
107+
}
108+
}
109+
}
110+
111+
return true;
112+
}
113+
30114
TraitItemReference
31115
ResolveTraitItemToRef::Resolve (
32116
HIR::TraitItem &item, TyTy::BaseType *self,
@@ -598,6 +682,10 @@ AssociatedImplTrait::bind_impl_for_projection (TyTy::ProjectionType &proj,
598682
resolved_args.emplace_back (&p, r);
599683
}
600684

685+
if (!validate_impl_substitution_bounds (resolved_args, locus,
686+
false /*emit_error*/))
687+
return TyTy::SubstitutionArgumentMappings::error ();
688+
601689
return TyTy::SubstitutionArgumentMappings (std::move (resolved_args),
602690
{} /*binding_args*/,
603691
TyTy::RegionParamList (0)
@@ -608,7 +696,7 @@ AssociatedImplTrait::bind_impl_for_projection (TyTy::ProjectionType &proj,
608696
TyTy::SubstitutionArgumentMappings
609697
AssociatedImplTrait::bind_impl_for_bound (TyTy::BaseType *receiver,
610698
const TyTy::TypeBoundPredicate &bound,
611-
location_t locus)
699+
location_t locus, bool emit_error)
612700
{
613701
// Same shape as bind_impl_for_projection but the receiver/trait-args are
614702
// taken from the (binding, bound) pair instead of a ProjectionType.
@@ -711,6 +799,9 @@ AssociatedImplTrait::bind_impl_for_bound (TyTy::BaseType *receiver,
711799
resolved_args.emplace_back (&p, r);
712800
}
713801

802+
if (!validate_impl_substitution_bounds (resolved_args, locus, emit_error))
803+
return TyTy::SubstitutionArgumentMappings::error ();
804+
714805
return TyTy::SubstitutionArgumentMappings (std::move (resolved_args),
715806
{} /*binding_args*/,
716807
TyTy::RegionParamList (0)

gcc/rust/typecheck/rust-type-util.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ lookup_associated_impl_block (const TyTy::TypeBoundPredicate &bound,
395395
if (found_impl_trait)
396396
{
397397
// compare the bounds from here i think is what we can do:
398-
if (bound.is_equal (associated->get_predicate ()))
398+
if (bound.get ()->is_equal (*associated->get_predicate ().get ()))
399399
{
400400
associated_impl_traits.push_back (associated);
401401
}

gcc/rust/typecheck/rust-tyty-call.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "rust-hir-type-check-expr.h"
2121
#include "rust-hir-type-check.h"
2222
#include "rust-type-util.h"
23+
#include "rust-hir-trait-reference.h"
2324

2425
namespace Rust {
2526
namespace TyTy {
@@ -53,6 +54,43 @@ emit_unexpected_argument_error (location_t loc,
5354
unexpected_arg_count);
5455
}
5556

57+
static bool
58+
validate_call_argument_associated_impl_bounds (BaseType *param_ty,
59+
BaseType *argument_ty,
60+
location_t locus)
61+
{
62+
auto *context = Resolver::TypeCheckContext::get ();
63+
64+
// impl bodies are checked generically
65+
if (context->have_function_context ()
66+
&& context->peek_context ().get_type ()
67+
== Resolver::TypeCheckContextItem::IMPL_ITEM)
68+
return true;
69+
70+
auto *resolved_argument_ty = argument_ty->destructure ();
71+
if (resolved_argument_ty->get_kind () == TypeKind::PARAM
72+
|| resolved_argument_ty->get_kind () == TypeKind::INFER
73+
|| resolved_argument_ty->get_kind () == TypeKind::PROJECTION)
74+
return true;
75+
76+
for (auto bound : param_ty->get_specified_bounds ())
77+
{
78+
bool ambigious = false;
79+
auto associated
80+
= Resolver::lookup_associated_impl_block (bound, argument_ty,
81+
&ambigious);
82+
if (associated == nullptr)
83+
continue;
84+
85+
auto mapping = associated->bind_impl_for_bound (argument_ty, bound, locus,
86+
true /*emit_error*/);
87+
if (mapping.is_error ())
88+
return false;
89+
}
90+
91+
return true;
92+
}
93+
5694
void
5795
TypeCheckCallExpr::visit (ADTType &type)
5896
{
@@ -192,6 +230,10 @@ TypeCheckCallExpr::visit (FnType &type)
192230
{
193231
return;
194232
}
233+
234+
if (!validate_call_argument_associated_impl_bounds (
235+
param_ty, argument_expr_tyty, argument->get_locus ()))
236+
return;
195237
}
196238
else
197239
{
@@ -420,6 +462,10 @@ TypeCheckMethodCallExpr::check (FnType &type)
420462
return new ErrorType (type.get_ref ());
421463
}
422464

465+
if (!validate_call_argument_associated_impl_bounds (
466+
param_ty, argument_expr_tyty, argument.get_locus ()))
467+
return new ErrorType (type.get_ref ());
468+
423469
i++;
424470
}
425471

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![feature(no_core)]
2+
#![no_core]
3+
#![feature(lang_items)]
4+
5+
#[lang = "sized"]
6+
trait Sized {}
7+
8+
trait A {}
9+
trait B {}
10+
11+
struct S<T>(T);
12+
13+
impl<T: A> B for S<T> {}
14+
15+
struct Bad;
16+
17+
fn require_b<T: B>(_: T) {}
18+
19+
fn main() {
20+
let b = S(Bad);
21+
require_b(b);
22+
// { dg-error "the trait bound .Bad: A. is not satisfied .E0277." "" { target *-*-* } .-1 }
23+
}

0 commit comments

Comments
 (0)