aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/rust/Make-lang.in1
-rw-r--r--gcc/rust/backend/rust-compile-expr.cc316
-rw-r--r--gcc/rust/backend/rust-compile-expr.h26
-rw-r--r--gcc/rust/backend/rust-compile.cc219
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check-expr.h201
-rw-r--r--gcc/rust/typecheck/rust-hir-type-check.h21
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_1.rs40
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_2.rs42
-rw-r--r--gcc/testsuite/rust/execute/torture/operator_overload_3.rs59
9 files changed, 696 insertions, 229 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 15c0a8f..f3302c2e 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -90,6 +90,7 @@ GRS_OBJS = \
rust/rust-hir-type-check-path.o \
rust/rust-compile-intrinsic.o \
rust/rust-base62.o \
+ rust/rust-compile-expr.o \
$(END)
# removed object files from here
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
new file mode 100644
index 0000000..c7941bc2
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -0,0 +1,316 @@
+// Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-compile.h"
+#include "rust-compile-item.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-struct-field-expr.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-dot-operator.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+CompileExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+ auto op = expr.get_expr_type ();
+ auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
+ auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
+
+ // this might be an operator overload situation lets check
+ TyTy::FnType *fntype;
+ bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+ expr.get_mappings ().get_hirid (), &fntype);
+ if (!is_op_overload)
+ {
+ translated = ctx->get_backend ()->arithmetic_or_logical_expression (
+ op, lhs, rhs, expr.get_locus ());
+ return;
+ }
+
+ // lookup the resolved name
+ NodeId resolved_node_id = UNKNOWN_NODEID;
+ if (!ctx->get_resolver ()->lookup_resolved_name (
+ expr.get_mappings ().get_nodeid (), &resolved_node_id))
+ {
+ rust_error_at (expr.get_locus (), "failed to lookup resolved MethodCall");
+ return;
+ }
+
+ // reverse lookup
+ HirId ref;
+ if (!ctx->get_mappings ()->lookup_node_to_hir (
+ expr.get_mappings ().get_crate_num (), resolved_node_id, &ref))
+ {
+ rust_fatal_error (expr.get_locus (), "reverse lookup failure");
+ return;
+ }
+
+ TyTy::BaseType *receiver = nullptr;
+ bool ok
+ = ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
+ &receiver);
+ rust_assert (ok);
+
+ bool is_dyn_dispatch
+ = receiver->get_root ()->get_kind () == TyTy::TypeKind::DYNAMIC;
+ bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
+ if (is_generic_receiver)
+ {
+ TyTy::ParamType *p = static_cast<TyTy::ParamType *> (receiver);
+ receiver = p->resolve ();
+ }
+
+ if (is_dyn_dispatch)
+ {
+ const TyTy::DynamicObjectType *dyn
+ = static_cast<const TyTy::DynamicObjectType *> (receiver->get_root ());
+
+ std::vector<HIR::Expr *> arguments;
+ arguments.push_back (expr.get_rhs ());
+
+ translated = compile_dyn_dispatch_call (dyn, receiver, fntype, lhs,
+ arguments, expr.get_locus ());
+ return;
+ }
+
+ // lookup compiled functions since it may have already been compiled
+ HIR::PathIdentSegment segment_name ("add");
+ Bexpression *fn_expr
+ = resolve_method_address (fntype, ref, receiver, segment_name,
+ expr.get_mappings (), expr.get_locus ());
+
+ // lookup the autoderef mappings
+ std::vector<Resolver::Adjustment> *adjustments = nullptr;
+ ok = ctx->get_tyctx ()->lookup_autoderef_mappings (
+ expr.get_mappings ().get_hirid (), &adjustments);
+ rust_assert (ok);
+
+ Bexpression *self = lhs;
+ for (auto &adjustment : *adjustments)
+ {
+ switch (adjustment.get_type ())
+ {
+ case Resolver::Adjustment::AdjustmentType::IMM_REF:
+ case Resolver::Adjustment::AdjustmentType::MUT_REF:
+ self = ctx->get_backend ()->address_expression (
+ self, expr.get_lhs ()->get_locus ());
+ break;
+
+ case Resolver::Adjustment::AdjustmentType::DEREF_REF:
+ Btype *expected_type
+ = TyTyResolveCompile::compile (ctx, adjustment.get_expected ());
+ self = ctx->get_backend ()->indirect_expression (
+ expected_type, self, true, /* known_valid*/
+ expr.get_lhs ()->get_locus ());
+ break;
+ }
+ }
+
+ std::vector<Bexpression *> args;
+ args.push_back (self); // adjusted self
+ args.push_back (rhs);
+
+ auto fncontext = ctx->peek_fn ();
+ translated
+ = ctx->get_backend ()->call_expression (fncontext.fndecl, fn_expr, args,
+ nullptr, expr.get_locus ());
+}
+
+Bexpression *
+CompileExpr::compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
+ TyTy::BaseType *receiver,
+ TyTy::FnType *fntype,
+ Bexpression *receiver_ref,
+ std::vector<HIR::Expr *> &arguments,
+ Location expr_locus)
+{
+ size_t offs = 0;
+ const Resolver::TraitItemReference *ref = nullptr;
+ for (auto &bound : dyn->get_object_items ())
+ {
+ const Resolver::TraitItemReference *item = bound.first;
+ auto t = item->get_tyty ();
+ rust_assert (t->get_kind () == TyTy::TypeKind::FNDEF);
+ auto ft = static_cast<TyTy::FnType *> (t);
+
+ if (ft->get_id () == fntype->get_id ())
+ {
+ ref = item;
+ break;
+ }
+ offs++;
+ }
+
+ if (ref == nullptr)
+ return ctx->get_backend ()->error_expression ();
+
+ // get any indirection sorted out
+ if (receiver->get_kind () == TyTy::TypeKind::REF)
+ {
+ TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
+ auto indirect_ty = r->get_base ();
+ Btype *indrect_compiled_tyty
+ = TyTyResolveCompile::compile (ctx, indirect_ty);
+
+ Bexpression *indirect
+ = ctx->get_backend ()->indirect_expression (indrect_compiled_tyty,
+ receiver_ref, true,
+ expr_locus);
+ receiver_ref = indirect;
+ }
+
+ // access the offs + 1 for the fnptr and offs=0 for the reciever obj
+ Bexpression *self_argument
+ = ctx->get_backend ()->struct_field_expression (receiver_ref, 0,
+ expr_locus);
+
+ // access the vtable for the fn
+ Bexpression *fn_vtable_access
+ = ctx->get_backend ()->struct_field_expression (receiver_ref, offs + 1,
+ expr_locus);
+
+ // cast it to the correct fntype
+ Btype *expected_fntype = TyTyResolveCompile::compile (ctx, fntype, true);
+ Bexpression *fn_convert_expr
+ = ctx->get_backend ()->convert_expression (expected_fntype,
+ fn_vtable_access, expr_locus);
+
+ fncontext fnctx = ctx->peek_fn ();
+ Bblock *enclosing_scope = ctx->peek_enclosing_scope ();
+ bool is_address_taken = false;
+ Bstatement *ret_var_stmt = nullptr;
+ Bvariable *fn_convert_expr_tmp
+ = ctx->get_backend ()->temporary_variable (fnctx.fndecl, enclosing_scope,
+ expected_fntype, fn_convert_expr,
+ is_address_taken, expr_locus,
+ &ret_var_stmt);
+ ctx->add_statement (ret_var_stmt);
+
+ std::vector<Bexpression *> args;
+ args.push_back (self_argument);
+ for (auto &argument : arguments)
+ {
+ Bexpression *compiled_expr = CompileExpr::Compile (argument, ctx);
+ args.push_back (compiled_expr);
+ }
+
+ Bexpression *fn_expr
+ = ctx->get_backend ()->var_expression (fn_convert_expr_tmp, expr_locus);
+
+ return ctx->get_backend ()->call_expression (fnctx.fndecl, fn_expr, args,
+ nullptr, expr_locus);
+}
+
+Bexpression *
+CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,
+ TyTy::BaseType *receiver,
+ HIR::PathIdentSegment &segment,
+ Analysis::NodeMapping expr_mappings,
+ Location expr_locus)
+{
+ // lookup compiled functions since it may have already been compiled
+ Bfunction *fn = nullptr;
+ if (ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
+ {
+ return ctx->get_backend ()->function_code_expression (fn, expr_locus);
+ }
+
+ // Now we can try and resolve the address since this might be a forward
+ // declared function, generic function which has not be compiled yet or
+ // its an not yet trait bound function
+ HIR::ImplItem *resolved_item
+ = ctx->get_mappings ()->lookup_hir_implitem (expr_mappings.get_crate_num (),
+ ref, nullptr);
+ if (resolved_item != nullptr)
+ {
+ if (!fntype->has_subsititions_defined ())
+ return CompileInherentImplItem::Compile (receiver, resolved_item, ctx,
+ true);
+
+ return CompileInherentImplItem::Compile (receiver, resolved_item, ctx,
+ true, fntype);
+ }
+
+ // it might be resolved to a trait item
+ HIR::TraitItem *trait_item = ctx->get_mappings ()->lookup_hir_trait_item (
+ expr_mappings.get_crate_num (), ref);
+ HIR::Trait *trait = ctx->get_mappings ()->lookup_trait_item_mapping (
+ trait_item->get_mappings ().get_hirid ());
+
+ Resolver::TraitReference *trait_ref
+ = &Resolver::TraitReference::error_node ();
+ bool ok = ctx->get_tyctx ()->lookup_trait_reference (
+ trait->get_mappings ().get_defid (), &trait_ref);
+ rust_assert (ok);
+
+ // the type resolver can only resolve type bounds to their trait
+ // item so its up to us to figure out if this path should resolve
+ // to an trait-impl-block-item or if it can be defaulted to the
+ // trait-impl-item's definition
+
+ auto root = receiver->get_root ();
+ std::vector<Resolver::PathProbeCandidate> candidates
+ = Resolver::PathProbeType::Probe (root, segment, true, false, true);
+
+ if (candidates.size () == 0)
+ {
+ // this means we are defaulting back to the trait_item if
+ // possible
+ Resolver::TraitItemReference *trait_item_ref = nullptr;
+ bool ok = trait_ref->lookup_hir_trait_item (*trait_item, &trait_item_ref);
+ rust_assert (ok); // found
+ rust_assert (trait_item_ref->is_optional ()); // has definition
+
+ // FIXME Optional means it has a definition and an associated
+ // block which can be a default implementation, if it does not
+ // contain an implementation we should actually return
+ // error_mark_node
+
+ return CompileTraitItem::Compile (receiver,
+ trait_item_ref->get_hir_trait_item (),
+ ctx, fntype, true, expr_locus);
+ }
+ else
+ {
+ std::vector<Resolver::Adjustment> adjustments;
+ Resolver::PathProbeCandidate *candidate
+ = Resolver::MethodResolution::Select (candidates, root, adjustments);
+
+ // FIXME this will be a case to return error_mark_node, there is
+ // an error scenario where a Trait Foo has a method Bar, but this
+ // receiver does not implement this trait or has an incompatible
+ // implementation and we should just return error_mark_node
+ rust_assert (candidate != nullptr);
+ rust_assert (candidate->is_impl_candidate ());
+
+ HIR::ImplItem *impl_item = candidate->item.impl.impl_item;
+ if (!fntype->has_subsititions_defined ())
+ return CompileInherentImplItem::Compile (receiver, impl_item, ctx,
+ true);
+
+ return CompileInherentImplItem::Compile (receiver, impl_item, ctx, true,
+ fntype);
+ }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index f43db50..c9d3c30 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -448,17 +448,7 @@ public:
constructor.push_back (translated_expr);
}
- void visit (HIR::ArithmeticOrLogicalExpr &expr) override
- {
- auto op = expr.get_expr_type ();
- auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
- auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
- auto location = expr.get_locus ();
-
- translated
- = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
- location);
- }
+ void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
void visit (HIR::ComparisonExpr &expr) override
{
@@ -999,6 +989,20 @@ public:
expr.get_locus ());
}
+protected:
+ Bexpression *compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
+ TyTy::BaseType *receiver,
+ TyTy::FnType *fntype,
+ Bexpression *receiver_ref,
+ std::vector<HIR::Expr *> &arguments,
+ Location expr_locus);
+
+ Bexpression *resolve_method_address (TyTy::FnType *fntype, HirId ref,
+ TyTy::BaseType *receiver,
+ HIR::PathIdentSegment &segment,
+ Analysis::NodeMapping expr_mappings,
+ Location expr_locus);
+
private:
CompileExpr (Context *ctx)
: HIRCompileBase (ctx), translated (nullptr), capacity_expr (nullptr)
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index e9aca2c..e53993a 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -242,220 +242,21 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
const TyTy::DynamicObjectType *dyn
= static_cast<const TyTy::DynamicObjectType *> (receiver->get_root ());
- size_t offs = 0;
- const Resolver::TraitItemReference *ref = nullptr;
- for (auto &bound : dyn->get_object_items ())
- {
- const Resolver::TraitItemReference *item = bound.first;
- auto t = item->get_tyty ();
- rust_assert (t->get_kind () == TyTy::TypeKind::FNDEF);
- auto ft = static_cast<TyTy::FnType *> (t);
-
- if (ft->get_id () == fntype->get_id ())
- {
- ref = item;
- break;
- }
- offs++;
- }
-
- if (ref == nullptr)
- {
- translated = ctx->get_backend ()->error_expression ();
- return;
- }
-
- // get any indirection sorted out
- auto receiver_ref = self;
- if (receiver->get_kind () == TyTy::TypeKind::REF)
- {
- TyTy::ReferenceType *r
- = static_cast<TyTy::ReferenceType *> (receiver);
- auto indirect_ty = r->get_base ();
- Btype *indrect_compiled_tyty
- = TyTyResolveCompile::compile (ctx, indirect_ty);
-
- Bexpression *indirect
- = ctx->get_backend ()->indirect_expression (indrect_compiled_tyty,
- receiver_ref, true,
- expr.get_locus ());
- receiver_ref = indirect;
- }
+ std::vector<HIR::Expr *> arguments;
+ for (auto &arg : expr.get_arguments ())
+ arguments.push_back (arg.get ());
- // access the offs + 1 for the fnptr and offs=0 for the reciever obj
- Bexpression *self_argument
- = ctx->get_backend ()->struct_field_expression (receiver_ref, 0,
- expr.get_locus ());
-
- // access the vtable for the fn
- Bexpression *fn_vtable_access
- = ctx->get_backend ()->struct_field_expression (receiver_ref, offs + 1,
- expr.get_locus ());
-
- // cast it to the correct fntype
- Btype *expected_fntype = TyTyResolveCompile::compile (ctx, fntype, true);
- Bexpression *fn_convert_expr
- = ctx->get_backend ()->convert_expression (expected_fntype,
- fn_vtable_access,
- expr.get_locus ());
-
- fncontext fnctx = ctx->peek_fn ();
- Bblock *enclosing_scope = ctx->peek_enclosing_scope ();
- bool is_address_taken = false;
- Bstatement *ret_var_stmt = nullptr;
-
- Bvariable *fn_convert_expr_tmp = ctx->get_backend ()->temporary_variable (
- fnctx.fndecl, enclosing_scope, expected_fntype, fn_convert_expr,
- is_address_taken, expr.get_locus (), &ret_var_stmt);
- ctx->add_statement (ret_var_stmt);
-
- std::vector<Bexpression *> args;
- args.push_back (self_argument);
- for (auto &argument : expr.get_arguments ())
- {
- Bexpression *compiled_expr
- = CompileExpr::Compile (argument.get (), ctx);
- args.push_back (compiled_expr);
- }
-
- Bexpression *fn_expr
- = ctx->get_backend ()->var_expression (fn_convert_expr_tmp,
- expr.get_locus ());
-
- translated
- = ctx->get_backend ()->call_expression (fnctx.fndecl, fn_expr, args,
- nullptr, expr.get_locus ());
+ translated = compile_dyn_dispatch_call (dyn, receiver, fntype, self,
+ arguments, expr.get_locus ());
return;
}
- // address of compiled function
- Bexpression *fn_expr = ctx->get_backend ()->error_expression ();
-
// lookup compiled functions since it may have already been compiled
- Bfunction *fn = nullptr;
- if (ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
- {
- fn_expr
- = ctx->get_backend ()->function_code_expression (fn, expr.get_locus ());
- }
- else
- {
- // Now we can try and resolve the address since this might be a forward
- // declared function, generic function which has not be compiled yet or
- // its an not yet trait bound function
- HIR::ImplItem *resolved_item = ctx->get_mappings ()->lookup_hir_implitem (
- expr.get_mappings ().get_crate_num (), ref, nullptr);
- if (resolved_item == nullptr)
- {
- // it might be resolved to a trait item
- HIR::TraitItem *trait_item
- = ctx->get_mappings ()->lookup_hir_trait_item (
- expr.get_mappings ().get_crate_num (), ref);
- HIR::Trait *trait = ctx->get_mappings ()->lookup_trait_item_mapping (
- trait_item->get_mappings ().get_hirid ());
-
- Resolver::TraitReference *trait_ref
- = &Resolver::TraitReference::error_node ();
- bool ok = ctx->get_tyctx ()->lookup_trait_reference (
- trait->get_mappings ().get_defid (), &trait_ref);
- rust_assert (ok);
-
- // the type resolver can only resolve type bounds to their trait
- // item so its up to us to figure out if this path should resolve
- // to an trait-impl-block-item or if it can be defaulted to the
- // trait-impl-item's definition
-
- auto root = receiver->get_root ();
- std::vector<Resolver::PathProbeCandidate> candidates
- = Resolver::PathProbeType::Probe (
- root, expr.get_method_name ().get_segment (), true, false, true);
-
- if (candidates.size () == 0)
- {
- // this means we are defaulting back to the trait_item if
- // possible
- Resolver::TraitItemReference *trait_item_ref = nullptr;
- bool ok = trait_ref->lookup_hir_trait_item (*trait_item,
- &trait_item_ref);
- rust_assert (ok); // found
- rust_assert (trait_item_ref->is_optional ()); // has definition
-
- // FIXME Optional means it has a definition and an associated
- // block which can be a default implementation, if it does not
- // contain an implementation we should actually return
- // error_mark_node
-
- TyTy::BaseType *self_type = nullptr;
- if (!ctx->get_tyctx ()->lookup_type (
- expr.get_receiver ()->get_mappings ().get_hirid (),
- &self_type))
- {
- rust_error_at (expr.get_locus (),
- "failed to resolve type for self param");
- return;
- }
-
- fn_expr = CompileTraitItem::Compile (
- self_type, trait_item_ref->get_hir_trait_item (), ctx, fntype,
- true, expr.get_locus ());
- }
- else
- {
- std::vector<Resolver::Adjustment> adjustments;
- Resolver::PathProbeCandidate *candidate
- = Resolver::MethodResolution::Select (candidates, root,
- adjustments);
-
- // FIXME this will be a case to return error_mark_node, there is
- // an error scenario where a Trait Foo has a method Bar, but this
- // receiver does not implement this trait or has an incompatible
- // implementation and we should just return error_mark_node
- rust_assert (candidate != nullptr);
- rust_assert (candidate->is_impl_candidate ());
-
- HIR::ImplItem *impl_item = candidate->item.impl.impl_item;
-
- TyTy::BaseType *self_type = nullptr;
- if (!ctx->get_tyctx ()->lookup_type (
- expr.get_receiver ()->get_mappings ().get_hirid (),
- &self_type))
- {
- rust_error_at (expr.get_locus (),
- "failed to resolve type for self param");
- return;
- }
-
- if (!fntype->has_subsititions_defined ())
- fn_expr
- = CompileInherentImplItem::Compile (self_type, impl_item, ctx,
- true);
- else
- fn_expr
- = CompileInherentImplItem::Compile (self_type, impl_item, ctx,
- true, fntype);
- }
- }
- else
- {
- TyTy::BaseType *self_type = nullptr;
- if (!ctx->get_tyctx ()->lookup_type (
- expr.get_receiver ()->get_mappings ().get_hirid (), &self_type))
- {
- rust_error_at (expr.get_locus (),
- "failed to resolve type for self param");
- return;
- }
-
- if (!fntype->has_subsititions_defined ())
- fn_expr
- = CompileInherentImplItem::Compile (self_type, resolved_item, ctx,
- true);
- else
- fn_expr
- = CompileInherentImplItem::Compile (self_type, resolved_item, ctx,
- true, fntype);
- }
- }
+ HIR::PathExprSegment method_name = expr.get_method_name ();
+ HIR::PathIdentSegment segment_name = method_name.get_segment ();
+ Bexpression *fn_expr
+ = resolve_method_address (fntype, ref, receiver, segment_name,
+ expr.get_mappings (), expr.get_locus ());
// lookup the autoderef mappings
std::vector<Resolver::Adjustment> *adjustments = nullptr;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
index 2a6bae9..b332a68 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -699,20 +699,203 @@ public:
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs (), false);
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs (), false);
- bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
- bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
- bool valid = valid_lhs && valid_rhs;
- if (!valid)
+ // in order to probe of the correct type paths we need the root type, which
+ // strips any references
+ const TyTy::BaseType *root = lhs->get_root ();
+
+ // look up lang item for arithmetic type
+ std::vector<PathProbeCandidate> candidates;
+ auto lang_item_type
+ = Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
+ std::string associated_item_name
+ = Analysis::RustLangItem::ToString (lang_item_type);
+ DefId respective_lang_item_id = UNKNOWN_DEFID;
+ bool lang_item_defined
+ = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+ // handle the case where we are within the impl block for this lang_item
+ // otherwise we end up with a recursive operator overload such as the i32
+ // operator overload trait
+ if (lang_item_defined)
+ {
+ TypeCheckContextItem &fn_context = context->peek_context ();
+ if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+ {
+ auto &impl_item = fn_context.get_impl_item ();
+ HIR::ImplBlock *parent = impl_item.first;
+ HIR::Function *fn = impl_item.second;
+
+ if (parent->has_trait_ref ()
+ && fn->get_function_name ().compare (associated_item_name) == 0)
+ {
+ TraitReference *trait_reference
+ = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
+ if (!trait_reference->is_error ())
+ {
+ TyTy::BaseType *lookup = nullptr;
+ bool ok
+ = context->lookup_type (fn->get_mappings ().get_hirid (),
+ &lookup);
+ rust_assert (ok);
+ rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+
+ TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+ rust_assert (fntype->is_method ());
+
+ bool is_lang_item_impl
+ = trait_reference->get_mappings ().get_defid ()
+ == respective_lang_item_id;
+ bool self_is_lang_item_self
+ = fntype->get_self_type ()->is_equal (*lhs);
+
+ bool recursive_operator_overload
+ = is_lang_item_impl && self_is_lang_item_self;
+ lang_item_defined = !recursive_operator_overload;
+ }
+ }
+ }
+ }
+
+ // probe for the lang-item
+ if (lang_item_defined)
+ {
+ bool receiver_is_type_param
+ = root->get_kind () == TyTy::TypeKind::PARAM;
+ bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC;
+
+ bool receiver_is_generic = receiver_is_type_param || receiver_is_dyn;
+ bool probe_bounds = true;
+ bool probe_impls = !receiver_is_generic;
+ bool ignore_mandatory_trait_items = !receiver_is_generic;
+
+ candidates = PathProbeType::Probe (
+ root, HIR::PathIdentSegment (associated_item_name), probe_impls,
+ probe_bounds, ignore_mandatory_trait_items, respective_lang_item_id);
+ }
+
+ bool have_implementation_for_lang_item = candidates.size () > 0;
+ if (!lang_item_defined || !have_implementation_for_lang_item)
+ {
+ bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
+ bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
+ bool valid = valid_lhs && valid_rhs;
+ if (!valid)
+ {
+ rust_error_at (expr.get_locus (),
+ "cannot apply this operator to types %s and %s",
+ lhs->as_string ().c_str (),
+ rhs->as_string ().c_str ());
+ return;
+ }
+
+ infered = lhs->unify (rhs);
+ return;
+ }
+
+ // now its just like a method-call-expr
+ context->insert_receiver (expr.get_mappings ().get_hirid (), lhs);
+
+ // autoderef
+ std::vector<Adjustment> adjustments;
+ PathProbeCandidate *resolved_candidate
+ = MethodResolution::Select (candidates, lhs, adjustments);
+ rust_assert (resolved_candidate != nullptr);
+
+ // store the adjustments for code-generation to know what to do
+ context->insert_autoderef_mappings (expr.get_mappings ().get_hirid (),
+ std::move (adjustments));
+
+ TyTy::BaseType *lookup_tyty = resolved_candidate->ty;
+ NodeId resolved_node_id
+ = resolved_candidate->is_impl_candidate ()
+ ? resolved_candidate->item.impl.impl_item->get_impl_mappings ()
+ .get_nodeid ()
+ : resolved_candidate->item.trait.item_ref->get_mappings ()
+ .get_nodeid ();
+
+ rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+ TyTy::BaseType *lookup = lookup_tyty;
+ TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+ rust_assert (fn->is_method ());
+
+ if (root->get_kind () == TyTy::TypeKind::ADT)
+ {
+ const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
+ if (adt->has_substitutions () && fn->needs_substitution ())
+ {
+ // consider the case where we have:
+ //
+ // struct Foo<X,Y>(X,Y);
+ //
+ // impl<T> Foo<T, i32> {
+ // fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
+ // }
+ //
+ // In this case we end up with an fn type of:
+ //
+ // fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
+ //
+ // This means the instance or self we are calling this method for
+ // will be substituted such that we can get the inherited type
+ // arguments but then need to use the turbo fish if available or
+ // infer the remaining arguments. Luckily rust does not allow for
+ // default types GenericParams on impl blocks since these must
+ // always be at the end of the list
+
+ auto s = fn->get_self_type ()->get_root ();
+ rust_assert (s->can_eq (adt, false, false));
+ rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+ const TyTy::ADTType *self_adt
+ = static_cast<const TyTy::ADTType *> (s);
+
+ // we need to grab the Self substitutions as the inherit type
+ // parameters for this
+ if (self_adt->needs_substitution ())
+ {
+ rust_assert (adt->was_substituted ());
+
+ TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+ = GetUsedSubstArgs::From (adt);
+
+ TyTy::SubstitutionArgumentMappings inherit_type_args
+ = self_adt->solve_mappings_from_receiver_for_self (
+ used_args_in_prev_segment);
+
+ // there may or may not be inherited type arguments
+ if (!inherit_type_args.is_error ())
+ {
+ // need to apply the inherited type arguments to the
+ // function
+ lookup = fn->handle_substitions (inherit_type_args);
+ }
+ }
+ }
+ }
+
+ // type check the arguments
+ TyTy::FnType *type = static_cast<TyTy::FnType *> (lookup);
+ rust_assert (type->num_params () == 2);
+ auto fnparam = type->param_at (1);
+ auto resolved_argument_type = fnparam.second->unify (rhs);
+ if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (expr.get_locus (),
- "cannot apply this operator to types %s and %s",
- lhs->as_string ().c_str (), rhs->as_string ().c_str ());
+ "Type Resolution failure on parameter");
return;
}
- infered = lhs->unify (rhs);
- infered->append_reference (lhs->get_ref ());
- infered->append_reference (rhs->get_ref ());
+ // get the return type
+ TyTy::BaseType *function_ret_tyty = fn->get_return_type ()->clone ();
+
+ // store the expected fntype
+ context->insert_operator_overload (expr.get_mappings ().get_hirid (), type);
+
+ // set up the resolved name on the path
+ resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+ resolved_node_id);
+
+ // return the result of the function back
+ infered = function_ret_tyty;
}
void visit (HIR::ComparisonExpr &expr) override
diff --git a/gcc/rust/typecheck/rust-hir-type-check.h b/gcc/rust/typecheck/rust-hir-type-check.h
index b165f9c..1add4fa 100644
--- a/gcc/rust/typecheck/rust-hir-type-check.h
+++ b/gcc/rust/typecheck/rust-hir-type-check.h
@@ -296,6 +296,24 @@ public:
return true;
}
+ void insert_operator_overload (HirId id, TyTy::FnType *call_site)
+ {
+ auto it = operator_overloads.find (id);
+ rust_assert (it == operator_overloads.end ());
+
+ operator_overloads[id] = call_site;
+ }
+
+ bool lookup_operator_overload (HirId id, TyTy::FnType **call)
+ {
+ auto it = operator_overloads.find (id);
+ if (it == operator_overloads.end ())
+ return false;
+
+ *call = it->second;
+ return true;
+ }
+
private:
TypeCheckContext ();
@@ -318,6 +336,9 @@ private:
// adjustment mappings
std::map<HirId, std::vector<Adjustment>> autoderef_mappings;
+ // operator overloads
+ std::map<HirId, TyTy::FnType *> operator_overloads;
+
// variants
std::map<HirId, HirId> variants;
};
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_1.rs b/gcc/testsuite/rust/execute/torture/operator_overload_1.rs
new file mode 100644
index 0000000..e52b394
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_1.rs
@@ -0,0 +1,40 @@
+/* { dg-output "3\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+ type Output;
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+ fn add(self, rhs: Rhs) -> Self::Output;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .rhs." "" { target *-*-* } .-2 }
+ // { dg-warning "unused name .Add::add." "" { target *-*-* } .-3 }
+}
+
+impl Add for i32 {
+ type Output = i32;
+
+ fn add(self, other: i32) -> i32 {
+ let res = self + other;
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res);
+ }
+
+ res
+ }
+}
+
+fn main() -> i32 {
+ let a;
+ a = 1 + 2;
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_2.rs b/gcc/testsuite/rust/execute/torture/operator_overload_2.rs
new file mode 100644
index 0000000..9d5615d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_2.rs
@@ -0,0 +1,42 @@
+/* { dg-output "3\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+ type Output;
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+ fn add(self, rhs: Rhs) -> Self::Output;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .rhs." "" { target *-*-* } .-2 }
+ // { dg-warning "unused name .Add::add." "" { target *-*-* } .-3 }
+}
+
+struct Foo(i32);
+
+impl Add for Foo {
+ type Output = Foo;
+
+ fn add(self, other: Foo) -> Foo {
+ let res = Foo(self.0 + other.0);
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res.0);
+ }
+
+ res
+ }
+}
+
+fn main() -> i32 {
+ let a;
+ a = Foo(1) + Foo(2);
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_3.rs b/gcc/testsuite/rust/execute/torture/operator_overload_3.rs
new file mode 100644
index 0000000..bd99b50
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_3.rs
@@ -0,0 +1,59 @@
+/* { dg-output "3\n3\n" } */
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+ type Output;
+ // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+ fn add(self, rhs: Rhs) -> Self::Output;
+ // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+ // { dg-warning "unused name .rhs." "" { target *-*-* } .-2 }
+ // { dg-warning "unused name .Add::add." "" { target *-*-* } .-3 }
+}
+
+impl Add for i32 {
+ type Output = i32;
+
+ fn add(self, other: i32) -> i32 {
+ let res = self + other;
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res);
+ }
+
+ res
+ }
+}
+
+struct Foo(i32);
+impl Add for Foo {
+ type Output = Foo;
+
+ fn add(self, other: Foo) -> Foo {
+ let res = Foo(self.0 + other.0);
+
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, res.0);
+ }
+
+ res
+ }
+}
+
+fn main() -> i32 {
+ let a;
+ a = Foo(1) + Foo(2);
+
+ 0
+}