Commit d3b4b2edf1

Veikka Tuominen <git@vexu.eu>
2022-08-30 14:21:50
Sema: shift of comptime int with runtime value
Closes #12290
1 parent 01d19a8
src/AstGen.zig
@@ -226,6 +226,8 @@ pub const ResultLoc = union(enum) {
     ref,
     /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
     ty: Zir.Inst.Ref,
+    /// Same as `ty` but for shift operands.
+    ty_shift_operand: Zir.Inst.Ref,
     /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
     /// so no `as` instruction needs to be emitted.
     coerced_ty: Zir.Inst.Ref,
@@ -259,7 +261,7 @@ pub const ResultLoc = union(enum) {
     fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
         switch (rl) {
             // In this branch there will not be any store_to_block_ptr instructions.
-            .none, .ty, .coerced_ty, .ref => return .{
+            .none, .ty, .ty_shift_operand, .coerced_ty, .ref => return .{
                 .tag = .break_operand,
                 .elide_store_to_block_ptr_instructions = false,
             },
@@ -302,6 +304,14 @@ pub const ResultLoc = union(enum) {
             else => rl,
         };
     }
+
+    fn zirTag(rl: ResultLoc) Zir.Inst.Tag {
+        return switch (rl) {
+            .ty => .as_node,
+            .ty_shift_operand => .as_shift_operand,
+            else => unreachable,
+        };
+    }
 };
 
 pub const align_rl: ResultLoc = .{ .ty = .u29_type };
@@ -1385,7 +1395,7 @@ fn arrayInitExpr(
             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
         },
-        .ty, .coerced_ty => {
+        .ty, .ty_shift_operand, .coerced_ty => {
             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
             const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
             return rvalue(gz, rl, result, node);
@@ -1631,7 +1641,7 @@ fn structInitExpr(
                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
             }
         },
-        .ty, .coerced_ty => |ty_inst| {
+        .ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
             if (struct_init.ast.type_expr == 0) {
                 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
                 return rvalue(gz, rl, result, node);
@@ -2327,6 +2337,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .anyframe_type,
             .as,
             .as_node,
+            .as_shift_operand,
             .bit_and,
             .bitcast,
             .bit_or,
@@ -2497,7 +2508,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .field_parent_ptr,
             .maximum,
             .minimum,
-            .builtin_async_call,
             .c_import,
             .@"resume",
             .@"await",
@@ -7278,7 +7288,7 @@ fn as(
 ) InnerError!Zir.Inst.Ref {
     const dest_type = try typeExpr(gz, scope, lhs);
     switch (rl) {
-        .none, .discard, .ref, .ty, .coerced_ty => {
+        .none, .discard, .ref, .ty, .ty_shift_operand, .coerced_ty => {
             const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
             return rvalue(gz, rl, result, node);
         },
@@ -7959,7 +7969,8 @@ fn builtinCall(
             return rvalue(gz, rl, result, node);
         },
         .async_call => {
-            const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{
+            const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
+                .node = gz.nodeIndexToRelative(node),
                 .frame_buffer = try expr(gz, scope, .none, params[0]),
                 .result_ptr = try expr(gz, scope, .none, params[1]),
                 .fn_ptr = try expr(gz, scope, .none, params[2]),
@@ -8178,7 +8189,7 @@ fn shiftOp(
 ) InnerError!Zir.Inst.Ref {
     const lhs = try expr(gz, scope, .none, lhs_node);
     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
-    const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node);
+    const rhs = try expr(gz, scope, .{ .ty_shift_operand = log2_int_type }, rhs_node);
     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
         .lhs = lhs,
         .rhs = rhs,
@@ -9409,7 +9420,7 @@ fn rvalue(
             }
             return indexToRef(gop.value_ptr.*);
         },
-        .ty => |ty_inst| {
+        .ty, .ty_shift_operand => |ty_inst| {
             // Quickly eliminate some common, unnecessary type coercion.
             const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
             const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
@@ -9470,7 +9481,7 @@ fn rvalue(
                 => return result, // type of result is already correct
 
                 // Need an explicit type coercion instruction.
-                else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{
+                else => return gz.addPlNode(rl.zirTag(), src_node, Zir.Inst.As{
                     .dest_type = ty_inst,
                     .operand = result,
                 }),
@@ -10350,7 +10361,7 @@ const GenZir = struct {
         // we emit ZIR for the block break instructions to have the result values,
         // and then rvalue() on that to pass the value to the result location.
         switch (parent_rl) {
-            .ty, .coerced_ty => |ty_inst| {
+            .ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
                 gz.rl_ty_inst = ty_inst;
                 gz.break_result_loc = parent_rl;
             },
@@ -11506,7 +11517,7 @@ const GenZir = struct {
     fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
         switch (rl) {
             .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node),
-            .ty => _ = try gz.addUnNode(.ret_node, operand, node),
+            .ty, .ty_shift_operand => _ = try gz.addUnNode(.ret_node, operand, node),
             else => unreachable,
         }
     }
src/Autodoc.zig
@@ -1888,7 +1888,7 @@ fn walkInstruction(
                 .expr = .{ .typeInfo = operand_index },
             };
         },
-        .as_node => {
+        .as_node, .as_shift_operand => {
             const pl_node = data[inst_index].pl_node;
             const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
             const dest_type_walk = try self.walkRef(
src/print_zir.zig
@@ -283,7 +283,6 @@ const Writer = struct {
             .mul_add => try self.writeMulAdd(stream, inst),
             .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
             .builtin_call => try self.writeBuiltinCall(stream, inst),
-            .builtin_async_call => try self.writeBuiltinAsyncCall(stream, inst),
 
             .struct_init_anon,
             .struct_init_anon_ref,
@@ -397,7 +396,7 @@ const Writer = struct {
             .field_val_named,
             => try self.writePlNodeFieldNamed(stream, inst),
 
-            .as_node => try self.writeAs(stream, inst),
+            .as_node, .as_shift_operand => try self.writeAs(stream, inst),
 
             .repeat,
             .repeat_inline,
@@ -531,6 +530,7 @@ const Writer = struct {
                 try stream.writeAll(") ");
                 try self.writeSrc(stream, src);
             },
+            .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
         }
     }
 
@@ -814,9 +814,8 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
-    fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
-        const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index).data;
+    fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
+        const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data;
         try self.writeInstRef(stream, extra.frame_buffer);
         try stream.writeAll(", ");
         try self.writeInstRef(stream, extra.result_ptr);
@@ -825,7 +824,7 @@ const Writer = struct {
         try stream.writeAll(", ");
         try self.writeInstRef(stream, extra.args);
         try stream.writeAll(") ");
-        try self.writeSrc(stream, inst_data.src());
+        try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node));
     }
 
     fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
src/Sema.zig
@@ -712,6 +712,7 @@ fn analyzeBodyInner(
             .vector_type                  => try sema.zirVectorType(block, inst),
             .as                           => try sema.zirAs(block, inst),
             .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),
             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
@@ -848,7 +849,6 @@ fn analyzeBodyInner(
             .mul_add                      => try sema.zirMulAdd(block, inst),
             .builtin_call                 => try sema.zirBuiltinCall(block, inst),
             .field_parent_ptr             => try sema.zirFieldParentPtr(block, inst),
-            .builtin_async_call           => try sema.zirBuiltinAsyncCall(block, inst),
             .@"resume"                    => try sema.zirResume(block, inst),
             .@"await"                     => try sema.zirAwait(block, inst),
             .array_base_ptr               => try sema.zirArrayBasePtr(block, inst),
@@ -956,6 +956,7 @@ fn analyzeBodyInner(
                     .error_to_int          => try sema.zirErrorToInt(        block, extended),
                     .int_to_error          => try sema.zirIntToError(        block, extended),
                     .reify                 => try sema.zirReify(             block, extended, inst),
+                    .builtin_async_call    => try sema.zirBuiltinAsyncCall(  block, extended),
                     // zig fmt: on
                     .fence => {
                         try sema.zirFence(block, extended);
@@ -8257,7 +8258,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst
     defer tracy.end();
 
     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
-    return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs);
+    return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
 }
 
 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8267,7 +8268,17 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
-    return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
+    return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
+}
+
+fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src = inst_data.src();
+    const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
+    return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
 }
 
 fn analyzeAs(
@@ -8276,6 +8287,7 @@ fn analyzeAs(
     src: LazySrcLoc,
     zir_dest_type: Zir.Inst.Ref,
     zir_operand: Zir.Inst.Ref,
+    no_cast_to_comptime_int: bool,
 ) CompileError!Air.Inst.Ref {
     const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
         sema.code.instructions.items(.tag)[ptr_index] == .ret_type
@@ -8287,7 +8299,7 @@ fn analyzeAs(
     if (dest_ty.zigTypeTag() == .NoReturn) {
         return sema.fail(block, src, "cannot cast to noreturn", .{});
     }
-    return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) {
+    return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) {
         error.NotCoercible => unreachable,
         else => |e| return e,
     };
@@ -10491,7 +10503,12 @@ fn zirShl(
 
     const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
         if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
-        const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
+        const rhs_val = maybe_rhs_val orelse {
+            if (scalar_ty.zigTypeTag() == .ComptimeInt) {
+                return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
+            }
+            break :rs rhs_src;
+        };
 
         const val = switch (air_tag) {
             .shl_exact => val: {
@@ -10615,7 +10632,10 @@ fn zirShr(
     const target = sema.mod.getTarget();
     const scalar_ty = lhs_ty.scalarType();
 
-    const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
+    const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
+    const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
+
+    const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
         if (rhs_val.isUndef()) {
             return sema.addConstUndef(lhs_ty);
         }
@@ -10647,7 +10667,7 @@ fn zirShr(
                 });
             }
         }
-        if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
+        if (maybe_lhs_val) |lhs_val| {
             if (lhs_val.isUndef()) {
                 return sema.addConstUndef(lhs_ty);
             }
@@ -10665,6 +10685,10 @@ fn zirShr(
         }
     } else rhs_src;
 
+    if (maybe_rhs_val == null and scalar_ty.zigTypeTag() == .ComptimeInt) {
+        return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
+    }
+
     try sema.requireRuntimeBlock(block, src, runtime_src);
     const result = try block.addBinOp(air_tag, lhs, rhs);
     if (block.wantSafety()) {
@@ -15385,7 +15409,7 @@ fn analyzeRet(
     if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
         try sema.addToInferredErrorSet(uncasted_operand);
     }
-    const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) {
+    const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
         error.NotCoercible => unreachable,
         else => |e| return e,
     };
@@ -19652,9 +19676,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     });
 }
 
-fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
+fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+    const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+    const src = LazySrcLoc.nodeOffset(extra.node);
     return sema.failWithUseOfAsync(block, src);
 }
 
@@ -22542,7 +22566,7 @@ fn coerce(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
-    return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) {
+    return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
         error.NotCoercible => unreachable,
         else => |e| return e,
     };
@@ -22554,14 +22578,22 @@ const CoersionError = CompileError || error{
     NotCoercible,
 };
 
+const CoerceOpts = struct {
+    /// Should coerceExtra emit error messages.
+    report_err: bool = true,
+    /// Ignored if `report_err == false`.
+    is_ret: bool = false,
+    /// Should coercion to comptime_int ermit an error message.
+    no_cast_to_comptime_int: bool = false,
+};
+
 fn coerceExtra(
     sema: *Sema,
     block: *Block,
     dest_ty_unresolved: Type,
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
-    report_err: bool,
-    is_ret: bool,
+    opts: CoerceOpts,
 ) CoersionError!Air.Inst.Ref {
     switch (dest_ty_unresolved.tag()) {
         .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
@@ -22613,7 +22645,7 @@ fn coerceExtra(
 
             // T to ?T
             const child_type = try dest_ty.optionalChildAlloc(sema.arena);
-            const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) {
+            const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
                 error.NotCoercible => {
                     if (in_memory_result == .no_match) {
                         // Try to give more useful notes
@@ -22729,7 +22761,7 @@ fn coerceExtra(
                         return sema.addConstant(dest_ty, Value.@"null");
                     },
                     .ComptimeInt => {
-                        const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) {
+                        const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
                             error.NotCoercible => break :pointer,
                             else => |e| return e,
                         };
@@ -22740,7 +22772,7 @@ fn coerceExtra(
                             .signed => Type.isize,
                             .unsigned => Type.usize,
                         };
-                        const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) {
+                        const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
                             error.NotCoercible => {
                                 // Try to give more useful notes
                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
@@ -22866,7 +22898,13 @@ fn coerceExtra(
         },
         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
             .Float, .ComptimeFloat => float: {
-                const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
+                const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
+                    if (dest_ty.zigTypeTag() == .ComptimeInt) {
+                        if (!opts.report_err) return error.NotCoercible;
+                        return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
+                    }
+                    break :float;
+                };
 
                 if (val.floatHasFraction()) {
                     return sema.fail(
@@ -22883,11 +22921,16 @@ fn coerceExtra(
                 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
                     // comptime known integer to other number
                     if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) {
-                        if (!report_err) return error.NotCoercible;
+                        if (!opts.report_err) return error.NotCoercible;
                         return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
                     }
                     return try sema.addConstant(dest_ty, val);
                 }
+                if (dest_ty.zigTypeTag() == .ComptimeInt) {
+                    if (!opts.report_err) return error.NotCoercible;
+                    if (opts.no_cast_to_comptime_int) return inst;
+                    return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
+                }
 
                 // integer widening
                 const dst_info = dest_ty.intInfo(target);
@@ -22924,6 +22967,7 @@ fn coerceExtra(
                     }
                     return try sema.addConstant(dest_ty, result_val);
                 } else if (dest_ty.zigTypeTag() == .ComptimeFloat) {
+                    if (!opts.report_err) return error.NotCoercible;
                     return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
                 }
 
@@ -22936,7 +22980,13 @@ fn coerceExtra(
                 }
             },
             .Int, .ComptimeInt => int: {
-                const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int;
+                const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
+                    if (dest_ty.zigTypeTag() == .ComptimeFloat) {
+                        if (!opts.report_err) return error.NotCoercible;
+                        return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
+                    }
+                    break :int;
+                };
                 const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
                 // TODO implement this compile error
                 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
@@ -23088,9 +23138,9 @@ fn coerceExtra(
         return sema.addConstUndef(dest_ty);
     }
 
-    if (!report_err) return error.NotCoercible;
+    if (!opts.report_err) return error.NotCoercible;
 
-    if (is_ret and dest_ty.zigTypeTag() == .NoReturn) {
+    if (opts.is_ret and dest_ty.zigTypeTag() == .NoReturn) {
         const msg = msg: {
             const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
             errdefer msg.destroy(sema.gpa);
@@ -23127,7 +23177,7 @@ fn coerceExtra(
         try in_memory_result.report(sema, block, inst_src, msg);
 
         // Add notes about function return type
-        if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
+        if (opts.is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
             const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
             if (inst_ty.isError() and !dest_ty.isError()) {
@@ -24088,7 +24138,7 @@ fn storePtr2(
     // https://github.com/ziglang/zig/issues/11154
     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
         const vector_ty = sema.typeOf(vector_ptr).childType();
-        const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
+        const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
             error.NotCoercible => unreachable,
             else => |e| return e,
         };
@@ -24096,7 +24146,7 @@ fn storePtr2(
         return;
     }
 
-    const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
+    const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
         error.NotCoercible => unreachable,
         else => |e| return e,
     };
@@ -26831,7 +26881,7 @@ fn wrapErrorUnionPayload(
     inst_src: LazySrcLoc,
 ) !Air.Inst.Ref {
     const dest_payload_ty = dest_ty.errorUnionPayload();
-    const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, false, false);
+    const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
     if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| {
         return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
     }
src/Zir.zig
@@ -242,6 +242,8 @@ pub const Inst = struct {
         /// Type coercion to the function's return type.
         /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
         as_node,
+        /// Same as `as_node` but ignores runtime to comptime int error.
+        as_shift_operand,
         /// Bitwise AND. `&`
         bit_and,
         /// Reinterpret the memory representation of a value as a different type.
@@ -942,9 +944,6 @@ pub const Inst = struct {
         /// Implements the `@maximum` builtin.
         /// Uses the `pl_node` union field with payload `Bin`
         maximum,
-        /// Implements the `@asyncCall` builtin.
-        /// Uses the `pl_node` union field with payload `AsyncCall`.
-        builtin_async_call,
         /// Implements the `@cImport` builtin.
         /// Uses the `pl_node` union field with payload `Block`.
         c_import,
@@ -1029,6 +1028,7 @@ pub const Inst = struct {
                 .anyframe_type,
                 .as,
                 .as_node,
+                .as_shift_operand,
                 .bit_and,
                 .bitcast,
                 .bit_or,
@@ -1231,7 +1231,6 @@ pub const Inst = struct {
                 .memcpy,
                 .memset,
                 .minimum,
-                .builtin_async_call,
                 .c_import,
                 .@"resume",
                 .@"await",
@@ -1339,6 +1338,7 @@ pub const Inst = struct {
                 .anyframe_type,
                 .as,
                 .as_node,
+                .as_shift_operand,
                 .bit_and,
                 .bitcast,
                 .bit_or,
@@ -1513,7 +1513,6 @@ pub const Inst = struct {
                 .field_parent_ptr,
                 .maximum,
                 .minimum,
-                .builtin_async_call,
                 .c_import,
                 .@"resume",
                 .@"await",
@@ -1577,6 +1576,7 @@ pub const Inst = struct {
                 .anyframe_type = .un_node,
                 .as = .bin,
                 .as_node = .pl_node,
+                .as_shift_operand = .pl_node,
                 .bit_and = .pl_node,
                 .bitcast = .pl_node,
                 .bit_not = .un_node,
@@ -1801,7 +1801,6 @@ pub const Inst = struct {
                 .memcpy = .pl_node,
                 .memset = .pl_node,
                 .minimum = .pl_node,
-                .builtin_async_call = .pl_node,
                 .c_import = .pl_node,
 
                 .alloc = .un_node,
@@ -1972,6 +1971,9 @@ pub const Inst = struct {
         /// `operand` is payload index to `UnNode`.
         /// `small` contains `NameStrategy
         reify,
+        /// Implements the `@asyncCall` builtin.
+        /// `operand` is payload index to `AsyncCall`.
+        builtin_async_call,
 
         pub const InstData = struct {
             opcode: Extended,
@@ -3454,6 +3456,7 @@ pub const Inst = struct {
     };
 
     pub const AsyncCall = struct {
+        node: i32,
         frame_buffer: Ref,
         result_ptr: Ref,
         fn_ptr: Ref,
test/cases/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig
@@ -1,9 +0,0 @@
-export fn entry(x: u8) u8 {
-    return 0x11 << x;
-}
-
-// error
-// backend=stage1
-// target=native
-//
-// tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known
test/cases/compile_errors/runtime_to_comptime_num.zig
@@ -0,0 +1,31 @@
+pub export fn entry() void {
+    var a: u32 = 0;
+    _ = @as(comptime_int, a);
+}
+pub export fn entry2() void{
+    var a: u32 = 0;
+    _ = @as(comptime_float, a);
+}
+pub export fn entry3() void{
+    comptime var aa: comptime_float = 0.0;
+    var a: f32 = 4;
+    aa = a;
+}
+pub export fn entry4() void{
+    comptime var aa: comptime_int = 0.0;
+    var a: f32 = 4;
+    aa = a;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :3:27: error: unable to resolve comptime value
+// :3:27: note: value being casted to 'comptime_int' must be comptime known
+// :7:29: error: unable to resolve comptime value
+// :7:29: note: value being casted to 'comptime_float' must be comptime known
+// :12:10: error: unable to resolve comptime value
+// :12:10: note: value being casted to 'comptime_float' must be comptime known
+// :17:10: error: unable to resolve comptime value
+// :17:10: note: value being casted to 'comptime_int' must be comptime known
test/cases/compile_errors/shifting_without_int_type_or_comptime_known.zig
@@ -0,0 +1,23 @@
+export fn entry(x: u8) u8 {
+    return 0x11 << x;
+}
+export fn entry1(x: u8) u8 {
+    return 0x11 >> x;
+}
+export fn entry2() void {
+    var x: u5 = 1;
+    _ = @shlExact(12345, x);
+}
+export fn entry3() void {
+    var x: u5 = 1;
+    _ = @shrExact(12345, x);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
+// :5:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
+// :9:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
+// :13:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known