| 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,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco |
| 3332 | static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, | 3332 | static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, |
| 3333 | bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) | 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 | if (is_comptime != nullptr || gen_is_const) { | 3344 | if (is_comptime != nullptr || gen_is_const) { |
| 3337 | var->mem_slot_index = exec_next_mem_slot(irb->exec); | 3345 | var->mem_slot_index = exec_next_mem_slot(irb->exec); |
| 3338 | var->owner_exec = irb->exec; | 3346 | var->owner_exec = irb->exec; |
| ... | @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod | ... | @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5186 | 5194 | ||
| 5187 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; | 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 | IrInstruction *type_instruction; | 5202 | IrInstruction *type_instruction; |
| 5190 | if (variable_declaration->type != nullptr) { | 5203 | if (variable_declaration->type != nullptr) { |
| 5191 | type_instruction = ir_gen_node(irb, variable_declaration->type, scope); | 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,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 5198 | bool is_shadowable = false; | 5211 | bool is_shadowable = false; |
| 5199 | bool is_const = variable_declaration->is_const; | 5212 | bool is_const = variable_declaration->is_const; |
| 5200 | bool is_extern = variable_declaration->is_extern; | 5213 | bool is_extern = variable_declaration->is_extern; |
| 5214 | |||
| 5201 | IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, | 5215 | IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, |
| 5202 | ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); | 5216 | ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); |
| 5203 | VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, | 5217 | VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, |
test/behavior.zig+1| ... | @@ -60,6 +60,7 @@ comptime { | ... | @@ -60,6 +60,7 @@ comptime { |
| 60 | _ = @import("cases/try.zig"); | 60 | _ = @import("cases/try.zig"); |
| 61 | _ = @import("cases/type_info.zig"); | 61 | _ = @import("cases/type_info.zig"); |
| 62 | _ = @import("cases/undefined.zig"); | 62 | _ = @import("cases/undefined.zig"); |
| 63 | _ = @import("cases/underscore.zig"); | ||
| 63 | _ = @import("cases/union.zig"); | 64 | _ = @import("cases/union.zig"); |
| 64 | _ = @import("cases/var_args.zig"); | 65 | _ = @import("cases/var_args.zig"); |
| 65 | _ = @import("cases/void.zig"); | 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,6 +22,64 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 22 | ".tmp_source.zig:3:28: error: @handle() in non-async function", | 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 | cases.add( | 83 | cases.add( |
| 26 | "while loop body expression ignored", | 84 | "while loop body expression ignored", |
| 27 | \\fn returns() usize { | 85 | \\fn returns() usize { |