From 298abbcff86629273f24891d243fb6e503392e8f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:55:31 +0900 Subject: [PATCH 1/5] better support for `_` identifier * disallow variable declaration of `_` * prevent `_` from shadowing itself * prevent read access of `_` closes #1204 closes #1320 --- src/ir.cpp | 16 ++++++++++- test/behavior.zig | 1 + test/cases/underscore.zig | 28 +++++++++++++++++++ test/compile_errors.zig | 58 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/cases/underscore.zig diff --git a/src/ir.cpp b/src/ir.cpp index 7d2881744d..8dd4bb41db 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3332,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) { - VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime); + bool is_underscored = name ? buf_eql_str(name, "_") : false; + VariableTableEntry *var = create_local_var( irb->codegen + , node + , scope + , (is_underscored ? nullptr : name) + , src_is_const + , gen_is_const + , (is_underscored ? true : is_shadowable) + , is_comptime ); if (is_comptime != nullptr || gen_is_const) { var->mem_slot_index = exec_next_mem_slot(irb->exec); var->owner_exec = irb->exec; @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + if (buf_eql_str(variable_declaration->symbol, "_")) { + add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); + return irb->codegen->invalid_instruction; + } + IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { 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 bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, diff --git a/test/behavior.zig b/test/behavior.zig index b03336eb71..e993d7e0dc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -60,6 +60,7 @@ comptime { _ = @import("cases/try.zig"); _ = @import("cases/type_info.zig"); _ = @import("cases/undefined.zig"); + _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); diff --git a/test/cases/underscore.zig b/test/cases/underscore.zig new file mode 100644 index 0000000000..44451e3723 --- /dev/null +++ b/test/cases/underscore.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "ignore lval with underscore" { + _ = false; +} + +test "ignore lval with underscore (for loop)" { + for ([]void{}) |_, i| { + for ([]void{}) |_, j| { + break; + } + break; + } +} + +test "ignore lval with underscore (while loop)" { + while (optionalReturnError()) |_| { + while (optionalReturnError()) |_| { + break; + } else |_| { } + break; + } else |_| { } +} + +fn optionalReturnError() !?u32 { + return error.optionalReturnError; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 948d212e58..56b2c51d74 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -22,6 +22,64 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:28: error: @handle() in non-async function", ); + cases.add( + "`_` is not a declarable symbol", + \\export fn f1() usize { + \\ var _: usize = 2; + \\ return _; + \\} + , + ".tmp_source.zig:2:5: error: `_` is not a declarable symbol", + ".tmp_source.zig:3:12: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside for", + \\export fn returns() void { + \\ for ([]void{}) |_, i| { + \\ for ([]void{}) |_, j| { + \\ return _; + \\ } + \\ } + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while", + \\export fn returns() void { + \\ while (optionalReturn()) |_| { + \\ while (optionalReturn()) |_| { + \\ return _; + \\ } + \\ } + \\} + \\fn optionalReturn() ?u32 { + \\ return 1; + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while else", + \\export fn returns() void { + \\ while (optionalReturnError()) |_| { + \\ while (optionalReturnError()) |_| { + \\ return; + \\ } else |_| { + \\ if (_ == error.optionalReturnError) return; + \\ } + \\ } + \\} + \\fn optionalReturnError() !?u32 { + \\ return error.optionalReturnError; + \\} + , + ".tmp_source.zig:6:17: error: use of undeclared identifier '_'", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { From c2a08d7c516899b4c794bd6c3fc07ddb22d5876a Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Fri, 3 Aug 2018 11:44:39 -0400 Subject: [PATCH 2/5] Fix the start-less-than-end assertion in std.rand.Random.range (#1325) The function returns a value in [start, end), but was asserting start <= end instead of start < end. With this fix, range(1, 1) will now assertion error instead of dividing by zero. --- std/rand/index.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/rand/index.zig b/std/rand/index.zig index 7daa558f13..2cbff049ea 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -30,7 +30,7 @@ pub const DefaultCsprng = Isaac64; pub const Random = struct { fillFn: fn (r: *Random, buf: []u8) void, - /// Read random bytes into the specified buffer until fill. + /// Read random bytes into the specified buffer until full. pub fn bytes(r: *Random, buf: []u8) void { r.fillFn(r, buf); } @@ -48,10 +48,10 @@ pub const Random = struct { } } - /// Get a random unsigned integer with even distribution between `start` - /// inclusive and `end` exclusive. + /// Return a random integer with even distribution between `start` + /// inclusive and `end` exclusive. `start` must be less than `end`. pub fn range(r: *Random, comptime T: type, start: T, end: T) T { - assert(start <= end); + assert(start < end); if (T.is_signed) { const uint = @IntType(false, T.bit_count); if (start >= 0 and end >= 0) { @@ -664,6 +664,7 @@ test "Random range" { testRange(&prng.random, -4, 3); testRange(&prng.random, -4, -1); testRange(&prng.random, 10, 14); + // TODO: test that prng.random.range(1, 1) causes an assertion error } fn testRange(r: *Random, start: i32, end: i32) void { From dcaaa241dfb7ce77b7070df8d6a939a9f0749e2c Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Fri, 3 Aug 2018 11:45:23 -0400 Subject: [PATCH 3/5] Fix a type error in std.os.linux.getpid() (#1326) syscall0() returns usize, but we were trying to @bitCast to i32. --- std/os/linux/index.zig | 2 +- std/os/linux/test.zig | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 69bc30bad0..15607ea6c0 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -944,7 +944,7 @@ pub fn setgroups(size: usize, list: *const u32) usize { } pub fn getpid() i32 { - return @bitCast(i32, u32(syscall0(SYS_getpid))); + return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); } pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index e7dae3a584..4de26012c7 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -3,6 +3,10 @@ const builtin = @import("builtin"); const linux = std.os.linux; const assert = std.debug.assert; +test "getpid" { + assert(linux.getpid() != 0); +} + test "timer" { const epoll_fd = linux.epoll_create(); var err = linux.getErrno(epoll_fd); From c66c6304f90aa9ea9983134c857ba7ea014004a3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Aug 2018 15:20:19 -0400 Subject: [PATCH 4/5] add a friendly note in .gitignore --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 5616da8e58..4b7bff11a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + zig-cache/ build/ build-*/ From 9bd8b01650f9cf21e601117951711b21aa5fd216 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Aug 2018 15:21:08 -0400 Subject: [PATCH 5/5] fix tagged union initialization with a runtime void closes #1328 --- src/ir.cpp | 11 ++++++++++- test/cases/union.zig | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8dd4bb41db..3e423487aa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9628,6 +9628,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un case ConstValSpecialStatic: return &value->value; case ConstValSpecialRuntime: + if (!type_has_bits(value->value.type)) { + return &value->value; + } ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; case ConstValSpecialUndef: @@ -16129,8 +16132,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type); + if (type_is_invalid(casted_field_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { + if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || + !type_has_bits(casted_field_value->value.type)) + { ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); if (!field_val) return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/union.zig b/test/cases/union.zig index 78b2dc8dd7..08969e64fe 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -297,3 +297,17 @@ test "access a member of tagged union with conflicting enum tag name" { comptime assert(Bar.A == u8); } + +test "tagged union initialization with runtime void" { + assert(testTaggedUnionInit({})); +} + +const TaggedUnionWithAVoid = union(enum) { + A, + B: i32, +}; + +fn testTaggedUnionInit(x: var) bool { + const y = TaggedUnionWithAVoid{ .A = x }; + return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; +}