Skip to content

Commit c47d5cb

Browse files
committed
Initial support operator overloading on [lang = "add"]
This change incorporates a few changes. 1. Create new gcc/rust/backend/rust-compile-expr.cc to split out implementation code 2. Create new type check context api calls: - TypeCheckContext::lookup_operator_overload - TypeCheckContext::insert_operator_overload 3. Update type checking for ArithmeticOrLogicalExpr to look for any operator overloading When we are looking for operator overloads we must look up the associated lang item type for this paticular operation, to resolve the operation to any known lang_items by looking up the specified lang_item to DefId. Then we must probe for the lang_item candidate for this paticular lang_item DefID to see if we can resolve it to a method call. Then based on the autoderef rules in a MethodCallExpr we must verify that we don't end up in a recursive operator overload by checking that the current context is not the same as the actual operator overload for this type. Finally we mark this expression as operator overload and setup everything as a resolved MethodCallExpr. Fixes #249
1 parent 89e02f5 commit c47d5cb

9 files changed

+696
-229
lines changed

gcc/rust/Make-lang.in

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ GRS_OBJS = \
9090
rust/rust-hir-type-check-path.o \
9191
rust/rust-compile-intrinsic.o \
9292
rust/rust-base62.o \
93+
rust/rust-compile-expr.o \
9394
$(END)
9495
# removed object files from here
9596

gcc/rust/backend/rust-compile-expr.cc

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
// Copyright (C) 2020-2021 Free Software Foundation, Inc.
2+
3+
// This file is part of GCC.
4+
5+
// GCC is free software; you can redistribute it and/or modify it under
6+
// the terms of the GNU General Public License as published by the Free
7+
// Software Foundation; either version 3, or (at your option) any later
8+
// version.
9+
10+
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11+
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
// for more details.
14+
15+
// You should have received a copy of the GNU General Public License
16+
// along with GCC; see the file COPYING3. If not see
17+
// <http://www.gnu.org/licenses/>.
18+
19+
#include "rust-compile.h"
20+
#include "rust-compile-item.h"
21+
#include "rust-compile-expr.h"
22+
#include "rust-compile-struct-field-expr.h"
23+
#include "rust-hir-trait-resolve.h"
24+
#include "rust-hir-path-probe.h"
25+
#include "rust-hir-type-bounds.h"
26+
#include "rust-hir-dot-operator.h"
27+
28+
namespace Rust {
29+
namespace Compile {
30+
31+
void
32+
CompileExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
33+
{
34+
auto op = expr.get_expr_type ();
35+
auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
36+
auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
37+
38+
// this might be an operator overload situation lets check
39+
TyTy::FnType *fntype;
40+
bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
41+
expr.get_mappings ().get_hirid (), &fntype);
42+
if (!is_op_overload)
43+
{
44+
translated = ctx->get_backend ()->arithmetic_or_logical_expression (
45+
op, lhs, rhs, expr.get_locus ());
46+
return;
47+
}
48+
49+
// lookup the resolved name
50+
NodeId resolved_node_id = UNKNOWN_NODEID;
51+
if (!ctx->get_resolver ()->lookup_resolved_name (
52+
expr.get_mappings ().get_nodeid (), &resolved_node_id))
53+
{
54+
rust_error_at (expr.get_locus (), "failed to lookup resolved MethodCall");
55+
return;
56+
}
57+
58+
// reverse lookup
59+
HirId ref;
60+
if (!ctx->get_mappings ()->lookup_node_to_hir (
61+
expr.get_mappings ().get_crate_num (), resolved_node_id, &ref))
62+
{
63+
rust_fatal_error (expr.get_locus (), "reverse lookup failure");
64+
return;
65+
}
66+
67+
TyTy::BaseType *receiver = nullptr;
68+
bool ok
69+
= ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
70+
&receiver);
71+
rust_assert (ok);
72+
73+
bool is_dyn_dispatch
74+
= receiver->get_root ()->get_kind () == TyTy::TypeKind::DYNAMIC;
75+
bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
76+
if (is_generic_receiver)
77+
{
78+
TyTy::ParamType *p = static_cast<TyTy::ParamType *> (receiver);
79+
receiver = p->resolve ();
80+
}
81+
82+
if (is_dyn_dispatch)
83+
{
84+
const TyTy::DynamicObjectType *dyn
85+
= static_cast<const TyTy::DynamicObjectType *> (receiver->get_root ());
86+
87+
std::vector<HIR::Expr *> arguments;
88+
arguments.push_back (expr.get_rhs ());
89+
90+
translated = compile_dyn_dispatch_call (dyn, receiver, fntype, lhs,
91+
arguments, expr.get_locus ());
92+
return;
93+
}
94+
95+
// lookup compiled functions since it may have already been compiled
96+
HIR::PathIdentSegment segment_name ("add");
97+
Bexpression *fn_expr
98+
= resolve_method_address (fntype, ref, receiver, segment_name,
99+
expr.get_mappings (), expr.get_locus ());
100+
101+
// lookup the autoderef mappings
102+
std::vector<Resolver::Adjustment> *adjustments = nullptr;
103+
ok = ctx->get_tyctx ()->lookup_autoderef_mappings (
104+
expr.get_mappings ().get_hirid (), &adjustments);
105+
rust_assert (ok);
106+
107+
Bexpression *self = lhs;
108+
for (auto &adjustment : *adjustments)
109+
{
110+
switch (adjustment.get_type ())
111+
{
112+
case Resolver::Adjustment::AdjustmentType::IMM_REF:
113+
case Resolver::Adjustment::AdjustmentType::MUT_REF:
114+
self = ctx->get_backend ()->address_expression (
115+
self, expr.get_lhs ()->get_locus ());
116+
break;
117+
118+
case Resolver::Adjustment::AdjustmentType::DEREF_REF:
119+
Btype *expected_type
120+
= TyTyResolveCompile::compile (ctx, adjustment.get_expected ());
121+
self = ctx->get_backend ()->indirect_expression (
122+
expected_type, self, true, /* known_valid*/
123+
expr.get_lhs ()->get_locus ());
124+
break;
125+
}
126+
}
127+
128+
std::vector<Bexpression *> args;
129+
args.push_back (self); // adjusted self
130+
args.push_back (rhs);
131+
132+
auto fncontext = ctx->peek_fn ();
133+
translated
134+
= ctx->get_backend ()->call_expression (fncontext.fndecl, fn_expr, args,
135+
nullptr, expr.get_locus ());
136+
}
137+
138+
Bexpression *
139+
CompileExpr::compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
140+
TyTy::BaseType *receiver,
141+
TyTy::FnType *fntype,
142+
Bexpression *receiver_ref,
143+
std::vector<HIR::Expr *> &arguments,
144+
Location expr_locus)
145+
{
146+
size_t offs = 0;
147+
const Resolver::TraitItemReference *ref = nullptr;
148+
for (auto &bound : dyn->get_object_items ())
149+
{
150+
const Resolver::TraitItemReference *item = bound.first;
151+
auto t = item->get_tyty ();
152+
rust_assert (t->get_kind () == TyTy::TypeKind::FNDEF);
153+
auto ft = static_cast<TyTy::FnType *> (t);
154+
155+
if (ft->get_id () == fntype->get_id ())
156+
{
157+
ref = item;
158+
break;
159+
}
160+
offs++;
161+
}
162+
163+
if (ref == nullptr)
164+
return ctx->get_backend ()->error_expression ();
165+
166+
// get any indirection sorted out
167+
if (receiver->get_kind () == TyTy::TypeKind::REF)
168+
{
169+
TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
170+
auto indirect_ty = r->get_base ();
171+
Btype *indrect_compiled_tyty
172+
= TyTyResolveCompile::compile (ctx, indirect_ty);
173+
174+
Bexpression *indirect
175+
= ctx->get_backend ()->indirect_expression (indrect_compiled_tyty,
176+
receiver_ref, true,
177+
expr_locus);
178+
receiver_ref = indirect;
179+
}
180+
181+
// access the offs + 1 for the fnptr and offs=0 for the reciever obj
182+
Bexpression *self_argument
183+
= ctx->get_backend ()->struct_field_expression (receiver_ref, 0,
184+
expr_locus);
185+
186+
// access the vtable for the fn
187+
Bexpression *fn_vtable_access
188+
= ctx->get_backend ()->struct_field_expression (receiver_ref, offs + 1,
189+
expr_locus);
190+
191+
// cast it to the correct fntype
192+
Btype *expected_fntype = TyTyResolveCompile::compile (ctx, fntype, true);
193+
Bexpression *fn_convert_expr
194+
= ctx->get_backend ()->convert_expression (expected_fntype,
195+
fn_vtable_access, expr_locus);
196+
197+
fncontext fnctx = ctx->peek_fn ();
198+
Bblock *enclosing_scope = ctx->peek_enclosing_scope ();
199+
bool is_address_taken = false;
200+
Bstatement *ret_var_stmt = nullptr;
201+
Bvariable *fn_convert_expr_tmp
202+
= ctx->get_backend ()->temporary_variable (fnctx.fndecl, enclosing_scope,
203+
expected_fntype, fn_convert_expr,
204+
is_address_taken, expr_locus,
205+
&ret_var_stmt);
206+
ctx->add_statement (ret_var_stmt);
207+
208+
std::vector<Bexpression *> args;
209+
args.push_back (self_argument);
210+
for (auto &argument : arguments)
211+
{
212+
Bexpression *compiled_expr = CompileExpr::Compile (argument, ctx);
213+
args.push_back (compiled_expr);
214+
}
215+
216+
Bexpression *fn_expr
217+
= ctx->get_backend ()->var_expression (fn_convert_expr_tmp, expr_locus);
218+
219+
return ctx->get_backend ()->call_expression (fnctx.fndecl, fn_expr, args,
220+
nullptr, expr_locus);
221+
}
222+
223+
Bexpression *
224+
CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,
225+
TyTy::BaseType *receiver,
226+
HIR::PathIdentSegment &segment,
227+
Analysis::NodeMapping expr_mappings,
228+
Location expr_locus)
229+
{
230+
// lookup compiled functions since it may have already been compiled
231+
Bfunction *fn = nullptr;
232+
if (ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
233+
{
234+
return ctx->get_backend ()->function_code_expression (fn, expr_locus);
235+
}
236+
237+
// Now we can try and resolve the address since this might be a forward
238+
// declared function, generic function which has not be compiled yet or
239+
// its an not yet trait bound function
240+
HIR::ImplItem *resolved_item
241+
= ctx->get_mappings ()->lookup_hir_implitem (expr_mappings.get_crate_num (),
242+
ref, nullptr);
243+
if (resolved_item != nullptr)
244+
{
245+
if (!fntype->has_subsititions_defined ())
246+
return CompileInherentImplItem::Compile (receiver, resolved_item, ctx,
247+
true);
248+
249+
return CompileInherentImplItem::Compile (receiver, resolved_item, ctx,
250+
true, fntype);
251+
}
252+
253+
// it might be resolved to a trait item
254+
HIR::TraitItem *trait_item = ctx->get_mappings ()->lookup_hir_trait_item (
255+
expr_mappings.get_crate_num (), ref);
256+
HIR::Trait *trait = ctx->get_mappings ()->lookup_trait_item_mapping (
257+
trait_item->get_mappings ().get_hirid ());
258+
259+
Resolver::TraitReference *trait_ref
260+
= &Resolver::TraitReference::error_node ();
261+
bool ok = ctx->get_tyctx ()->lookup_trait_reference (
262+
trait->get_mappings ().get_defid (), &trait_ref);
263+
rust_assert (ok);
264+
265+
// the type resolver can only resolve type bounds to their trait
266+
// item so its up to us to figure out if this path should resolve
267+
// to an trait-impl-block-item or if it can be defaulted to the
268+
// trait-impl-item's definition
269+
270+
auto root = receiver->get_root ();
271+
std::vector<Resolver::PathProbeCandidate> candidates
272+
= Resolver::PathProbeType::Probe (root, segment, true, false, true);
273+
274+
if (candidates.size () == 0)
275+
{
276+
// this means we are defaulting back to the trait_item if
277+
// possible
278+
Resolver::TraitItemReference *trait_item_ref = nullptr;
279+
bool ok = trait_ref->lookup_hir_trait_item (*trait_item, &trait_item_ref);
280+
rust_assert (ok); // found
281+
rust_assert (trait_item_ref->is_optional ()); // has definition
282+
283+
// FIXME Optional means it has a definition and an associated
284+
// block which can be a default implementation, if it does not
285+
// contain an implementation we should actually return
286+
// error_mark_node
287+
288+
return CompileTraitItem::Compile (receiver,
289+
trait_item_ref->get_hir_trait_item (),
290+
ctx, fntype, true, expr_locus);
291+
}
292+
else
293+
{
294+
std::vector<Resolver::Adjustment> adjustments;
295+
Resolver::PathProbeCandidate *candidate
296+
= Resolver::MethodResolution::Select (candidates, root, adjustments);
297+
298+
// FIXME this will be a case to return error_mark_node, there is
299+
// an error scenario where a Trait Foo has a method Bar, but this
300+
// receiver does not implement this trait or has an incompatible
301+
// implementation and we should just return error_mark_node
302+
rust_assert (candidate != nullptr);
303+
rust_assert (candidate->is_impl_candidate ());
304+
305+
HIR::ImplItem *impl_item = candidate->item.impl.impl_item;
306+
if (!fntype->has_subsititions_defined ())
307+
return CompileInherentImplItem::Compile (receiver, impl_item, ctx,
308+
true);
309+
310+
return CompileInherentImplItem::Compile (receiver, impl_item, ctx, true,
311+
fntype);
312+
}
313+
}
314+
315+
} // namespace Compile
316+
} // namespace Rust

gcc/rust/backend/rust-compile-expr.h

+15-11
Original file line numberDiff line numberDiff line change
@@ -448,17 +448,7 @@ class CompileExpr : public HIRCompileBase
448448
constructor.push_back (translated_expr);
449449
}
450450

451-
void visit (HIR::ArithmeticOrLogicalExpr &expr) override
452-
{
453-
auto op = expr.get_expr_type ();
454-
auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
455-
auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
456-
auto location = expr.get_locus ();
457-
458-
translated
459-
= ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
460-
location);
461-
}
451+
void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
462452

463453
void visit (HIR::ComparisonExpr &expr) override
464454
{
@@ -999,6 +989,20 @@ class CompileExpr : public HIRCompileBase
999989
expr.get_locus ());
1000990
}
1001991

992+
protected:
993+
Bexpression *compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
994+
TyTy::BaseType *receiver,
995+
TyTy::FnType *fntype,
996+
Bexpression *receiver_ref,
997+
std::vector<HIR::Expr *> &arguments,
998+
Location expr_locus);
999+
1000+
Bexpression *resolve_method_address (TyTy::FnType *fntype, HirId ref,
1001+
TyTy::BaseType *receiver,
1002+
HIR::PathIdentSegment &segment,
1003+
Analysis::NodeMapping expr_mappings,
1004+
Location expr_locus);
1005+
10021006
private:
10031007
CompileExpr (Context *ctx)
10041008
: HIRCompileBase (ctx), translated (nullptr), capacity_expr (nullptr)

0 commit comments

Comments
 (0)