Skip to content

Commit

Permalink
Add support for varadic extern "c" functions like printf
Browse files Browse the repository at this point in the history
Varadic functions are only allowed in extern functions as far as I know.
  • Loading branch information
philberty committed Jul 27, 2021
1 parent 2818017 commit 7f8adcc
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 26 deletions.
11 changes: 8 additions & 3 deletions gcc/rust/backend/rust-compile-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,14 @@ class TyTyResolveCompile : public TyTy::TyVisitor
parameters.push_back (compiled_param);
}

translated = ctx->get_backend ()->function_type (
receiver, parameters, results, NULL,
ctx->get_mappings ()->lookup_location (type.get_ref ()));
if (!type.is_varadic ())
translated = ctx->get_backend ()->function_type (
receiver, parameters, results, NULL,
ctx->get_mappings ()->lookup_location (type.get_ref ()));
else
translated = ctx->get_backend ()->function_type_varadic (
receiver, parameters, results, NULL,
ctx->get_mappings ()->lookup_location (type.get_ref ()));
}

void visit (TyTy::FnPtr &type) override
Expand Down
6 changes: 2 additions & 4 deletions gcc/rust/backend/rust-compile-extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ class CompileExternItem : public HIRCompileBase
rust_assert (ok);

std::string name = item.get_item_name ();

// FIXME
// this is assuming C ABI
std::string asm_name = "_" + name;
// FIXME this is assuming C ABI
std::string asm_name = name;

Btype *type = TyTyResolveCompile::compile (ctx, resolved_type);
bool is_external = true;
Expand Down
12 changes: 9 additions & 3 deletions gcc/rust/backend/rust-compile-tyty.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,15 @@ class TyTyCompile : public TyTy::TyVisitor
parameters.push_back (compiled_param);
}

translated
= backend->function_type (receiver, parameters, results, NULL,
mappings->lookup_location (type.get_ref ()));
if (!type.is_varadic ())
translated
= backend->function_type (receiver, parameters, results, NULL,
mappings->lookup_location (type.get_ref ()));
else
translated
= backend->function_type_varadic (receiver, parameters, results, NULL,
mappings->lookup_location (
type.get_ref ()));
}

void visit (TyTy::BoolType &) override
Expand Down
7 changes: 7 additions & 0 deletions gcc/rust/rust-backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ class Backend
Btype *result_struct, Location location)
= 0;

virtual Btype *
function_type_varadic (const Btyped_identifier &receiver,
const std::vector<Btyped_identifier> &parameters,
const std::vector<Btyped_identifier> &results,
Btype *result_struct, Location location)
= 0;

virtual Btype *function_ptr_type (Btype *result,
const std::vector<Btype *> &praameters,
Location location)
Expand Down
61 changes: 61 additions & 0 deletions gcc/rust/rust-gcc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ class Gcc_backend : public Backend
const std::vector<Btyped_identifier> &, Btype *,
const Location);

Btype *function_type_varadic (const Btyped_identifier &,
const std::vector<Btyped_identifier> &,
const std::vector<Btyped_identifier> &, Btype *,
const Location);

Btype *function_ptr_type (Btype *, const std::vector<Btype *> &, Location);

Btype *struct_type (const std::vector<Btyped_identifier> &);
Expand Down Expand Up @@ -1048,6 +1053,62 @@ Gcc_backend::function_type (const Btyped_identifier &receiver,
return this->make_type (build_pointer_type (fntype));
}

Btype *
Gcc_backend::function_type_varadic (
const Btyped_identifier &receiver,
const std::vector<Btyped_identifier> &parameters,
const std::vector<Btyped_identifier> &results, Btype *result_struct, Location)
{
size_t n = parameters.size () + (receiver.btype != NULL ? 1 : 0);
tree *args = XALLOCAVEC (tree, n);
size_t offs = 0;

if (receiver.btype != NULL)
{
tree t = receiver.btype->get_tree ();
if (t == error_mark_node)
return this->error_type ();

args[offs++] = t;
}

for (std::vector<Btyped_identifier>::const_iterator p = parameters.begin ();
p != parameters.end (); ++p)
{
tree t = p->btype->get_tree ();
if (t == error_mark_node)
return this->error_type ();
args[offs++] = t;
}

tree result;
if (results.empty ())
result = void_type_node;
else if (results.size () == 1)
result = results.front ().btype->get_tree ();
else
{
gcc_assert (result_struct != NULL);
result = result_struct->get_tree ();
}
if (result == error_mark_node)
return this->error_type ();

// The libffi library cannot represent a zero-sized object. To
// avoid causing confusion on 32-bit SPARC, we treat a function that
// returns a zero-sized value as returning void. That should do no
// harm since there is no actual value to be returned. See
// https://gcc.gnu.org/PR72814 for details.
if (result != void_type_node && int_size_in_bytes (result) == 0)
result = void_type_node;

tree fntype = build_varargs_function_type_array (result, n, args);
if (fntype == error_mark_node)
return this->error_type ();

return this->make_type (build_pointer_type (fntype));
}

Btype *
Gcc_backend::function_ptr_type (Btype *result_type,
const std::vector<Btype *> &parameters,
Expand Down
14 changes: 9 additions & 5 deletions gcc/rust/typecheck/rust-hir-type-check-implitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,15 @@ class TypeCheckTopLevelExternItem : public TypeCheckBase
context->insert_type (param.get_mappings (), param_tyty);
}

auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
function.get_mappings ().get_defid (),
function.get_item_name (),
FNTYPE_IS_EXTERN_FLAG, std::move (params),
ret_type, std::move (substitutions));
uint8_t flags = FNTYPE_IS_EXTERN_FLAG;
if (function.is_variadic ())
flags |= FNTYPE_IS_VARADIC_FLAG;

auto fnType
= new TyTy::FnType (function.get_mappings ().get_hirid (),
function.get_mappings ().get_defid (),
function.get_item_name (), flags, std::move (params),
ret_type, std::move (substitutions));
context->insert_type (function.get_mappings (), fnType);
}

Expand Down
41 changes: 30 additions & 11 deletions gcc/rust/typecheck/rust-tyty.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2055,15 +2055,27 @@ TypeCheckCallExpr::visit (FnType &type)
{
if (call.num_params () != type.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
call.num_params (), type.num_params ());
return;
if (type.is_varadic ())
{
if (call.num_params () < type.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
call.num_params (), type.num_params ());
return;
}
}
else
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu",
call.num_params (), type.num_params ());
return;
}
}

size_t i = 0;
call.iterate_params ([&] (HIR::Expr *param) mutable -> bool {
auto fnparam = type.param_at (i);
auto argument_expr_tyty = Resolver::TypeCheckExpr::Resolve (param, false);
if (argument_expr_tyty == nullptr)
{
Expand All @@ -2072,12 +2084,19 @@ TypeCheckCallExpr::visit (FnType &type)
return false;
}

auto resolved_argument_type = fnparam.second->unify (argument_expr_tyty);
if (resolved_argument_type == nullptr)
auto resolved_argument_type = argument_expr_tyty;

// it might be a varadic function
if (i < type.num_params ())
{
rust_error_at (param->get_locus_slow (),
"Type Resolution failure on parameter");
return false;
auto fnparam = type.param_at (i);
resolved_argument_type = fnparam.second->unify (argument_expr_tyty);
if (resolved_argument_type == nullptr)
{
rust_error_at (param->get_locus_slow (),
"Type Resolution failure on parameter");
return false;
}
}

context->insert_type (param->get_mappings (), resolved_argument_type);
Expand All @@ -2086,7 +2105,7 @@ TypeCheckCallExpr::visit (FnType &type)
return true;
});

if (i != call.num_params ())
if (i < call.num_params ())
{
rust_error_at (call.get_locus (),
"unexpected number of arguments %lu expected %lu", i,
Expand Down
3 changes: 3 additions & 0 deletions gcc/rust/typecheck/rust-tyty.h
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ class FnType : public BaseType, public SubstitutionRef
#define FNTYPE_DEFAULT_FLAGS 0x00
#define FNTYPE_IS_METHOD_FLAG 0x01
#define FNTYPE_IS_EXTERN_FLAG 0x02
#define FNTYPE_IS_VARADIC_FLAG 0X04

FnType (HirId ref, DefId id, std::string identifier, uint8_t flags,
std::vector<std::pair<HIR::Pattern *, BaseType *> > params,
Expand Down Expand Up @@ -1031,6 +1032,8 @@ class FnType : public BaseType, public SubstitutionRef

bool is_extern () const { return (flags & FNTYPE_IS_EXTERN_FLAG) != 0; }

bool is_varadic () const { return (flags & FNTYPE_IS_VARADIC_FLAG) != 0; }

DefId get_id () const { return id; }

// get the Self type for the method
Expand Down
15 changes: 15 additions & 0 deletions gcc/testsuite/rust/execute/torture/helloworld2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* { dg-output "Hello World 123\n" }*/
extern "C" {
fn printf(s: *const i8, ...);
}

fn main() -> i32 {
unsafe {
let a = "Hello World %i\n";
let b = a as *const str;
let c = b as *const i8;

printf(c, 123);
}
0
}

0 comments on commit 7f8adcc

Please sign in to comment.