Skip to content

Commit

Permalink
Ensure we emit the code for coercion sites on CallExpr and MethodCall…
Browse files Browse the repository at this point in the history
…Expr

When we coerce the types of arguments to the parameters of functions for
example we must store the actual type of the argument at that HIR ID not
the coerced ones. This gives the backend a chance to then figure out
when to actually implement any coercion site code. such as computing the
dynamic objects.

Fixes: #700
  • Loading branch information
philberty committed Oct 5, 2021
1 parent 591b43e commit f6a04f3
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 18 deletions.
22 changes: 19 additions & 3 deletions gcc/rust/backend/rust-compile-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,12 +363,28 @@ class CompileExpr : public HIRCompileBase
void visit (HIR::AssignmentExpr &expr) override
{
fncontext fn = ctx->peek_fn ();
auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
auto lvalue = CompileExpr::Compile (expr.get_lhs (), ctx);
auto rvalue = CompileExpr::Compile (expr.get_rhs (), ctx);

// assignments are coercion sites so lets convert the rvalue if necessary
TyTy::BaseType *expected = nullptr;
TyTy::BaseType *actual = nullptr;

bool ok;
ok = ctx->get_tyctx ()->lookup_type (
expr.get_lhs ()->get_mappings ().get_hirid (), &expected);
rust_assert (ok);

ok = ctx->get_tyctx ()->lookup_type (
expr.get_rhs ()->get_mappings ().get_hirid (), &actual);
rust_assert (ok);

rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());

Bstatement *assignment
= ctx->get_backend ()->assignment_statement (fn.fndecl, lhs, rhs,
= ctx->get_backend ()->assignment_statement (fn.fndecl, lvalue, rvalue,
expr.get_locus ());

ctx->add_statement (assignment);
}

Expand Down
111 changes: 96 additions & 15 deletions gcc/rust/backend/rust-compile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,39 +69,120 @@ CompileExpr::visit (HIR::CallExpr &expr)
|| tyty->get_kind () == TyTy::TypeKind::FNPTR;
if (!is_fn)
{
Btype *type = TyTyResolveCompile::compile (ctx, tyty);
rust_assert (tyty->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyty);
Btype *compiled_adt_type = TyTyResolveCompile::compile (ctx, tyty);

// this assumes all fields are in order from type resolution and if a
// base struct was specified those fields are filed via accesors
std::vector<Bexpression *> vals;
for (auto &argument : expr.get_arguments ())
for (size_t i = 0; i < expr.get_arguments ().size (); i++)
{
Bexpression *e = CompileExpr::Compile (argument.get (), ctx);
vals.push_back (e);
auto &argument = expr.get_arguments ().at (i);
auto rvalue = CompileExpr::Compile (argument.get (), ctx);

// assignments are coercion sites so lets convert the rvalue if
// necessary
auto respective_field = adt->get_field (i);
auto expected = respective_field->get_field_type ();

TyTy::BaseType *actual = nullptr;
bool ok = ctx->get_tyctx ()->lookup_type (
argument->get_mappings ().get_hirid (), &actual);
rust_assert (ok);

// coerce it if required
rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());

// add it to the list
vals.push_back (rvalue);
}

translated
= ctx->get_backend ()->constructor_expression (type, vals, -1,
expr.get_locus ());
= ctx->get_backend ()->constructor_expression (compiled_adt_type, vals,
-1, expr.get_locus ());
}
else
{
// must be a call to a function
Bexpression *fn = CompileExpr::Compile (expr.get_fnexpr (), ctx);
rust_assert (fn != nullptr);
auto get_parameter_tyty_at_index
= [] (const TyTy::BaseType *base, size_t index,
TyTy::BaseType **result) -> bool {
bool is_fn = base->get_kind () == TyTy::TypeKind::FNDEF
|| base->get_kind () == TyTy::TypeKind::FNPTR;
rust_assert (is_fn);

if (base->get_kind () == TyTy::TypeKind::FNPTR)
{
const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (base);
*result = fn->param_at (index);

return true;
}

const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (base);
auto param = fn->param_at (index);
*result = param.second;

return true;
};

bool is_varadic = false;
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
{
const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
is_varadic = fn->is_varadic ();
}

size_t required_num_args;
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
{
const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
required_num_args = fn->num_params ();
}
else
{
const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (tyty);
required_num_args = fn->num_params ();
}

std::vector<Bexpression *> args;
for (auto &argument : expr.get_arguments ())
for (size_t i = 0; i < expr.get_arguments ().size (); i++)
{
Bexpression *compiled_expr
= CompileExpr::Compile (argument.get (), ctx);
args.push_back (compiled_expr);
auto &argument = expr.get_arguments ().at (i);
auto rvalue = CompileExpr::Compile (argument.get (), ctx);

if (is_varadic && i >= required_num_args)
{
args.push_back (rvalue);
continue;
}

// assignments are coercion sites so lets convert the rvalue if
// necessary
bool ok;
TyTy::BaseType *expected = nullptr;
ok = get_parameter_tyty_at_index (tyty, i, &expected);
rust_assert (ok);

TyTy::BaseType *actual = nullptr;
ok = ctx->get_tyctx ()->lookup_type (
argument->get_mappings ().get_hirid (), &actual);
rust_assert (ok);

// coerce it if required
rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());

// add it to the list
args.push_back (rvalue);
}

// must be a call to a function
auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
auto fncontext = ctx->peek_fn ();
translated
= ctx->get_backend ()->call_expression (fncontext.fndecl, fn, args,
nullptr, expr.get_locus ());
= ctx->get_backend ()->call_expression (fncontext.fndecl, fn_address,
args, nullptr,
expr.get_locus ());
}
}

Expand Down
43 changes: 43 additions & 0 deletions gcc/testsuite/rust/execute/torture/coercion1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* { dg-output "123\n123\n" } */
extern "C" {
fn printf(s: *const i8, ...);
}

struct Foo(i32);
trait Bar {
fn baz(&self);
// { dg-warning "unused name" "" { target *-*-* } .-1 }
}

impl Bar for Foo {
fn baz(&self) {
// { dg-warning "unused name" "" { target *-*-* } .-1 }
unsafe {
let a = "%i\n\0";
let b = a as *const str;
let c = b as *const i8;

printf(c, self.0);
}
}
}

fn static_dispatch<T: Bar>(t: &T) {
t.baz();
}

fn dynamic_dispatch(t: &dyn Bar) {
t.baz();
}

fn main() -> i32 {
let a;
a = Foo(123);
static_dispatch(&a);

let b: &dyn Bar;
b = &a;
dynamic_dispatch(b);

0
}
41 changes: 41 additions & 0 deletions gcc/testsuite/rust/execute/torture/coercion2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* { dg-output "123\n123\n" } */
extern "C" {
fn printf(s: *const i8, ...);
}

struct Foo(i32);
trait Bar {
fn baz(&self);
// { dg-warning "unused name" "" { target *-*-* } .-1 }
}

impl Bar for Foo {
fn baz(&self) {
// { dg-warning "unused name" "" { target *-*-* } .-1 }
unsafe {
let a = "%i\n\0";
let b = a as *const str;
let c = b as *const i8;

printf(c, self.0);
}
}
}

fn static_dispatch<T: Bar>(t: &T) {
t.baz();
}

fn dynamic_dispatch(t: &dyn Bar) {
t.baz();
}

fn main() -> i32 {
let a;
a = &Foo(123);

static_dispatch(a);
dynamic_dispatch(a);

0
}

0 comments on commit f6a04f3

Please sign in to comment.