Commit 35d82d31be

mlugg <mlugg@mlugg.co.uk>
2023-04-14 23:32:31
Add `@inComptime` builtin
Resolves: #868
1 parent 42ee364
doc/langref.html.in
@@ -8587,6 +8587,17 @@ test "@hasDecl" {
       {#see_also|Compile Variables|@embedFile#}
       {#header_close#}
 
+      {#header_open|@inComptime#}
+      <pre>{#syntax#}@inComptime() bool{#endsyntax#}</pre>
+      <p>
+      Returns whether the builtin was run in a {#syntax#}comptime{#endsyntax#} context. The result is a compile-time constant.
+      </p>
+      <p>
+      This can be used to provide alternative, comptime-friendly implementations of functions. It should not be used, for instance, to exclude certain functions from being evaluated at comptime.
+      </p>
+      {#see_also|comptime#}
+      {#header_close#}
+
       {#header_open|@intCast#}
       <pre>{#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
       <p>
lib/std/crypto/sha2.zig
@@ -71,12 +71,6 @@ const Sha256Params = Sha2Params32{
 
 const v4u32 = @Vector(4, u32);
 
-// TODO: Remove once https://github.com/ziglang/zig/issues/868 is resolved.
-fn isComptime() bool {
-    var a: u8 = 0;
-    return @typeInfo(@TypeOf(.{a})).Struct.fields[0].is_comptime;
-}
-
 /// SHA-224
 pub const Sha224 = Sha2x32(Sha224Params);
 
@@ -203,7 +197,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
                 s[i] = mem.readIntBig(u32, mem.asBytes(elem));
             }
 
-            if (!isComptime()) {
+            if (!@inComptime()) {
                 switch (builtin.cpu.arch) {
                     .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .sha2)) {
                         var x: v4u32 = d.s[0..4].*;
lib/std/fmt.zig
@@ -1428,8 +1428,7 @@ pub fn formatInt(
     var a: MinInt = abs_value;
     var index: usize = buf.len;
 
-    // TODO isComptime here because of https://github.com/ziglang/zig/issues/13335.
-    if (base == 10 and !isComptime()) {
+    if (base == 10) {
         while (a >= 100) : (a = @divTrunc(a, 100)) {
             index -= 2;
             buf[index..][0..2].* = digits2(@intCast(usize, a % 100));
@@ -1469,12 +1468,6 @@ pub fn formatInt(
     return formatBuf(buf[index..], options, writer);
 }
 
-// TODO: Remove once https://github.com/ziglang/zig/issues/868 is resolved.
-fn isComptime() bool {
-    var a: u8 = 0;
-    return @typeInfo(@TypeOf(.{a})).Struct.fields[0].is_comptime;
-}
-
 pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, case: Case, options: FormatOptions) usize {
     var fbs = std.io.fixedBufferStream(out_buf);
     formatInt(value, base, case, options, fbs.writer()) catch unreachable;
src/AstGen.zig
@@ -8174,6 +8174,7 @@ fn builtinCall(
         .frame              => return rvalue(gz, ri, try gz.addNodeExtended(.frame,              node), node),
         .frame_address      => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,      node), node),
         .breakpoint         => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,         node), node),
+        .in_comptime        => return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime,        node), node),
 
         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
src/BuiltinFn.zig
@@ -58,6 +58,7 @@ pub const Tag = enum {
     has_decl,
     has_field,
     import,
+    in_comptime,
     int_cast,
     int_to_enum,
     int_to_error,
@@ -560,6 +561,13 @@ pub const list = list: {
                 .param_count = 1,
             },
         },
+        .{
+            "@inComptime",
+            .{
+                .tag = .in_comptime,
+                .param_count = 0,
+            },
+        },
         .{
             "@intCast",
             .{
src/print_zir.zig
@@ -466,6 +466,7 @@ const Writer = struct {
             .frame_address,
             .breakpoint,
             .c_va_start,
+            .in_comptime,
             => try self.writeExtNode(stream, extended),
 
             .builtin_src => {
src/Sema.zig
@@ -1166,6 +1166,7 @@ fn analyzeBodyInner(
                     .work_item_id          => try sema.zirWorkItem(          block, extended, extended.opcode),
                     .work_group_size       => try sema.zirWorkItem(          block, extended, extended.opcode),
                     .work_group_id         => try sema.zirWorkItem(          block, extended, extended.opcode),
+                    .in_comptime           => try sema.zirInComptime(        block),
                     // zig fmt: on
 
                     .fence => {
@@ -22466,6 +22467,18 @@ fn zirWorkItem(
     });
 }
 
+fn zirInComptime(
+    sema: *Sema,
+    block: *Block,
+) CompileError!Air.Inst.Ref {
+    _ = sema;
+    if (block.is_comptime) {
+        return Air.Inst.Ref.bool_true;
+    } else {
+        return Air.Inst.Ref.bool_false;
+    }
+}
+
 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
     if (block.is_comptime) {
         const msg = msg: {
src/Zir.zig
@@ -1994,10 +1994,10 @@ pub const Inst = struct {
         /// Implement builtin `@cVaArg`.
         /// `operand` is payload index to `BinNode`.
         c_va_arg,
-        /// Implement builtin `@cVaStart`.
+        /// Implement builtin `@cVaCopy`.
         /// `operand` is payload index to `UnNode`.
         c_va_copy,
-        /// Implement builtin `@cVaStart`.
+        /// Implement builtin `@cVaEnd`.
         /// `operand` is payload index to `UnNode`.
         c_va_end,
         /// Implement builtin `@cVaStart`.
@@ -2018,6 +2018,9 @@ pub const Inst = struct {
         /// Implements the `@workGroupId` builtin.
         /// `operand` is payload index to `UnNode`.
         work_group_id,
+        /// Implements the `@inComptime` builtin.
+        /// `operand` is `src_node: i32`.
+        in_comptime,
 
         pub const InstData = struct {
             opcode: Extended,
test/behavior/eval.zig
@@ -1649,3 +1649,15 @@ test "early exit in container level const" {
     };
     try expect(S.value == 1);
 }
+
+test "@inComptime" {
+    const S = struct {
+        fn inComptime() bool {
+            return @inComptime();
+        }
+    };
+    try expectEqual(false, @inComptime());
+    try expectEqual(true, comptime @inComptime());
+    try expectEqual(false, S.inComptime());
+    try expectEqual(true, comptime S.inComptime());
+}