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....@@ -58,7 +58,6 @@ compromises backward compatibility.
58 * structs58 * structs
59 * loops59 * loops
60 * enums60 * enums
61 * calling external variadic functions and exporting variadic functions
62 * inline assembly and syscalls61 * inline assembly and syscalls
63 * conditional compilation and ability to check target platform and architecture62 * conditional compilation and ability to check target platform and architecture
64 * main function with command line arguments63 * main function with command line arguments
...@@ -69,6 +68,9 @@ compromises backward compatibility....@@ -69,6 +68,9 @@ compromises backward compatibility.
69 * static initializers68 * static initializers
70 * assert69 * assert
71 * function pointers70 * function pointers
71 * hex literal, binary literal, float literal, hex float literal
72 * += and -= operators
73 * fix a + b + c
72 * running code at compile time74 * running code at compile time
73 * standard library print functions75 * standard library print functions
74 * panic! macro or statement that prints a stack trace to stderr in debug mode76 * panic! macro or statement that prints a stack trace to stderr in debug mode
...@@ -144,7 +146,7 @@ FnDef : FnProto Block...@@ -144,7 +146,7 @@ FnDef : FnProto Block
144146
145ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)147ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
146148
147ParamDecl : token(Symbol) token(Colon) Type149ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
148150
149Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType151Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType
150152
example/hello_world/hello.zig+3-3
...@@ -2,11 +2,11 @@ export executable "hello";...@@ -2,11 +2,11 @@ export executable "hello";
22
3#link("c")3#link("c")
4extern {4extern {
5 fn puts(s: *const u8) -> i32;5 fn printf(__format: *const u8, ...) -> i32;
6 fn exit(code: i32) -> unreachable;6 fn exit(__status: i32) -> unreachable;
7}7}
88
9export fn _start() -> unreachable {9export fn _start() -> unreachable {
10 puts("Hello, world!");10 printf("Hello, world!\n");
11 exit(0);11 exit(0);
12}12}
src/analyze.cpp+14-1
...@@ -163,9 +163,12 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {...@@ -163,9 +163,12 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
163 {163 {
164 resolve_type(g, node->data.type.child_type);164 resolve_type(g, node->data.type.child_type);
165 TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;165 TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
166 assert(child_type);
166 if (child_type == g->builtin_types.entry_unreachable) {167 if (child_type == g->builtin_types.entry_unreachable) {
167 add_node_error(g, node,168 add_node_error(g, node,
168 buf_create_from_str("pointer to unreachable not allowed"));169 buf_create_from_str("pointer to unreachable not allowed"));
170 } else if (child_type->id == TypeTableEntryIdInvalid) {
171 return child_type;
169 }172 }
170 type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const);173 type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const);
171 return type_node->entry;174 return type_node->entry;
...@@ -312,6 +315,10 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -312,6 +315,10 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
312 skip = true;315 skip = true;
313 }316 }
314 }317 }
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 }
315 if (!skip) {322 if (!skip) {
316 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);323 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
317 fn_table_entry->import_entry = import;324 fn_table_entry->import_entry = import;
...@@ -743,7 +750,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -743,7 +750,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
743 // count parameters750 // count parameters
744 int expected_param_count = fn_proto->params.length;751 int expected_param_count = fn_proto->params.length;
745 int actual_param_count = node->data.fn_call_expr.params.length;752 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) {
747 add_node_error(g, node,760 add_node_error(g, node,
748 buf_sprintf("wrong number of arguments. Expected %d, got %d.",761 buf_sprintf("wrong number of arguments. Expected %d, got %d.",
749 expected_param_count, actual_param_count));762 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) {...@@ -138,19 +138,32 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
138 fn_table_entry = g->fn_table.get(name);138 fn_table_entry = g->fn_table.get(name);
139139
140 assert(fn_table_entry->proto_node->type == NodeTypeFnProto);140 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;
142 int actual_param_count = node->data.fn_call_expr.params.length;144 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
145 // don't really include void values149 // 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 }
147 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count);156 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count);
148157
158 int loop_end = max(gen_param_count, actual_param_count);
159
149 int gen_param_index = 0;160 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) {
151 AstNode *expr_node = node->data.fn_call_expr.params.at(i);162 AstNode *expr_node = node->data.fn_call_expr.params.at(i);
152 LLVMValueRef param_value = gen_expr(g, expr_node);163 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 {
154 gen_param_values[gen_param_index] = param_value;167 gen_param_values[gen_param_index] = param_value;
155 gen_param_index += 1;168 gen_param_index += 1;
156 }169 }
...@@ -773,7 +786,7 @@ static void do_code_gen(CodeGen *g) {...@@ -773,7 +786,7 @@ static void do_code_gen(CodeGen *g) {
773 param_types[gen_param_index] = to_llvm_type(type_node);786 param_types[gen_param_index] = to_llvm_type(type_node);
774 gen_param_index += 1;787 gen_param_index += 1;
775 }788 }
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);
777 LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type);790 LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type);
778791
779 LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);792 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) {...@@ -132,10 +132,10 @@ static bool resolves_to_void(ParseH *p, CXType raw_type) {
132static Buf *to_zig_type(ParseH *p, CXType raw_type) {132static Buf *to_zig_type(ParseH *p, CXType raw_type) {
133 if (raw_type.kind == CXType_Unexposed) {133 if (raw_type.kind == CXType_Unexposed) {
134 CXType canonical = clang_getCanonicalType(raw_type);134 CXType canonical = clang_getCanonicalType(raw_type);
135 if (canonical.kind != CXType_Unexposed)135 if (canonical.kind == CXType_Unexposed)
136 return to_zig_type(p, canonical);
137 else
138 zig_panic("clang C api insufficient");136 zig_panic("clang C api insufficient");
137 else
138 return to_zig_type(p, canonical);
139 }139 }
140 switch (raw_type.kind) {140 switch (raw_type.kind) {
141 case CXType_Invalid:141 case CXType_Invalid:
...@@ -453,6 +453,10 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl...@@ -453,6 +453,10 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl
453 } else if (underlying_type.kind == CXType_Record) {453 } else if (underlying_type.kind == CXType_Record) {
454 CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type);454 CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type);
455 skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false);455 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;
456 } else {460 } else {
457 skip_typedef = false;461 skip_typedef = false;
458 }462 }
src/parser.cpp+30-14
...@@ -517,32 +517,39 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token...@@ -517,32 +517,39 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
517}517}
518518
519/*519/*
520ParamDecl : token(Symbol) token(Colon) Type520ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse)
521*/521*/
522static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {522static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {
523 Token *param_name = &pc->tokens->at(token_index);523 Token *param_name = &pc->tokens->at(token_index);
524 token_index += 1;524 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 Token *colon = &pc->tokens->at(token_index);
531532 token_index += 1;
532 Token *colon = &pc->tokens->at(token_index);533 ast_expect_token(pc, colon, TokenIdColon);
533 token_index += 1;
534 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;537 *new_token_index = token_index;
539 return node;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 }
540}545}
541546
542547
543static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *new_token_index,548static 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)
545{550{
551 *is_var_args = false;
552
546 Token *l_paren = &pc->tokens->at(token_index);553 Token *l_paren = &pc->tokens->at(token_index);
547 token_index += 1;554 token_index += 1;
548 ast_expect_token(pc, l_paren, TokenIdLParen);555 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...@@ -556,13 +563,21 @@ static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *ne
556563
557 for (;;) {564 for (;;) {
558 AstNode *param_decl_node = ast_parse_param_decl(pc, token_index, &token_index);565 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
561 Token *token = &pc->tokens->at(token_index);574 Token *token = &pc->tokens->at(token_index);
562 token_index += 1;575 token_index += 1;
563 if (token->id == TokenIdRParen) {576 if (token->id == TokenIdRParen) {
564 *new_token_index = token_index;577 *new_token_index = token_index;
565 return;578 return;
579 } else if (expect_end) {
580 ast_invalid_token_error(pc, token);
566 } else {581 } else {
567 ast_expect_token(pc, token, TokenIdComma);582 ast_expect_token(pc, token, TokenIdComma);
568 }583 }
...@@ -1421,7 +1436,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand...@@ -1421,7 +1436,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
1421 ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);1436 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
1426 Token *arrow = &pc->tokens->at(*token_index);1442 Token *arrow = &pc->tokens->at(*token_index);
1427 if (arrow->id == TokenIdArrow) {1443 if (arrow->id == TokenIdArrow) {
src/parser.hpp+1
...@@ -63,6 +63,7 @@ struct AstNodeFnProto {...@@ -63,6 +63,7 @@ struct AstNodeFnProto {
63 Buf name;63 Buf name;
64 ZigList<AstNode *> params;64 ZigList<AstNode *> params;
65 AstNode *return_type;65 AstNode *return_type;
66 bool is_var_args;
66};67};
6768
68struct AstNodeFnDef {69struct AstNodeFnDef {
src/tokenizer.cpp+36
...@@ -104,6 +104,8 @@ enum TokenizeState {...@@ -104,6 +104,8 @@ enum TokenizeState {
104 TokenizeStateBang,104 TokenizeStateBang,
105 TokenizeStateLessThan,105 TokenizeStateLessThan,
106 TokenizeStateGreaterThan,106 TokenizeStateGreaterThan,
107 TokenizeStateDot,
108 TokenizeStateDotDot,
107 TokenizeStateError,109 TokenizeStateError,
108};110};
109111
...@@ -323,10 +325,40 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -323,10 +325,40 @@ void tokenize(Buf *buf, Tokenization *out) {
323 begin_token(&t, TokenIdCmpGreaterThan);325 begin_token(&t, TokenIdCmpGreaterThan);
324 t.state = TokenizeStateGreaterThan;326 t.state = TokenizeStateGreaterThan;
325 break;327 break;
328 case '.':
329 begin_token(&t, TokenIdDot);
330 t.state = TokenizeStateDot;
331 break;
326 default:332 default:
327 tokenize_error(&t, "invalid character: '%c'", c);333 tokenize_error(&t, "invalid character: '%c'", c);
328 }334 }
329 break;335 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;
330 case TokenizeStateGreaterThan:362 case TokenizeStateGreaterThan:
331 switch (c) {363 switch (c) {
332 case '=':364 case '=':
...@@ -561,9 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -561,9 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) {
561 case TokenizeStateBang:593 case TokenizeStateBang:
562 case TokenizeStateLessThan:594 case TokenizeStateLessThan:
563 case TokenizeStateGreaterThan:595 case TokenizeStateGreaterThan:
596 case TokenizeStateDot:
564 end_token(&t);597 end_token(&t);
565 break;598 break;
566 case TokenizeStateSawSlash:599 case TokenizeStateSawSlash:
600 case TokenizeStateDotDot:
567 tokenize_error(&t, "unexpected EOF");601 tokenize_error(&t, "unexpected EOF");
568 break;602 break;
569 case TokenizeStateLineComment:603 case TokenizeStateLineComment:
...@@ -637,6 +671,8 @@ static const char * token_name(Token *token) {...@@ -637,6 +671,8 @@ static const char * token_name(Token *token) {
637 case TokenIdBitShiftRight: return "BitShiftRight";671 case TokenIdBitShiftRight: return "BitShiftRight";
638 case TokenIdSlash: return "Slash";672 case TokenIdSlash: return "Slash";
639 case TokenIdPercent: return "Percent";673 case TokenIdPercent: return "Percent";
674 case TokenIdDot: return "Dot";
675 case TokenIdEllipse: return "Ellipse";
640 }676 }
641 return "(invalid token)";677 return "(invalid token)";
642}678}
src/tokenizer.hpp+2
...@@ -64,6 +64,8 @@ enum TokenId {...@@ -64,6 +64,8 @@ enum TokenId {
64 TokenIdBitShiftRight,64 TokenIdBitShiftRight,
65 TokenIdSlash,65 TokenIdSlash,
66 TokenIdPercent,66 TokenIdPercent,
67 TokenIdDot,
68 TokenIdEllipse,
67};69};
6870
69struct Token {71struct Token {
test/run_tests.cpp+5
...@@ -577,6 +577,11 @@ fn f() {...@@ -577,6 +577,11 @@ fn f() {
577 ".tmp_source.zig:5:8: error: array subscripts must be integers",577 ".tmp_source.zig:5:8: error: array subscripts must be integers",
578 ".tmp_source.zig:5:19: error: array access of non-array",578 ".tmp_source.zig:5:19: error: array access of non-array",
579 ".tmp_source.zig:5:19: error: array subscripts must be integers");579 ".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
580}585}
581586
582static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {587static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {