// 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
// .
#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 (receiver);
receiver = p->resolve ();
}
if (is_dyn_dispatch)
{
const TyTy::DynamicObjectType *dyn
= static_cast (receiver->get_root ());
std::vector 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 *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 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 &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 (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 (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 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 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 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