ability to call external variadic functions

This commit is contained in:
Andrew Kelley 2015-12-09 01:03:04 -07:00
parent 4eff5f114b
commit dfda85e870
10 changed files with 121 additions and 29 deletions

View file

@ -58,7 +58,6 @@ compromises backward compatibility.
* structs
* loops
* enums
* calling external variadic functions and exporting variadic functions
* inline assembly and syscalls
* conditional compilation and ability to check target platform and architecture
* main function with command line arguments
@ -69,6 +68,9 @@ compromises backward compatibility.
* static initializers
* assert
* function pointers
* hex literal, binary literal, float literal, hex float literal
* += and -= operators
* fix a + b + c
* running code at compile time
* standard library print functions
* panic! macro or statement that prints a stack trace to stderr in debug mode
@ -144,7 +146,7 @@ FnDef : FnProto Block
ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
ParamDecl : token(Symbol) token(Colon) Type
ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType

View file

@ -2,11 +2,11 @@ export executable "hello";
#link("c")
extern {
fn puts(s: *const u8) -> i32;
fn exit(code: i32) -> unreachable;
fn printf(__format: *const u8, ...) -> i32;
fn exit(__status: i32) -> unreachable;
}
export fn _start() -> unreachable {
puts("Hello, world!");
printf("Hello, world!\n");
exit(0);
}

View file

@ -163,9 +163,12 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
{
resolve_type(g, node->data.type.child_type);
TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
assert(child_type);
if (child_type == g->builtin_types.entry_unreachable) {
add_node_error(g, node,
buf_create_from_str("pointer to unreachable not allowed"));
} else if (child_type->id == TypeTableEntryIdInvalid) {
return child_type;
}
type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const);
return type_node->entry;
@ -312,6 +315,10 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
skip = true;
}
}
if (proto_node->data.fn_proto.is_var_args) {
add_node_error(g, node,
buf_sprintf("variadic arguments only allowed in extern functions"));
}
if (!skip) {
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
fn_table_entry->import_entry = import;
@ -743,7 +750,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
// count parameters
int expected_param_count = fn_proto->params.length;
int actual_param_count = node->data.fn_call_expr.params.length;
if (expected_param_count != actual_param_count) {
if (fn_proto->is_var_args) {
if (actual_param_count < expected_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected at least %d, got %d.",
expected_param_count, actual_param_count));
}
} else if (expected_param_count != actual_param_count) {
add_node_error(g, node,
buf_sprintf("wrong number of arguments. Expected %d, got %d.",
expected_param_count, actual_param_count));

View file

@ -138,19 +138,32 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
fn_table_entry = g->fn_table.get(name);
assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length;
AstNodeFnProto *fn_proto_data = &fn_table_entry->proto_node->data.fn_proto;
int expected_param_count = fn_proto_data->params.length;
int actual_param_count = node->data.fn_call_expr.params.length;
assert(expected_param_count == actual_param_count);
bool is_var_args = fn_proto_data->is_var_args;
assert((is_var_args && actual_param_count >= expected_param_count) ||
actual_param_count == expected_param_count);
// don't really include void values
int gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params);
int gen_param_count;
if (is_var_args) {
gen_param_count = actual_param_count;
} else {
gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params);
}
LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count);
int loop_end = max(gen_param_count, actual_param_count);
int gen_param_index = 0;
for (int i = 0; i < actual_param_count; i += 1) {
for (int i = 0; i < loop_end; i += 1) {
AstNode *expr_node = node->data.fn_call_expr.params.at(i);
LLVMValueRef param_value = gen_expr(g, expr_node);
if (!is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i))) {
if (is_var_args ||
!is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i)))
{
gen_param_values[gen_param_index] = param_value;
gen_param_index += 1;
}
@ -773,7 +786,7 @@ static void do_code_gen(CodeGen *g) {
param_types[gen_param_index] = to_llvm_type(type_node);
gen_param_index += 1;
}
LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, 0);
LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args);
LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type);
LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);

View file

@ -132,10 +132,10 @@ static bool resolves_to_void(ParseH *p, CXType raw_type) {
static Buf *to_zig_type(ParseH *p, CXType raw_type) {
if (raw_type.kind == CXType_Unexposed) {
CXType canonical = clang_getCanonicalType(raw_type);
if (canonical.kind != CXType_Unexposed)
return to_zig_type(p, canonical);
else
if (canonical.kind == CXType_Unexposed)
zig_panic("clang C api insufficient");
else
return to_zig_type(p, canonical);
}
switch (raw_type.kind) {
case CXType_Invalid:
@ -453,6 +453,10 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl
} else if (underlying_type.kind == CXType_Record) {
CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type);
skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false);
} else if (underlying_type.kind == CXType_Invalid) {
fprintf(stderr, "warning: invalid type\n");
print_location(p);
skip_typedef = true;
} else {
skip_typedef = false;
}

View file

@ -517,32 +517,39 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
}
/*
ParamDecl : token(Symbol) token(Colon) Type
ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
*/
static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {
Token *param_name = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, param_name, TokenIdSymbol);
AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
if (param_name->id == TokenIdSymbol) {
AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
Token *colon = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, colon, TokenIdColon);
Token *colon = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, colon, TokenIdColon);
node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index);
node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index);
*new_token_index = token_index;
return node;
*new_token_index = token_index;
return node;
} else if (param_name->id == TokenIdEllipse) {
*new_token_index = token_index;
return nullptr;
} else {
ast_invalid_token_error(pc, param_name);
}
}
static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *new_token_index,
ZigList<AstNode *> *params)
ZigList<AstNode *> *params, bool *is_var_args)
{
*is_var_args = false;
Token *l_paren = &pc->tokens->at(token_index);
token_index += 1;
ast_expect_token(pc, l_paren, TokenIdLParen);
@ -556,13 +563,21 @@ static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *ne
for (;;) {
AstNode *param_decl_node = ast_parse_param_decl(pc, token_index, &token_index);
params->append(param_decl_node);
bool expect_end = false;
if (param_decl_node) {
params->append(param_decl_node);
} else {
*is_var_args = true;
expect_end = true;
}
Token *token = &pc->tokens->at(token_index);
token_index += 1;
if (token->id == TokenIdRParen) {
*new_token_index = token_index;
return;
} else if (expect_end) {
ast_invalid_token_error(pc, token);
} else {
ast_expect_token(pc, token, TokenIdComma);
}
@ -1421,7 +1436,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);
ast_parse_param_decl_list(pc, *token_index, token_index, &node->data.fn_proto.params);
ast_parse_param_decl_list(pc, *token_index, token_index,
&node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
Token *arrow = &pc->tokens->at(*token_index);
if (arrow->id == TokenIdArrow) {

View file

@ -63,6 +63,7 @@ struct AstNodeFnProto {
Buf name;
ZigList<AstNode *> params;
AstNode *return_type;
bool is_var_args;
};
struct AstNodeFnDef {

View file

@ -104,6 +104,8 @@ enum TokenizeState {
TokenizeStateBang,
TokenizeStateLessThan,
TokenizeStateGreaterThan,
TokenizeStateDot,
TokenizeStateDotDot,
TokenizeStateError,
};
@ -323,10 +325,40 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdCmpGreaterThan);
t.state = TokenizeStateGreaterThan;
break;
case '.':
begin_token(&t, TokenIdDot);
t.state = TokenizeStateDot;
break;
default:
tokenize_error(&t, "invalid character: '%c'", c);
}
break;
case TokenizeStateDot:
switch (c) {
case '.':
t.state = TokenizeStateDotDot;
t.cur_tok->id = TokenIdEllipse;
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateDotDot:
switch (c) {
case '.':
t.state = TokenizeStateStart;
end_token(&t);
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateGreaterThan:
switch (c) {
case '=':
@ -561,9 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateBang:
case TokenizeStateLessThan:
case TokenizeStateGreaterThan:
case TokenizeStateDot:
end_token(&t);
break;
case TokenizeStateSawSlash:
case TokenizeStateDotDot:
tokenize_error(&t, "unexpected EOF");
break;
case TokenizeStateLineComment:
@ -637,6 +671,8 @@ static const char * token_name(Token *token) {
case TokenIdBitShiftRight: return "BitShiftRight";
case TokenIdSlash: return "Slash";
case TokenIdPercent: return "Percent";
case TokenIdDot: return "Dot";
case TokenIdEllipse: return "Ellipse";
}
return "(invalid token)";
}

View file

@ -64,6 +64,8 @@ enum TokenId {
TokenIdBitShiftRight,
TokenIdSlash,
TokenIdPercent,
TokenIdDot,
TokenIdEllipse,
};
struct Token {

View file

@ -577,6 +577,11 @@ fn f() {
".tmp_source.zig:5:8: error: array subscripts must be integers",
".tmp_source.zig:5:19: error: array access of non-array",
".tmp_source.zig:5:19: error: array subscripts must be integers");
add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE(
fn f(...) {}
)SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions");
}
static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {