diff --git a/doc/langref.md b/doc/langref.md index 642431a967..aca127305a 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -151,7 +151,7 @@ GotoExpression = "goto" Symbol GroupedExpression = "(" Expression ")" -KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" +KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "zeroes" | "error" | "type" ``` ## Operator Precedence diff --git a/src/all_types.hpp b/src/all_types.hpp index ed08eb68e8..4785c21d20 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -62,11 +62,18 @@ struct ConstErrValue { ConstExprValue *payload; }; -struct ConstExprValue { - bool ok; // true if constant expression evalution worked - bool depends_on_compile_var; - bool undef; +enum ConstValSpecial { + ConstValSpecialOther, + ConstValSpecialUndef, + ConstValSpecialZeroes, +}; +struct ConstExprValue { + bool ok; + bool depends_on_compile_var; + ConstValSpecial special; + + // populated if val_type == ConstValTypeOk union { BigNum x_bignum; bool x_bool; @@ -167,6 +174,7 @@ enum NodeType { NodeTypeBoolLiteral, NodeTypeNullLiteral, NodeTypeUndefinedLiteral, + NodeTypeZeroesLiteral, NodeTypeIfBoolExpr, NodeTypeIfVarExpr, NodeTypeWhileExpr, @@ -694,6 +702,12 @@ struct AstNodeUndefinedLiteral { Expr resolved_expr; }; +struct AstNodeZeroesLiteral { + // populated by semantic analyzer + StructValExprCodeGen resolved_struct_val_expr; + Expr resolved_expr; +}; + struct AstNodeSymbolExpr { Buf *symbol; @@ -791,6 +805,7 @@ struct AstNode { AstNodeStructValueField struct_val_field; AstNodeNullLiteral null_literal; AstNodeUndefinedLiteral undefined_literal; + AstNodeZeroesLiteral zeroes_literal; AstNodeSymbolExpr symbol_expr; AstNodeBoolLiteral bool_literal; AstNodeBreakExpr break_expr; diff --git a/src/analyze.cpp b/src/analyze.cpp index bc38d86a44..1f66f41b17 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -90,6 +90,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeLabel: @@ -1849,6 +1850,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: @@ -3937,7 +3939,19 @@ static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEnt ConstExprValue *const_val = &expr->const_val; const_val->ok = true; - const_val->undef = true; + const_val->special = ConstValSpecialUndef; + + return expected_type ? expected_type : g->builtin_types.entry_undef; +} + +static TypeTableEntry *analyze_zeroes_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + Expr *expr = get_resolved_expr(node); + ConstExprValue *const_val = &expr->const_val; + + const_val->ok = true; + const_val->special = ConstValSpecialZeroes; return expected_type ? expected_type : g->builtin_types.entry_undef; } @@ -4252,7 +4266,7 @@ static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import } ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; - if (cond_val->undef) { + if (cond_val->special == ConstValSpecialUndef) { add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value")); return cond_type; } @@ -6504,6 +6518,9 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn case NodeTypeUndefinedLiteral: return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node); break; + case NodeTypeZeroesLiteral: + return_type = analyze_zeroes_literal_expr(g, import, context, expected_type, node); + break; case NodeTypeSymbol: return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only); break; @@ -6771,6 +6788,7 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: case NodeTypeIfBoolExpr: @@ -7029,6 +7047,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.null_literal.resolved_expr; case NodeTypeUndefinedLiteral: return &node->data.undefined_literal.resolved_expr; + case NodeTypeZeroesLiteral: + return &node->data.zeroes_literal.resolved_expr; case NodeTypeGoto: return &node->data.goto_expr.resolved_expr; case NodeTypeBreak: @@ -7111,6 +7131,7 @@ static TopLevelDecl *get_as_top_level_decl(AstNode *node) { case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 42edde8a6c..a4a1873c76 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -171,6 +171,8 @@ static const char *node_type_str(NodeType node_type) { return "NullLiteral"; case NodeTypeUndefinedLiteral: return "UndefinedLiteral"; + case NodeTypeZeroesLiteral: + return "ZeroesLiteral"; case NodeTypeIfBoolExpr: return "IfBoolExpr"; case NodeTypeIfVarExpr: @@ -591,6 +593,8 @@ static void render_node(AstRender *ar, AstNode *node) { zig_panic("TODO"); case NodeTypeUndefinedLiteral: zig_panic("TODO"); + case NodeTypeZeroesLiteral: + zig_panic("TODO"); case NodeTypeIfBoolExpr: zig_panic("TODO"); case NodeTypeIfVarExpr: diff --git a/src/codegen.cpp b/src/codegen.cpp index 990b7d135e..d77eda5bf6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3141,11 +3141,15 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa } bool have_init_expr = false; + bool want_zeroes = false; if (var_decl->expr) { ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val; - if (!const_val->ok || !const_val->undef) { + if (!const_val->ok || const_val->special == ConstValSpecialOther) { have_init_expr = true; } + if (const_val->ok && const_val->special == ConstValSpecialZeroes) { + want_zeroes = true; + } } if (have_init_expr) { 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 } } } - if (!ignore_uninit && want_debug_safety(g, source_node)) { + bool want_safe = want_debug_safety(g, source_node); + if (!ignore_uninit && (want_safe || want_zeroes)) { TypeTableEntry *usize = g->builtin_types.entry_usize; uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref); 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 // memset uninitialized memory to 0xa set_debug_source_node(g, source_node); LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false); LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, ""); LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false); @@ -3535,6 +3540,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeCharLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeErrorType: case NodeTypeTypeLiteral: case NodeTypeArrayType: @@ -3562,8 +3568,14 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) { assert(const_val->ok); - if (const_val->undef) { - return LLVMGetUndef(type_entry->type_ref); + switch (const_val->special) { + case ConstValSpecialUndef: + return LLVMGetUndef(type_entry->type_ref); + case ConstValSpecialZeroes: + return LLVMConstNull(type_entry->type_ref); + case ConstValSpecialOther: + break; + } switch (type_entry->id) { diff --git a/src/eval.cpp b/src/eval.cpp index b235040b87..1196e52d5c 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -545,7 +545,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, ConstExprValue *const_val, TypeTableEntry *new_type) { const_val->depends_on_compile_var = other_val->depends_on_compile_var; - const_val->undef = other_val->undef; + const_val->special = other_val->special; assert(other_val != const_val); switch (cast_op) { @@ -572,7 +572,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, const_val->data.x_ptr.ptr = ptr_val; const_val->data.x_ptr.len = 1; const_val->ok = true; - const_val->undef = other_val->undef; + const_val->special = other_val->special; const_val->depends_on_compile_var = other_val->depends_on_compile_var; } else { zig_panic("TODO"); @@ -608,7 +608,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, const_val->data.x_maybe = ptr_parent; const_val->ok = true; - const_val->undef = other_val->undef; + const_val->special = other_val->special; const_val->depends_on_compile_var = other_val->depends_on_compile_var; } else { zig_panic("TODO"); @@ -1277,6 +1277,7 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeSliceExpr: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: + case NodeTypeZeroesLiteral: case NodeTypeIfVarExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: diff --git a/src/parser.cpp b/src/parser.cpp index 4939fa94b0..59da471b63 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -606,7 +606,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand /* PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol") -KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" +KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "zeroes" | "error" | "type" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -654,6 +654,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordZeroes) { + AstNode *node = ast_create_node(pc, NodeTypeZeroesLiteral, token); + *token_index += 1; + return node; } else if (token->id == TokenIdKeywordType) { AstNode *node = ast_create_node(pc, NodeTypeTypeLiteral, token); *token_index += 1; @@ -2539,6 +2543,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeUndefinedLiteral: // none break; + case NodeTypeZeroesLiteral: + // none + break; case NodeTypeIfBoolExpr: visit_field(&node->data.if_bool_expr.condition, visit, context); 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, case NodeTypeUndefinedLiteral: // none break; + case NodeTypeZeroesLiteral: + // none + break; case NodeTypeIfBoolExpr: clone_subtree_field(&new_node->data.if_bool_expr.condition, old_node->data.if_bool_expr.condition, next_node_index); clone_subtree_field(&new_node->data.if_bool_expr.then_block, old_node->data.if_bool_expr.then_block, next_node_index); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 8ce0ce33ab..bec95ae10f 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -137,6 +137,7 @@ static const struct ZigKeyword zig_keywords[] = { {"var", TokenIdKeywordVar}, {"volatile", TokenIdKeywordVolatile}, {"while", TokenIdKeywordWhile}, + {"zeroes", TokenIdKeywordZeroes}, }; bool is_zig_keyword(Buf *buf) { @@ -1467,6 +1468,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordUndefined: return "undefined"; + case TokenIdKeywordZeroes: return "zeroes"; case TokenIdKeywordError: return "error"; case TokenIdKeywordType: return "type"; case TokenIdKeywordInline: return "inline"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 49e88c5cc8..9ebaccb456 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -40,6 +40,7 @@ enum TokenId { TokenIdKeywordNoAlias, TokenIdKeywordSwitch, TokenIdKeywordUndefined, + TokenIdKeywordZeroes, TokenIdKeywordError, TokenIdKeywordType, TokenIdKeywordInline, diff --git a/test/cases/zeroes.zig b/test/cases/zeroes.zig new file mode 100644 index 0000000000..94774e434d --- /dev/null +++ b/test/cases/zeroes.zig @@ -0,0 +1,18 @@ +const assert = @import("std").debug.assert; + +struct Foo { + a: f32, + b: i32, + c: bool, + d: ?i32, +} + +#attribute("test") +fn initializing_a_struct_with_zeroes() { + const foo: Foo = zeroes; + assert(foo.a == 0.0); + assert(foo.b == 0); + assert(foo.c == false); + assert(if (const x ?= foo.d) false else true); +} + diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 6d04159a35..d666d922e7 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -4,6 +4,7 @@ const str = std.str; const cstr = std.cstr; const other = @import("other.zig"); const cases_return_type_type = @import("cases/return_type_type.zig"); +const test_zeroes = @import("cases/zeroes.zig"); // normal comment /// this is a documentation comment