| author | |
| committer | |
| log | 2ed949a6ae053262ee563b198a8758ba82f50e84 |
| tree | 6bb367b942f9f4b319a9e1b452260e9a7725d6a3 |
| parent | 0d5ecc4312f45f9288c4a349837f04b733405960 |
11 files changed, 99 insertions(+), 14 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -151,7 +151,7 @@ GotoExpression = "goto" Symbol |
| 151 | 151 | |
| 152 | 152 | GroupedExpression = "(" Expression ")" |
| 153 | 153 | |
| 154 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | |
| 154 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "zeroes" | "error" | "type" | |
| 155 | 155 | ``` |
| 156 | 156 | |
| 157 | 157 | ## Operator Precedence |
src/all_types.hpp+17-2| ... | ... | @@ -62,11 +62,18 @@ struct ConstErrValue { |
| 62 | 62 | ConstExprValue *payload; |
| 63 | 63 | }; |
| 64 | 64 | |
| 65 | enum ConstValSpecial { | |
| 66 | ConstValSpecialOther, | |
| 67 | ConstValSpecialUndef, | |
| 68 | ConstValSpecialZeroes, | |
| 69 | }; | |
| 70 | ||
| 65 | 71 | struct ConstExprValue { |
| 66 | bool ok; // true if constant expression evalution worked | |
| 72 | bool ok; | |
| 67 | 73 | bool depends_on_compile_var; |
| 68 | bool undef; | |
| 74 | ConstValSpecial special; | |
| 69 | 75 | |
| 76 | // populated if val_type == ConstValTypeOk | |
| 70 | 77 | union { |
| 71 | 78 | BigNum x_bignum; |
| 72 | 79 | bool x_bool; |
| ... | ... | @@ -167,6 +174,7 @@ enum NodeType { |
| 167 | 174 | NodeTypeBoolLiteral, |
| 168 | 175 | NodeTypeNullLiteral, |
| 169 | 176 | NodeTypeUndefinedLiteral, |
| 177 | NodeTypeZeroesLiteral, | |
| 170 | 178 | NodeTypeIfBoolExpr, |
| 171 | 179 | NodeTypeIfVarExpr, |
| 172 | 180 | NodeTypeWhileExpr, |
| ... | ... | @@ -694,6 +702,12 @@ struct AstNodeUndefinedLiteral { |
| 694 | 702 | Expr resolved_expr; |
| 695 | 703 | }; |
| 696 | 704 | |
| 705 | struct AstNodeZeroesLiteral { | |
| 706 | // populated by semantic analyzer | |
| 707 | StructValExprCodeGen resolved_struct_val_expr; | |
| 708 | Expr resolved_expr; | |
| 709 | }; | |
| 710 | ||
| 697 | 711 | struct AstNodeSymbolExpr { |
| 698 | 712 | Buf *symbol; |
| 699 | 713 | |
| ... | ... | @@ -791,6 +805,7 @@ struct AstNode { |
| 791 | 805 | AstNodeStructValueField struct_val_field; |
| 792 | 806 | AstNodeNullLiteral null_literal; |
| 793 | 807 | AstNodeUndefinedLiteral undefined_literal; |
| 808 | AstNodeZeroesLiteral zeroes_literal; | |
| 794 | 809 | AstNodeSymbolExpr symbol_expr; |
| 795 | 810 | AstNodeBoolLiteral bool_literal; |
| 796 | 811 | AstNodeBreakExpr break_expr; |
src/analyze.cpp+23-2| ... | ... | @@ -90,6 +90,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 90 | 90 | case NodeTypeBoolLiteral: |
| 91 | 91 | case NodeTypeNullLiteral: |
| 92 | 92 | case NodeTypeUndefinedLiteral: |
| 93 | case NodeTypeZeroesLiteral: | |
| 93 | 94 | case NodeTypeIfBoolExpr: |
| 94 | 95 | case NodeTypeIfVarExpr: |
| 95 | 96 | case NodeTypeLabel: |
| ... | ... | @@ -1849,6 +1850,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) |
| 1849 | 1850 | case NodeTypeBoolLiteral: |
| 1850 | 1851 | case NodeTypeNullLiteral: |
| 1851 | 1852 | case NodeTypeUndefinedLiteral: |
| 1853 | case NodeTypeZeroesLiteral: | |
| 1852 | 1854 | case NodeTypeSymbol: |
| 1853 | 1855 | case NodeTypePrefixOpExpr: |
| 1854 | 1856 | case NodeTypeIfBoolExpr: |
| ... | ... | @@ -3937,7 +3939,19 @@ static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEnt |
| 3937 | 3939 | ConstExprValue *const_val = &expr->const_val; |
| 3938 | 3940 | |
| 3939 | 3941 | const_val->ok = true; |
| 3940 | const_val->undef = true; | |
| 3942 | const_val->special = ConstValSpecialUndef; | |
| 3943 | ||
| 3944 | return expected_type ? expected_type : g->builtin_types.entry_undef; | |
| 3945 | } | |
| 3946 | ||
| 3947 | static TypeTableEntry *analyze_zeroes_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 3948 | TypeTableEntry *expected_type, AstNode *node) | |
| 3949 | { | |
| 3950 | Expr *expr = get_resolved_expr(node); | |
| 3951 | ConstExprValue *const_val = &expr->const_val; | |
| 3952 | ||
| 3953 | const_val->ok = true; | |
| 3954 | const_val->special = ConstValSpecialZeroes; | |
| 3941 | 3955 | |
| 3942 | 3956 | return expected_type ? expected_type : g->builtin_types.entry_undef; |
| 3943 | 3957 | } |
| ... | ... | @@ -4252,7 +4266,7 @@ static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import |
| 4252 | 4266 | } |
| 4253 | 4267 | |
| 4254 | 4268 | ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; |
| 4255 | if (cond_val->undef) { | |
| 4269 | if (cond_val->special == ConstValSpecialUndef) { | |
| 4256 | 4270 | add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value")); |
| 4257 | 4271 | return cond_type; |
| 4258 | 4272 | } |
| ... | ... | @@ -6504,6 +6518,9 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn |
| 6504 | 6518 | case NodeTypeUndefinedLiteral: |
| 6505 | 6519 | return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node); |
| 6506 | 6520 | break; |
| 6521 | case NodeTypeZeroesLiteral: | |
| 6522 | return_type = analyze_zeroes_literal_expr(g, import, context, expected_type, node); | |
| 6523 | break; | |
| 6507 | 6524 | case NodeTypeSymbol: |
| 6508 | 6525 | return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only); |
| 6509 | 6526 | break; |
| ... | ... | @@ -6771,6 +6788,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte |
| 6771 | 6788 | case NodeTypeBoolLiteral: |
| 6772 | 6789 | case NodeTypeNullLiteral: |
| 6773 | 6790 | case NodeTypeUndefinedLiteral: |
| 6791 | case NodeTypeZeroesLiteral: | |
| 6774 | 6792 | case NodeTypeSymbol: |
| 6775 | 6793 | case NodeTypePrefixOpExpr: |
| 6776 | 6794 | case NodeTypeIfBoolExpr: |
| ... | ... | @@ -7029,6 +7047,8 @@ Expr *get_resolved_expr(AstNode *node) { |
| 7029 | 7047 | return &node->data.null_literal.resolved_expr; |
| 7030 | 7048 | case NodeTypeUndefinedLiteral: |
| 7031 | 7049 | return &node->data.undefined_literal.resolved_expr; |
| 7050 | case NodeTypeZeroesLiteral: | |
| 7051 | return &node->data.zeroes_literal.resolved_expr; | |
| 7032 | 7052 | case NodeTypeGoto: |
| 7033 | 7053 | return &node->data.goto_expr.resolved_expr; |
| 7034 | 7054 | case NodeTypeBreak: |
| ... | ... | @@ -7111,6 +7131,7 @@ static TopLevelDecl *get_as_top_level_decl(AstNode *node) { |
| 7111 | 7131 | case NodeTypeBoolLiteral: |
| 7112 | 7132 | case NodeTypeNullLiteral: |
| 7113 | 7133 | case NodeTypeUndefinedLiteral: |
| 7134 | case NodeTypeZeroesLiteral: | |
| 7114 | 7135 | case NodeTypeLabel: |
| 7115 | 7136 | case NodeTypeGoto: |
| 7116 | 7137 | case NodeTypeBreak: |
src/ast_render.cpp+4| ... | ... | @@ -171,6 +171,8 @@ static const char *node_type_str(NodeType node_type) { |
| 171 | 171 | return "NullLiteral"; |
| 172 | 172 | case NodeTypeUndefinedLiteral: |
| 173 | 173 | return "UndefinedLiteral"; |
| 174 | case NodeTypeZeroesLiteral: | |
| 175 | return "ZeroesLiteral"; | |
| 174 | 176 | case NodeTypeIfBoolExpr: |
| 175 | 177 | return "IfBoolExpr"; |
| 176 | 178 | case NodeTypeIfVarExpr: |
| ... | ... | @@ -591,6 +593,8 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 591 | 593 | zig_panic("TODO"); |
| 592 | 594 | case NodeTypeUndefinedLiteral: |
| 593 | 595 | zig_panic("TODO"); |
| 596 | case NodeTypeZeroesLiteral: | |
| 597 | zig_panic("TODO"); | |
| 594 | 598 | case NodeTypeIfBoolExpr: |
| 595 | 599 | zig_panic("TODO"); |
| 596 | 600 | case NodeTypeIfVarExpr: |
src/codegen.cpp+17-5| ... | ... | @@ -3141,11 +3141,15 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 3141 | 3141 | } |
| 3142 | 3142 | |
| 3143 | 3143 | bool have_init_expr = false; |
| 3144 | bool want_zeroes = false; | |
| 3144 | 3145 | if (var_decl->expr) { |
| 3145 | 3146 | ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val; |
| 3146 | if (!const_val->ok || !const_val->undef) { | |
| 3147 | if (!const_val->ok || const_val->special == ConstValSpecialOther) { | |
| 3147 | 3148 | have_init_expr = true; |
| 3148 | 3149 | } |
| 3150 | if (const_val->ok && const_val->special == ConstValSpecialZeroes) { | |
| 3151 | want_zeroes = true; | |
| 3152 | } | |
| 3149 | 3153 | } |
| 3150 | 3154 | if (have_init_expr) { |
| 3151 | 3155 | TypeTableEntry *expr_type = get_expr_type(var_decl->expr); |
| ... | ... | @@ -3204,7 +3208,8 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 3204 | 3208 | } |
| 3205 | 3209 | } |
| 3206 | 3210 | } |
| 3207 | if (!ignore_uninit && want_debug_safety(g, source_node)) { | |
| 3211 | bool want_safe = want_debug_safety(g, source_node); | |
| 3212 | if (!ignore_uninit && (want_safe || want_zeroes)) { | |
| 3208 | 3213 | TypeTableEntry *usize = g->builtin_types.entry_usize; |
| 3209 | 3214 | uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); |
| 3210 | 3215 | uint64_t align_bytes = get_memcpy_align(g, variable->type); |
| ... | ... | @@ -3212,7 +3217,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa |
| 3212 | 3217 | // memset uninitialized memory to 0xa |
| 3213 | 3218 | set_debug_source_node(g, source_node); |
| 3214 | 3219 | LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); |
| 3215 | LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); | |
| 3220 | LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false); | |
| 3216 | 3221 | LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, ""); |
| 3217 | 3222 | LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); |
| 3218 | 3223 | LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false); |
| ... | ... | @@ -3535,6 +3540,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 3535 | 3540 | case NodeTypeCharLiteral: |
| 3536 | 3541 | case NodeTypeNullLiteral: |
| 3537 | 3542 | case NodeTypeUndefinedLiteral: |
| 3543 | case NodeTypeZeroesLiteral: | |
| 3538 | 3544 | case NodeTypeErrorType: |
| 3539 | 3545 | case NodeTypeTypeLiteral: |
| 3540 | 3546 | case NodeTypeArrayType: |
| ... | ... | @@ -3562,8 +3568,14 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 3562 | 3568 | static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) { |
| 3563 | 3569 | assert(const_val->ok); |
| 3564 | 3570 | |
| 3565 | if (const_val->undef) { | |
| 3566 | return LLVMGetUndef(type_entry->type_ref); | |
| 3571 | switch (const_val->special) { | |
| 3572 | case ConstValSpecialUndef: | |
| 3573 | return LLVMGetUndef(type_entry->type_ref); | |
| 3574 | case ConstValSpecialZeroes: | |
| 3575 | return LLVMConstNull(type_entry->type_ref); | |
| 3576 | case ConstValSpecialOther: | |
| 3577 | break; | |
| 3578 | ||
| 3567 | 3579 | } |
| 3568 | 3580 | |
| 3569 | 3581 | switch (type_entry->id) { |
src/eval.cpp+4-3| ... | ... | @@ -545,7 +545,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, |
| 545 | 545 | ConstExprValue *const_val, TypeTableEntry *new_type) |
| 546 | 546 | { |
| 547 | 547 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; |
| 548 | const_val->undef = other_val->undef; | |
| 548 | const_val->special = other_val->special; | |
| 549 | 549 | |
| 550 | 550 | assert(other_val != const_val); |
| 551 | 551 | switch (cast_op) { |
| ... | ... | @@ -572,7 +572,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, |
| 572 | 572 | const_val->data.x_ptr.ptr = ptr_val; |
| 573 | 573 | const_val->data.x_ptr.len = 1; |
| 574 | 574 | const_val->ok = true; |
| 575 | const_val->undef = other_val->undef; | |
| 575 | const_val->special = other_val->special; | |
| 576 | 576 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; |
| 577 | 577 | } else { |
| 578 | 578 | zig_panic("TODO"); |
| ... | ... | @@ -608,7 +608,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, |
| 608 | 608 | |
| 609 | 609 | const_val->data.x_maybe = ptr_parent; |
| 610 | 610 | const_val->ok = true; |
| 611 | const_val->undef = other_val->undef; | |
| 611 | const_val->special = other_val->special; | |
| 612 | 612 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; |
| 613 | 613 | } else { |
| 614 | 614 | zig_panic("TODO"); |
| ... | ... | @@ -1277,6 +1277,7 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { |
| 1277 | 1277 | case NodeTypeSliceExpr: |
| 1278 | 1278 | case NodeTypeNullLiteral: |
| 1279 | 1279 | case NodeTypeUndefinedLiteral: |
| 1280 | case NodeTypeZeroesLiteral: | |
| 1280 | 1281 | case NodeTypeIfVarExpr: |
| 1281 | 1282 | case NodeTypeSwitchExpr: |
| 1282 | 1283 | case NodeTypeSwitchProng: |
src/parser.cpp+11-1| ... | ... | @@ -606,7 +606,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand |
| 606 | 606 | |
| 607 | 607 | /* |
| 608 | 608 | PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol") |
| 609 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | |
| 609 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "zeroes" | "error" | "type" | |
| 610 | 610 | */ |
| 611 | 611 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 612 | 612 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -654,6 +654,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 654 | 654 | AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); |
| 655 | 655 | *token_index += 1; |
| 656 | 656 | return node; |
| 657 | } else if (token->id == TokenIdKeywordZeroes) { | |
| 658 | AstNode *node = ast_create_node(pc, NodeTypeZeroesLiteral, token); | |
| 659 | *token_index += 1; | |
| 660 | return node; | |
| 657 | 661 | } else if (token->id == TokenIdKeywordType) { |
| 658 | 662 | AstNode *node = ast_create_node(pc, NodeTypeTypeLiteral, token); |
| 659 | 663 | *token_index += 1; |
| ... | ... | @@ -2539,6 +2543,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 2539 | 2543 | case NodeTypeUndefinedLiteral: |
| 2540 | 2544 | // none |
| 2541 | 2545 | break; |
| 2546 | case NodeTypeZeroesLiteral: | |
| 2547 | // none | |
| 2548 | break; | |
| 2542 | 2549 | case NodeTypeIfBoolExpr: |
| 2543 | 2550 | visit_field(&node->data.if_bool_expr.condition, visit, context); |
| 2544 | 2551 | visit_field(&node->data.if_bool_expr.then_block, visit, context); |
| ... | ... | @@ -2813,6 +2820,9 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, |
| 2813 | 2820 | case NodeTypeUndefinedLiteral: |
| 2814 | 2821 | // none |
| 2815 | 2822 | break; |
| 2823 | case NodeTypeZeroesLiteral: | |
| 2824 | // none | |
| 2825 | break; | |
| 2816 | 2826 | case NodeTypeIfBoolExpr: |
| 2817 | 2827 | clone_subtree_field(&new_node->data.if_bool_expr.condition, old_node->data.if_bool_expr.condition, next_node_index); |
| 2818 | 2828 | clone_subtree_field(&new_node->data.if_bool_expr.then_block, old_node->data.if_bool_expr.then_block, next_node_index); |
src/tokenizer.cpp+2| ... | ... | @@ -137,6 +137,7 @@ static const struct ZigKeyword zig_keywords[] = { |
| 137 | 137 | {"var", TokenIdKeywordVar}, |
| 138 | 138 | {"volatile", TokenIdKeywordVolatile}, |
| 139 | 139 | {"while", TokenIdKeywordWhile}, |
| 140 | {"zeroes", TokenIdKeywordZeroes}, | |
| 140 | 141 | }; |
| 141 | 142 | |
| 142 | 143 | bool is_zig_keyword(Buf *buf) { |
| ... | ... | @@ -1467,6 +1468,7 @@ const char * token_name(TokenId id) { |
| 1467 | 1468 | case TokenIdKeywordNoAlias: return "noalias"; |
| 1468 | 1469 | case TokenIdKeywordSwitch: return "switch"; |
| 1469 | 1470 | case TokenIdKeywordUndefined: return "undefined"; |
| 1471 | case TokenIdKeywordZeroes: return "zeroes"; | |
| 1470 | 1472 | case TokenIdKeywordError: return "error"; |
| 1471 | 1473 | case TokenIdKeywordType: return "type"; |
| 1472 | 1474 | case TokenIdKeywordInline: return "inline"; |
src/tokenizer.hpp+1| ... | ... | @@ -40,6 +40,7 @@ enum TokenId { |
| 40 | 40 | TokenIdKeywordNoAlias, |
| 41 | 41 | TokenIdKeywordSwitch, |
| 42 | 42 | TokenIdKeywordUndefined, |
| 43 | TokenIdKeywordZeroes, | |
| 43 | 44 | TokenIdKeywordError, |
| 44 | 45 | TokenIdKeywordType, |
| 45 | 46 | TokenIdKeywordInline, |
test/cases/zeroes.zig created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | const assert = @import("std").debug.assert; | |
| 2 | ||
| 3 | struct Foo { | |
| 4 | a: f32, | |
| 5 | b: i32, | |
| 6 | c: bool, | |
| 7 | d: ?i32, | |
| 8 | } | |
| 9 | ||
| 10 | #attribute("test") | |
| 11 | fn initializing_a_struct_with_zeroes() { | |
| 12 | const foo: Foo = zeroes; | |
| 13 | assert(foo.a == 0.0); | |
| 14 | assert(foo.b == 0); | |
| 15 | assert(foo.c == false); | |
| 16 | assert(if (const x ?= foo.d) false else true); | |
| 17 | } | |
| 18 |
test/self_hosted.zig+1| ... | ... | @@ -4,6 +4,7 @@ const str = std.str; |
| 4 | 4 | const cstr = std.cstr; |
| 5 | 5 | const other = @import("other.zig"); |
| 6 | 6 | const cases_return_type_type = @import("cases/return_type_type.zig"); |
| 7 | const test_zeroes = @import("cases/zeroes.zig"); | |
| 7 | 8 | |
| 8 | 9 | // normal comment |
| 9 | 10 | /// this is a documentation comment |