Commit a7221ef4e9

Andrew Kelley <andrew@ziglang.org>
2021-05-07 07:30:44
Sema: implement `@typeInfo` for functions
The goal is to get start code to be able to inspect the calling convention of `main` in order to determine whether to export a main for libc to call, or to allow the root source file to do it.
1 parent 3acd98f
lib/std/start.zig
@@ -28,8 +28,10 @@ comptime {
     // self-hosted is capable enough to handle all of the real start.zig logic.
     if (builtin.zig_is_stage2) {
         if (builtin.output_mode == .Exe) {
-            if (builtin.link_libc or builtin.object_format == .c) {
-                @export(main2, .{ .name = "main" });
+            if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) {
+                if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
+                    @export(main2, .{ .name = "main" });
+                }
             } else {
                 if (!@hasDecl(root, "_start")) {
                     @export(_start2, .{ .name = "_start" });
src/Sema.zig
@@ -4708,7 +4708,44 @@ fn zirBuiltinSrc(
 fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
-    return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeInfo", .{});
+    const ty = try sema.resolveType(block, src, inst_data.operand);
+    const type_info_ty = try sema.getBuiltinType(block, src, "TypeInfo");
+    const target = sema.mod.getTarget();
+
+    switch (ty.zigTypeTag()) {
+        .Fn => {
+            const field_values = try sema.arena.alloc(Value, 6);
+            // calling_convention: CallingConvention,
+            field_values[0] = try Value.Tag.enum_field_index.create(
+                sema.arena,
+                @enumToInt(ty.fnCallingConvention()),
+            );
+            // alignment: comptime_int,
+            field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.ptrAlignment(target));
+            // is_generic: bool,
+            field_values[2] = Value.initTag(.bool_false); // TODO
+            // is_var_args: bool,
+            field_values[3] = Value.initTag(.bool_false); // TODO
+            // return_type: ?type,
+            field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType());
+            // args: []const FnArg,
+            field_values[5] = Value.initTag(.null_value); // TODO
+
+            return sema.mod.constInst(sema.arena, src, .{
+                .ty = type_info_ty,
+                .val = try Value.Tag.@"union".create(sema.arena, .{
+                    .tag = try Value.Tag.enum_field_index.create(
+                        sema.arena,
+                        @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
+                    ),
+                    .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
+                }),
+            });
+        },
+        else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{
+            @tagName(t),
+        }),
+    }
 }
 
 fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
src/type.zig
@@ -1214,7 +1214,7 @@ pub const Type = extern union {
                 if (ptr_info.@"align" != 0) {
                     return ptr_info.@"align";
                 } else {
-                    return ptr_info.pointee_type.abiAlignment();
+                    return ptr_info.pointee_type.abiAlignment(target);
                 }
             },
 
src/value.zig
@@ -120,6 +120,8 @@ pub const Value = extern union {
         error_union,
         /// An instance of a struct.
         @"struct",
+        /// An instance of a union.
+        @"union",
         /// This is a special value that tracks a set of types that have been stored
         /// to an inferred allocation. It does not support any of the normal value queries.
         inferred_alloc,
@@ -228,6 +230,7 @@ pub const Value = extern union {
                 .@"error" => Payload.Error,
                 .inferred_alloc => Payload.InferredAlloc,
                 .@"struct" => Payload.Struct,
+                .@"union" => Payload.Union,
             };
         }
 
@@ -446,6 +449,7 @@ pub const Value = extern union {
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
             .@"struct" => @panic("TODO can't copy struct value without knowing the type"),
+            .@"union" => @panic("TODO can't copy union value without knowing the type"),
 
             .inferred_alloc => unreachable,
         }
@@ -528,6 +532,9 @@ pub const Value = extern union {
             .@"struct" => {
                 return out_stream.writeAll("(struct value)");
             },
+            .@"union" => {
+                return out_stream.writeAll("(union value)");
+            },
             .null_value => return out_stream.writeAll("null"),
             .undef => return out_stream.writeAll("undefined"),
             .zero => return out_stream.writeAll("0"),
@@ -709,6 +716,7 @@ pub const Value = extern union {
             .error_union,
             .empty_struct_value,
             .@"struct",
+            .@"union",
             .inferred_alloc,
             .abi_align_default,
             => unreachable,
@@ -1225,6 +1233,7 @@ pub const Value = extern union {
             .export_options_type,
             .extern_options_type,
             .@"struct",
+            .@"union",
             => @panic("TODO this hash function looks pretty broken. audit it"),
         }
         return hasher.final();
@@ -1413,6 +1422,7 @@ pub const Value = extern union {
             .error_union,
             .empty_struct_value,
             .@"struct",
+            .@"union",
             .null_value,
             .abi_align_default,
             => false,
@@ -1564,6 +1574,16 @@ pub const Value = extern union {
             /// Field values. The number and type are according to the struct type.
             data: [*]Value,
         };
+
+        pub const Union = struct {
+            pub const base_tag = Tag.@"union";
+
+            base: Payload = .{ .tag = base_tag },
+            data: struct {
+                tag: Value,
+                val: Value,
+            },
+        };
     };
 
     /// Big enough to fit any non-BigInt value
BRANCH_TODO
@@ -5,6 +5,9 @@
  * modify stage2 CBE tests so that only 1 uses pub export main and the
    rest use pub fn main
 
+ * get the test runner and `zig test` working
+ * get behavior tests passing for stage2
+
  * use a hash map for instructions because the array is too big
    - no, actually modify the Zir.Inst.Ref strategy so that each decl gets
      their indexes starting at 0 so that we can use an array to store Sema