From ae7b32eb62cb00a09fe2e0e30b307eb83e9f0a86 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 17:22:16 +0300 Subject: [PATCH] Sema: validate deref operator type and value --- lib/c.zig | 2 +- lib/std/os.zig | 2 +- lib/std/process.zig | 2 +- src/AstGen.zig | 2 ++ src/Sema.zig | 27 +++++++++++++++++++ src/Zir.zig | 6 +++++ src/print_zir.zig | 1 + .../assign_to_invalid_dereference.zig | 9 +++++++ .../{stage1 => }/deref_on_undefined_value.zig | 4 +-- .../deref_slice_and_get_len_field.zig | 4 +-- .../{stage1/obj => }/dereference_an_array.zig | 6 ++--- .../compile_errors/dereference_slice.zig | 12 +++++++++ .../dereference_unknown_length_pointer.zig | 9 +++++++ .../invalid_deref_on_switch_target.zig | 4 +-- .../invalid_multiple_dereferences.zig | 6 ++--- .../comptime_ptrcast_of_zero-sized_type.zig | 0 .../obj/assign_to_invalid_dereference.zig | 9 ------- .../dereference_unknown_length_pointer.zig | 9 ------- .../obj/take_slice_of_invalid_dereference.zig | 10 ------- .../take_slice_of_invalid_dereference.zig | 10 +++++++ 20 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 test/cases/compile_errors/assign_to_invalid_dereference.zig rename test/cases/compile_errors/{stage1 => }/deref_on_undefined_value.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/deref_slice_and_get_len_field.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/dereference_an_array.zig (52%) create mode 100644 test/cases/compile_errors/dereference_slice.zig create mode 100644 test/cases/compile_errors/dereference_unknown_length_pointer.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_deref_on_switch_target.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/invalid_multiple_dereferences.zig (61%) rename test/cases/compile_errors/stage1/{obj => }/comptime_ptrcast_of_zero-sized_type.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig delete mode 100644 test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig delete mode 100644 test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig create mode 100644 test/cases/compile_errors/take_slice_of_invalid_dereference.zig diff --git a/lib/c.zig b/lib/c.zig index 30c6e0cd76..9df1b3fb01 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -82,7 +82,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { var d = dest.?; var n = len; while (true) { - d.* = c; + d[0] = c; n -= 1; if (n == 0) break; d += 1; diff --git a/lib/std/os.zig b/lib/std/os.zig index 578a8ddcbc..02ed710dd3 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1868,7 +1868,7 @@ pub fn getenv(key: []const u8) ?[]const u8 { } // Search the entire `environ` because we don't have a null terminated pointer. var ptr = std.c.environ; - while (ptr.*) |line| : (ptr += 1) { + while (ptr[0]) |line| : (ptr += 1) { var line_i: usize = 0; while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} const this_key = line[0..line_i]; diff --git a/lib/std/process.zig b/lib/std/process.zig index 0b64b5910d..dffd6b1701 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -313,7 +313,7 @@ pub fn getEnvMap(allocator: Allocator) !EnvMap { return result; } else if (builtin.link_libc) { var ptr = std.c.environ; - while (ptr.*) |line| : (ptr += 1) { + while (ptr[0]) |line| : (ptr += 1) { var line_i: usize = 0; while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} const key = line[0..line_i]; diff --git a/src/AstGen.zig b/src/AstGen.zig index c9abb1859b..8707728313 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -812,6 +812,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr .deref => { const lhs = try expr(gz, scope, .none, node_datas[node].lhs); + _ = try gz.addUnTok(.validate_deref, lhs, main_tokens[node]); switch (rl) { .ref => return lhs, else => { @@ -2500,6 +2501,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .memset, .validate_array_init_ty, .validate_struct_init_ty, + .validate_deref, => break :b true, } } else switch (maybe_unused_result) { diff --git a/src/Sema.zig b/src/Sema.zig index 30cebe18b3..0e7188a6c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1080,6 +1080,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .validate_deref => { + try sema.zirValidateDeref(block, inst); + i += 1; + continue; + }, .@"export" => { try sema.zirExport(block, inst); i += 1; @@ -3849,6 +3854,28 @@ fn zirValidateArrayInit( } } +fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const inst_data = sema.code.instructions.items(.data)[inst].un_tok; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .token_offset = inst_data.src_tok + 1 }; + const operand = try sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + + if (operand_ty.zigTypeTag() != .Pointer) { + return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(sema.mod)}); + } else switch (operand_ty.ptrSize()) { + .One, .C => {}, + .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(sema.mod)}), + .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(sema.mod)}), + } + + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { + if (val.isUndef()) { + return sema.fail(block, src, "cannot dereference undefined value", .{}); + } + } +} + fn failWithBadMemberAccess( sema: *Sema, block: *Block, diff --git a/src/Zir.zig b/src/Zir.zig index e6acfe8ed2..26bb09860d 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -729,6 +729,9 @@ pub const Inst = struct { /// Same as `validate_array_init` but additionally communicates that the /// resulting array initialization value is within a comptime scope. validate_array_init_comptime, + /// Check that operand type supports the dereference operand (.*). + /// Uses the `un_tok` field. + validate_deref, /// A struct literal with a specified type, with no fields. /// Uses the `un_node` field. struct_init_empty, @@ -1156,6 +1159,7 @@ pub const Inst = struct { .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, + .validate_deref, .struct_init_empty, .struct_init, .struct_init_ref, @@ -1309,6 +1313,7 @@ pub const Inst = struct { .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, + .validate_deref, .@"export", .export_value, .set_cold, @@ -1709,6 +1714,7 @@ pub const Inst = struct { .validate_struct_init_comptime = .pl_node, .validate_array_init = .pl_node, .validate_array_init_comptime = .pl_node, + .validate_deref = .un_tok, .struct_init_empty = .un_node, .field_type = .pl_node, .field_type_ref = .pl_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 480a3e2a4f..fe8446b34a 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -242,6 +242,7 @@ const Writer = struct { .ret_tok, .ensure_err_payload_void, .closure_capture, + .validate_deref, => try self.writeUnTok(stream, inst), .bool_br_and, diff --git a/test/cases/compile_errors/assign_to_invalid_dereference.zig b/test/cases/compile_errors/assign_to_invalid_dereference.zig new file mode 100644 index 0000000000..cb35004034 --- /dev/null +++ b/test/cases/compile_errors/assign_to_invalid_dereference.zig @@ -0,0 +1,9 @@ +export fn entry() void { + 'a'.* = 1; +} + +// error +// backend=stage2 +// target=native +// +// :2:8: error: cannot dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/stage1/deref_on_undefined_value.zig b/test/cases/compile_errors/deref_on_undefined_value.zig similarity index 51% rename from test/cases/compile_errors/stage1/deref_on_undefined_value.zig rename to test/cases/compile_errors/deref_on_undefined_value.zig index f64d567a26..fa12e2824c 100644 --- a/test/cases/compile_errors/stage1/deref_on_undefined_value.zig +++ b/test/cases/compile_errors/deref_on_undefined_value.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: attempt to dereference undefined value +// :3:10: error: cannot dereference undefined value diff --git a/test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig b/test/cases/compile_errors/deref_slice_and_get_len_field.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig rename to test/cases/compile_errors/deref_slice_and_get_len_field.zig index 98097597cc..1ba03c6d50 100644 --- a/test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig +++ b/test/cases/compile_errors/deref_slice_and_get_len_field.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8' +// :3:10: error: index syntax required for slice type '[]u8' diff --git a/test/cases/compile_errors/stage1/obj/dereference_an_array.zig b/test/cases/compile_errors/dereference_an_array.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/dereference_an_array.zig rename to test/cases/compile_errors/dereference_an_array.zig index 0dd91f70e5..f5aabf081c 100644 --- a/test/cases/compile_errors/stage1/obj/dereference_an_array.zig +++ b/test/cases/compile_errors/dereference_an_array.zig @@ -5,10 +5,10 @@ pub fn pass(in: []u8) []u8 { return out.*[0..1]; } -export fn entry() usize { return @sizeOf(@TypeOf(pass)); } +export fn entry() usize { return @sizeOf(@TypeOf(&pass)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8' +// :4:10: error: cannot dereference non-pointer type '[10]u8' diff --git a/test/cases/compile_errors/dereference_slice.zig b/test/cases/compile_errors/dereference_slice.zig new file mode 100644 index 0000000000..7dba3b55d8 --- /dev/null +++ b/test/cases/compile_errors/dereference_slice.zig @@ -0,0 +1,12 @@ +fn entry(x: []i32) i32 { + return x.*; +} +comptime { + _ = entry; +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: index syntax required for slice type '[]i32' diff --git a/test/cases/compile_errors/dereference_unknown_length_pointer.zig b/test/cases/compile_errors/dereference_unknown_length_pointer.zig new file mode 100644 index 0000000000..353f94b8d9 --- /dev/null +++ b/test/cases/compile_errors/dereference_unknown_length_pointer.zig @@ -0,0 +1,9 @@ +export fn entry(x: [*]i32) i32 { + return x.*; +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig b/test/cases/compile_errors/invalid_deref_on_switch_target.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig rename to test/cases/compile_errors/invalid_deref_on_switch_target.zig index 966a881543..a880b16fca 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig +++ b/test/cases/compile_errors/invalid_deref_on_switch_target.zig @@ -11,7 +11,7 @@ const Tile = enum { }; // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile' +// :3:17: error: cannot dereference non-pointer type 'tmp.Tile' diff --git a/test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig b/test/cases/compile_errors/invalid_multiple_dereferences.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig rename to test/cases/compile_errors/invalid_multiple_dereferences.zig index f8a0b8013f..3edebf7b1f 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig +++ b/test/cases/compile_errors/invalid_multiple_dereferences.zig @@ -12,8 +12,8 @@ pub const Box = struct { }; // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box' -// tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box' +// :3:8: error: cannot dereference non-pointer type 'tmp.Box' +// :8:13: error: cannot dereference non-pointer type 'tmp.Box' diff --git a/test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig b/test/cases/compile_errors/stage1/comptime_ptrcast_of_zero-sized_type.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig rename to test/cases/compile_errors/stage1/comptime_ptrcast_of_zero-sized_type.zig diff --git a/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig deleted file mode 100644 index 7fef5db83c..0000000000 --- a/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - 'a'.* = 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig b/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig deleted file mode 100644 index c305e4bc98..0000000000 --- a/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry(x: [*]i32) i32 { - return x.*; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig deleted file mode 100644 index c039be3737..0000000000 --- a/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const x = 'a'.*[0..]; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/take_slice_of_invalid_dereference.zig b/test/cases/compile_errors/take_slice_of_invalid_dereference.zig new file mode 100644 index 0000000000..35c1b2de0d --- /dev/null +++ b/test/cases/compile_errors/take_slice_of_invalid_dereference.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const x = 'a'.*[0..]; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: cannot dereference non-pointer type 'comptime_int'