mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
ability to call external variadic functions
This commit is contained in:
parent
4eff5f114b
commit
dfda85e870
10 changed files with 121 additions and 29 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ struct AstNodeFnProto {
|
|||
Buf name;
|
||||
ZigList<AstNode *> params;
|
||||
AstNode *return_type;
|
||||
bool is_var_args;
|
||||
};
|
||||
|
||||
struct AstNodeFnDef {
|
||||
|
|
|
|||
|
|
@ -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)";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ enum TokenId {
|
|||
TokenIdBitShiftRight,
|
||||
TokenIdSlash,
|
||||
TokenIdPercent,
|
||||
TokenIdDot,
|
||||
TokenIdEllipse,
|
||||
};
|
||||
|
||||
struct Token {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue