Skip to content

Commit 7f8adcc

Browse files
committed
Add support for varadic extern "c" functions like printf
Varadic functions are only allowed in extern functions as far as I know.
1 parent 2818017 commit 7f8adcc

File tree

9 files changed

+144
-26
lines changed

9 files changed

+144
-26
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,14 @@ class TyTyResolveCompile : public TyTy::TyVisitor
371371
parameters.push_back (compiled_param);
372372
}
373373

374-
translated = ctx->get_backend ()->function_type (
375-
receiver, parameters, results, NULL,
376-
ctx->get_mappings ()->lookup_location (type.get_ref ()));
374+
if (!type.is_varadic ())
375+
translated = ctx->get_backend ()->function_type (
376+
receiver, parameters, results, NULL,
377+
ctx->get_mappings ()->lookup_location (type.get_ref ()));
378+
else
379+
translated = ctx->get_backend ()->function_type_varadic (
380+
receiver, parameters, results, NULL,
381+
ctx->get_mappings ()->lookup_location (type.get_ref ()));
377382
}
378383

379384
void visit (TyTy::FnPtr &type) override

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@ class CompileExternItem : public HIRCompileBase
5151
rust_assert (ok);
5252

5353
std::string name = item.get_item_name ();
54-
55-
// FIXME
56-
// this is assuming C ABI
57-
std::string asm_name = "_" + name;
54+
// FIXME this is assuming C ABI
55+
std::string asm_name = name;
5856

5957
Btype *type = TyTyResolveCompile::compile (ctx, resolved_type);
6058
bool is_external = true;

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,15 @@ class TyTyCompile : public TyTy::TyVisitor
9595
parameters.push_back (compiled_param);
9696
}
9797

98-
translated
99-
= backend->function_type (receiver, parameters, results, NULL,
100-
mappings->lookup_location (type.get_ref ()));
98+
if (!type.is_varadic ())
99+
translated
100+
= backend->function_type (receiver, parameters, results, NULL,
101+
mappings->lookup_location (type.get_ref ()));
102+
else
103+
translated
104+
= backend->function_type_varadic (receiver, parameters, results, NULL,
105+
mappings->lookup_location (
106+
type.get_ref ()));
101107
}
102108

103109
void visit (TyTy::BoolType &) override

gcc/rust/rust-backend.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ class Backend
163163
Btype *result_struct, Location location)
164164
= 0;
165165

166+
virtual Btype *
167+
function_type_varadic (const Btyped_identifier &receiver,
168+
const std::vector<Btyped_identifier> &parameters,
169+
const std::vector<Btyped_identifier> &results,
170+
Btype *result_struct, Location location)
171+
= 0;
172+
166173
virtual Btype *function_ptr_type (Btype *result,
167174
const std::vector<Btype *> &praameters,
168175
Location location)

gcc/rust/rust-gcc.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ class Gcc_backend : public Backend
256256
const std::vector<Btyped_identifier> &, Btype *,
257257
const Location);
258258

259+
Btype *function_type_varadic (const Btyped_identifier &,
260+
const std::vector<Btyped_identifier> &,
261+
const std::vector<Btyped_identifier> &, Btype *,
262+
const Location);
263+
259264
Btype *function_ptr_type (Btype *, const std::vector<Btype *> &, Location);
260265

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

1056+
Btype *
1057+
Gcc_backend::function_type_varadic (
1058+
const Btyped_identifier &receiver,
1059+
const std::vector<Btyped_identifier> &parameters,
1060+
const std::vector<Btyped_identifier> &results, Btype *result_struct, Location)
1061+
{
1062+
size_t n = parameters.size () + (receiver.btype != NULL ? 1 : 0);
1063+
tree *args = XALLOCAVEC (tree, n);
1064+
size_t offs = 0;
1065+
1066+
if (receiver.btype != NULL)
1067+
{
1068+
tree t = receiver.btype->get_tree ();
1069+
if (t == error_mark_node)
1070+
return this->error_type ();
1071+
1072+
args[offs++] = t;
1073+
}
1074+
1075+
for (std::vector<Btyped_identifier>::const_iterator p = parameters.begin ();
1076+
p != parameters.end (); ++p)
1077+
{
1078+
tree t = p->btype->get_tree ();
1079+
if (t == error_mark_node)
1080+
return this->error_type ();
1081+
args[offs++] = t;
1082+
}
1083+
1084+
tree result;
1085+
if (results.empty ())
1086+
result = void_type_node;
1087+
else if (results.size () == 1)
1088+
result = results.front ().btype->get_tree ();
1089+
else
1090+
{
1091+
gcc_assert (result_struct != NULL);
1092+
result = result_struct->get_tree ();
1093+
}
1094+
if (result == error_mark_node)
1095+
return this->error_type ();
1096+
1097+
// The libffi library cannot represent a zero-sized object. To
1098+
// avoid causing confusion on 32-bit SPARC, we treat a function that
1099+
// returns a zero-sized value as returning void. That should do no
1100+
// harm since there is no actual value to be returned. See
1101+
// https://gcc.gnu.org/PR72814 for details.
1102+
if (result != void_type_node && int_size_in_bytes (result) == 0)
1103+
result = void_type_node;
1104+
1105+
tree fntype = build_varargs_function_type_array (result, n, args);
1106+
if (fntype == error_mark_node)
1107+
return this->error_type ();
1108+
1109+
return this->make_type (build_pointer_type (fntype));
1110+
}
1111+
10511112
Btype *
10521113
Gcc_backend::function_ptr_type (Btype *result_type,
10531114
const std::vector<Btype *> &parameters,

gcc/rust/typecheck/rust-hir-type-check-implitem.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,15 @@ class TypeCheckTopLevelExternItem : public TypeCheckBase
111111
context->insert_type (param.get_mappings (), param_tyty);
112112
}
113113

114-
auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
115-
function.get_mappings ().get_defid (),
116-
function.get_item_name (),
117-
FNTYPE_IS_EXTERN_FLAG, std::move (params),
118-
ret_type, std::move (substitutions));
114+
uint8_t flags = FNTYPE_IS_EXTERN_FLAG;
115+
if (function.is_variadic ())
116+
flags |= FNTYPE_IS_VARADIC_FLAG;
117+
118+
auto fnType
119+
= new TyTy::FnType (function.get_mappings ().get_hirid (),
120+
function.get_mappings ().get_defid (),
121+
function.get_item_name (), flags, std::move (params),
122+
ret_type, std::move (substitutions));
119123
context->insert_type (function.get_mappings (), fnType);
120124
}
121125

gcc/rust/typecheck/rust-tyty.cc

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,15 +2055,27 @@ TypeCheckCallExpr::visit (FnType &type)
20552055
{
20562056
if (call.num_params () != type.num_params ())
20572057
{
2058-
rust_error_at (call.get_locus (),
2059-
"unexpected number of arguments %lu expected %lu",
2060-
call.num_params (), type.num_params ());
2061-
return;
2058+
if (type.is_varadic ())
2059+
{
2060+
if (call.num_params () < type.num_params ())
2061+
{
2062+
rust_error_at (call.get_locus (),
2063+
"unexpected number of arguments %lu expected %lu",
2064+
call.num_params (), type.num_params ());
2065+
return;
2066+
}
2067+
}
2068+
else
2069+
{
2070+
rust_error_at (call.get_locus (),
2071+
"unexpected number of arguments %lu expected %lu",
2072+
call.num_params (), type.num_params ());
2073+
return;
2074+
}
20622075
}
20632076

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

2075-
auto resolved_argument_type = fnparam.second->unify (argument_expr_tyty);
2076-
if (resolved_argument_type == nullptr)
2087+
auto resolved_argument_type = argument_expr_tyty;
2088+
2089+
// it might be a varadic function
2090+
if (i < type.num_params ())
20772091
{
2078-
rust_error_at (param->get_locus_slow (),
2079-
"Type Resolution failure on parameter");
2080-
return false;
2092+
auto fnparam = type.param_at (i);
2093+
resolved_argument_type = fnparam.second->unify (argument_expr_tyty);
2094+
if (resolved_argument_type == nullptr)
2095+
{
2096+
rust_error_at (param->get_locus_slow (),
2097+
"Type Resolution failure on parameter");
2098+
return false;
2099+
}
20812100
}
20822101

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

2089-
if (i != call.num_params ())
2108+
if (i < call.num_params ())
20902109
{
20912110
rust_error_at (call.get_locus (),
20922111
"unexpected number of arguments %lu expected %lu", i,

gcc/rust/typecheck/rust-tyty.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ class FnType : public BaseType, public SubstitutionRef
973973
#define FNTYPE_DEFAULT_FLAGS 0x00
974974
#define FNTYPE_IS_METHOD_FLAG 0x01
975975
#define FNTYPE_IS_EXTERN_FLAG 0x02
976+
#define FNTYPE_IS_VARADIC_FLAG 0X04
976977

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

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

1035+
bool is_varadic () const { return (flags & FNTYPE_IS_VARADIC_FLAG) != 0; }
1036+
10341037
DefId get_id () const { return id; }
10351038

10361039
// get the Self type for the method
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* { dg-output "Hello World 123\n" }*/
2+
extern "C" {
3+
fn printf(s: *const i8, ...);
4+
}
5+
6+
fn main() -> i32 {
7+
unsafe {
8+
let a = "Hello World %i\n";
9+
let b = a as *const str;
10+
let c = b as *const i8;
11+
12+
printf(c, 123);
13+
}
14+
0
15+
}

0 commit comments

Comments
 (0)