Commit dd75e7bcb1

Andrew Kelley <andrew@ziglang.org>
2025-06-13 01:03:32
Sema: add missing error and test for bool not on vector of ints
1 parent dcdb442
Changed files (3)
src/Sema.zig
@@ -1171,11 +1171,11 @@ fn analyzeBodyInner(
             .as_node                      => try sema.zirAsNode(block, inst),
             .as_shift_operand             => try sema.zirAsShiftOperand(block, inst),
             .bit_and                      => try sema.zirBitwise(block, inst, .bit_and),
-            .bit_not                      => try sema.zirBitNot(block, inst, false),
+            .bit_not                      => try sema.zirBitNot(block, inst),
             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
             .bitcast                      => try sema.zirBitcast(block, inst),
             .suspend_block                => try sema.zirSuspendBlock(block, inst),
-            .bool_not                     => try sema.zirBitNot(block, inst, true),
+            .bool_not                     => try sema.zirBoolNot(block, inst),
             .bool_br_and                  => try sema.zirBoolBr(block, inst, false),
             .bool_br_or                   => try sema.zirBoolBr(block, inst, true),
             .c_import                     => try sema.zirCImport(block, inst),
@@ -14464,57 +14464,55 @@ fn zirBitwise(
     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
 }
 
-fn zirBitNot(
-    sema: *Sema,
-    block: *Block,
-    inst: Zir.Inst.Index,
-    is_bool_not: bool,
-) CompileError!Air.Inst.Ref {
-    const tracy = trace(@src());
-    defer tracy.end();
-
+fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const pt = sema.pt;
     const zcu = pt.zcu;
     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
-    const src = block.nodeOffset(inst_data.src_node);
     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
-
+    const src = block.nodeOffset(inst_data.src_node);
     const operand = try sema.resolveInst(inst_data.operand);
-    const operand_type = sema.typeOf(operand);
-    const scalar_type = operand_type.scalarType(zcu);
-    const scalar_tag = scalar_type.zigTypeTag(zcu);
+    const operand_ty = sema.typeOf(operand);
+    const scalar_ty = operand_ty.scalarType(zcu);
+    const scalar_tag = scalar_ty.zigTypeTag(zcu);
 
-    const is_finite_int_or_bool = scalar_tag == .int or scalar_tag == .bool;
-    const is_allowed_type = if (is_bool_not) scalar_tag == .bool else is_finite_int_or_bool;
+    if (scalar_tag != .int and scalar_tag != .bool)
+        return sema.fail(block, operand_src, "bitwise not operation on type '{}'", .{operand_ty.fmt(pt)});
 
-    if (!is_allowed_type) {
-        return sema.fail(block, src, "unable to perform {s} not operation on type '{}'", .{
-            if (is_bool_not) "boolean" else "binary", operand_type.fmt(pt),
-        });
-    }
+    return analyzeBitNot(sema, block, operand, src);
+}
 
+fn analyzeBitNot(
+    sema: *Sema,
+    block: *Block,
+    operand: Air.Inst.Ref,
+    src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
+    const pt = sema.pt;
+    const zcu = pt.zcu;
+    const operand_ty = sema.typeOf(operand);
+    const scalar_ty = operand_ty.scalarType(zcu);
     if (try sema.resolveValue(operand)) |val| {
         if (val.isUndef(zcu)) {
-            return pt.undefRef(operand_type);
-        } else if (operand_type.zigTypeTag(zcu) == .vector) {
-            const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen(zcu));
+            return pt.undefRef(operand_ty);
+        } else if (operand_ty.zigTypeTag(zcu) == .vector) {
+            const vec_len = try sema.usizeCast(block, src, operand_ty.vectorLen(zcu));
             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
             for (elems, 0..) |*elem, i| {
                 const elem_val = try val.elemValue(pt, i);
-                elem.* = (try elem_val.bitwiseNot(scalar_type, sema.arena, pt)).toIntern();
+                elem.* = (try elem_val.bitwiseNot(scalar_ty, sema.arena, pt)).toIntern();
             }
             return Air.internedToRef((try pt.intern(.{ .aggregate = .{
-                .ty = operand_type.toIntern(),
+                .ty = operand_ty.toIntern(),
                 .storage = .{ .elems = elems },
             } })));
         } else {
-            const result_val = try val.bitwiseNot(operand_type, sema.arena, pt);
+            const result_val = try val.bitwiseNot(operand_ty, sema.arena, pt);
             return Air.internedToRef(result_val.toIntern());
         }
     }
 
     try sema.requireRuntimeBlock(block, src, null);
-    return block.addTyOp(.not, operand_type, operand);
+    return block.addTyOp(.not, operand_ty, operand);
 }
 
 fn analyzeTupleCat(
@@ -18367,6 +18365,30 @@ fn zirTypeofPeer(
     return Air.internedToRef(result_type.toIntern());
 }
 
+fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+    const pt = sema.pt;
+    const zcu = pt.zcu;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
+    const src = block.nodeOffset(inst_data.src_node);
+    const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
+    const uncasted_operand = try sema.resolveInst(inst_data.operand);
+    const uncasted_ty = sema.typeOf(uncasted_operand);
+    if (uncasted_ty.isVector(zcu)) {
+        if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {
+            return sema.fail(block, operand_src, "boolean not operation on type '{}'", .{
+                uncasted_ty.fmt(pt),
+            });
+        }
+        return analyzeBitNot(sema, block, uncasted_operand, src);
+    }
+    const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src);
+    if (try sema.resolveValue(operand)) |val| {
+        return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true;
+    }
+    try sema.requireRuntimeBlock(block, src, null);
+    return block.addTyOp(.not, .bool, operand);
+}
+
 fn zirBoolBr(
     sema: *Sema,
     parent_block: *Block,
test/cases/compile_errors/binary_not_on_number_literal.zig
@@ -10,4 +10,4 @@ export fn entry() usize {
 // backend=stage2
 // target=native
 //
-// :3:60: error: unable to perform binary not operation on type 'comptime_int'
+// :3:61: error: bitwise not operation on type 'comptime_int'
test/cases/compile_errors/bool_not_on_vector_of_ints.zig
@@ -0,0 +1,10 @@
+export fn foo() void {
+    var x: @Vector(2, u1) = .{ 0, 1 };
+    x = !x;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :3:10: error: boolean not operation on type '@Vector(2, u1)'