Commit 5699ab5e77

Andrew Kelley <andrew@ziglang.org>
2019-02-13 00:20:00
C pointers: errors for nested pointer casting regarding null
See #1059
1 parent 270933b
src/all_types.hpp
@@ -691,15 +691,17 @@ struct AstNodePointerType {
     AstNode *align_expr;
     BigInt *bit_offset_start;
     BigInt *host_int_bytes;
+    AstNode *op_expr;
+    Token *allow_zero_token;
     bool is_const;
     bool is_volatile;
-    AstNode *op_expr;
 };
 
 struct AstNodeArrayType {
     AstNode *size;
     AstNode *child_type;
     AstNode *align_expr;
+    Token *allow_zero_token;
     bool is_const;
     bool is_volatile;
 };
@@ -1050,6 +1052,7 @@ struct ZigTypePointer {
     uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned
     bool is_const;
     bool is_volatile;
+    bool allow_zero;
 };
 
 struct ZigTypeInt {
@@ -1499,11 +1502,12 @@ struct TypeId {
         struct {
             ZigType *child_type;
             PtrLen ptr_len;
-            bool is_const;
-            bool is_volatile;
             uint32_t alignment;
             uint32_t bit_offset_in_host;
             uint32_t host_int_bytes;
+            bool is_const;
+            bool is_volatile;
+            bool allow_zero;
         } pointer;
         struct {
             ZigType *child_type;
@@ -2592,6 +2596,7 @@ struct IrInstructionPtrType {
     PtrLen ptr_len;
     bool is_const;
     bool is_volatile;
+    bool allow_zero;
 };
 
 struct IrInstructionPromiseType {
@@ -2607,6 +2612,7 @@ struct IrInstructionSliceType {
     IrInstruction *child_type;
     bool is_const;
     bool is_volatile;
+    bool allow_zero;
 };
 
 struct IrInstructionAsm {
src/analyze.cpp
@@ -433,6 +433,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
         bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
         uint32_t bit_offset_in_host, uint32_t host_int_bytes)
 {
+    // TODO when implementing https://github.com/ziglang/zig/issues/1953
+    // move this to a parameter
+    bool allow_zero = (ptr_len == PtrLenC);
     assert(!type_is_invalid(child_type));
     assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
 
@@ -452,7 +455,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
 
     TypeId type_id = {};
     ZigType **parent_pointer = nullptr;
-    if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle) {
+    if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) {
         type_id.id = ZigTypeIdPointer;
         type_id.data.pointer.child_type = child_type;
         type_id.data.pointer.is_const = is_const;
@@ -461,6 +464,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
         type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
         type_id.data.pointer.host_int_bytes = host_int_bytes;
         type_id.data.pointer.ptr_len = ptr_len;
+        type_id.data.pointer.allow_zero = allow_zero;
 
         auto existing_entry = g->type_table.maybe_get(type_id);
         if (existing_entry)
@@ -481,18 +485,28 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     const char *star_str = ptr_len_to_star_str(ptr_len);
     const char *const_str = is_const ? "const " : "";
     const char *volatile_str = is_volatile ? "volatile " : "";
+    const char *allow_zero_str;
+    if (ptr_len == PtrLenC) {
+        assert(allow_zero);
+        allow_zero_str = "";
+    } else {
+        allow_zero_str = allow_zero ? "allowzero " : "";
+    }
     buf_resize(&entry->name, 0);
     if (host_int_bytes == 0 && byte_alignment == 0) {
-        buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name));
+        buf_appendf(&entry->name, "%s%s%s%s%s",
+                star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
     } else if (host_int_bytes == 0) {
-        buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment,
-                const_str, volatile_str, buf_ptr(&child_type->name));
+        buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
+                const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
     } else if (byte_alignment == 0) {
-        buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str,
-                bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
+        buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str,
+                bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+                buf_ptr(&child_type->name));
     } else {
-        buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment,
-                bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
+        buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
+                bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+                buf_ptr(&child_type->name));
     }
 
     assert(child_type->id != ZigTypeIdInvalid);
@@ -500,7 +514,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     entry->zero_bits = !type_has_bits(child_type);
 
     if (!entry->zero_bits) {
-        if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || bit_offset_in_host != 0) {
+        if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
+            bit_offset_in_host != 0 || allow_zero)
+        {
             ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false,
                     PtrLenSingle, 0, 0, host_int_bytes);
             entry->type_ref = peer_type->type_ref;
@@ -534,6 +550,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     entry->data.pointer.explicit_alignment = byte_alignment;
     entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
     entry->data.pointer.host_int_bytes = host_int_bytes;
+    entry->data.pointer.allow_zero = allow_zero;
 
     if (parent_pointer) {
         *parent_pointer = entry;
@@ -850,7 +867,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
 
     ZigType *child_type = ptr_type->data.pointer.child_type;
     if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
-        ptr_type->data.pointer.explicit_alignment != 0)
+        ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
     {
         ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
                 PtrLenUnknown, 0, 0, 0);
@@ -873,7 +890,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
         ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
         assert(child_ptr_type->id == ZigTypeIdPointer);
         if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
-            child_ptr_type->data.pointer.explicit_alignment != 0)
+            child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero)
         {
             ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
             ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
@@ -4053,7 +4070,9 @@ ZigType *get_src_ptr_type(ZigType *type) {
     if (type->id == ZigTypeIdFn) return type;
     if (type->id == ZigTypeIdPromise) return type;
     if (type->id == ZigTypeIdOptional) {
-        if (type->data.maybe.child_type->id == ZigTypeIdPointer) return type->data.maybe.child_type;
+        if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
+            return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
+        }
         if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
         if (type->data.maybe.child_type->id == ZigTypeIdPromise) return type->data.maybe.child_type;
     }
@@ -6289,6 +6308,7 @@ uint32_t type_id_hash(TypeId x) {
                 ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
                 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
                 (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
+                (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
                 (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
                 (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
                 (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881);
@@ -6339,6 +6359,7 @@ bool type_id_eql(TypeId a, TypeId b) {
                 a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
                 a.data.pointer.is_const == b.data.pointer.is_const &&
                 a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
+                a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
                 a.data.pointer.alignment == b.data.pointer.alignment &&
                 a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
                 a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
src/ir.cpp
@@ -61,7 +61,7 @@ enum ConstCastResultId {
     ConstCastResultIdType,
     ConstCastResultIdUnresolvedInferredErrSet,
     ConstCastResultIdAsyncAllocatorType,
-    ConstCastResultIdNullWrapPtr
+    ConstCastResultIdBadAllowsZero,
 };
 
 struct ConstCastOnly;
@@ -83,6 +83,7 @@ struct ConstCastErrUnionErrSetMismatch;
 struct ConstCastErrUnionPayloadMismatch;
 struct ConstCastErrSetMismatch;
 struct ConstCastTypeMismatch;
+struct ConstCastBadAllowsZero;
 
 struct ConstCastOnly {
     ConstCastResultId id;
@@ -99,6 +100,7 @@ struct ConstCastOnly {
         ConstCastOnly *null_wrap_ptr_child;
         ConstCastArg fn_arg;
         ConstCastArgNoAlias arg_no_alias;
+        ConstCastBadAllowsZero *bad_allows_zero;
     } data;
 };
 
@@ -141,6 +143,12 @@ struct ConstCastErrSetMismatch {
     ZigList<ErrorTableEntry *> missing_errors;
 };
 
+struct ConstCastBadAllowsZero {
+    ZigType *wanted_type;
+    ZigType *actual_type;
+};
+
+
 enum UndefAllowed {
     UndefOk,
     UndefBad,
@@ -8636,6 +8644,14 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
     return err_set_type;
 }
 
+static bool ptr_allows_addr_zero(ZigType *ptr_type) {
+    if (ptr_type->id == ZigTypeIdPointer) {
+        return ptr_type->data.pointer.allow_zero;
+    } else if (ptr_type->id == ZigTypeIdOptional) {
+        return true;
+    }
+    return false;
+}
 
 static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
         ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
@@ -8649,34 +8665,35 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     if (wanted_type == actual_type)
         return result;
 
-    // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively
-    // but not if we want a mutable pointer
-    // and not if the actual pointer has zero bits
-    if (!wanted_is_mutable && wanted_type->id == ZigTypeIdOptional &&
-        wanted_type->data.maybe.child_type->id == ZigTypeIdPointer &&
-        actual_type->id == ZigTypeIdPointer && type_has_bits(actual_type))
-    {
-        ConstCastOnly child = types_match_const_cast_only(ira,
-                wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable);
-        if (child.id == ConstCastResultIdInvalid)
-            return child;
-        if (child.id != ConstCastResultIdOk) {
-            result.id = ConstCastResultIdNullWrapPtr;
-            result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1);
-            *result.data.null_wrap_ptr_child = child;
-        }
-        return result;
-    }
-
-    // pointer const
+    // If pointers have the same representation in memory, they can be "const-casted".
+    // `const` attribute can be gained
+    // `volatile` attribute can be gained
+    // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer)
+    //   but only if !wanted_is_mutable
+    // alignment can be decreased
+    // bit offset attributes must match exactly
+    // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
     ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
     ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
+    bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
+    bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
     bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC;
     bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC;
-    if ((wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) ||
-        (wanted_ptr_type != nullptr && actual_is_c_ptr) ||
-        (actual_ptr_type != nullptr && wanted_is_c_ptr))
-    {
+    bool wanted_opt_or_ptr = wanted_ptr_type != nullptr &&
+        (wanted_type->id == ZigTypeIdPointer || wanted_type->id == ZigTypeIdOptional);
+    bool actual_opt_or_ptr = actual_ptr_type != nullptr &&
+        (actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional);
+    if (wanted_opt_or_ptr && actual_opt_or_ptr) {
+        bool ok_allows_zero = (wanted_allows_zero &&
+                (actual_allows_zero || wanted_ptr_type->data.pointer.is_const)) ||
+            (!wanted_allows_zero && !actual_allows_zero);
+        if (!ok_allows_zero) {
+            result.id = ConstCastResultIdBadAllowsZero;
+            result.data.bad_allows_zero = allocate_nonzero<ConstCastBadAllowsZero>(1);
+            result.data.bad_allows_zero->wanted_type = wanted_type;
+            result.data.bad_allows_zero->actual_type = actual_type;
+            return result;
+        }
         ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
                 actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
         if (child.id == ConstCastResultIdInvalid)
@@ -8699,6 +8716,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
         }
         bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
         if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&
+            type_has_bits(wanted_type) == type_has_bits(actual_type) &&
             (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
             (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
             actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
@@ -9922,7 +9940,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
             if (undef_allowed == UndefOk) {
                 return &value->value;
             } else {
-                ir_add_error(ira, value, buf_sprintf("use of undefined value"));
+                ir_add_error(ira, value, buf_sprintf("use of undefined value here causes undefined behavior"));
                 return nullptr;
             }
     }
@@ -10828,6 +10846,26 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
             report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg);
             break;
         }
+        case ConstCastResultIdBadAllowsZero: {
+            bool wanted_allows_zero = ptr_allows_addr_zero(cast_result->data.bad_allows_zero->wanted_type);
+            bool actual_allows_zero = ptr_allows_addr_zero(cast_result->data.bad_allows_zero->actual_type);
+            ZigType *wanted_ptr_type = get_src_ptr_type(cast_result->data.bad_allows_zero->wanted_type);
+            ZigType *actual_ptr_type = get_src_ptr_type(cast_result->data.bad_allows_zero->actual_type);
+            ZigType *wanted_elem_type = wanted_ptr_type->data.pointer.child_type;
+            ZigType *actual_elem_type = actual_ptr_type->data.pointer.child_type;
+            if (actual_allows_zero && !wanted_allows_zero) {
+                add_error_note(ira->codegen, parent_msg, source_node,
+                        buf_sprintf("'%s' could have null values which are illegal in type '%s'",
+                            buf_ptr(&actual_elem_type->name),
+                            buf_ptr(&wanted_elem_type->name)));
+            } else {
+                add_error_note(ira->codegen, parent_msg, source_node,
+                        buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'",
+                            buf_ptr(&cast_result->data.bad_allows_zero->wanted_type->name),
+                            buf_ptr(&cast_result->data.bad_allows_zero->actual_type->name)));
+            }
+            break;
+        }
         case ConstCastResultIdFnAlign: // TODO
         case ConstCastResultIdFnCC: // TODO
         case ConstCastResultIdFnVarArgs: // TODO
@@ -10838,7 +10876,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
         case ConstCastResultIdFnArgNoAlias: // TODO
         case ConstCastResultIdUnresolvedInferredErrSet: // TODO
         case ConstCastResultIdAsyncAllocatorType: // TODO
-        case ConstCastResultIdNullWrapPtr: // TODO
             break;
     }
 }
@@ -20589,12 +20626,14 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
     // We have a check for zero bits later so we use get_src_ptr_type to
     // validate src_type and dest_type.
 
-    if (get_src_ptr_type(src_type) == nullptr) {
+    ZigType *src_ptr_type = get_src_ptr_type(src_type);
+    if (src_ptr_type == nullptr) {
         ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
         return ira->codegen->invalid_instruction;
     }
 
-    if (get_src_ptr_type(dest_type) == nullptr) {
+    ZigType *dest_ptr_type = get_src_ptr_type(dest_type);
+    if (dest_ptr_type == nullptr) {
         ir_add_error(ira, dest_type_src,
                 buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
         return ira->codegen->invalid_instruction;
@@ -20606,6 +20645,8 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
     }
 
     if (instr_is_comptime(ptr)) {
+        // Undefined is OK here; @ptrCast is defined to reinterpret the bit pattern
+        // of the pointer as the new pointer type.
         ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk);
         if (!val)
             return ira->codegen->invalid_instruction;
test/compile_errors.zig
@@ -1,6 +1,30 @@
 const tests = @import("tests.zig");
 
 pub fn addCases(cases: *tests.CompileErrorContext) void {
+    cases.addTest(
+        "implicit casting C pointers which would mess up null semantics",
+        \\export fn entry() void {
+        \\    var slice: []const u8 = "aoeu";
+        \\    const opt_many_ptr: [*]const u8 = slice.ptr;
+        \\    var ptr_opt_many_ptr = &opt_many_ptr;
+        \\    var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
+        \\    ptr_opt_many_ptr = c_ptr;
+        \\}
+        \\export fn entry2() void {
+        \\    var buf: [4]u8 = "aoeu";
+        \\    var slice: []u8 = &buf;
+        \\    var opt_many_ptr: [*]u8 = slice.ptr;
+        \\    var ptr_opt_many_ptr = &opt_many_ptr;
+        \\    var c_ptr: [*c]const [*c]u8 = ptr_opt_many_ptr;
+        \\}
+    ,
+        ".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'",
+        ".tmp_source.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'",
+        ".tmp_source.zig:13:35: error: expected type '[*c]const [*c]u8', found '*[*]u8'",
+        ".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]u8'",
+        ".tmp_source.zig:13:35: note: mutable '[*c]u8' allows illegal null values stored to type '[*]u8'",
+    );
+
     cases.addTest(
         "implicit casting too big integers to C pointers",
         \\export fn a() void {
@@ -31,7 +55,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    var z = @truncate(u8, u16(undefined));
         \\}
     ,
-        ".tmp_source.zig:2:30: error: use of undefined value",
+        ".tmp_source.zig:2:30: error: use of undefined value here causes undefined behavior",
     );
 
     cases.addTest(
@@ -392,7 +416,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    f(i32);
         \\}
     ,
-        ".tmp_source.zig:4:5: error: use of undefined value",
+        ".tmp_source.zig:4:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -792,7 +816,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    command.exec();
         \\}
     ,
-        ".tmp_source.zig:6:12: error: use of undefined value",
+        ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -805,7 +829,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    command.exec();
         \\}
     ,
-        ".tmp_source.zig:6:12: error: use of undefined value",
+        ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2776,7 +2800,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\
         \\export fn entry() usize { return @sizeOf(@typeOf(x)); }
     ,
-        ".tmp_source.zig:1:15: error: use of undefined value",
+        ".tmp_source.zig:1:15: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2786,7 +2810,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a / a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2796,7 +2820,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a /= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2806,7 +2830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a % a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2816,7 +2840,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a %= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2826,7 +2850,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a + a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2836,7 +2860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a += a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2846,7 +2870,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a +% a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2856,7 +2880,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a +%= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2866,7 +2890,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a - a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2876,7 +2900,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a -= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2886,7 +2910,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a -% a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2896,7 +2920,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a -%= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2906,7 +2930,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a * a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2916,7 +2940,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a *= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2926,7 +2950,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a *% a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2936,7 +2960,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a *%= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2946,7 +2970,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a << 2;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2956,7 +2980,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a <<= 2;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2966,7 +2990,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a >> 2;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2976,7 +3000,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a >>= 2;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2986,7 +3010,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a & a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -2996,7 +3020,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a &= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3006,7 +3030,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a | a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3016,7 +3040,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a |= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3026,7 +3050,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a ^ a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3036,7 +3060,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    a ^= a;
         \\}
     ,
-        ".tmp_source.zig:3:5: error: use of undefined value",
+        ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3046,7 +3070,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a == a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3056,7 +3080,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a != a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3066,7 +3090,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a > a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3076,7 +3100,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a >= a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3086,7 +3110,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a < a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3096,7 +3120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a <= a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3106,7 +3130,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a and a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3116,7 +3140,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a or a;
         \\}
     ,
-        ".tmp_source.zig:3:9: error: use of undefined value",
+        ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3126,7 +3150,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = -a;
         \\}
     ,
-        ".tmp_source.zig:3:10: error: use of undefined value",
+        ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3136,7 +3160,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = -%a;
         \\}
     ,
-        ".tmp_source.zig:3:11: error: use of undefined value",
+        ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3146,7 +3170,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = ~a;
         \\}
     ,
-        ".tmp_source.zig:3:10: error: use of undefined value",
+        ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3156,7 +3180,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = !a;
         \\}
     ,
-        ".tmp_source.zig:3:10: error: use of undefined value",
+        ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3166,7 +3190,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a orelse false;
         \\}
     ,
-        ".tmp_source.zig:3:11: error: use of undefined value",
+        ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(
@@ -3176,7 +3200,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = a catch |err| false;
         \\}
     ,
-        ".tmp_source.zig:3:11: error: use of undefined value",
+        ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
     );
 
     cases.add(