Skip to content

Commit c6a5a81

Browse files
philbertyCohenArthur
authored andcommitted
gccrs: Add method selection to operator overloading
When we do operator overloading we can get multiple candidates and we need to use the optional arguments to filter the candidates event further. In the bug we had: <integer> + <usize> Without the Add impl blocks for the primitive interger types we unify against the rhs to figure out that the lhs should be a usize but when we are using the impl blocks we need to use the rhs to ensure that its possible to coerce the rhs to the expected fntype parameter to filter the candidates. Fixes Rust-GCC#2304 gcc/rust/ChangeLog: * typecheck/rust-autoderef.cc: use new selection filter * typecheck/rust-hir-dot-operator.cc (MethodResolver::Select): new slection Filter * typecheck/rust-hir-dot-operator.h: New select prototype * typecheck/rust-hir-type-check-expr.cc: call select * typecheck/rust-type-util.cc (try_coercion): new helper * typecheck/rust-type-util.h (try_coercion): helper prototype gcc/testsuite/ChangeLog: * rust/compile/issue-2304.rs: New test. Signed-off-by: Philip Herron <[email protected]>
1 parent 93f0bec commit c6a5a81

File tree

7 files changed

+102
-6
lines changed

7 files changed

+102
-6
lines changed

gcc/rust/typecheck/rust-autoderef.cc

+5-3
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,20 @@ resolve_operator_overload_fn (
170170
}
171171
}
172172

173-
bool have_implementation_for_lang_item = resolved_candidates.size () > 0;
173+
auto selected_candidates
174+
= MethodResolver::Select (resolved_candidates, lhs, {});
175+
bool have_implementation_for_lang_item = selected_candidates.size () > 0;
174176
if (!have_implementation_for_lang_item)
175177
return false;
176178

177-
if (resolved_candidates.size () > 1)
179+
if (selected_candidates.size () > 1)
178180
{
179181
// no need to error out as we are just trying to see if there is a fit
180182
return false;
181183
}
182184

183185
// Get the adjusted self
184-
MethodCandidate candidate = *resolved_candidates.begin ();
186+
MethodCandidate candidate = *selected_candidates.begin ();
185187
Adjuster adj (lhs);
186188
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
187189

gcc/rust/typecheck/rust-hir-dot-operator.cc

+39
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,45 @@ MethodResolver::Probe (TyTy::BaseType *receiver,
4141
return resolver.result;
4242
}
4343

44+
std::set<MethodCandidate>
45+
MethodResolver::Select (std::set<MethodCandidate> &candidates,
46+
TyTy::BaseType *receiver,
47+
std::vector<TyTy::BaseType *> arguments)
48+
{
49+
std::set<MethodCandidate> selected;
50+
for (auto &candidate : candidates)
51+
{
52+
TyTy::BaseType *candidate_type = candidate.candidate.ty;
53+
rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
54+
TyTy::FnType &fn = *static_cast<TyTy::FnType *> (candidate_type);
55+
56+
// match the number of arguments
57+
if (fn.num_params () != (arguments.size () + 1))
58+
continue;
59+
60+
// match the arguments
61+
bool failed = false;
62+
for (size_t i = 0; i < arguments.size (); i++)
63+
{
64+
TyTy::BaseType *arg = arguments.at (i);
65+
TyTy::BaseType *param = fn.get_params ().at (i + 1).second;
66+
TyTy::BaseType *coerced
67+
= try_coercion (0, TyTy::TyWithLocation (param),
68+
TyTy::TyWithLocation (arg), Location ());
69+
if (coerced->get_kind () == TyTy::TypeKind::ERROR)
70+
{
71+
failed = true;
72+
break;
73+
}
74+
}
75+
76+
if (!failed)
77+
selected.insert (candidate);
78+
}
79+
80+
return selected;
81+
}
82+
4483
void
4584
MethodResolver::try_hook (const TyTy::BaseType &r)
4685
{

gcc/rust/typecheck/rust-hir-dot-operator.h

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ class MethodResolver : private TypeCheckBase, protected AutoderefCycle
5757
Probe (TyTy::BaseType *receiver, const HIR::PathIdentSegment &segment_name,
5858
bool autoderef_flag = false);
5959

60+
static std::set<MethodCandidate>
61+
Select (std::set<MethodCandidate> &candidates, TyTy::BaseType *receiver,
62+
std::vector<TyTy::BaseType *> arguments);
63+
6064
static std::vector<predicate_candidate> get_predicate_items (
6165
const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
6266
const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);

gcc/rust/typecheck/rust-hir-type-check-expr.cc

+9-3
Original file line numberDiff line numberDiff line change
@@ -1619,11 +1619,17 @@ TypeCheckExpr::resolve_operator_overload (
16191619
}
16201620
}
16211621

1622-
bool have_implementation_for_lang_item = resolved_candidates.size () > 0;
1622+
std::vector<TyTy::BaseType *> select_args = {};
1623+
if (rhs != nullptr)
1624+
select_args = {rhs};
1625+
auto selected_candidates
1626+
= MethodResolver::Select (resolved_candidates, lhs, select_args);
1627+
1628+
bool have_implementation_for_lang_item = selected_candidates.size () > 0;
16231629
if (!have_implementation_for_lang_item)
16241630
return false;
16251631

1626-
if (resolved_candidates.size () > 1)
1632+
if (selected_candidates.size () > 1)
16271633
{
16281634
// mutliple candidates
16291635
RichLocation r (expr.get_locus ());
@@ -1637,7 +1643,7 @@ TypeCheckExpr::resolve_operator_overload (
16371643
}
16381644

16391645
// Get the adjusted self
1640-
MethodCandidate candidate = *resolved_candidates.begin ();
1646+
MethodCandidate candidate = *selected_candidates.begin ();
16411647
Adjuster adj (lhs);
16421648
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
16431649

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

+18
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,24 @@ coercion_site (HirId id, TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
231231
return coerced;
232232
}
233233

234+
TyTy::BaseType *
235+
try_coercion (HirId id, TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
236+
Location locus)
237+
{
238+
TyTy::BaseType *expected = lhs.get_ty ();
239+
TyTy::BaseType *expr = rhs.get_ty ();
240+
241+
rust_debug ("try_coercion_site id={%u} expected={%s} expr={%s}", id,
242+
expected->debug_str ().c_str (), expr->debug_str ().c_str ());
243+
244+
auto result = TypeCoercionRules::TryCoerce (expr, expected, locus,
245+
true /*allow-autodref*/);
246+
if (result.is_error ())
247+
return new TyTy::ErrorType (id);
248+
249+
return result.tyty;
250+
}
251+
234252
TyTy::BaseType *
235253
cast_site (HirId id, TyTy::TyWithLocation from, TyTy::TyWithLocation to,
236254
Location cast_locus)

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

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ TyTy::BaseType *
4545
coercion_site (HirId id, TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
4646
Location coercion_locus);
4747

48+
TyTy::BaseType *
49+
try_coercion (HirId id, TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
50+
Location coercion_locus);
51+
4852
TyTy::BaseType *
4953
cast_site (HirId id, TyTy::TyWithLocation from, TyTy::TyWithLocation to,
5054
Location cast_locus);
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[lang = "add"]
2+
pub trait Add<RHS = Self> {
3+
type Output;
4+
5+
fn add(self, rhs: RHS) -> Self::Output;
6+
}
7+
macro_rules! add_impl {
8+
($($t:ty)*) => ($(
9+
impl Add for $t {
10+
type Output = $t;
11+
12+
fn add(self, other: $t) -> $t { self + other }
13+
}
14+
)*)
15+
}
16+
17+
add_impl! { usize u8 u16 u32 u64 /*isize i8 i16 i32 i64*/ f32 f64 }
18+
19+
pub fn test() {
20+
let x: usize = 123;
21+
let mut i = 0;
22+
let _bug = i + x;
23+
}

0 commit comments

Comments
 (0)