Commit ae7b32eb62

Veikka Tuominen <git@vexu.eu>
2022-06-30 16:22:16
Sema: validate deref operator type and value
1 parent 3c73f71
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];
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];
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;
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) {
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,
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,
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,
test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig
@@ -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'
test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig
@@ -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'
test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig
@@ -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'
test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig → test/cases/compile_errors/stage1/comptime_ptrcast_of_zero-sized_type.zig
File renamed without changes
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'
test/cases/compile_errors/stage1/deref_on_undefined_value.zig → 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
test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig → 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'
test/cases/compile_errors/stage1/obj/dereference_an_array.zig → 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'
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'
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'
test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig → 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'
test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig → 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'
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'