authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-09 01:03:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-09 01:03:04-07:00
logdfda85e870df1b0620c418940db3b946fdd3d620
tree94d4d7e158f62ee2cd153b002165bc3ef7aa7c47
parent4eff5f114b463ddd887665129f2e1d29b0f13b7f

ability to call external variadic functions


10 files changed, 121 insertions(+), 29 deletions(-)

README.md+4-2
......@@ -58,7 +58,6 @@ compromises backward compatibility.
5858 * structs
5959 * loops
6060 * enums
61 * calling external variadic functions and exporting variadic functions
6261 * inline assembly and syscalls
6362 * conditional compilation and ability to check target platform and architecture
6463 * main function with command line arguments
......@@ -69,6 +68,9 @@ compromises backward compatibility.
6968 * static initializers
7069 * assert
7170 * function pointers
71 * hex literal, binary literal, float literal, hex float literal
72 * += and -= operators
73 * fix a + b + c
7274 * running code at compile time
7375 * standard library print functions
7476 * panic! macro or statement that prints a stack trace to stderr in debug mode
......@@ -144,7 +146,7 @@ FnDef : FnProto Block
144146
145147ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
146148
147ParamDecl : token(Symbol) token(Colon) Type
149ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
148150
149151Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType
150152
example/hello_world/hello.zig+3-3
......@@ -2,11 +2,11 @@ export executable "hello";
22
33#link("c")
44extern {
5 fn puts(s: *const u8) -> i32;
6 fn exit(code: i32) -> unreachable;
5 fn printf(__format: *const u8, ...) -> i32;
6 fn exit(__status: i32) -> unreachable;
77}
88
99export fn _start() -> unreachable {
10 puts("Hello, world!");
10 printf("Hello, world!\n");
1111 exit(0);
1212}
src/analyze.cpp+14-1
......@@ -163,9 +163,12 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
163163 {
164164 resolve_type(g, node->data.type.child_type);
165165 TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
166 assert(child_type);
166167 if (child_type == g->builtin_types.entry_unreachable) {
167168 add_node_error(g, node,
168169 buf_create_from_str("pointer to unreachable not allowed"));
170 } else if (child_type->id == TypeTableEntryIdInvalid) {
171 return child_type;
169172 }
170173 type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const);
171174 return type_node->entry;
......@@ -312,6 +315,10 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
312315 skip = true;
313316 }
314317 }
318 if (proto_node->data.fn_proto.is_var_args) {
319 add_node_error(g, node,
320 buf_sprintf("variadic arguments only allowed in extern functions"));
321 }
315322 if (!skip) {
316323 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
317324 fn_table_entry->import_entry = import;
......@@ -743,7 +750,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
743750 // count parameters
744751 int expected_param_count = fn_proto->params.length;
745752 int actual_param_count = node->data.fn_call_expr.params.length;
746 if (expected_param_count != actual_param_count) {
753 if (fn_proto->is_var_args) {
754 if (actual_param_count < expected_param_count) {
755 add_node_error(g, node,
756 buf_sprintf("wrong number of arguments. Expected at least %d, got %d.",
757 expected_param_count, actual_param_count));
758 }
759 } else if (expected_param_count != actual_param_count) {
747760 add_node_error(g, node,
748761 buf_sprintf("wrong number of arguments. Expected %d, got %d.",
749762 expected_param_count, actual_param_count));
src/codegen.cpp+19-6
......@@ -138,19 +138,32 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
138138 fn_table_entry = g->fn_table.get(name);
139139
140140 assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
141 int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length;
141 AstNodeFnProto *fn_proto_data = &fn_table_entry->proto_node->data.fn_proto;
142
143 int expected_param_count = fn_proto_data->params.length;
142144 int actual_param_count = node->data.fn_call_expr.params.length;
143 assert(expected_param_count == actual_param_count);
145 bool is_var_args = fn_proto_data->is_var_args;
146 assert((is_var_args && actual_param_count >= expected_param_count) ||
147 actual_param_count == expected_param_count);
144148
145149 // don't really include void values
146 int gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params);
150 int gen_param_count;
151 if (is_var_args) {
152 gen_param_count = actual_param_count;
153 } else {
154 gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params);
155 }
147156 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count);
148157
158 int loop_end = max(gen_param_count, actual_param_count);
159
149160 int gen_param_index = 0;
150 for (int i = 0; i < actual_param_count; i += 1) {
161 for (int i = 0; i < loop_end; i += 1) {
151162 AstNode *expr_node = node->data.fn_call_expr.params.at(i);
152163 LLVMValueRef param_value = gen_expr(g, expr_node);
153 if (!is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i))) {
164 if (is_var_args ||
165 !is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i)))
166 {
154167 gen_param_values[gen_param_index] = param_value;
155168 gen_param_index += 1;
156169 }
......@@ -773,7 +786,7 @@ static void do_code_gen(CodeGen *g) {
773786 param_types[gen_param_index] = to_llvm_type(type_node);
774787 gen_param_index += 1;
775788 }
776 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, 0);
789 LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args);
777790 LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type);
778791
779792 LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);
src/parseh.cpp+7-3
......@@ -132,10 +132,10 @@ static bool resolves_to_void(ParseH *p, CXType raw_type) {
132132static Buf *to_zig_type(ParseH *p, CXType raw_type) {
133133 if (raw_type.kind == CXType_Unexposed) {
134134 CXType canonical = clang_getCanonicalType(raw_type);
135 if (canonical.kind != CXType_Unexposed)
136 return to_zig_type(p, canonical);
137 else
135 if (canonical.kind == CXType_Unexposed)
138136 zig_panic("clang C api insufficient");
137 else
138 return to_zig_type(p, canonical);
139139 }
140140 switch (raw_type.kind) {
141141 case CXType_Invalid:
......@@ -453,6 +453,10 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl
453453 } else if (underlying_type.kind == CXType_Record) {
454454 CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type);
455455 skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false);
456 } else if (underlying_type.kind == CXType_Invalid) {
457 fprintf(stderr, "warning: invalid type\n");
458 print_location(p);
459 skip_typedef = true;
456460 } else {
457461 skip_typedef = false;
458462 }
src/parser.cpp+30-14
......@@ -517,32 +517,39 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
517517}
518518
519519/*
520ParamDecl : token(Symbol) token(Colon) Type
520ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
521521*/
522522static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {
523523 Token *param_name = &pc->tokens->at(token_index);
524524 token_index += 1;
525 ast_expect_token(pc, param_name, TokenIdSymbol);
526525
527 AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
526 if (param_name->id == TokenIdSymbol) {
527 AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
528528
529 ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
529530
530 ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
531
532 Token *colon = &pc->tokens->at(token_index);
533 token_index += 1;
534 ast_expect_token(pc, colon, TokenIdColon);
531 Token *colon = &pc->tokens->at(token_index);
532 token_index += 1;
533 ast_expect_token(pc, colon, TokenIdColon);
535534
536 node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index);
535 node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index);
537536
538 *new_token_index = token_index;
539 return node;
537 *new_token_index = token_index;
538 return node;
539 } else if (param_name->id == TokenIdEllipse) {
540 *new_token_index = token_index;
541 return nullptr;
542 } else {
543 ast_invalid_token_error(pc, param_name);
544 }
540545}
541546
542547
543548static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *new_token_index,
544 ZigList<AstNode *> *params)
549 ZigList<AstNode *> *params, bool *is_var_args)
545550{
551 *is_var_args = false;
552
546553 Token *l_paren = &pc->tokens->at(token_index);
547554 token_index += 1;
548555 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
556563
557564 for (;;) {
558565 AstNode *param_decl_node = ast_parse_param_decl(pc, token_index, &token_index);
559 params->append(param_decl_node);
566 bool expect_end = false;
567 if (param_decl_node) {
568 params->append(param_decl_node);
569 } else {
570 *is_var_args = true;
571 expect_end = true;
572 }
560573
561574 Token *token = &pc->tokens->at(token_index);
562575 token_index += 1;
563576 if (token->id == TokenIdRParen) {
564577 *new_token_index = token_index;
565578 return;
579 } else if (expect_end) {
580 ast_invalid_token_error(pc, token);
566581 } else {
567582 ast_expect_token(pc, token, TokenIdComma);
568583 }
......@@ -1421,7 +1436,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
14211436 ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);
14221437
14231438
1424 ast_parse_param_decl_list(pc, *token_index, token_index, &node->data.fn_proto.params);
1439 ast_parse_param_decl_list(pc, *token_index, token_index,
1440 &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
14251441
14261442 Token *arrow = &pc->tokens->at(*token_index);
14271443 if (arrow->id == TokenIdArrow) {
src/parser.hpp+1
......@@ -63,6 +63,7 @@ struct AstNodeFnProto {
6363 Buf name;
6464 ZigList<AstNode *> params;
6565 AstNode *return_type;
66 bool is_var_args;
6667};
6768
6869struct AstNodeFnDef {
src/tokenizer.cpp+36
......@@ -104,6 +104,8 @@ enum TokenizeState {
104104 TokenizeStateBang,
105105 TokenizeStateLessThan,
106106 TokenizeStateGreaterThan,
107 TokenizeStateDot,
108 TokenizeStateDotDot,
107109 TokenizeStateError,
108110};
109111
......@@ -323,10 +325,40 @@ void tokenize(Buf *buf, Tokenization *out) {
323325 begin_token(&t, TokenIdCmpGreaterThan);
324326 t.state = TokenizeStateGreaterThan;
325327 break;
328 case '.':
329 begin_token(&t, TokenIdDot);
330 t.state = TokenizeStateDot;
331 break;
326332 default:
327333 tokenize_error(&t, "invalid character: '%c'", c);
328334 }
329335 break;
336 case TokenizeStateDot:
337 switch (c) {
338 case '.':
339 t.state = TokenizeStateDotDot;
340 t.cur_tok->id = TokenIdEllipse;
341 break;
342 default:
343 t.pos -= 1;
344 end_token(&t);
345 t.state = TokenizeStateStart;
346 continue;
347 }
348 break;
349 case TokenizeStateDotDot:
350 switch (c) {
351 case '.':
352 t.state = TokenizeStateStart;
353 end_token(&t);
354 break;
355 default:
356 t.pos -= 1;
357 end_token(&t);
358 t.state = TokenizeStateStart;
359 continue;
360 }
361 break;
330362 case TokenizeStateGreaterThan:
331363 switch (c) {
332364 case '=':
......@@ -561,9 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) {
561593 case TokenizeStateBang:
562594 case TokenizeStateLessThan:
563595 case TokenizeStateGreaterThan:
596 case TokenizeStateDot:
564597 end_token(&t);
565598 break;
566599 case TokenizeStateSawSlash:
600 case TokenizeStateDotDot:
567601 tokenize_error(&t, "unexpected EOF");
568602 break;
569603 case TokenizeStateLineComment:
......@@ -637,6 +671,8 @@ static const char * token_name(Token *token) {
637671 case TokenIdBitShiftRight: return "BitShiftRight";
638672 case TokenIdSlash: return "Slash";
639673 case TokenIdPercent: return "Percent";
674 case TokenIdDot: return "Dot";
675 case TokenIdEllipse: return "Ellipse";
640676 }
641677 return "(invalid token)";
642678}
src/tokenizer.hpp+2
......@@ -64,6 +64,8 @@ enum TokenId {
6464 TokenIdBitShiftRight,
6565 TokenIdSlash,
6666 TokenIdPercent,
67 TokenIdDot,
68 TokenIdEllipse,
6769};
6870
6971struct Token {
test/run_tests.cpp+5
......@@ -577,6 +577,11 @@ fn f() {
577577 ".tmp_source.zig:5:8: error: array subscripts must be integers",
578578 ".tmp_source.zig:5:19: error: array access of non-array",
579579 ".tmp_source.zig:5:19: error: array subscripts must be integers");
580
581 add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE(
582fn f(...) {}
583 )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions");
584
580585}
581586
582587static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {