Commit 17622b9db1

Veikka Tuominen <git@vexu.eu>
2022-07-29 10:55:53
Sema: implement `@Type` for functions
Closes #12280
1 parent f43ea43
src/Sema.zig
@@ -15759,6 +15759,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
     const tag_ty = type_info_ty.unionTagType().?;
     const target = mod.getTarget();
     const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?;
+    if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src);
     switch (@intToEnum(std.builtin.TypeId, tag_index)) {
         .Type => return Air.Inst.Ref.type_type,
         .Void => return Air.Inst.Ref.void_type,
@@ -15828,7 +15829,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const is_allowzero_val = struct_val[6];
             const sentinel_val = struct_val[7];
 
-            const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target)); // TODO: Validate this value.
+            if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
+                return sema.fail(block, src, "alignment must fit in 'u32'", .{});
+            }
+            const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target));
 
             var buffer: Value.ToTypeBuffer = undefined;
             const unresolved_elem_ty = child_val.toType(&buffer);
@@ -15855,6 +15859,39 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                 actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
             }
 
+            if (elem_ty.zigTypeTag() == .NoReturn) {
+                return sema.fail(block, src, "pointer to noreturn not allowed", .{});
+            } else if (elem_ty.zigTypeTag() == .Fn) {
+                if (ptr_size != .One) {
+                    return sema.fail(block, src, "function pointers must be single pointers", .{});
+                }
+                const fn_align = elem_ty.fnInfo().alignment;
+                if (abi_align != 0 and fn_align != 0 and
+                    abi_align != fn_align)
+                {
+                    return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{});
+                }
+            } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
+                return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
+            } else if (ptr_size == .C) {
+                if (!(try sema.validateExternType(elem_ty, .other))) {
+                    const msg = msg: {
+                        const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
+                        errdefer msg.destroy(sema.gpa);
+
+                        const src_decl = sema.mod.declPtr(block.src_decl);
+                        try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), elem_ty, .other);
+
+                        try sema.addDeclaredHereNote(msg, elem_ty);
+                        break :msg msg;
+                    };
+                    return sema.failWithOwnedErrorMsg(block, msg);
+                }
+                if (elem_ty.zigTypeTag() == .Opaque) {
+                    return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
+                }
+            }
+
             const ty = try Type.ptr(sema.arena, mod, .{
                 .size = ptr_size,
                 .mutable = !is_const_val.toBool(),
@@ -15915,6 +15952,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena);
             const payload_ty = try payload_val.toType(&buffer).copy(sema.arena);
 
+            if (error_set_ty.zigTypeTag() != .ErrorSet) {
+                return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
+            }
+
             const ty = try Type.Tag.error_union.create(sema.arena, .{
                 .error_set = error_set_ty,
                 .payload = payload_ty,
@@ -15928,7 +15969,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const decl_index = slice_val.ptr.pointerDecl().?;
             try sema.ensureDeclAnalyzed(decl_index);
             const decl = mod.declPtr(decl_index);
-            const array_val = decl.val.castTag(.aggregate).?.data;
+            const array_val: []Value = if (decl.val.castTag(.aggregate)) |some| some.data else &.{};
 
             var names: Module.ErrorSet.NameMap = .{};
             try names.ensureUnusedCapacity(sema.arena, array_val.len);
@@ -15940,7 +15981,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                 const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod);
 
                 const kv = try mod.getErrorValue(name_str);
-                names.putAssumeCapacityNoClobber(kv.key, {});
+                const gop = names.getOrPutAssumeCapacity(kv.key);
+                if (gop.found_existing) {
+                    return sema.fail(block, src, "duplicate error '{s}'", .{name_str});
+                }
             }
 
             // names must be sorted
@@ -16022,13 +16066,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             new_decl.owns_tv = true;
             errdefer mod.abortAnonDecl(new_decl_index);
 
-            // Enum tag type
-            var buffer: Value.ToTypeBuffer = undefined;
-            const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
-
             enum_obj.* = .{
                 .owner_decl = new_decl_index,
-                .tag_ty = int_tag_ty,
+                .tag_ty = Type.@"null",
                 .tag_ty_inferred = false,
                 .fields = .{},
                 .values = .{},
@@ -16040,6 +16080,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                 },
             };
 
+            // Enum tag type
+            var buffer: Value.ToTypeBuffer = undefined;
+            const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
+
+            if (int_tag_ty.zigTypeTag() != .Int) {
+                return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
+            }
+            enum_obj.tag_ty = int_tag_ty;
+
             // Fields
             const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
             if (fields_len > 0) {
@@ -16077,6 +16126,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                         .mod = mod,
                     });
                 }
+            } else {
+                return sema.fail(block, src, "enums must have at least one field", .{});
             }
 
             try new_decl.finalizeNewArena(&new_decl_arena);
@@ -16186,11 +16237,17 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
 
             // Tag type
+            var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
             var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
             const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
             if (tag_type_val.optionalValue()) |payload_val| {
                 var buffer: Value.ToTypeBuffer = undefined;
                 union_obj.tag_ty = try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
+
+                if (union_obj.tag_ty.zigTypeTag() != .Enum) {
+                    return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{});
+                }
+                tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena);
             } else {
                 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null);
                 enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
@@ -16222,6 +16279,19 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                         set.putAssumeCapacity(field_name, {});
                     }
 
+                    if (tag_ty_field_names) |*names| {
+                        const enum_has_field = names.orderedRemove(field_name);
+                        if (!enum_has_field) {
+                            const msg = msg: {
+                                const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) });
+                                errdefer msg.destroy(sema.gpa);
+                                try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
+                                break :msg msg;
+                            };
+                            return sema.failWithOwnedErrorMsg(block, msg);
+                        }
+                    }
+
                     const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
                     if (gop.found_existing) {
                         // TODO: better source location
@@ -16234,12 +16304,108 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                         .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
                     };
                 }
+            } else {
+                return sema.fail(block, src, "unions must have at least one field", .{});
+            }
+
+            if (tag_ty_field_names) |names| {
+                if (names.count() > 0) {
+                    const msg = msg: {
+                        const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
+                        errdefer msg.destroy(sema.gpa);
+
+                        const enum_ty = union_obj.tag_ty;
+                        for (names.keys()) |field_name| {
+                            const field_index = enum_ty.enumFieldIndex(field_name).?;
+                            try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name});
+                        }
+                        try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
+                        break :msg msg;
+                    };
+                    return sema.failWithOwnedErrorMsg(block, msg);
+                }
             }
 
             try new_decl.finalizeNewArena(&new_decl_arena);
             return sema.analyzeDeclVal(block, src, new_decl_index);
         },
-        .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
+        .Fn => {
+            const struct_val = union_val.val.castTag(.aggregate).?.data;
+            // TODO use reflection instead of magic numbers here
+            // calling_convention: CallingConvention,
+            const cc = struct_val[0].toEnum(std.builtin.CallingConvention);
+            // alignment: comptime_int,
+            const alignment_val = struct_val[1];
+            // is_generic: bool,
+            const is_generic = struct_val[2].toBool();
+            // is_var_args: bool,
+            const is_var_args = struct_val[3].toBool();
+            // return_type: ?type,
+            const return_type_val = struct_val[4];
+            // args: []const Param,
+            const args_val = struct_val[5];
+
+            if (is_generic) {
+                return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
+            }
+
+            if (is_var_args and cc != .C) {
+                return sema.fail(block, src, "varargs functions must have C calling convention", .{});
+            }
+
+            const alignment = @intCast(u29, alignment_val.toUnsignedInt(target)); // TODO: Validate this value.
+            var buf: Value.ToTypeBuffer = undefined;
+
+            const args: []Value = if (args_val.castTag(.aggregate)) |some| some.data else &.{};
+            var param_types = try sema.arena.alloc(Type, args.len);
+            var comptime_params = try sema.arena.alloc(bool, args.len);
+            var noalias_bits: u32 = 0;
+            for (args) |arg, i| {
+                const arg_val = arg.castTag(.aggregate).?.data;
+                // TODO use reflection instead of magic numbers here
+                // is_generic: bool,
+                const arg_is_generic = arg_val[0].toBool();
+                // is_noalias: bool,
+                const arg_is_noalias = arg_val[1].toBool();
+                // arg_type: ?type,
+                const param_type_val = arg_val[2];
+
+                if (arg_is_generic) {
+                    return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
+                }
+
+                if (arg_is_noalias) {
+                    noalias_bits = @as(u32, 1) << (std.math.cast(u5, i) orelse
+                        return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
+                }
+
+                const param_type = param_type_val.optionalValue() orelse
+                    return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{});
+
+                param_types[i] = try param_type.toType(&buf).copy(sema.arena);
+            }
+
+            const return_type = return_type_val.optionalValue() orelse
+                return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
+
+            var fn_info = Type.Payload.Function.Data{
+                .param_types = param_types,
+                .comptime_params = comptime_params.ptr,
+                .noalias_bits = noalias_bits,
+                .return_type = try return_type.toType(&buf).copy(sema.arena),
+                .alignment = alignment,
+                .cc = cc,
+                .is_var_args = is_var_args,
+                .is_generic = false,
+                .align_is_generic = false,
+                .cc_is_generic = false,
+                .section_is_generic = false,
+                .addrspace_is_generic = false,
+            };
+
+            const ty = try Type.Tag.function.create(sema.arena, fn_info);
+            return sema.addType(ty);
+        },
         .BoundFn => @panic("TODO delete BoundFn from the language"),
         .Frame => @panic("TODO implement https://github.com/ziglang/zig/issues/10710"),
     }
@@ -16382,6 +16548,11 @@ fn reifyStruct(
         // alignment: comptime_int,
         const alignment_val = field_struct_val[4];
 
+        if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
+            return sema.fail(block, src, "alignment must fit in 'u32'", .{});
+        }
+        const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target));
+
         const field_name = try name_val.toAllocatedBytes(
             Type.initTag(.const_slice_u8),
             new_decl_arena_allocator,
@@ -16405,7 +16576,7 @@ fn reifyStruct(
         var buffer: Value.ToTypeBuffer = undefined;
         gop.value_ptr.* = .{
             .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator),
-            .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
+            .abi_align = abi_align,
             .default_val = default_val,
             .is_comptime = is_comptime_val.toBool(),
             .offset = undefined,
@@ -27314,7 +27485,8 @@ fn enumFieldSrcLoc(
         .container_decl_arg_trailing,
         => tree.containerDeclArg(enum_node),
 
-        else => unreachable,
+        // Container was constructed with `@Type`.
+        else => return LazySrcLoc.nodeOffset(node_offset),
     };
     var it_index: usize = 0;
     for (container_decl.ast.members) |member_node| {
src/value.zig
@@ -2766,6 +2766,19 @@ pub const Value = extern union {
         return self.isUndef();
     }
 
+    /// Returns true if any value contained in `self` is undefined.
+    /// TODO: check for cases such as array that is not marked undef but all the element
+    /// values are marked undef, or struct that is not marked undef but all fields are marked
+    /// undef, etc.
+    pub fn anyUndef(self: Value) bool {
+        if (self.castTag(.aggregate)) |aggregate| {
+            for (aggregate.data) |val| {
+                if (val.anyUndef()) return true;
+            }
+        }
+        return self.isUndef();
+    }
+
     /// Asserts the value is not undefined and not unreachable.
     /// Integer value 0 is considered null because of C pointers.
     pub fn isNull(self: Value) bool {
test/cases/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig → test/cases/compile_errors/stage1/reify_type.Pointer_with_invalid_address_space.zig
File renamed without changes
test/cases/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig → test/cases/compile_errors/stage1/reify_type_with_non-constant_expression.zig
File renamed without changes
test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig → test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig
@@ -11,7 +11,7 @@ const Foo = @Type(.{
 comptime { _ = Foo; }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: Type.Fn.is_generic must be false for @Type
+// :1:13: error: Type.Fn.is_generic must be false for @Type
test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig → test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig
@@ -11,7 +11,7 @@ const Foo = @Type(.{
 comptime { _ = Foo; }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: varargs functions must have C calling convention
+// :1:13: error: varargs functions must have C calling convention
test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig → test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig
@@ -11,7 +11,7 @@ const Foo = @Type(.{
 comptime { _ = Foo; }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: Type.Fn.return_type must be non-null for @Type
+// :1:13: error: Type.Fn.return_type must be non-null for @Type
test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig → test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig
@@ -12,7 +12,7 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: Type.Enum.tag_type must be an integer type, not 'bool'
+// :1:13: error: Type.Enum.tag_type must be an integer type
test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig → test/cases/compile_errors/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig
@@ -12,7 +12,7 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: use of undefined value here causes undefined behavior
+// :1:13: error: use of undefined value here causes undefined behavior
test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig → test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig
@@ -12,7 +12,7 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: enums must have 1 or more fields
+// :1:13: error: enums must have at least one field
test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig → test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig
@@ -28,7 +28,9 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:14:23: error: enum field missing: 'arst'
+// :14:16: error: enum field(s) missing in union
+// :1:13: note: field 'arst' missing, declared here
+// :1:13: note: enum declared here
test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig → test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig
@@ -28,8 +28,8 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:13:23: error: enum field not found: 'arst'
-// tmp.zig:1:20: note: enum declared here
+// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_264'
+// :1:13: note: enum declared here
test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig → test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig
@@ -11,7 +11,7 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:25: error: unions must have 1 or more fields
+// :1:18: error: unions must have at least one field
test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig → test/cases/compile_errors/reify_type_union_payload_is_undefined.zig
@@ -4,7 +4,7 @@ const Foo = @Type(.{
 comptime { _ = Foo; }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:1:20: error: use of undefined value here causes undefined behavior
+// :1:13: error: use of undefined value here causes undefined behavior
test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig → test/cases/compile_errors/reify_type_with_Type.Int.zig
@@ -7,7 +7,9 @@ export fn entry() void {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int'
+// :3:31: error: expected type 'builtin.Type', found 'builtin.Type.Int'
+// :?:?: note: struct declared here
+// :?:?: note: union declared here
test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig → test/cases/compile_errors/reify_type_with_undefined.zig
@@ -13,8 +13,8 @@ comptime {
 }
 
 // error
-// backend=stage1
+// backend=stage2
 // target=native
 //
-// tmp.zig:2:16: error: use of undefined value here causes undefined behavior
-// tmp.zig:5:16: error: use of undefined value here causes undefined behavior
+// :2:9: error: use of undefined value here causes undefined behavior
+// :5:9: error: use of undefined value here causes undefined behavior