Commit dfda85e870
Changed files (10)
example
hello_world
test
example/hello_world/hello.zig
@@ -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);
}
src/analyze.cpp
@@ -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));
src/codegen.cpp
@@ -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);
src/parseh.cpp
@@ -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;
}
src/parser.cpp
@@ -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) {
src/parser.hpp
@@ -63,6 +63,7 @@ struct AstNodeFnProto {
Buf name;
ZigList<AstNode *> params;
AstNode *return_type;
+ bool is_var_args;
};
struct AstNodeFnDef {
src/tokenizer.cpp
@@ -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)";
}
src/tokenizer.hpp
@@ -64,6 +64,8 @@ enum TokenId {
TokenIdBitShiftRight,
TokenIdSlash,
TokenIdPercent,
+ TokenIdDot,
+ TokenIdEllipse,
};
struct Token {
test/run_tests.cpp
@@ -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) {
README.md
@@ -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