| author | |
| committer | |
| log | 298abbcff86629273f24891d243fb6e503392e8f |
| tree | f5554887205c70c2679e79ba3e5d8cf00daec3b1 |
| parent | fb05b96492f4fb1476106bf735788ac16f69c7ef |
* disallow variable declaration of `_`
* prevent `_` from shadowing itself
* prevent read access of `_`
closes #1204
closes #13204 files changed, 102 insertions(+), 1 deletions(-)
src/ir.cpp+15-1| ... | ... | @@ -3332,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco |
| 3332 | 3332 | static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, |
| 3333 | 3333 | bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) |
| 3334 | 3334 | { |
| 3335 | VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime); | |
| 3335 | bool is_underscored = name ? buf_eql_str(name, "_") : false; | |
| 3336 | VariableTableEntry *var = create_local_var( irb->codegen | |
| 3337 | , node | |
| 3338 | , scope | |
| 3339 | , (is_underscored ? nullptr : name) | |
| 3340 | , src_is_const | |
| 3341 | , gen_is_const | |
| 3342 | , (is_underscored ? true : is_shadowable) | |
| 3343 | , is_comptime ); | |
| 3336 | 3344 | if (is_comptime != nullptr || gen_is_const) { |
| 3337 | 3345 | var->mem_slot_index = exec_next_mem_slot(irb->exec); |
| 3338 | 3346 | var->owner_exec = irb->exec; |
| ... | ... | @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5186 | 5194 | |
| 5187 | 5195 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; |
| 5188 | 5196 | |
| 5197 | if (buf_eql_str(variable_declaration->symbol, "_")) { | |
| 5198 | add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); | |
| 5199 | return irb->codegen->invalid_instruction; | |
| 5200 | } | |
| 5201 | ||
| 5189 | 5202 | IrInstruction *type_instruction; |
| 5190 | 5203 | if (variable_declaration->type != nullptr) { |
| 5191 | 5204 | type_instruction = ir_gen_node(irb, variable_declaration->type, scope); |
| ... | ... | @@ -5198,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5198 | 5211 | bool is_shadowable = false; |
| 5199 | 5212 | bool is_const = variable_declaration->is_const; |
| 5200 | 5213 | bool is_extern = variable_declaration->is_extern; |
| 5214 | ||
| 5201 | 5215 | IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, |
| 5202 | 5216 | ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); |
| 5203 | 5217 | VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, |
test/behavior.zig+1| ... | ... | @@ -60,6 +60,7 @@ comptime { |
| 60 | 60 | _ = @import("cases/try.zig"); |
| 61 | 61 | _ = @import("cases/type_info.zig"); |
| 62 | 62 | _ = @import("cases/undefined.zig"); |
| 63 | _ = @import("cases/underscore.zig"); | |
| 63 | 64 | _ = @import("cases/union.zig"); |
| 64 | 65 | _ = @import("cases/var_args.zig"); |
| 65 | 66 | _ = @import("cases/void.zig"); |
test/cases/underscore.zig created+28| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | ||
| 4 | test "ignore lval with underscore" { | |
| 5 | _ = false; | |
| 6 | } | |
| 7 | ||
| 8 | test "ignore lval with underscore (for loop)" { | |
| 9 | for ([]void{}) |_, i| { | |
| 10 | for ([]void{}) |_, j| { | |
| 11 | break; | |
| 12 | } | |
| 13 | break; | |
| 14 | } | |
| 15 | } | |
| 16 | ||
| 17 | test "ignore lval with underscore (while loop)" { | |
| 18 | while (optionalReturnError()) |_| { | |
| 19 | while (optionalReturnError()) |_| { | |
| 20 | break; | |
| 21 | } else |_| { } | |
| 22 | break; | |
| 23 | } else |_| { } | |
| 24 | } | |
| 25 | ||
| 26 | fn optionalReturnError() !?u32 { | |
| 27 | return error.optionalReturnError; | |
| 28 | } |
test/compile_errors.zig+58| ... | ... | @@ -22,6 +22,64 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 22 | 22 | ".tmp_source.zig:3:28: error: @handle() in non-async function", |
| 23 | 23 | ); |
| 24 | 24 | |
| 25 | cases.add( | |
| 26 | "`_` is not a declarable symbol", | |
| 27 | \\export fn f1() usize { | |
| 28 | \\ var _: usize = 2; | |
| 29 | \\ return _; | |
| 30 | \\} | |
| 31 | , | |
| 32 | ".tmp_source.zig:2:5: error: `_` is not a declarable symbol", | |
| 33 | ".tmp_source.zig:3:12: error: use of undeclared identifier '_'", | |
| 34 | ); | |
| 35 | ||
| 36 | cases.add( | |
| 37 | "`_` should not be usable inside for", | |
| 38 | \\export fn returns() void { | |
| 39 | \\ for ([]void{}) |_, i| { | |
| 40 | \\ for ([]void{}) |_, j| { | |
| 41 | \\ return _; | |
| 42 | \\ } | |
| 43 | \\ } | |
| 44 | \\} | |
| 45 | , | |
| 46 | ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", | |
| 47 | ); | |
| 48 | ||
| 49 | cases.add( | |
| 50 | "`_` should not be usable inside while", | |
| 51 | \\export fn returns() void { | |
| 52 | \\ while (optionalReturn()) |_| { | |
| 53 | \\ while (optionalReturn()) |_| { | |
| 54 | \\ return _; | |
| 55 | \\ } | |
| 56 | \\ } | |
| 57 | \\} | |
| 58 | \\fn optionalReturn() ?u32 { | |
| 59 | \\ return 1; | |
| 60 | \\} | |
| 61 | , | |
| 62 | ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", | |
| 63 | ); | |
| 64 | ||
| 65 | cases.add( | |
| 66 | "`_` should not be usable inside while else", | |
| 67 | \\export fn returns() void { | |
| 68 | \\ while (optionalReturnError()) |_| { | |
| 69 | \\ while (optionalReturnError()) |_| { | |
| 70 | \\ return; | |
| 71 | \\ } else |_| { | |
| 72 | \\ if (_ == error.optionalReturnError) return; | |
| 73 | \\ } | |
| 74 | \\ } | |
| 75 | \\} | |
| 76 | \\fn optionalReturnError() !?u32 { | |
| 77 | \\ return error.optionalReturnError; | |
| 78 | \\} | |
| 79 | , | |
| 80 | ".tmp_source.zig:6:17: error: use of undeclared identifier '_'", | |
| 81 | ); | |
| 82 | ||
| 25 | 83 | cases.add( |
| 26 | 84 | "while loop body expression ignored", |
| 27 | 85 | \\fn returns() usize { |