From c5383173a0de17210f3036acd942738e906997e5 Mon Sep 17 00:00:00 2001
From: Matthew Lugg
{#syntax#}@Type(comptime info: std.builtin.Type) type{#endsyntax#}
- - This function is the inverse of {#link|@typeInfo#}. It reifies type information - into a {#syntax#}type{#endsyntax#}. -
-- It is available for the following types: -
-{#syntax#}@EnumLiteral() type{#endsyntax#}
+ Returns the comptime-only "enum literal" type. This is the type of uncoerced {#link|Enum Literals#}. Values of this type can coerce to any {#link|enum#} with a matching field.
{#header_close#} + + {#header_open|@Int#} +{#syntax#}@Int(comptime signedness: std.builtin.Signedness, comptime bits: u16) type{#endsyntax#}
+ Returns an integer type with the given signedness and bit width.
+For instance, {#syntax#}@Int(.unsigned, 18){#endsyntax#} returns the type {#syntax#}u18{#endsyntax#}.
+ {#header_close#} + + {#header_open|@Tuple#} +{#syntax#}@Tuple(comptime field_types: []const type) type{#endsyntax#}
+ Returns a {#link|tuple|Tuples#} type with the given field types.
+ {#header_close#} + + {#header_open|@Pointer#} +{#syntax#}@Pointer(
+ comptime size: std.builtin.Type.Pointer.Size,
+ comptime attrs: std.builtin.Type.Pointer.Attributes,
+ comptime Element: type,
+ comptime sentinel: ?Element,
+) type{#endsyntax#}
+ Returns a {#link|pointer|Pointers#} type with the properties specified by the arguments.
+ {#header_close#} + + {#header_open|@Fn#} +{#syntax#}@Fn(
+ comptime param_types: []const type,
+ comptime param_attrs: *const [param_types.len]std.builtin.Type.Fn.Param.Attributes,
+ comptime ReturnType: type,
+ comptime attrs: std.builtin.Type.Fn.Attributes,
+) type{#endsyntax#}
+ Returns a {#link|function|Functions#} type with the properties specified by the arguments.
+ {#header_close#} + + {#header_open|@Struct#} +{#syntax#}@Struct(
+ comptime layout: std.builtin.Type.ContainerLayout,
+ comptime BackingInt: ?type,
+ comptime field_names: []const []const u8,
+ comptime field_types: *const [field_names.len]type,
+ comptime field_attrs: *const [field_names.len]std.builtin.Type.StructField.Attributes,
+) type{#endsyntax#}
+ Returns a {#link|struct#} type with the properties specified by the arguments.
+ {#header_close#} + + {#header_open|@Union#} +{#syntax#}@Union(
+ comptime layout: std.builtin.Type.ContainerLayout,
+ /// Either the integer tag type, or the integer backing type, depending on `layout`.
+ comptime ArgType: ?type,
+ comptime field_names: []const []const u8,
+ comptime field_types: *const [field_names.len]type,
+ comptime field_attrs: *const [field_names.len]std.builtin.Type.UnionField.Attributes,
+) type{#endsyntax#}
+ Returns a {#link|union#} type with the properties specified by the arguments.
+ {#header_close#} + + {#header_open|@Enum#} +{#syntax#}@Enum(
+ comptime TagInt: type,
+ comptime mode: std.builtin.Type.Enum.Mode,
+ comptime field_names: []const []const u8,
+ comptime field_values: *const [field_names.len]TagInt,
+) type{#endsyntax#}
+ Returns an {#link|enum#} type with the properties specified by the arguments.
+ {#header_close#} + {#header_open|@typeInfo#}{#syntax#}@typeInfo(comptime T: type) std.builtin.Type{#endsyntax#}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 7b60289edf..1ef77de9a5 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -548,19 +548,19 @@ pub const TypeId = std.meta.Tag(Type); /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const Type = union(enum) { - type: void, - void: void, - bool: void, - noreturn: void, + type, + void, + bool, + noreturn, int: Int, float: Float, pointer: Pointer, array: Array, @"struct": Struct, - comptime_float: void, - comptime_int: void, - undefined: void, - null: void, + comptime_float, + comptime_int, + undefined, + null, optional: Optional, error_union: ErrorUnion, error_set: ErrorSet, @@ -571,7 +571,7 @@ pub const Type = union(enum) { frame: Frame, @"anyframe": AnyFrame, vector: Vector, - enum_literal: void, + enum_literal, /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. @@ -619,6 +619,16 @@ pub const Type = union(enum) { slice, c, }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Attributes = struct { + @"const": bool = false, + @"volatile": bool = false, + @"allowzero": bool = false, + @"addrspace": ?AddressSpace = null, + @"align": ?usize = null, + }; }; /// This data structure is used by the Zig language code generation and @@ -668,6 +678,14 @@ pub const Type = union(enum) { const dp: *const sf.type = @ptrCast(@alignCast(sf.default_value_ptr orelse return null)); return dp.*; } + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Attributes = struct { + @"comptime": bool = false, + @"align": ?usize = null, + default_value_ptr: ?*const anyopaque = null, + }; }; /// This data structure is used by the Zig language code generation and @@ -718,6 +736,10 @@ pub const Type = union(enum) { fields: []const EnumField, decls: []const Declaration, is_exhaustive: bool, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Mode = enum { exhaustive, nonexhaustive }; }; /// This data structure is used by the Zig language code generation and @@ -726,6 +748,12 @@ pub const Type = union(enum) { name: [:0]const u8, type: type, alignment: comptime_int, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Attributes = struct { + @"align": ?usize = null, + }; }; /// This data structure is used by the Zig language code generation and @@ -753,6 +781,19 @@ pub const Type = union(enum) { is_generic: bool, is_noalias: bool, type: ?type, + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Attributes = struct { + @"noalias": bool = false, + }; + }; + + /// This data structure is used by the Zig language code generation and + /// therefore must be kept in sync with the compiler implementation. + pub const Attributes = struct { + @"callconv": CallingConvention = .auto, + varargs: bool = false, }; }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 4c0203b83d..c8a0dcde3b 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -773,7 +773,6 @@ pub const EnvVar = enum { pub const SimpleComptimeReason = enum(u32) { // Evaluating at comptime because a builtin operand must be comptime-known. // These messages all mention a specific builtin. - operand_Type, operand_setEvalBranchQuota, operand_setFloatMode, operand_branchHint, @@ -809,25 +808,34 @@ pub const SimpleComptimeReason = enum(u32) { // Evaluating at comptime because types must be comptime-known. // Reasons other than `.type` are just more specific messages. type, + int_signedness, + int_bit_width, array_sentinel, + array_length, + pointer_size, + pointer_attrs, pointer_sentinel, slice_sentinel, - array_length, vector_length, - error_set_contents, - struct_fields, - enum_fields, - union_fields, - function_ret_ty, - function_parameters, + fn_ret_ty, + fn_param_types, + fn_param_attrs, + fn_attrs, + struct_layout, + struct_field_names, + struct_field_types, + struct_field_attrs, + union_layout, + union_field_names, + union_field_types, + union_field_attrs, + tuple_field_types, + enum_field_names, + enum_field_values, // Evaluating at comptime because decl/field name must be comptime-known. decl_name, field_name, - struct_field_name, - enum_field_name, - union_field_name, - tuple_field_name, tuple_field_index, // Evaluating at comptime because it is an attribute of a global declaration. @@ -856,7 +864,6 @@ pub const SimpleComptimeReason = enum(u32) { pub fn message(r: SimpleComptimeReason) []const u8 { return switch (r) { // zig fmt: off - .operand_Type => "operand to '@Type' must be comptime-known", .operand_setEvalBranchQuota => "operand to '@setEvalBranchQuota' must be comptime-known", .operand_setFloatMode => "operand to '@setFloatMode' must be comptime-known", .operand_branchHint => "operand to '@branchHint' must be comptime-known", @@ -888,24 +895,33 @@ pub const SimpleComptimeReason = enum(u32) { .clobber => "clobber must be comptime-known", .type => "types must be comptime-known", + .int_signedness => "integer signedness must be comptime-known", + .int_bit_width => "integer bit width must be comptime-known", .array_sentinel => "array sentinel value must be comptime-known", + .array_length => "array length must be comptime-known", + .pointer_size => "pointer size must be comptime-known", + .pointer_attrs => "pointer attributes must be comptime-known", .pointer_sentinel => "pointer sentinel value must be comptime-known", .slice_sentinel => "slice sentinel value must be comptime-known", - .array_length => "array length must be comptime-known", .vector_length => "vector length must be comptime-known", - .error_set_contents => "error set contents must be comptime-known", - .struct_fields => "struct fields must be comptime-known", - .enum_fields => "enum fields must be comptime-known", - .union_fields => "union fields must be comptime-known", - .function_ret_ty => "function return type must be comptime-known", - .function_parameters => "function parameters must be comptime-known", + .fn_ret_ty => "function return type must be comptime-known", + .fn_param_types => "function parameter types must be comptime-known", + .fn_param_attrs => "function parameter attributes must be comptime-known", + .fn_attrs => "function attributes must be comptime-known", + .struct_layout => "struct layout must be comptime-known", + .struct_field_names => "struct field names must be comptime-known", + .struct_field_types => "struct field types must be comptime-known", + .struct_field_attrs => "struct field attributes must be comptime-known", + .union_layout => "union layout must be comptime-known", + .union_field_names => "union field names must be comptime-known", + .union_field_types => "union field types must be comptime-known", + .union_field_attrs => "union field attributes must be comptime-known", + .tuple_field_types => "tuple field types must be comptime-known", + .enum_field_names => "enum field names must be comptime-known", + .enum_field_values => "enum field values must be comptime-known", .decl_name => "declaration name must be comptime-known", .field_name => "field name must be comptime-known", - .struct_field_name => "struct field name must be comptime-known", - .enum_field_name => "enum field name must be comptime-known", - .union_field_name => "union field name must be comptime-known", - .tuple_field_name => "tuple field name must be comptime-known", .tuple_field_index => "tuple field index must be comptime-known", .container_var_init => "initializer of container-level variable must be comptime-known", diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index c7d7f24938..ac72d395cf 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -833,7 +833,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE => { var buf: [2]Ast.Node.Index = undefined; const params = tree.builtinCallParams(&buf, node).?; - return builtinCall(gz, scope, ri, node, params, false); + return builtinCall(gz, scope, ri, node, params, false, .anon); }, .call_one, @@ -1194,14 +1194,20 @@ fn nameStratExpr( }, .builtin_call_two, .builtin_call_two_comma, + .builtin_call, + .builtin_call_comma, => { const builtin_token = tree.nodeMainToken(node); const builtin_name = tree.tokenSlice(builtin_token); - if (!std.mem.eql(u8, builtin_name, "@Type")) return null; - var buf: [2]Ast.Node.Index = undefined; - const params = tree.builtinCallParams(&buf, node).?; - if (params.len != 1) return null; // let `builtinCall` error - return try builtinReify(gz, scope, ri, node, params[0], name_strat); + const info = BuiltinFn.list.get(builtin_name) orelse return null; + switch (info.tag) { + .Enum, .Struct, .Union => { + var buf: [2]Ast.Node.Index = undefined; + const params = tree.builtinCallParams(&buf, node).?; + return try builtinCall(gz, scope, ri, node, params, false, name_strat); + }, + else => return null, + } }, else => return null, } @@ -1406,7 +1412,7 @@ fn fnProtoExprInner( .none; const ret_ty_node = fn_proto.ast.return_type.unwrap().?; - const ret_ty = try comptimeExpr(&block_scope, scope, coerced_type_ri, ret_ty_node, .function_ret_ty); + const ret_ty = try comptimeExpr(&block_scope, scope, coerced_type_ri, ret_ty_node, .fn_ret_ty); const result = try block_scope.addFunc(.{ .src_node = fn_proto.ast.proto_node, @@ -2629,7 +2635,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod const params = tree.builtinCallParams(&buf, inner_node).?; try emitDbgNode(gz, inner_node); - const result = try builtinCall(gz, scope, .{ .rl = .none }, inner_node, params, allow_branch_hint); + const result = try builtinCall(gz, scope, .{ .rl = .none }, inner_node, params, allow_branch_hint, .anon); noreturn_src_node = try addEnsureResult(gz, result, inner_node); }, @@ -2707,6 +2713,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .elem_type, .indexable_ptr_elem_type, .splat_op_result_ty, + .reify_int, .vector_type, .indexable_ptr_len, .anyframe_type, @@ -8942,7 +8949,7 @@ fn unionInit( params: []const Ast.Node.Index, ) InnerError!Zir.Inst.Ref { const union_type = try typeExpr(gz, scope, params[0]); - const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .union_field_name); + const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .union_field_names); const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{ .container_type = union_type, .field_name = field_name, @@ -9210,6 +9217,7 @@ fn builtinCall( node: Ast.Node.Index, params: []const Ast.Node.Index, allow_branch_hint: bool, + reify_name_strat: Zir.Inst.NameStrategy, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const tree = astgen.tree; @@ -9443,9 +9451,140 @@ fn builtinCall( return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node); }, - .Type => { - return builtinReify(gz, scope, ri, node, params[0], .anon); + .EnumLiteral => return rvalue(gz, ri, .enum_literal_type, node), + .Int => { + const signedness_ty = try gz.addBuiltinValue(node, .signedness); + const result = try gz.addPlNode(.reify_int, node, Zir.Inst.Bin{ + .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = signedness_ty } }, params[0], .int_signedness), + .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, params[1], .int_bit_width), + }); + return rvalue(gz, ri, result, node); }, + .Tuple => { + const result = try gz.addExtendedPayload(.reify_tuple, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_type_type } }, params[0], .tuple_field_types), + }); + return rvalue(gz, ri, result, node); + }, + .Pointer => { + const ptr_size_ty = try gz.addBuiltinValue(node, .pointer_size); + const ptr_attrs_ty = try gz.addBuiltinValue(node, .pointer_attributes); + const size = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = ptr_size_ty } }, params[0], .pointer_size); + const attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = ptr_attrs_ty } }, params[1], .pointer_attrs); + const elem_ty = try typeExpr(gz, scope, params[2]); + const sentinel_ty = try gz.addExtendedPayload(.reify_pointer_sentinel_ty, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(params[2]), + .operand = elem_ty, + }); + const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, params[3], .pointer_sentinel); + const result = try gz.addExtendedPayload(.reify_pointer, Zir.Inst.ReifyPointer{ + .node = gz.nodeIndexToRelative(node), + .size = size, + .attrs = attrs, + .elem_ty = elem_ty, + .sentinel = sentinel, + }); + return rvalue(gz, ri, result, node); + }, + .Fn => { + const fn_attrs_ty = try gz.addBuiltinValue(node, .fn_attributes); + const param_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_type_type } }, params[0], .fn_param_types); + const param_attrs_ty = try gz.addExtendedPayloadSmall( + .reify_slice_arg_ty, + @intFromEnum(Zir.Inst.ReifySliceArgInfo.type_to_fn_param_attrs), + Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[0]), .operand = param_types }, + ); + const param_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = param_attrs_ty } }, params[1], .fn_param_attrs); + const ret_ty = try comptimeExpr(gz, scope, coerced_type_ri, params[2], .fn_ret_ty); + const fn_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = fn_attrs_ty } }, params[3], .fn_attrs); + const result = try gz.addExtendedPayload(.reify_fn, Zir.Inst.ReifyFn{ + .node = gz.nodeIndexToRelative(node), + .param_types = param_types, + .param_attrs = param_attrs, + .ret_ty = ret_ty, + .fn_attrs = fn_attrs, + }); + return rvalue(gz, ri, result, node); + }, + .Struct => { + const container_layout_ty = try gz.addBuiltinValue(node, .container_layout); + const layout = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = container_layout_ty } }, params[0], .struct_layout); + const backing_ty = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .optional_type_type } }, params[1], .type); + const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .struct_field_names); + const field_types_ty = try gz.addExtendedPayloadSmall( + .reify_slice_arg_ty, + @intFromEnum(Zir.Inst.ReifySliceArgInfo.string_to_struct_field_type), + Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names }, + ); + const field_attrs_ty = try gz.addExtendedPayloadSmall( + .reify_slice_arg_ty, + @intFromEnum(Zir.Inst.ReifySliceArgInfo.string_to_struct_field_attrs), + Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names }, + ); + const field_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_types_ty } }, params[3], .struct_field_types); + const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .struct_field_attrs); + const result = try gz.addExtendedPayloadSmall(.reify_struct, @intFromEnum(reify_name_strat), Zir.Inst.ReifyStruct{ + .src_line = gz.astgen.source_line, + .node = node, + .layout = layout, + .backing_ty = backing_ty, + .field_names = field_names, + .field_types = field_types, + .field_attrs = field_attrs, + }); + return rvalue(gz, ri, result, node); + }, + .Union => { + const container_layout_ty = try gz.addBuiltinValue(node, .container_layout); + const layout = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = container_layout_ty } }, params[0], .union_layout); + const arg_ty = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .optional_type_type } }, params[1], .type); + const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .union_field_names); + const field_types_ty = try gz.addExtendedPayloadSmall( + .reify_slice_arg_ty, + @intFromEnum(Zir.Inst.ReifySliceArgInfo.string_to_union_field_type), + Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names }, + ); + const field_attrs_ty = try gz.addExtendedPayloadSmall( + .reify_slice_arg_ty, + @intFromEnum(Zir.Inst.ReifySliceArgInfo.string_to_union_field_attrs), + Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names }, + ); + const field_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_types_ty } }, params[3], .union_field_types); + const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .union_field_attrs); + const result = try gz.addExtendedPayloadSmall(.reify_union, @intFromEnum(reify_name_strat), Zir.Inst.ReifyUnion{ + .src_line = gz.astgen.source_line, + .node = node, + .layout = layout, + .arg_ty = arg_ty, + .field_names = field_names, + .field_types = field_types, + .field_attrs = field_attrs, + }); + return rvalue(gz, ri, result, node); + }, + .Enum => { + const enum_mode_ty = try gz.addBuiltinValue(node, .enum_mode); + const tag_ty = try typeExpr(gz, scope, params[0]); + const mode = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = enum_mode_ty } }, params[1], .type); + const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .enum_field_names); + const field_values_ty = try gz.addExtendedPayload(.reify_enum_value_slice_ty, Zir.Inst.BinNode{ + .node = gz.nodeIndexToRelative(node), + .lhs = tag_ty, + .rhs = field_names, + }); + const field_values = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_values_ty } }, params[3], .enum_field_values); + const result = try gz.addExtendedPayloadSmall(.reify_enum, @intFromEnum(reify_name_strat), Zir.Inst.ReifyEnum{ + .src_line = gz.astgen.source_line, + .node = node, + .tag_ty = tag_ty, + .mode = mode, + .field_names = field_names, + .field_values = field_values, + }); + return rvalue(gz, ri, result, node); + }, + .panic => { try emitDbgNode(gz, node); return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic); @@ -9764,41 +9903,6 @@ fn builtinCall( }, } } -fn builtinReify( - gz: *GenZir, - scope: *Scope, - ri: ResultInfo, - node: Ast.Node.Index, - arg_node: Ast.Node.Index, - name_strat: Zir.Inst.NameStrategy, -) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const gpa = astgen.gpa; - - const type_info_ty = try gz.addBuiltinValue(node, .type_info); - const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, arg_node); - - try gz.instructions.ensureUnusedCapacity(gpa, 1); - try astgen.instructions.ensureUnusedCapacity(gpa, 1); - - const payload_index = try astgen.addExtra(Zir.Inst.Reify{ - .node = node, // Absolute node index -- see the definition of `Reify`. - .operand = operand, - .src_line = astgen.source_line, - }); - const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); - astgen.instructions.appendAssumeCapacity(.{ - .tag = .extended, - .data = .{ .extended = .{ - .opcode = .reify, - .small = @intFromEnum(name_strat), - .operand = payload_index, - } }, - }); - gz.instructions.appendAssumeCapacity(new_index); - const result = new_index.toRef(); - return rvalue(gz, ri, result, node); -} fn hasDeclOrField( gz: *GenZir, diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index 5beb6fec73..c7e9abf2bb 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -866,6 +866,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. // These builtins take no args and do not consume the result pointer. .src, .This, + .EnumLiteral, .return_address, .error_return_trace, .frame, @@ -906,7 +907,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .embed_file, .error_name, .set_runtime_safety, - .Type, + .Tuple, .c_undef, .c_include, .wasm_memory_size, @@ -1058,6 +1059,48 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. _ = try astrl.expr(args[3], block, ResultInfo.none); return false; }, + .Int => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + return false; + }, + .Pointer => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + _ = try astrl.expr(args[2], block, ResultInfo.type_only); + _ = try astrl.expr(args[3], block, ResultInfo.type_only); + return false; + }, + .Fn => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + _ = try astrl.expr(args[2], block, ResultInfo.type_only); + _ = try astrl.expr(args[3], block, ResultInfo.type_only); + return false; + }, + .Struct => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + _ = try astrl.expr(args[2], block, ResultInfo.type_only); + _ = try astrl.expr(args[3], block, ResultInfo.type_only); + _ = try astrl.expr(args[4], block, ResultInfo.type_only); + return false; + }, + .Union => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + _ = try astrl.expr(args[2], block, ResultInfo.type_only); + _ = try astrl.expr(args[3], block, ResultInfo.type_only); + _ = try astrl.expr(args[4], block, ResultInfo.type_only); + return false; + }, + .Enum => { + _ = try astrl.expr(args[0], block, ResultInfo.type_only); + _ = try astrl.expr(args[1], block, ResultInfo.type_only); + _ = try astrl.expr(args[2], block, ResultInfo.type_only); + _ = try astrl.expr(args[3], block, ResultInfo.type_only); + return false; + }, .Vector => { _ = try astrl.expr(args[0], block, ResultInfo.type_only); _ = try astrl.expr(args[1], block, ResultInfo.type_only); diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 5ef28fdfaf..cdc63f9666 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/lib/std/zig/BuiltinFn.zig @@ -110,7 +110,14 @@ pub const Tag = enum { This, trap, truncate, - Type, + EnumLiteral, + Int, + Tuple, + Pointer, + Fn, + Struct, + Union, + Enum, type_info, type_name, TypeOf, @@ -937,12 +944,61 @@ pub const list = list: { }, }, .{ - "@Type", + "@EnumLiteral", .{ - .tag = .Type, + .tag = .EnumLiteral, + .param_count = 0, + }, + }, + .{ + "@Int", + .{ + .tag = .Int, + .param_count = 2, + }, + }, + .{ + "@Tuple", + .{ + .tag = .Tuple, .param_count = 1, }, }, + .{ + "@Pointer", + .{ + .tag = .Pointer, + .param_count = 4, + }, + }, + .{ + "@Fn", + .{ + .tag = .Fn, + .param_count = 4, + }, + }, + .{ + "@Struct", + .{ + .tag = .Struct, + .param_count = 5, + }, + }, + .{ + "@Union", + .{ + .tag = .Union, + .param_count = 5, + }, + }, + .{ + "@Enum", + .{ + .tag = .Enum, + .param_count = 4, + }, + }, .{ "@typeInfo", .{ diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 7a1069fe7d..08770def8d 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -260,6 +260,10 @@ pub const Inst = struct { /// `[N:S]T` syntax. Source location is the array type expression node. /// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`. array_type_sentinel, + /// `@Int` builtin. + /// Uses the `pl_node` union field with `Bin` payload. + /// lhs is signedness, rhs is bit count. + reify_int, /// `@Vector` builtin. /// Uses the `pl_node` union field with `Bin` payload. /// lhs is length, rhs is element type. @@ -1112,6 +1116,7 @@ pub const Inst = struct { .array_mul, .array_type, .array_type_sentinel, + .reify_int, .vector_type, .elem_type, .indexable_ptr_elem_type, @@ -1409,6 +1414,7 @@ pub const Inst = struct { .array_mul, .array_type, .array_type_sentinel, + .reify_int, .vector_type, .elem_type, .indexable_ptr_elem_type, @@ -1644,6 +1650,7 @@ pub const Inst = struct { .array_mul = .pl_node, .array_type = .pl_node, .array_type_sentinel = .pl_node, + .reify_int = .pl_node, .vector_type = .pl_node, .elem_type = .un_node, .indexable_ptr_elem_type = .un_node, @@ -2035,10 +2042,43 @@ pub const Inst = struct { /// Implement builtin `@errorFromInt`. /// `operand` is payload index to `UnNode`. error_from_int, - /// Implement builtin `@Type`. - /// `operand` is payload index to `Reify`. + /// Given a comptime-known operand of type `[]const A`, returns the type `*const [operand.len]B`. + /// The types `A` and `B` are determined from `ReifySliceArgInfo`. + /// This instruction is used to provide result types to arguments of `@Fn`, `@Struct`, etc. + /// `operand` is payload index to `UnNode`. + /// `small` is a bitcast `ReifySliceArgInfo`. + reify_slice_arg_ty, + /// Like `reify_slice_arg_ty` for the specific case of `[]const []const u8` to `[]const TagInt`, + /// as needed for `@Enum`. + /// `operand` is payload index to `BinNode`. lhs is the type `TagInt`. rhs is the `[]const []const u8` value. + /// `small` is unused. + reify_enum_value_slice_ty, + /// Given a comptime-known operand of type `type`, returns the type `?operand` if possible, otherwise `?noreturn`. + /// Used for the final arg of `@Pointer` to allow reifying pointers to opaque types. + /// `operand` is payload index to `UnNode`. + /// `small` is unused. + reify_pointer_sentinel_ty, + /// Implements builtin `@Tuple`. + /// `operand` is payload index to `UnNode`. + reify_tuple, + /// Implements builtin `@Pointer`. + /// `operand` is payload index to `ReifyPointer`. + reify_pointer, + /// Implements builtin `@Fn`. + /// `operand` is payload index to `ReifyFn`. + reify_fn, + /// Implements builtin `@Struct`. + /// `operand` is payload index to `ReifyStruct`. /// `small` contains `NameStrategy`. - reify, + reify_struct, + /// Implements builtin `@Union`. + /// `operand` is payload index to `ReifyUnion`. + /// `small` contains `NameStrategy`. + reify_union, + /// Implements builtin `@Enum`. + /// `operand` is payload index to `ReifyEnum`. + /// `small` contains `NameStrategy`. + reify_enum, /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins. /// `small` 0=>weak 1=>strong /// `operand` is payload index to `Cmpxchg`. @@ -2226,6 +2266,11 @@ pub const Inst = struct { manyptr_const_u8_sentinel_0_type, slice_const_u8_type, slice_const_u8_sentinel_0_type, + manyptr_const_slice_const_u8_type, + slice_const_slice_const_u8_type, + optional_type_type, + manyptr_const_type_type, + slice_const_type_type, vector_8_i8_type, vector_16_i8_type, vector_32_i8_type, @@ -3169,6 +3214,23 @@ pub const Inst = struct { rhs: Ref, }; + pub const ReifySliceArgInfo = enum(u16) { + /// Input element type is `type`. + /// Output element type is `std.builtin.Type.Fn.Param.Attributes`. + type_to_fn_param_attrs, + /// Input element type is `[]const u8`. + /// Output element type is `type`. + string_to_struct_field_type, + /// Identical to `string_to_struct_field_type` aside from emitting slightly different error messages. + string_to_union_field_type, + /// Input element type is `[]const u8`. + /// Output element type is `std.builtin.Type.StructField.Attributes`. + string_to_struct_field_attrs, + /// Input element type is `[]const u8`. + /// Output element type is `std.builtin.Type.UnionField.Attributes`. + string_to_union_field_attrs, + }; + pub const UnNode = struct { node: Ast.Node.Offset, operand: Ref, @@ -3179,12 +3241,55 @@ pub const Inst = struct { index: u32, }; - pub const Reify = struct { + pub const ReifyPointer = struct { + node: Ast.Node.Offset, + size: Ref, + attrs: Ref, + elem_ty: Ref, + sentinel: Ref, + }; + + pub const ReifyFn = struct { + node: Ast.Node.Offset, + param_types: Ref, + param_attrs: Ref, + ret_ty: Ref, + fn_attrs: Ref, + }; + + pub const ReifyStruct = struct { + src_line: u32, /// This node is absolute, because `reify` instructions are tracked across updates, and /// this simplifies the logic for getting source locations for types. node: Ast.Node.Index, - operand: Ref, + layout: Ref, + backing_ty: Ref, + field_names: Ref, + field_types: Ref, + field_attrs: Ref, + }; + + pub const ReifyUnion = struct { src_line: u32, + /// This node is absolute, because `reify` instructions are tracked across updates, and + /// this simplifies the logic for getting source locations for types. + node: Ast.Node.Index, + layout: Ref, + arg_ty: Ref, + field_names: Ref, + field_types: Ref, + field_attrs: Ref, + }; + + pub const ReifyEnum = struct { + src_line: u32, + /// This node is absolute, because `reify` instructions are tracked across updates, and + /// this simplifies the logic for getting source locations for types. + node: Ast.Node.Index, + tag_ty: Ref, + mode: Ref, + field_names: Ref, + field_values: Ref, }; /// Trailing: @@ -3496,14 +3601,19 @@ pub const Inst = struct { calling_convention, address_space, float_mode, + signedness, reduce_op, call_modifier, prefetch_options, export_options, extern_options, - type_info, branch_hint, clobbers, + pointer_size, + pointer_attributes, + fn_attributes, + container_layout, + enum_mode, // Values calling_convention_c, calling_convention_inline, @@ -4190,6 +4300,7 @@ fn findTrackableInner( .array_mul, .array_type, .array_type_sentinel, + .reify_int, .vector_type, .elem_type, .indexable_ptr_elem_type, @@ -4432,6 +4543,12 @@ fn findTrackableInner( .select, .int_from_error, .error_from_int, + .reify_slice_arg_ty, + .reify_enum_value_slice_ty, + .reify_pointer_sentinel_ty, + .reify_tuple, + .reify_pointer, + .reify_fn, .cmpxchg, .c_va_arg, .c_va_copy, @@ -4463,7 +4580,11 @@ fn findTrackableInner( }, // Reifications and opaque declarations need tracking, but have no body. - .reify, .opaque_decl => return contents.other.append(gpa, inst), + .reify_enum, + .reify_struct, + .reify_union, + .opaque_decl, + => return contents.other.append(gpa, inst), // Struct declarations need tracking and have bodies. .struct_decl => { @@ -5246,7 +5367,9 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void { .union_decl, .enum_decl, .opaque_decl, - .reify, + .reify_enum, + .reify_struct, + .reify_union, => {}, // tracked in order, as the owner instructions of explicit container types else => unreachable, // assertion failure; not trackable }, diff --git a/src/Air.zig b/src/Air.zig index e210ab17b8..b5cb950d49 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1062,6 +1062,11 @@ pub const Inst = struct { manyptr_const_u8_sentinel_0_type = @intFromEnum(InternPool.Index.manyptr_const_u8_sentinel_0_type), slice_const_u8_type = @intFromEnum(InternPool.Index.slice_const_u8_type), slice_const_u8_sentinel_0_type = @intFromEnum(InternPool.Index.slice_const_u8_sentinel_0_type), + manyptr_const_slice_const_u8_type = @intFromEnum(InternPool.Index.manyptr_const_slice_const_u8_type), + slice_const_slice_const_u8_type = @intFromEnum(InternPool.Index.slice_const_slice_const_u8_type), + optional_type_type = @intFromEnum(InternPool.Index.optional_type_type), + manyptr_const_type_type = @intFromEnum(InternPool.Index.manyptr_const_type_type), + slice_const_type_type = @intFromEnum(InternPool.Index.slice_const_type_type), vector_8_i8_type = @intFromEnum(InternPool.Index.vector_8_i8_type), vector_16_i8_type = @intFromEnum(InternPool.Index.vector_16_i8_type), vector_32_i8_type = @intFromEnum(InternPool.Index.vector_32_i8_type), diff --git a/src/InternPool.zig b/src/InternPool.zig index e2912f2c69..4e8372f1f8 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2017,8 +2017,7 @@ pub const Key = union(enum) { error_union_type: ErrorUnionType, simple_type: SimpleType, /// This represents a struct that has been explicitly declared in source code, - /// or was created with `@Type`. It is unique and based on a declaration. - /// It may be a tuple, if declared like this: `struct {A, B, C}`. + /// or was created with `@Struct`. It is unique and based on a declaration. struct_type: NamespaceType, /// This is a tuple type. Tuples are logically similar to structs, but have some /// important differences in semantics; they do not undergo staged type resolution, @@ -2175,7 +2174,7 @@ pub const Key = union(enum) { /// The union for which this is a tag type. union_type: Index, }, - /// This type originates from a reification via `@Type`, or from an anonymous initialization. + /// This type originates from a reification via `@Enum`, `@Struct`, `@Union` or from an anonymous initialization. /// It is hashed based on its ZIR instruction index and fields, attributes, etc. /// To avoid making this key overly complex, the type-specific data is hashed by Sema. reified: struct { @@ -4641,6 +4640,13 @@ pub const Index = enum(u32) { slice_const_u8_type, slice_const_u8_sentinel_0_type, + manyptr_const_slice_const_u8_type, + slice_const_slice_const_u8_type, + + optional_type_type, + manyptr_const_type_type, + slice_const_type_type, + vector_8_i8_type, vector_16_i8_type, vector_32_i8_type, @@ -5201,6 +5207,45 @@ pub const static_keys: [static_len]Key = .{ }, } }, + // [*]const []const u8 + .{ .ptr_type = .{ + .child = .slice_const_u8_type, + .flags = .{ + .size = .many, + .is_const = true, + }, + } }, + + // []const []const u8 + .{ .ptr_type = .{ + .child = .slice_const_u8_type, + .flags = .{ + .size = .slice, + .is_const = true, + }, + } }, + + // ?type + .{ .opt_type = .type_type }, + + // [*]const type + .{ .ptr_type = .{ + .child = .type_type, + .flags = .{ + .size = .many, + .is_const = true, + }, + } }, + + // []const type + .{ .ptr_type = .{ + .child = .type_type, + .flags = .{ + .size = .slice, + .is_const = true, + }, + } }, + // @Vector(8, i8) .{ .vector_type = .{ .len = 8, .child = .i8_type } }, // @Vector(16, i8) @@ -10225,16 +10270,8 @@ pub fn getGeneratedTagEnumType( } pub const OpaqueTypeInit = struct { - key: union(enum) { - declared: struct { - zir_index: TrackedInst.Index, - captures: []const CaptureValue, - }, - reified: struct { - zir_index: TrackedInst.Index, - // No type hash since reifid opaques have no data other than the `@Type` location - }, - }, + zir_index: TrackedInst.Index, + captures: []const CaptureValue, }; pub fn getOpaqueType( @@ -10243,16 +10280,10 @@ pub fn getOpaqueType( tid: Zcu.PerThread.Id, ini: OpaqueTypeInit, ) Allocator.Error!WipNamespaceType.Result { - var gop = try ip.getOrPutKey(gpa, tid, .{ .opaque_type = switch (ini.key) { - .declared => |d| .{ .declared = .{ - .zir_index = d.zir_index, - .captures = .{ .external = d.captures }, - } }, - .reified => |r| .{ .reified = .{ - .zir_index = r.zir_index, - .type_hash = 0, - } }, - } }); + var gop = try ip.getOrPutKey(gpa, tid, .{ .opaque_type = .{ .declared = .{ + .zir_index = ini.zir_index, + .captures = .{ .external = ini.captures }, + } } }); defer gop.deinit(); if (gop == .existing) return .{ .existing = gop.existing }; @@ -10261,30 +10292,19 @@ pub fn getOpaqueType( const extra = local.getMutableExtra(gpa); try items.ensureUnusedCapacity(1); - try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + switch (ini.key) { - .declared => |d| d.captures.len, - .reified => 0, - }); + try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".fields.len + ini.captures.len); const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{ .name = undefined, // set by `finish` .name_nav = undefined, // set by `finish` .namespace = undefined, // set by `finish` - .zir_index = switch (ini.key) { - inline else => |x| x.zir_index, - }, - .captures_len = switch (ini.key) { - .declared => |d| @intCast(d.captures.len), - .reified => std.math.maxInt(u32), - }, + .zir_index = ini.zir_index, + .captures_len = @intCast(ini.captures.len), }); items.appendAssumeCapacity(.{ .tag = .type_opaque, .data = extra_index, }); - switch (ini.key) { - .declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}), - .reified => {}, - } + extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); return .{ .wip = .{ .tid = tid, @@ -10555,6 +10575,8 @@ pub fn slicePtrType(ip: *const InternPool, index: Index) Index { switch (index) { .slice_const_u8_type => return .manyptr_const_u8_type, .slice_const_u8_sentinel_0_type => return .manyptr_const_u8_sentinel_0_type, + .slice_const_slice_const_u8_type => return .manyptr_const_slice_const_u8_type, + .slice_const_type_type => return .manyptr_const_type_type, else => {}, } const item = index.unwrap(ip).getItem(ip); @@ -12013,8 +12035,13 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .manyptr_u8_type, .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, + .manyptr_const_slice_const_u8_type, .slice_const_u8_type, .slice_const_u8_sentinel_0_type, + .slice_const_slice_const_u8_type, + .optional_type_type, + .manyptr_const_type_type, + .slice_const_type_type, .vector_8_i8_type, .vector_16_i8_type, .vector_32_i8_type, @@ -12355,8 +12382,12 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .manyptr_u8_type, .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, + .manyptr_const_slice_const_u8_type, .slice_const_u8_type, .slice_const_u8_sentinel_0_type, + .slice_const_slice_const_u8_type, + .manyptr_const_type_type, + .slice_const_type_type, => .pointer, .vector_8_i8_type, @@ -12408,6 +12439,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .vector_8_f64_type, => .vector, + .optional_type_type => .optional, .optional_noreturn_type => .optional, .anyerror_void_error_union_type => .error_union, .empty_tuple_type => .@"struct", diff --git a/src/Sema.zig b/src/Sema.zig index 51273d8812..224fc3fa39 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1167,6 +1167,7 @@ fn analyzeBodyInner( .array_mul => try sema.zirArrayMul(block, inst), .array_type => try sema.zirArrayType(block, inst), .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), + .reify_int => try sema.zirReifyInt(block, inst), .vector_type => try sema.zirVectorType(block, inst), .as_node => try sema.zirAsNode(block, inst), .as_shift_operand => try sema.zirAsShiftOperand(block, inst), @@ -1411,7 +1412,6 @@ fn analyzeBodyInner( .select => try sema.zirSelect( block, extended), .int_from_error => try sema.zirIntFromError( block, extended), .error_from_int => try sema.zirErrorFromInt( block, extended), - .reify => try sema.zirReify( block, extended, inst), .cmpxchg => try sema.zirCmpxchg( block, extended), .c_va_arg => try sema.zirCVaArg( block, extended), .c_va_copy => try sema.zirCVaCopy( block, extended), @@ -1424,6 +1424,16 @@ fn analyzeBodyInner( .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), .in_comptime => try sema.zirInComptime( block), .closure_get => try sema.zirClosureGet( block, extended), + + .reify_slice_arg_ty => try sema.zirReifySliceArgTy( block, extended), + .reify_enum_value_slice_ty => try sema.zirReifyEnumValueSliceTy(block, extended), + .reify_pointer_sentinel_ty => try sema.zirReifyPointerSentinelTy(block, extended), + .reify_tuple => try sema.zirReifyTuple( block, extended), + .reify_pointer => try sema.zirReifyPointer( block, extended), + .reify_fn => try sema.zirReifyFn( block, extended), + .reify_struct => try sema.zirReifyStruct( block, extended, inst), + .reify_union => try sema.zirReifyUnion( block, extended, inst), + .reify_enum => try sema.zirReifyEnum( block, extended, inst), // zig fmt: on .set_float_mode => { @@ -3517,10 +3527,8 @@ fn zirOpaqueDecl( extra_index += captures_len * 2; const opaque_init: InternPool.OpaqueTypeInit = .{ - .key = .{ .declared = .{ - .zir_index = tracked_inst, - .captures = captures, - } }, + .zir_index = tracked_inst, + .captures = captures, }; const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { .existing => |ty| { @@ -7386,7 +7394,7 @@ fn analyzeCall( const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); generic_block.comptime_reason = .{ .reason = .{ - .r = .{ .simple = .function_parameters }, + .r = .{ .simple = .fn_param_types }, .src = param_src, } }; @@ -7470,7 +7478,7 @@ fn analyzeCall( sema.inst_map = generic_inst_map; generic_block.comptime_reason = .{ .reason = .{ - .r = .{ .simple = .function_ret_ty }, + .r = .{ .simple = .fn_ret_ty }, .src = func_ret_ty_src, } }; @@ -8927,7 +8935,7 @@ fn zirFunc( const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len); extra_index += ret_ty_body.len; - const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .function_ret_ty }); + const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .fn_ret_ty }); break :blk ret_ty_val.toType(); }, }; @@ -9171,6 +9179,181 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: } } +fn checkParamTypeCommon( + sema: *Sema, + block: *Block, + param_idx: u32, + param_ty: Type, + param_is_noalias: bool, + param_src: LazySrcLoc, + cc: std.builtin.CallingConvention, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const target = zcu.getTarget(); + + if (!param_ty.isValidParamType(zcu)) { + const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; + return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ + opaque_str, param_ty.fmt(pt), + }); + } + if (!param_ty.isGenericPoison() and + !target_util.fnCallConvAllowsZigTypes(cc) and + !try sema.validateExternType(param_ty, .param_ty)) + { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{ + param_ty.fmt(pt), @tagName(cc), + }); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); + + try sema.addDeclaredHereNote(msg, param_ty); + break :msg msg; + }); + } + switch (cc) { + .x86_64_interrupt, .x86_interrupt => { + const err_code_size = target.ptrBitWidth(); + switch (param_idx) { + 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}), + 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }), + else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), param_idx + 1 }), + } + }, + .arc_interrupt, + .arm_interrupt, + .microblaze_interrupt, + .mips64_interrupt, + .mips_interrupt, + .riscv64_interrupt, + .riscv32_interrupt, + .sh_interrupt, + .avr_interrupt, + .csky_interrupt, + .m68k_interrupt, + .msp430_interrupt, + .avr_signal, + => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}), + else => {}, + } + if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) { + return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); + } +} + +fn checkReturnTypeAndCallConvCommon( + sema: *Sema, + block: *Block, + bare_ret_ty: Type, + ret_ty_src: LazySrcLoc, + @"callconv": std.builtin.CallingConvention, + callconv_src: LazySrcLoc, + /// non-`null` only if the function is varargs. + opt_varargs_src: ?LazySrcLoc, + inferred_error_set: bool, + is_noinline: bool, +) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = zcu.gpa; + if (opt_varargs_src) |varargs_src| { + try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv"); + } + if (inferred_error_set and !bare_ret_ty.isGenericPoison()) { + try sema.validateErrorUnionPayloadType(block, bare_ret_ty, ret_ty_src); + } + const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else ""; + if (!bare_ret_ty.isValidReturnType(zcu)) { + const opaque_str = if (bare_ret_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; + return sema.fail(block, ret_ty_src, "{s}return type '{s}{f}' not allowed", .{ + opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt), + }); + } + if (!bare_ret_ty.isGenericPoison() and + !target_util.fnCallConvAllowsZigTypes(@"callconv") and + (inferred_error_set or !try sema.validateExternType(bare_ret_ty, .ret_ty))) + { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(ret_ty_src, "return type '{s}{f}' not allowed in function with calling convention '{s}'", .{ + ies_ret_ty_prefix, bare_ret_ty.fmt(pt), @tagName(@"callconv"), + }); + errdefer msg.destroy(gpa); + if (!inferred_error_set) { + try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, bare_ret_ty, .ret_ty); + try sema.addDeclaredHereNote(msg, bare_ret_ty); + } + break :msg msg; + }); + } + validate_incoming_stack_align: { + const a: u64 = switch (@"callconv") { + inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment")) + payload.incoming_stack_alignment orelse break :validate_incoming_stack_align + else + break :validate_incoming_stack_align, + }; + if (!std.math.isPowerOfTwo(a)) { + return sema.fail(block, callconv_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a}); + } + } + switch (@"callconv") { + .x86_64_interrupt, + .x86_interrupt, + .arm_interrupt, + .mips64_interrupt, + .mips_interrupt, + .riscv64_interrupt, + .riscv32_interrupt, + .sh_interrupt, + .arc_interrupt, + .avr_interrupt, + .csky_interrupt, + .m68k_interrupt, + .microblaze_interrupt, + .msp430_interrupt, + .avr_signal, + => { + const ret_ok = !inferred_error_set and switch (bare_ret_ty.toIntern()) { + .void_type, .noreturn_type => true, + else => false, + }; + if (!ret_ok) { + return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(@"callconv")}); + } + }, + .@"inline" => if (is_noinline) { + return sema.fail(block, callconv_src, "'noinline' function cannot have calling convention 'inline'", .{}); + }, + else => {}, + } + switch (zcu.callconvSupported(@"callconv")) { + .ok => {}, + .bad_arch => |allowed_archs| { + const ArchListFormatter = struct { + archs: []const std.Target.Cpu.Arch, + pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { + for (formatter.archs, 0..) |arch, i| { + if (i != 0) + try w.writeAll(", "); + try w.print("'{s}'", .{@tagName(arch)}); + } + } + }; + return sema.fail(block, callconv_src, "calling convention '{s}' only available on architectures {f}", .{ + @tagName(@"callconv"), + ArchListFormatter{ .archs = allowed_archs }, + }); + }, + .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{ + @tagName(@"callconv"), + @tagName(bad_backend), + }), + } +} + fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool { return switch (cc) { .naked, @@ -9259,7 +9442,6 @@ fn funcCommon( const pt = sema.pt; const zcu = pt.zcu; const gpa = sema.gpa; - const target = zcu.getTarget(); const ip = &zcu.intern_pool; const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); @@ -9293,26 +9475,14 @@ fn funcCommon( if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) { return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); } - if (!param_ty.isValidParamType(zcu)) { - const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; - return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ - opaque_str, param_ty.fmt(pt), - }); - } - if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) { - const msg = msg: { - const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{ - param_ty.fmt(pt), @tagName(cc), - }); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); - - try sema.addDeclaredHereNote(msg, param_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } + try sema.checkParamTypeCommon( + block, + @intCast(i), + param_ty, + is_noalias, + param_src, + cc, + ); if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) { const msg = msg: { const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{ @@ -9327,209 +9497,40 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (!param_ty_generic and is_noalias and - !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu))) - { - return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); - } - switch (cc) { - .x86_64_interrupt, .x86_interrupt => { - const err_code_size = target.ptrBitWidth(); - switch (i) { - 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}), - 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }), - else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), i + 1 }), - } - }, - .arc_interrupt, - .arm_interrupt, - .microblaze_interrupt, - .mips64_interrupt, - .mips_interrupt, - .riscv64_interrupt, - .riscv32_interrupt, - .sh_interrupt, - .avr_interrupt, - .csky_interrupt, - .m68k_interrupt, - .msp430_interrupt, - .avr_signal, - => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}), - else => {}, - } } - if (var_args) { - if (is_generic) { - return sema.fail(block, func_src, "generic function cannot be variadic", .{}); - } - const va_args_src = block.src(.{ - .fn_proto_param = .{ - .fn_proto_node_offset = src_node_offset, - .param_index = @intCast(block.params.len), // va_arg must be the last parameter - }, - }); - try sema.checkCallConvSupportsVarArgs(block, va_args_src, cc); + if (var_args and is_generic) { + return sema.fail(block, func_src, "generic function cannot be variadic", .{}); } - const ret_poison = bare_return_type.isGenericPoison(); - - const param_types = block.params.items(.ty); - - if (inferred_error_set) { - assert(has_body); - if (!ret_poison) - try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); - const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{ - .owner_nav = sema.owner.unwrap().nav_val, - - .param_types = param_types, - .noalias_bits = noalias_bits, - .comptime_bits = comptime_bits, - .bare_return_type = bare_return_type.toIntern(), - .cc = cc, - .is_var_args = var_args, - .is_generic = is_generic, - .is_noinline = is_noinline, - - .zir_body_inst = try block.trackZir(func_inst), - .lbrace_line = src_locs.lbrace_line, - .rbrace_line = src_locs.rbrace_line, - .lbrace_column = @as(u16, @truncate(src_locs.columns)), - .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), - }); - return finishFunc( - sema, - block, - func_index, - .none, - ret_poison, - bare_return_type, - ret_ty_src, - cc, - ret_ty_requires_comptime, - func_inst, - cc_src, - is_noinline, - ); - } - - const func_ty = try ip.getFuncType(gpa, pt.tid, .{ - .param_types = param_types, - .noalias_bits = noalias_bits, - .comptime_bits = comptime_bits, - .return_type = bare_return_type.toIntern(), - .cc = cc, - .is_var_args = var_args, - .is_generic = is_generic, - .is_noinline = is_noinline, - }); - - if (has_body) { - const func_index = try ip.getFuncDecl(gpa, pt.tid, .{ - .owner_nav = sema.owner.unwrap().nav_val, - .ty = func_ty, - .cc = cc, - .is_noinline = is_noinline, - .zir_body_inst = try block.trackZir(func_inst), - .lbrace_line = src_locs.lbrace_line, - .rbrace_line = src_locs.rbrace_line, - .lbrace_column = @as(u16, @truncate(src_locs.columns)), - .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), - }); - return finishFunc( - sema, - block, - func_index, - func_ty, - ret_poison, - bare_return_type, - ret_ty_src, - cc, - ret_ty_requires_comptime, - func_inst, - cc_src, - is_noinline, - ); - } - - return finishFunc( - sema, + try sema.checkReturnTypeAndCallConvCommon( block, - .none, - func_ty, - ret_poison, bare_return_type, ret_ty_src, cc, - ret_ty_requires_comptime, - func_inst, cc_src, + if (var_args) block.src(.{ .fn_proto_param = .{ + .fn_proto_node_offset = src_node_offset, + .param_index = @intCast(block.params.len), + } }) else null, + inferred_error_set, is_noinline, ); -} - -fn finishFunc( - sema: *Sema, - block: *Block, - opt_func_index: InternPool.Index, - func_ty: InternPool.Index, - ret_poison: bool, - bare_return_type: Type, - ret_ty_src: LazySrcLoc, - cc_resolved: std.builtin.CallingConvention, - ret_ty_requires_comptime: bool, - func_inst: Zir.Inst.Index, - cc_src: LazySrcLoc, - is_noinline: bool, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const gpa = sema.gpa; - - const return_type: Type = if (opt_func_index == .none or ret_poison) - bare_return_type - else - .fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index))); - - if (!return_type.isValidReturnType(zcu)) { - const opaque_str = if (return_type.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; - return sema.fail(block, ret_ty_src, "{s}return type '{f}' not allowed", .{ - opaque_str, return_type.fmt(pt), - }); - } - if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and - !try sema.validateExternType(return_type, .ret_ty)) - { - const msg = msg: { - const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{s}'", .{ - return_type.fmt(pt), @tagName(cc_resolved), - }); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, return_type, .ret_ty); - - try sema.addDeclaredHereNote(msg, return_type); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } // If the return type is comptime-only but not dependent on parameters then // all parameter types also need to be comptime. - if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { + if (has_body and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { for (block.params.items(.is_comptime)) |is_comptime| { if (!is_comptime) break; } else break :comptime_check; - + const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else ""; const msg = try sema.errMsg( ret_ty_src, - "function with comptime-only return type '{f}' requires all parameters to be comptime", - .{return_type.fmt(pt)}, + "function with comptime-only return type '{s}{f}' requires all parameters to be comptime", + .{ ies_ret_ty_prefix, bare_return_type.fmt(pt) }, ); errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsComptime(msg, ret_ty_src, return_type); + try sema.explainWhyTypeIsComptime(msg, ret_ty_src, bare_return_type); const tags = sema.code.instructions.items(.tag); const data = sema.code.instructions.items(.data); @@ -9556,68 +9557,56 @@ fn finishFunc( return sema.failWithOwnedErrorMsg(block, msg); } - validate_incoming_stack_align: { - const a: u64 = switch (cc_resolved) { - inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment")) - payload.incoming_stack_alignment orelse break :validate_incoming_stack_align - else - break :validate_incoming_stack_align, - }; - if (!std.math.isPowerOfTwo(a)) { - return sema.fail(block, cc_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a}); - } + const param_types = block.params.items(.ty); + + if (inferred_error_set) { + assert(has_body); + return .fromIntern(try ip.getFuncDeclIes(gpa, pt.tid, .{ + .owner_nav = sema.owner.unwrap().nav_val, + + .param_types = param_types, + .noalias_bits = noalias_bits, + .comptime_bits = comptime_bits, + .bare_return_type = bare_return_type.toIntern(), + .cc = cc, + .is_var_args = var_args, + .is_generic = is_generic, + .is_noinline = is_noinline, + + .zir_body_inst = try block.trackZir(func_inst), + .lbrace_line = src_locs.lbrace_line, + .rbrace_line = src_locs.rbrace_line, + .lbrace_column = @as(u16, @truncate(src_locs.columns)), + .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), + })); } - switch (cc_resolved) { - .x86_64_interrupt, - .x86_interrupt, - .arm_interrupt, - .mips64_interrupt, - .mips_interrupt, - .riscv64_interrupt, - .riscv32_interrupt, - .sh_interrupt, - .arc_interrupt, - .avr_interrupt, - .csky_interrupt, - .m68k_interrupt, - .microblaze_interrupt, - .msp430_interrupt, - .avr_signal, - => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) { - return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)}); - }, - .@"inline" => if (is_noinline) { - return sema.fail(block, cc_src, "'noinline' function cannot have calling convention 'inline'", .{}); - }, - else => {}, + const func_ty = try ip.getFuncType(gpa, pt.tid, .{ + .param_types = param_types, + .noalias_bits = noalias_bits, + .comptime_bits = comptime_bits, + .return_type = bare_return_type.toIntern(), + .cc = cc, + .is_var_args = var_args, + .is_generic = is_generic, + .is_noinline = is_noinline, + }); + + if (has_body) { + return .fromIntern(try ip.getFuncDecl(gpa, pt.tid, .{ + .owner_nav = sema.owner.unwrap().nav_val, + .ty = func_ty, + .cc = cc, + .is_noinline = is_noinline, + .zir_body_inst = try block.trackZir(func_inst), + .lbrace_line = src_locs.lbrace_line, + .rbrace_line = src_locs.rbrace_line, + .lbrace_column = @as(u16, @truncate(src_locs.columns)), + .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), + })); } - switch (zcu.callconvSupported(cc_resolved)) { - .ok => {}, - .bad_arch => |allowed_archs| { - const ArchListFormatter = struct { - archs: []const std.Target.Cpu.Arch, - pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { - for (formatter.archs, 0..) |arch, i| { - if (i != 0) - try w.writeAll(", "); - try w.print("'{s}'", .{@tagName(arch)}); - } - } - }; - return sema.fail(block, cc_src, "calling convention '{s}' only available on architectures {f}", .{ - @tagName(cc_resolved), - ArchListFormatter{ .archs = allowed_archs }, - }); - }, - .bad_backend => |bad_backend| return sema.fail(block, cc_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{ - @tagName(cc_resolved), - @tagName(bad_backend), - }), - } - - return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty); + return .fromIntern(func_ty); } fn zirParam( @@ -19395,7 +19384,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (union_ty.zigTypeTag(pt.zcu) != .@"union") { return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)}); } - const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_name }); + const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_names }); const init = try sema.resolveInst(extra.init); return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); } @@ -20553,7 +20542,338 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return block.addUnOp(.tag_name, casted_operand); } -fn zirReify( +fn zirReifyInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; + const signedness_src = block.builtinCallArgSrc(inst_data.src_node, 0); + const bits_src = block.builtinCallArgSrc(inst_data.src_node, 1); + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const signedness = try sema.resolveBuiltinEnum(block, signedness_src, extra.lhs, .Signedness, .{ .simple = .int_signedness }); + const bits: u16 = @intCast(try sema.resolveInt(block, bits_src, extra.rhs, .u16, .{ .simple = .int_bit_width })); + return .fromType(try sema.pt.intType(signedness, bits)); +} + +fn zirReifySliceArgTy( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const info: Zir.Inst.ReifySliceArgInfo = @enumFromInt(extended.small); + + const src = block.nodeOffset(extra.node); + + const comptime_reason: std.zig.SimpleComptimeReason, const in_scalar_ty: Type, const out_scalar_ty: Type = switch (info) { + // zig fmt: off + .type_to_fn_param_attrs => .{ .fn_param_attrs, .type, try sema.getBuiltinType(src, .@"Type.Fn.Param.Attributes") }, + .string_to_struct_field_type => .{ .struct_field_types, .slice_const_u8, .type }, + .string_to_union_field_type => .{ .union_field_types, .slice_const_u8, .type }, + .string_to_struct_field_attrs => .{ .struct_field_attrs, .slice_const_u8, try sema.getBuiltinType(src, .@"Type.StructField.Attributes") }, + .string_to_union_field_attrs => .{ .union_field_attrs, .slice_const_u8, try sema.getBuiltinType(src, .@"Type.UnionField.Attributes") }, + // zig fmt: on + }; + + const operand_ty = try pt.ptrTypeSema(.{ + .child = in_scalar_ty.toIntern(), + .flags = .{ .size = .slice, .is_const = true }, + }); + + const operand_uncoerced = try sema.resolveInst(extra.operand); + const operand_coerced = try sema.coerce(block, operand_ty, operand_uncoerced, src); + const operand_val = try sema.resolveConstDefinedValue(block, src, operand_coerced, .{ .simple = comptime_reason }); + const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); + if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); + const len = try len_val.toUnsignedIntSema(pt); + + return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ + .len = len, + .child = out_scalar_ty.toIntern(), + }))); +} + +fn zirReifyEnumValueSliceTy( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + + const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; + + const int_tag_ty_src = block.builtinCallArgSrc(extra.node, 0); + const field_names_src = block.builtinCallArgSrc(extra.node, 2); + + const int_tag_ty = try sema.resolveType(block, int_tag_ty_src, extra.lhs); + + const operand_uncoerced = try sema.resolveInst(extra.rhs); + const operand_coerced = try sema.coerce(block, .slice_const_slice_const_u8, operand_uncoerced, field_names_src); + const operand_val = try sema.resolveConstDefinedValue(block, field_names_src, operand_coerced, .{ .simple = .enum_field_names }); + const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); + if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, field_names_src, null); + const len = try len_val.toUnsignedIntSema(pt); + + return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ + .len = len, + .child = int_tag_ty.toIntern(), + }))); +} + +fn zirReifyPointerSentinelTy( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const src = block.nodeOffset(extra.node); + const elem_ty = try sema.resolveType(block, src, extra.operand); + return .fromType(switch (elem_ty.zigTypeTag(zcu)) { + else => try pt.optionalType(elem_ty.toIntern()), + // These types cannot be the child of an optional. To allow reifying pointers to them still, + // we treat the "sentinel" argument to `@Pointer` as `?noreturn` instead of `?T`. + .@"opaque", .null => .optional_noreturn, + }); +} + +fn zirReifyTuple( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const operand_src = block.builtinCallArgSrc(extra.node, 0); + + const types_uncoerced = try sema.resolveInst(extra.operand); + const types_coerced = try sema.coerce(block, .slice_const_type, types_uncoerced, operand_src); + const types_slice_val = try sema.resolveConstDefinedValue(block, operand_src, types_coerced, .{ .simple = .tuple_field_types }); + const types_array_val = try sema.derefSliceAsArray(block, operand_src, types_slice_val, .{ .simple = .tuple_field_types }); + const fields_len: u32 = @intCast(types_array_val.typeOf(zcu).arrayLen(zcu)); + + const field_types = try sema.arena.alloc(InternPool.Index, fields_len); + for (field_types, 0..) |*field_ty, field_idx| { + const field_ty_val = try types_array_val.elemValue(pt, field_idx); + if (field_ty_val.isUndef(zcu)) { + return sema.failWithUseOfUndef(block, operand_src, null); + } + field_ty.* = field_ty_val.toIntern(); + } + + const field_values = try sema.arena.alloc(InternPool.Index, fields_len); + @memset(field_values, .none); + + return .fromIntern(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ + .types = field_types, + .values = field_values, + })); +} + +fn zirReifyPointer( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + + const extra = sema.code.extraData(Zir.Inst.ReifyPointer, extended.operand).data; + const src = block.nodeOffset(extra.node); + const size_src = block.builtinCallArgSrc(extra.node, 0); + const attrs_src = block.builtinCallArgSrc(extra.node, 1); + const elem_ty_src = block.builtinCallArgSrc(extra.node, 2); + const sentinel_src = block.builtinCallArgSrc(extra.node, 3); + + const size_ty = try sema.getBuiltinType(size_src, .@"Type.Pointer.Size"); + const attrs_ty = try sema.getBuiltinType(attrs_src, .@"Type.Pointer.Attributes"); + + const size_uncoerced = try sema.resolveInst(extra.size); + const size_coerced = try sema.coerce(block, size_ty, size_uncoerced, size_src); + const size_val = try sema.resolveConstDefinedValue(block, size_src, size_coerced, .{ .simple = .pointer_size }); + const size = try sema.interpretBuiltinType(block, size_src, size_val, std.builtin.Type.Pointer.Size); + + const attrs_uncoerced = try sema.resolveInst(extra.attrs); + const attrs_coerced = try sema.coerce(block, attrs_ty, attrs_uncoerced, attrs_src); + const attrs_val = try sema.resolveConstDefinedValue(block, attrs_src, attrs_coerced, .{ .simple = .pointer_attrs }); + const attrs = try sema.interpretBuiltinType(block, attrs_src, attrs_val, std.builtin.Type.Pointer.Attributes); + + const @"align": Alignment = if (attrs.@"align") |bytes| a: { + break :a try sema.validateAlign(block, attrs_src, bytes); + } else .none; + + const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_ty); + + switch (elem_ty.zigTypeTag(zcu)) { + .noreturn => return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}), + // This needs to be disallowed, because the sentinel parameter would otherwise have type + // `?@TypeOf(null)`, which is not a valid type because you cannot differentiate between + // constructing the "inner" null value and the "outer" null value. + .null => return sema.fail(block, elem_ty_src, "cannot reify pointer to '@TypeOf(null)'", .{}), + .@"fn" => switch (size) { + .one => {}, + .many, .c, .slice => return sema.fail(block, src, "function pointers must be single pointers", .{}), + }, + .@"opaque" => switch (size) { + .one => {}, + .many, .c, .slice => return sema.fail(block, src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}), + }, + else => {}, + } + + if (size == .c and !try sema.validateExternType(elem_ty, .other)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other); + try sema.addDeclaredHereNote(msg, elem_ty); + break :msg msg; + }); + } + + const sentinel_ty = try pt.optionalType(elem_ty.toIntern()); + const sentinel_uncoerced = try sema.resolveInst(extra.sentinel); + const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src); + const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel_coerced, .{ .simple = .pointer_sentinel }); + const opt_sentinel = sentinel_val.optionalValue(zcu); + if (opt_sentinel) |sentinel| { + switch (size) { + .many, .slice => {}, + .one, .c => return sema.fail(block, sentinel_src, "sentinels are only allowed on slices and unknown-length pointers", .{}), + } + try checkSentinelType(sema, block, sentinel_src, elem_ty); + if (sentinel.canMutateComptimeVarState(zcu)) { + const sentinel_name = try ip.getOrPutString(gpa, pt.tid, "sentinel", .no_embedded_nulls); + return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel); + } + } + + return .fromType(try pt.ptrTypeSema(.{ + .child = elem_ty.toIntern(), + .sentinel = if (opt_sentinel) |s| s.toIntern() else .none, + .flags = .{ + .size = size, + .is_const = attrs.@"const", + .is_volatile = attrs.@"volatile", + .is_allowzero = attrs.@"allowzero", + .address_space = attrs.@"addrspace" orelse as: { + if (elem_ty.zigTypeTag(zcu) == .@"fn" and zcu.getTarget().cpu.arch == .avr) break :as .flash; + break :as .generic; + }, + .alignment = @"align", + }, + })); +} + +fn zirReifyFn( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + + const extra = sema.code.extraData(Zir.Inst.ReifyFn, extended.operand).data; + const param_types_src = block.builtinCallArgSrc(extra.node, 0); + const param_attrs_src = block.builtinCallArgSrc(extra.node, 1); + const ret_ty_src = block.builtinCallArgSrc(extra.node, 2); + const fn_attrs_src = block.builtinCallArgSrc(extra.node, 3); + + const single_param_attrs_ty = try sema.getBuiltinType(param_attrs_src, .@"Type.Fn.Param.Attributes"); + const fn_attrs_ty = try sema.getBuiltinType(fn_attrs_src, .@"Type.Fn.Attributes"); + + const param_types_uncoerced = try sema.resolveInst(extra.param_types); + const param_types_coerced = try sema.coerce(block, .slice_const_type, param_types_uncoerced, param_types_src); + const param_types_slice = try sema.resolveConstDefinedValue(block, param_types_src, param_types_coerced, .{ .simple = .fn_param_types }); + const param_types_arr = try sema.derefSliceAsArray(block, param_types_src, param_types_slice, .{ .simple = .fn_param_types }); + + const params_len = param_types_arr.typeOf(zcu).arrayLen(zcu); + + const param_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = params_len, + .child = single_param_attrs_ty.toIntern(), + })); + const param_attrs_uncoerced = try sema.resolveInst(extra.param_attrs); + const param_attrs_coerced = try sema.coerce(block, param_attrs_ty, param_attrs_uncoerced, param_attrs_src); + const param_attrs_slice = try sema.resolveConstDefinedValue(block, param_attrs_src, param_attrs_coerced, .{ .simple = .fn_param_attrs }); + const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs }); + + const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty); + + const fn_attrs_uncoerced = try sema.resolveInst(extra.fn_attrs); + const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src); + const fn_attrs_val = try sema.resolveConstDefinedValue(block, fn_attrs_src, fn_attrs_coerced, .{ .simple = .fn_attrs }); + const fn_attrs = try sema.interpretBuiltinType(block, fn_attrs_src, fn_attrs_val, std.builtin.Type.Fn.Attributes); + + var noalias_bits: u32 = 0; + const param_types_ip = try sema.arena.alloc(InternPool.Index, @intCast(params_len)); + for (param_types_ip, 0..@intCast(params_len)) |*param_ty_ip, param_idx| { + const param_ty: Type = (try param_types_arr.elemValue(pt, param_idx)).toType(); + const param_attrs = try sema.interpretBuiltinType( + block, + param_attrs_src, + try param_attrs_arr.elemValue(pt, param_idx), + std.builtin.Type.Fn.Param.Attributes, + ); + try sema.checkParamTypeCommon( + block, + @intCast(param_idx), + param_ty, + param_attrs.@"noalias", + param_types_src, + fn_attrs.@"callconv", + ); + if (try param_ty.comptimeOnlySema(pt)) { + return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only parameter type '{f}'", .{param_ty.fmt(pt)}); + } + if (param_attrs.@"noalias") { + if (param_idx > 31) { + return sema.fail(block, param_attrs_src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}); + } + noalias_bits |= @as(u32, 1) << @intCast(param_idx); + } + param_ty_ip.* = param_ty.toIntern(); + } + + if (fn_attrs.varargs) { + try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv"); + } + + try sema.checkReturnTypeAndCallConvCommon( + block, + ret_ty, + ret_ty_src, + fn_attrs.@"callconv", + fn_attrs_src, + if (fn_attrs.varargs) fn_attrs_src else null, + false, + false, + ); + if (try ret_ty.comptimeOnlySema(pt)) { + return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only return type '{f}'", .{ret_ty.fmt(pt)}); + } + + return .fromIntern(try ip.getFuncType(gpa, pt.tid, .{ + .param_types = param_types_ip, + .noalias_bits = noalias_bits, + .comptime_bits = 0, + .return_type = ret_ty.toIntern(), + .cc = fn_attrs.@"callconv", + .is_var_args = fn_attrs.varargs, + .is_generic = false, + .is_noinline = false, + })); +} + +fn zirReifyStruct( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, @@ -20563,579 +20883,188 @@ fn zirReify( const zcu = pt.zcu; const gpa = sema.gpa; const ip = &zcu.intern_pool; + const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); - const extra = sema.code.extraData(Zir.Inst.Reify, extended.operand).data; + const extra = sema.code.extraData(Zir.Inst.ReifyStruct, extended.operand).data; const tracked_inst = try block.trackZir(inst); const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, - .offset = LazySrcLoc.Offset.nodeOffset(.zero), + .offset = .nodeOffset(.zero), }; - const operand_src: LazySrcLoc = .{ + + const layout_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, - .offset = .{ - .node_offset_builtin_call_arg = .{ - .builtin_call_node = .zero, // `tracked_inst` is precisely the `reify` instruction, so offset is 0 - .arg_index = 0, - }, - }, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 0, + } }, }; - const type_info_ty = try sema.getBuiltinType(src, .Type); - const uncasted_operand = try sema.resolveInst(extra.operand); - const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); - const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type }); - const union_val = ip.indexToKey(val.toIntern()).un; - if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) { - return sema.failWithUseOfUndef(block, operand_src, null); + const backing_ty_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 1, + } }, + }; + const field_names_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 2, + } }, + }; + const field_types_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 3, + } }, + }; + const field_attrs_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 4, + } }, + }; + + const container_layout_ty = try sema.getBuiltinType(layout_src, .@"Type.ContainerLayout"); + const single_field_attrs_ty = try sema.getBuiltinType(field_attrs_src, .@"Type.StructField.Attributes"); + + const layout_uncoerced = try sema.resolveInst(extra.layout); + const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); + const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .struct_layout }); + const layout = try sema.interpretBuiltinType(block, layout_src, layout_val, std.builtin.Type.ContainerLayout); + + const backing_int_ty_uncoerced = try sema.resolveInst(extra.backing_ty); + const backing_int_ty_coerced = try sema.coerce(block, .optional_type, backing_int_ty_uncoerced, backing_ty_src); + const backing_int_ty_val = try sema.resolveConstDefinedValue(block, backing_ty_src, backing_int_ty_coerced, .{ .simple = .type }); + + const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); + const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .struct_field_names }); + const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .struct_field_names }); + + const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); + + const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = fields_len, + .child = .type_type, + })); + const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = fields_len, + .child = single_field_attrs_ty.toIntern(), + })); + + const field_types_uncoerced = try sema.resolveInst(extra.field_types); + const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); + const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .struct_field_types }); + const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .struct_field_types }); + + const field_attrs_uncoerced = try sema.resolveInst(extra.field_attrs); + const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); + const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .struct_field_attrs }); + const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .struct_field_attrs }); + + // Before we begin, check for undefs... + if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) { + return sema.failWithUseOfUndef(block, field_attrs_src, null); } - const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), zcu).?; - switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { - .type => return .type_type, - .void => return .void_type, - .bool => return .bool_type, - .noreturn => return .noreturn_type, - .comptime_float => return .comptime_float_type, - .comptime_int => return .comptime_int_type, - .undefined => return .undefined_type, - .null => return .null_type, - .@"anyframe" => return sema.failWithUseOfAsync(block, src), - .enum_literal => return .enum_literal_type, - .int => { - const int = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Int); - const ty = try pt.intType(int.signedness, int.bits); - return Air.internedToRef(ty.toIntern()); - }, - .vector => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), - ).?); - const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), - ).?); - - const len: u32 = @intCast(try len_val.toUnsignedIntSema(pt)); - const child_ty = child_val.toType(); - - try sema.checkVectorElemType(block, src, child_ty); - - const ty = try pt.vectorType(.{ - .len = len, - .child = child_ty.toIntern(), - }); - return Air.internedToRef(ty.toIntern()); - }, - .float => { - const float = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Float); - - const ty: Type = switch (float.bits) { - 16 => .f16, - 32 => .f32, - 64 => .f64, - 80 => .f80, - 128 => .f128, - else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}), - }; - return Air.internedToRef(ty.toIntern()); - }, - .pointer => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const size_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "size", .no_embedded_nulls), - ).?); - const is_const_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_const", .no_embedded_nulls), - ).?); - const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_volatile", .no_embedded_nulls), - ).?); - const alignment_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "alignment", .no_embedded_nulls), - ).?); - const address_space_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "address_space", .no_embedded_nulls), - ).?); - const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), - ).?); - const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_allowzero", .no_embedded_nulls), - ).?); - const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), - ).?); - - if (!try sema.intFitsInType(alignment_val, align_ty, null)) { - return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); - } - const alignment_val_int = try alignment_val.toUnsignedIntSema(pt); - const abi_align = try sema.validateAlign(block, src, alignment_val_int); - - const elem_ty = child_val.toType(); - if (abi_align != .none) { - try elem_ty.resolveLayout(pt); - } - - const ptr_size = try sema.interpretBuiltinType(block, operand_src, size_val, std.builtin.Type.Pointer.Size); - - const actual_sentinel: InternPool.Index = s: { - if (!sentinel_val.isNull(zcu)) { - if (ptr_size == .one or ptr_size == .c) { - return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); - } - const sentinel_ptr_val = sentinel_val.optionalValue(zcu).?; - const ptr_ty = try pt.singleMutPtrType(elem_ty); - const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; - try sema.checkSentinelType(block, src, elem_ty); - if (sent_val.canMutateComptimeVarState(zcu)) { - const sentinel_name = try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls); - return sema.failWithContainsReferenceToComptimeVar(block, src, sentinel_name, "sentinel", sent_val); - } - break :s sent_val.toIntern(); - } - break :s .none; - }; - - if (elem_ty.zigTypeTag(zcu) == .noreturn) { - return sema.fail(block, src, "pointer to noreturn not allowed", .{}); - } else if (elem_ty.zigTypeTag(zcu) == .@"fn") { - if (ptr_size != .one) { - return sema.fail(block, src, "function pointers must be single pointers", .{}); - } - } else if (ptr_size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") { - return sema.fail(block, src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}); - } else if (ptr_size == .c) { - if (!try sema.validateExternType(elem_ty, .other)) { - const msg = msg: { - const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotExtern(msg, src, elem_ty, .other); - - try sema.addDeclaredHereNote(msg, elem_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - const ty = try pt.ptrTypeSema(.{ - .child = elem_ty.toIntern(), - .sentinel = actual_sentinel, - .flags = .{ - .size = ptr_size, - .is_const = is_const_val.toBool(), - .is_volatile = is_volatile_val.toBool(), - .alignment = abi_align, - .address_space = try sema.interpretBuiltinType(block, operand_src, address_space_val, std.builtin.AddressSpace), - .is_allowzero = is_allowzero_val.toBool(), - }, - }); - return Air.internedToRef(ty.toIntern()); - }, - .array => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), - ).?); - const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), - ).?); - const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), - ).?); - - const len = try len_val.toUnsignedIntSema(pt); - const child_ty = child_val.toType(); - const sentinel = if (sentinel_val.optionalValue(zcu)) |p| blk: { - const ptr_ty = try pt.singleMutPtrType(child_ty); - try sema.checkSentinelType(block, src, child_ty); - const sentinel = (try sema.pointerDeref(block, src, p, ptr_ty)).?; - if (sentinel.canMutateComptimeVarState(zcu)) { - const sentinel_name = try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls); - return sema.failWithContainsReferenceToComptimeVar(block, src, sentinel_name, "sentinel", sentinel); - } - break :blk sentinel; - } else null; - - const ty = try pt.arrayType(.{ - .len = len, - .sentinel = if (sentinel) |s| s.toIntern() else .none, - .child = child_ty.toIntern(), - }); - return Air.internedToRef(ty.toIntern()); - }, - .optional => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls), - ).?); - - const child_ty = child_val.toType(); - - const ty = try pt.optionalType(child_ty.toIntern()); - return Air.internedToRef(ty.toIntern()); - }, - .error_union => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const error_set_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "error_set", .no_embedded_nulls), - ).?); - const payload_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "payload", .no_embedded_nulls), - ).?); - - const error_set_ty = error_set_val.toType(); - const payload_ty = payload_val.toType(); - - if (error_set_ty.zigTypeTag(zcu) != .error_set) { - return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{}); - } - - const ty = try pt.errorUnionType(error_set_ty, payload_ty); - return Air.internedToRef(ty.toIntern()); - }, - .error_set => { - const payload_val = Value.fromInterned(union_val.val).optionalValue(zcu) orelse - return .anyerror_type; - - const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ .simple = .error_set_contents }); - - const len = try sema.usizeCast(block, src, names_val.typeOf(zcu).arrayLen(zcu)); - var names: InferredErrorSet.NameMap = .{}; - try names.ensureUnusedCapacity(sema.arena, len); - for (0..len) |i| { - const elem_val = try names_val.elemValue(pt, i); - const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); - const name_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), - ).?); - - const name = try sema.sliceToIpString(block, src, name_val, .{ .simple = .error_set_contents }); - _ = try pt.getErrorValue(name); - const gop = names.getOrPutAssumeCapacity(name); - if (gop.found_existing) { - return sema.fail(block, src, "duplicate error '{f}'", .{ - name.fmt(ip), - }); - } - } - - const ty = try pt.errorSetFromUnsortedNames(names.keys()); - return Air.internedToRef(ty.toIntern()); - }, - .@"struct" => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), - ).?); - const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "backing_integer", .no_embedded_nulls), - ).?); - const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), - ).?); - const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), - ).?); - const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_tuple", .no_embedded_nulls), - ).?); - - const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); - - // Decls - if (try decls_val.sliceLen(pt) > 0) { - return sema.fail(block, src, "reified structs must have no decls", .{}); - } - - if (layout != .@"packed" and !backing_integer_val.isNull(zcu)) { - return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); - } - - const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .struct_fields }); - - if (is_tuple_val.toBool()) { - switch (layout) { - .@"extern" => return sema.fail(block, src, "extern tuples are not supported", .{}), - .@"packed" => return sema.fail(block, src, "packed tuples are not supported", .{}), - .auto => {}, - } - return sema.reifyTuple(block, src, fields_arr); - } else { - return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy); - } - }, - .@"enum" => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), - ).?); - const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), - ).?); - const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), - ).?); - const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_exhaustive", .no_embedded_nulls), - ).?); - - if (try decls_val.sliceLen(pt) > 0) { - return sema.fail(block, src, "reified enums must have no decls", .{}); - } - - const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .enum_fields }); - - return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy); - }, - .@"opaque" => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), - ).?); - - // Decls - if (try decls_val.sliceLen(pt) > 0) { - return sema.fail(block, src, "reified opaque must have no decls", .{}); - } - - const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, .{ - .key = .{ .reified = .{ - .zir_index = try block.trackZir(inst), - } }, - })) { - .existing => |ty| { - try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); - }, - .wip => |wip| wip, - }; - errdefer wip_ty.cancel(ip, pt.tid); - - const type_name = try sema.createTypeName( - block, - name_strategy, - "opaque", - inst, - wip_ty.index, - ); - wip_ty.setName(ip, type_name.name, type_name.nav); - - const new_namespace_index = try pt.createNamespace(.{ - .parent = block.namespace.toOptional(), - .owner_type = wip_ty.index, - .file_scope = block.getFileScopeIndex(zcu), - .generation = zcu.generation, - }); - - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); - }, - .@"union" => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls), - ).?); - const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls), - ).?); - const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls), - ).?); - const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls), - ).?); - - if (try decls_val.sliceLen(pt) > 0) { - return sema.fail(block, src, "reified unions must have no decls", .{}); - } - const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout); - - const has_tag = tag_type_val.optionalValue(zcu) != null; - - if (has_tag) { - switch (layout) { - .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}), - .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}), - .auto => {}, - } - } - - const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields }); - - return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy); - }, - .@"fn" => { - const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); - const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "calling_convention", .no_embedded_nulls), - ).?); - const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), - ).?); - const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_var_args", .no_embedded_nulls), - ).?); - const return_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "return_type", .no_embedded_nulls), - ).?); - const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "params", .no_embedded_nulls), - ).?); - - const is_generic = is_generic_val.toBool(); - if (is_generic) { - return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{}); - } - - const is_var_args = is_var_args_val.toBool(); - const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val); - if (is_var_args) { - try sema.checkCallConvSupportsVarArgs(block, src, cc); - } - - const return_type = return_type_val.optionalValue(zcu) orelse - return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); - - const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ .simple = .function_parameters }); - - const args_len = try sema.usizeCast(block, src, params_val.typeOf(zcu).arrayLen(zcu)); - const param_types = try sema.arena.alloc(InternPool.Index, args_len); - - var noalias_bits: u32 = 0; - for (param_types, 0..) |*param_type, i| { - const elem_val = try params_val.elemValue(pt, i); - const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); - const param_is_generic_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls), - ).?); - const param_is_noalias_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "is_noalias", .no_embedded_nulls), - ).?); - const opt_param_type_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, pt.tid, "type", .no_embedded_nulls), - ).?); - - if (param_is_generic_val.toBool()) { - return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{}); - } - - const param_type_val = opt_param_type_val.optionalValue(zcu) orelse - return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{}); - param_type.* = param_type_val.toIntern(); - - if (param_is_noalias_val.toBool()) { - if (!Type.fromInterned(param_type.*).isPtrAtRuntime(zcu)) { - return sema.fail(block, src, "non-pointer parameter declared 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 ty = try pt.funcType(.{ - .param_types = param_types, - .noalias_bits = noalias_bits, - .return_type = return_type.toIntern(), - .cc = cc, - .is_var_args = is_var_args, - }); - return Air.internedToRef(ty.toIntern()); - }, - .frame => return sema.failWithUseOfAsync(block, src), + if (try sema.anyUndef(block, field_types_src, field_types_arr)) { + return sema.failWithUseOfUndef(block, field_types_src, null); + } + // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. + if (try sema.anyUndef(block, backing_ty_src, backing_int_ty_val)) { + return sema.failWithUseOfUndef(block, backing_ty_src, null); } -} - -fn reifyEnum( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, - src: LazySrcLoc, - tag_ty: Type, - is_exhaustive: bool, - fields_val: Value, - name_strategy: Zir.Inst.NameStrategy, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const gpa = sema.gpa; - const ip = &zcu.intern_pool; - - // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. - - const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the // inputs turn out to be invalid, we can cancel the WIP type later. + var any_comptime_fields = false; + var any_default_inits = false; + var any_aligned_fields = false; + // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, tag_ty.toIntern()); - std.hash.autoHash(&hasher, is_exhaustive); - std.hash.autoHash(&hasher, fields_len); - + std.hash.autoHash(&hasher, layout); + std.hash.autoHash(&hasher, backing_int_ty_val); + // The field *type* array has already been deduplicated for us thanks to the InternPool! + std.hash.autoHash(&hasher, field_types_arr); + // However, for field names and attributes, we need to actually iterate the individual fields, + // because the presence of pointers (the `[]const u8` for the name and the `*const anyopaque` + // for the default value) means that distinct interned values could ultimately result in the + // same struct type. for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); - const field_name_val = try field_info.fieldValue(pt, 0); - const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .struct_field_names }); - const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .enum_field_name }); + const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "comptime", + ).?); + const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "align", + ).?); + const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "default_value_ptr", + ).?); + + const field_default: InternPool.Index = d: { + const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none; + const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); + const ptr_ty = try pt.singleConstPtrType(field_ty); + const deref_val = try sema.pointerDeref(block, field_attrs_src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( + block, + field_attrs_src, + .{ .simple = .struct_field_default_value }, + ); + // Resolve the value so that lazy values do not create distinct types. + break :d (try sema.resolveLazyValue(deref_val)).toIntern(); + }; std.hash.autoHash(&hasher, .{ field_name, - field_value_val.toIntern(), + field_attr_comptime, + field_attr_align, + field_default, }); + + if (field_attr_comptime.toBool()) any_comptime_fields = true; + if (field_attr_align.optionalValue(zcu)) |_| any_aligned_fields = true; + if (field_default != .none) any_default_inits = true; } - const tracked_inst = try block.trackZir(inst); + // Some basic validation to avoid a bogus `getStructType` call... + const backing_int_ty: ?Type = if (backing_int_ty_val.optionalValue(zcu)) |backing| ty: { + switch (layout) { + .auto, .@"extern" => return sema.fail(block, backing_ty_src, "non-packed struct does not support backing integer type", .{}), + .@"packed" => {}, + } + break :ty backing.toType(); + } else null; + if (any_aligned_fields and layout == .@"packed") { + return sema.fail(block, field_attrs_src, "packed struct fields cannot be aligned", .{}); + } + if (any_comptime_fields and layout != .auto) { + return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout}); + } - const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{ - .has_values = true, - .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive, - .fields_len = fields_len, + const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{ + .layout = layout, + .fields_len = @intCast(fields_len), + .known_non_opv = false, + .requires_comptime = .unknown, + .any_comptime_fields = any_comptime_fields, + .any_default_inits = any_default_inits, + .any_aligned_fields = any_aligned_fields, + .inits_resolved = true, .key = .{ .reified = .{ .zir_index = tracked_inst, .type_hash = hasher.final(), @@ -21148,22 +21077,136 @@ fn reifyEnum( return Air.internedToRef(ty); }, }; - var done = false; - errdefer if (!done) wip_ty.cancel(ip, pt.tid); - - if (tag_ty.zigTypeTag(zcu) != .int) { - return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); - } + errdefer wip_ty.cancel(ip, pt.tid); const type_name = try sema.createTypeName( block, name_strategy, - "enum", + "struct", inst, wip_ty.index, ); wip_ty.setName(ip, type_name.name, type_name.nav); + const wip_struct_type = ip.loadStructType(wip_ty.index); + + for (0..fields_len) |field_idx| { + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); + + const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); + + // Don't pass a reason; first loop acts as a check that this is valid. + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); + if (wip_struct_type.addFieldName(ip, field_name)) |prev_index| { + _ = prev_index; // TODO: better source location + return sema.fail(block, field_names_src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); + } + + const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "comptime", + ).?); + const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "align", + ).?); + const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( + std.builtin.Type.StructField.Attributes, + "default_value_ptr", + ).?); + + if (field_attr_align.optionalValue(zcu)) |field_align_val| { + assert(layout != .@"packed"); + const bytes = try field_align_val.toUnsignedIntSema(pt); + const a = try sema.validateAlign(block, field_attrs_src, bytes); + wip_struct_type.field_aligns.get(ip)[field_idx] = a; + } else if (any_aligned_fields) { + assert(layout != .@"packed"); + wip_struct_type.field_aligns.get(ip)[field_idx] = .none; + } + + const field_default: InternPool.Index = d: { + const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none; + assert(any_default_inits); + const ptr_ty = try pt.singleConstPtrType(field_ty); + // The first loop checked that this is comptime-dereferencable. + const deref_val = (try sema.pointerDeref(block, field_attrs_src, ptr_val, ptr_ty)).?; + // ...but we've not checked this yet! + if (deref_val.canMutateComptimeVarState(zcu)) { + return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val); + } + break :d (try sema.resolveLazyValue(deref_val)).toIntern(); + }; + + if (field_attr_comptime.toBool()) { + assert(layout == .auto); + if (field_default == .none) { + return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{}); + } + wip_struct_type.setFieldComptime(ip, field_idx); + } + + wip_struct_type.field_types.get(ip)[field_idx] = field_ty.toIntern(); + if (field_default != .none) { + wip_struct_type.field_inits.get(ip)[field_idx] = field_default; + } + + switch (field_ty.zigTypeTag(zcu)) { + .@"opaque" => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(gpa); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }), + .noreturn => return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "struct fields cannot be 'noreturn'", .{}); + errdefer msg.destroy(gpa); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }), + else => {}, + } + + switch (layout) { + .auto => {}, + .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, field_types_src, field_ty, .struct_field); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + }, + .@"packed" => if (!try sema.validatePackedType(field_ty)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + }, + } + } + + if (layout == .@"packed") { + var fields_bit_sum: u64 = 0; + for (0..wip_struct_type.field_types.len) |field_idx| { + const field_ty: Type = .fromInterned(wip_struct_type.field_types.get(ip)[field_idx]); + try field_ty.resolveLayout(pt); + fields_bit_sum += field_ty.bitSize(zcu); + } + if (backing_int_ty) |ty| { + try sema.checkBackingIntType(block, src, ty, fields_bit_sum); + wip_struct_type.setBackingIntType(ip, ty.toIntern()); + } else { + const ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); + wip_struct_type.setBackingIntType(ip, ty.toIntern()); + } + } + const new_namespace_index = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), .owner_type = wip_ty.index, @@ -21171,56 +21214,7 @@ fn reifyEnum( .generation = zcu.generation, }); - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - wip_ty.prepare(ip, new_namespace_index); - wip_ty.setTagTy(ip, tag_ty.toIntern()); - done = true; - - for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); - - const field_name_val = try field_info.fieldValue(pt, 0); - const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1)); - - // Don't pass a reason; first loop acts as an assertion that this is valid. - const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); - - if (!try sema.intFitsInType(field_value_val, tag_ty, null)) { - // TODO: better source location - return sema.fail(block, src, "field '{f}' with enumeration value '{f}' is too large for backing int type '{f}'", .{ - field_name.fmt(ip), - field_value_val.fmtValueSema(pt, sema), - tag_ty.fmt(pt), - }); - } - - const coerced_field_val = try pt.getCoerced(field_value_val, tag_ty); - if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| { - return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { - .name => msg: { - const msg = try sema.errMsg(src, "duplicate enum field '{f}'", .{field_name.fmt(ip)}); - errdefer msg.destroy(gpa); - _ = conflict.prev_field_idx; // TODO: this note is incorrect - try sema.errNote(src, msg, "other field here", .{}); - break :msg msg; - }, - .value => msg: { - const msg = try sema.errMsg(src, "enum tag value {f} already taken", .{field_value_val.fmtValueSema(pt, sema)}); - errdefer msg.destroy(gpa); - _ = conflict.prev_field_idx; // TODO: this note is incorrect - try sema.errNote(src, msg, "other enum tag value here", .{}); - break :msg msg; - }, - }); - } - } - - if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) { - return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); - } - + try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); codegen_type: { if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; @@ -21228,75 +21222,177 @@ fn reifyEnum( zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } - return Air.internedToRef(wip_ty.index); + try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); + return .fromIntern(wip_ty.finish(ip, new_namespace_index)); } -fn reifyUnion( +fn zirReifyUnion( sema: *Sema, block: *Block, + extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, - src: LazySrcLoc, - layout: std.builtin.Type.ContainerLayout, - opt_tag_type_val: Value, - fields_val: Value, - name_strategy: Zir.Inst.NameStrategy, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const gpa = sema.gpa; const ip = &zcu.intern_pool; - // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. + const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); + const extra = sema.code.extraData(Zir.Inst.ReifyUnion, extended.operand).data; + const tracked_inst = try block.trackZir(inst); + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; - const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); + const layout_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 0, + } }, + }; + const arg_ty_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 1, + } }, + }; + const field_names_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 2, + } }, + }; + const field_types_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 3, + } }, + }; + const field_attrs_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 4, + } }, + }; + + const container_layout_ty = try sema.getBuiltinType(layout_src, .@"Type.ContainerLayout"); + const single_field_attrs_ty = try sema.getBuiltinType(field_attrs_src, .@"Type.UnionField.Attributes"); + + const layout_uncoerced = try sema.resolveInst(extra.layout); + const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); + const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .union_layout }); + const layout = try sema.interpretBuiltinType(block, layout_src, layout_val, std.builtin.Type.ContainerLayout); + + const arg_ty_uncoerced = try sema.resolveInst(extra.arg_ty); + const arg_ty_coerced = try sema.coerce(block, .optional_type, arg_ty_uncoerced, arg_ty_src); + const arg_ty_val = try sema.resolveConstDefinedValue(block, arg_ty_src, arg_ty_coerced, .{ .simple = .type }); + + const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); + const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .union_field_names }); + const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .union_field_names }); + + const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); + + const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = fields_len, + .child = .type_type, + })); + const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = fields_len, + .child = single_field_attrs_ty.toIntern(), + })); + + const field_types_uncoerced = try sema.resolveInst(extra.field_types); + const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); + const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .union_field_types }); + const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .union_field_types }); + + const field_attrs_uncoerced = try sema.resolveInst(extra.field_attrs); + const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); + const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .union_field_attrs }); + const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .union_field_attrs }); + + // Before we begin, check for undefs... + if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) { + return sema.failWithUseOfUndef(block, field_attrs_src, null); + } + if (try sema.anyUndef(block, field_types_src, field_types_arr)) { + return sema.failWithUseOfUndef(block, field_types_src, null); + } + // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. + if (try sema.anyUndef(block, arg_ty_src, arg_ty_val)) { + return sema.failWithUseOfUndef(block, arg_ty_src, null); + } // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the // inputs turn out to be invalid, we can cancel the WIP type later. + var any_aligned_fields = false; + // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, layout); - std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); - std.hash.autoHash(&hasher, fields_len); - + std.hash.autoHash(&hasher, arg_ty_val); + // `field_types_arr` and `field_attrs_arr` are already deduplicated by the InternPool! + std.hash.autoHash(&hasher, field_types_arr); + std.hash.autoHash(&hasher, field_attrs_arr); + // However, for field names, we need to iterate the individual fields, because the pointers (the + // names are slices) mean that distinct values could ultimately result in the same union type. for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .union_field_names }); + std.hash.autoHash(&hasher, field_name); - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2)); - - const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name }); - std.hash.autoHash(&hasher, .{ - field_name, - field_type_val.toIntern(), - field_align_val.toIntern(), - }); + const field_attrs = try sema.interpretBuiltinType( + block, + field_attrs_src, + try field_attrs_arr.elemValue(pt, field_idx), + std.builtin.Type.UnionField.Attributes, + ); + if (field_attrs.@"align" != null) { + any_aligned_fields = true; + } } - const tracked_inst = try block.trackZir(inst); + // Some basic validation to avoid a bogus `getUnionType` call... + const explicit_tag_ty: ?Type = if (arg_ty_val.optionalValue(zcu)) |arg_ty| ty: { + switch (layout) { + .@"extern", .@"packed" => return sema.fail(block, arg_ty_src, "{t} union does not support enum tag type", .{layout}), + .auto => {}, + } + break :ty arg_ty.toType(); + } else null; + if (any_aligned_fields and layout == .@"packed") { + return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{}); + } const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{ .flags = .{ .layout = layout, .status = .none, - .runtime_tag = if (opt_tag_type_val.optionalValue(zcu) != null) - .tagged - else if (layout != .auto) - .none - else switch (block.wantSafeTypes()) { - true => .safety, - false => .none, + .runtime_tag = rt: { + if (explicit_tag_ty != null) break :rt .tagged; + if (layout == .auto and block.wantSafeTypes()) break :rt .safety; + break :rt .none; }, - .any_aligned_fields = layout != .@"packed", + .any_aligned_fields = any_aligned_fields, .requires_comptime = .unknown, .assumed_runtime_bits = false, .assumed_pointer_aligned = false, .alignment = .none, }, - .fields_len = fields_len, + .fields_len = @intCast(fields_len), .enum_tag_ty = .none, // set later because not yet validated .field_types = &.{}, // set later .field_aligns = &.{}, // set later @@ -21325,128 +21421,117 @@ fn reifyUnion( const loaded_union = ip.loadUnionType(wip_ty.index); - const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: { - switch (ip.indexToKey(tag_type_val.toIntern())) { - .enum_type => {}, - else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), + const enum_tag_ty, const has_explicit_tag = if (explicit_tag_ty) |enum_tag_ty| tag: { + if (enum_tag_ty.zigTypeTag(zcu) != .@"enum") { + return sema.fail(block, arg_ty_src, "tag type must be an enum type", .{}); } - const enum_tag_ty = tag_type_val.toType(); - // We simply track which fields of the tag type have been seen. const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu); - var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + // Don't pass a reason; first loop acts as a check that this is valid. + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_alignment_val = try field_info.fieldValue(pt, 2); - - // Don't pass a reason; first loop acts as an assertion that this is valid. - const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); - - const enum_index = enum_tag_ty.enumFieldIndex(field_name, zcu) orelse { - // TODO: better source location - return sema.fail(block, src, "no field named '{f}' in enum '{f}'", .{ + if (field_idx >= tag_ty_fields_len) { + return sema.fail(block, field_names_src, "no field named '{f}' in enum '{f}'", .{ field_name.fmt(ip), enum_tag_ty.fmt(pt), }); - }; - if (seen_tags.isSet(enum_index)) { - // TODO: better source location - return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); } - seen_tags.set(enum_index); - loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); - const byte_align = try field_alignment_val.toUnsignedIntSema(pt); - if (layout == .@"packed") { - if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); - } else { - loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); + const enum_field_name = enum_tag_ty.enumFieldName(field_idx, zcu); + if (enum_field_name != field_name) { + return sema.fail(block, field_names_src, "union field name '{f}' does not match enum field name '{f}'", .{ + field_name.fmt(ip), enum_field_name.fmt(ip), + }); } } - if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "enum fields missing in union", .{}); + const msg = try sema.errMsg(field_names_src, "{d} enum fields missing in union", .{ + tag_ty_fields_len - fields_len, + }); errdefer msg.destroy(gpa); - var it = seen_tags.iterator(.{ .kind = .unset }); - while (it.next()) |enum_index| { - const field_name = enum_tag_ty.enumFieldName(enum_index, zcu); - try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{f}' missing, declared here", .{ - field_name.fmt(ip), + for (fields_len..tag_ty_fields_len) |enum_field_idx| { + try sema.addFieldErrNote(enum_tag_ty, enum_field_idx, msg, "field '{f}' missing, declared here", .{ + enum_tag_ty.enumFieldName(enum_field_idx, zcu).fmt(ip), }); } try sema.addDeclaredHereNote(msg, enum_tag_ty); break :msg msg; }); - - break :tag_ty .{ enum_tag_ty.toIntern(), true }; - } else tag_ty: { + break :tag .{ enum_tag_ty.toIntern(), true }; + } else tag: { // We must track field names and set up the tag type ourselves. var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; try field_names.ensureTotalCapacity(sema.arena, fields_len); for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); - - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_alignment_val = try field_info.fieldValue(pt, 2); - - // Don't pass a reason; first loop acts as an assertion that this is valid. - const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + // Don't pass a reason; first loop acts as a check that this is valid. + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); const gop = field_names.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { // TODO: better source location - return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); - } - - loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); - const byte_align = try field_alignment_val.toUnsignedIntSema(pt); - if (layout == .@"packed") { - if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); - } else { - loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); + return sema.fail(block, field_names_src, "duplicate union field {f}", .{field_name.fmt(ip)}); } } - const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name); - break :tag_ty .{ enum_tag_ty, false }; + break :tag .{ enum_tag_ty, false }; }; errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error - for (loaded_union.field_types.get(ip)) |field_ty_ip| { - const field_ty: Type = .fromInterned(field_ty_ip); + for (0..fields_len) |field_idx| { + const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); + const field_attrs = try sema.interpretBuiltinType( + block, + field_attrs_src, + try field_attrs_arr.elemValue(pt, field_idx), + std.builtin.Type.UnionField.Attributes, + ); + if (field_ty.zigTypeTag(zcu) == .@"opaque") { return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); + const msg = try sema.errMsg(field_types_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; }); } - if (layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .union_field); + switch (layout) { + .auto => {}, + .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); + try sema.explainWhyTypeIsNotExtern(msg, field_types_src, field_ty, .union_field); - try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + }, + .@"packed" => if (!try sema.validatePackedType(field_ty)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_types_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); + try sema.explainWhyTypeIsNotPacked(msg, field_types_src, field_ty); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + }, + } + + loaded_union.field_types.get(ip)[field_idx] = field_ty.toIntern(); + if (field_attrs.@"align") |bytes| { + assert(layout != .@"packed"); + const a = try sema.validateAlign(block, field_attrs_src, bytes); + loaded_union.field_aligns.get(ip)[field_idx] = a; + } else if (any_aligned_fields) { + assert(layout != .@"packed"); + loaded_union.field_aligns.get(ip)[field_idx] = .none; } } @@ -21471,116 +21556,94 @@ fn reifyUnion( try sema.declareDependency(.{ .interned = wip_ty.index }); try sema.addTypeReferenceEntry(src, wip_ty.index); if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); + return .fromIntern(wip_ty.finish(ip, new_namespace_index)); } -fn reifyTuple( +fn zirReifyEnum( sema: *Sema, block: *Block, - src: LazySrcLoc, - fields_val: Value, + extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const gpa = sema.gpa; const ip = &zcu.intern_pool; - const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); + const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small); + const extra = sema.code.extraData(Zir.Inst.ReifyEnum, extended.operand).data; + const tracked_inst = try block.trackZir(inst); + const src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .nodeOffset(.zero), + }; - const types = try sema.arena.alloc(InternPool.Index, fields_len); - const inits = try sema.arena.alloc(InternPool.Index, fields_len); + const tag_ty_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 0, + } }, + }; + const mode_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 1, + } }, + }; + const field_names_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 2, + } }, + }; + const field_values_src: LazySrcLoc = .{ + .base_node_inst = tracked_inst, + .offset = .{ .node_offset_builtin_call_arg = .{ + .builtin_call_node = .zero, + .arg_index = 3, + } }, + }; - for (types, inits, 0..) |*field_ty, *field_init, field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); + const enum_mode_ty = try sema.getBuiltinType(mode_src, .@"Type.Enum.Mode"); - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_default_value_val = try field_info.fieldValue(pt, 2); - const field_is_comptime_val = try field_info.fieldValue(pt, 3); - const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); - - const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .tuple_field_name }); - const field_type = field_type_val.toType(); - const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { - const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); - // We need to do this deref here, so we won't check for this error case later on. - const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( - block, - src, - .{ .simple = .tuple_field_default_value }, - ); - if (val.canMutateComptimeVarState(zcu)) { - return sema.failWithContainsReferenceToComptimeVar(block, src, field_name, "field default value", val); - } - // Resolve the value so that lazy values do not create distinct types. - break :d (try sema.resolveLazyValue(val)).toIntern(); - } else .none; - - const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail( - block, - src, - "tuple cannot have non-numeric field '{f}'", - .{field_name.fmt(ip)}, - ); - if (field_name_index != field_idx) { - return sema.fail( - block, - src, - "tuple field name '{d}' does not match field index {d}", - .{ field_name_index, field_idx }, - ); - } - - try sema.validateTupleFieldType(block, field_type, src); - - { - const alignment_ok = ok: { - if (field_alignment_val.toIntern() == .zero) break :ok true; - const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false; - const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0; - break :ok abi_align == given_align; - }; - if (!alignment_ok) { - return sema.fail(block, src, "tuple fields cannot specify alignment", .{}); - } - } - - if (field_is_comptime_val.toBool() and field_default_value == .none) { - return sema.fail(block, src, "comptime field without default initialization value", .{}); - } - - if (!field_is_comptime_val.toBool() and field_default_value != .none) { - return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{}); - } - - field_ty.* = field_type.toIntern(); - field_init.* = field_default_value; + const tag_ty = try sema.resolveType(block, tag_ty_src, extra.tag_ty); + if (tag_ty.zigTypeTag(zcu) != .int) { + return sema.fail(block, tag_ty_src, "tag type must be an integer type", .{}); } - return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ - .types = types, - .values = inits, + const mode_uncoerced = try sema.resolveInst(extra.mode); + const mode_coerced = try sema.coerce(block, enum_mode_ty, mode_uncoerced, mode_src); + const mode_val = try sema.resolveConstDefinedValue(block, mode_src, mode_coerced, .{ .simple = .type }); + const nonexhaustive = switch (try sema.interpretBuiltinType(block, mode_src, mode_val, std.builtin.Type.Enum.Mode)) { + .exhaustive => false, + .nonexhaustive => true, + }; + + const field_names_uncoerced = try sema.resolveInst(extra.field_names); + const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); + const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .enum_field_names }); + const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .enum_field_names }); + + const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); + + const field_values_ty = try pt.singleConstPtrType(try pt.arrayType(.{ + .len = fields_len, + .child = tag_ty.toIntern(), })); -} -fn reifyStruct( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, - src: LazySrcLoc, - layout: std.builtin.Type.ContainerLayout, - opt_backing_int_val: Value, - fields_val: Value, - name_strategy: Zir.Inst.NameStrategy, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const gpa = sema.gpa; - const ip = &zcu.intern_pool; + const field_values_uncoerced = try sema.resolveInst(extra.field_values); + const field_values_coerced = try sema.coerce(block, field_values_ty, field_values_uncoerced, field_values_src); + const field_values_slice = try sema.resolveConstDefinedValue(block, field_values_src, field_values_coerced, .{ .simple = .enum_field_values }); + const field_values_arr = try sema.derefSliceAsArray(block, field_values_src, field_values_slice, .{ .simple = .enum_field_values }); - // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. - - const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); + // Before we begin, check for undefs... + if (try sema.anyUndef(block, field_values_src, field_values_arr)) { + return sema.failWithUseOfUndef(block, field_values_src, null); + } + // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the @@ -21589,62 +21652,23 @@ fn reifyStruct( // For deduplication purposes, we must create a hash including all details of this type. // TODO: use a longer hash! var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, layout); - std.hash.autoHash(&hasher, opt_backing_int_val.toIntern()); + std.hash.autoHash(&hasher, tag_ty.toIntern()); + std.hash.autoHash(&hasher, nonexhaustive); std.hash.autoHash(&hasher, fields_len); - - var any_comptime_fields = false; - var any_default_inits = false; - + // `field_values_arr` is already deduplicated by the InternPool! + std.hash.autoHash(&hasher, field_values_arr); + // However, for field names, we need to iterate the individual fields, because the pointers (the + // names are slices) mean that distinct values could ultimately result in the same enum type. for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); - - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_default_value_val = try field_info.fieldValue(pt, 2); - const field_is_comptime_val = try field_info.fieldValue(pt, 3); - const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); - - const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .struct_field_name }); - const field_is_comptime = field_is_comptime_val.toBool(); - const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { - const ptr_ty = try pt.singleConstPtrType(field_type_val.toType()); - // We need to do this deref here, so we won't check for this error case later on. - const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( - block, - src, - .{ .simple = .struct_field_default_value }, - ); - if (val.canMutateComptimeVarState(zcu)) { - return sema.failWithContainsReferenceToComptimeVar(block, src, field_name, "field default value", val); - } - // Resolve the value so that lazy values do not create distinct types. - break :d (try sema.resolveLazyValue(val)).toIntern(); - } else .none; - - std.hash.autoHash(&hasher, .{ - field_name, - field_type_val.toIntern(), - field_default_value, - field_is_comptime, - field_alignment_val.toIntern(), - }); - - if (field_is_comptime) any_comptime_fields = true; - if (field_default_value != .none) any_default_inits = true; + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .enum_field_names }); + std.hash.autoHash(&hasher, field_name); } - const tracked_inst = try block.trackZir(inst); - - const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{ - .layout = layout, - .fields_len = fields_len, - .known_non_opv = false, - .requires_comptime = .unknown, - .any_comptime_fields = any_comptime_fields, - .any_default_inits = any_default_inits, - .any_aligned_fields = layout != .@"packed", - .inits_resolved = true, + const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{ + .has_values = true, + .tag_mode = if (nonexhaustive) .nonexhaustive else .explicit, + .fields_len = @intCast(fields_len), .key = .{ .reified = .{ .zir_index = tracked_inst, .type_hash = hasher.final(), @@ -21654,144 +21678,21 @@ fn reifyStruct( .existing => |ty| { try sema.declareDependency(.{ .interned = ty }); try sema.addTypeReferenceEntry(src, ty); - return Air.internedToRef(ty); + return .fromIntern(ty); }, }; - errdefer wip_ty.cancel(ip, pt.tid); + var done = false; + errdefer if (!done) wip_ty.cancel(ip, pt.tid); const type_name = try sema.createTypeName( block, name_strategy, - "struct", + "enum", inst, wip_ty.index, ); wip_ty.setName(ip, type_name.name, type_name.nav); - const struct_type = ip.loadStructType(wip_ty.index); - - for (0..fields_len) |field_idx| { - const field_info = try fields_val.elemValue(pt, field_idx); - - const field_name_val = try field_info.fieldValue(pt, 0); - const field_type_val = try field_info.fieldValue(pt, 1); - const field_default_value_val = try field_info.fieldValue(pt, 2); - const field_is_comptime_val = try field_info.fieldValue(pt, 3); - const field_alignment_val = try field_info.fieldValue(pt, 4); - - const field_ty = field_type_val.toType(); - // Don't pass a reason; first loop acts as an assertion that this is valid. - const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); - if (struct_type.addFieldName(ip, field_name)) |prev_index| { - _ = prev_index; // TODO: better source location - return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); - } - - if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) { - return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); - } - const byte_align = try field_alignment_val.toUnsignedIntSema(pt); - if (layout == .@"packed") { - if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{}); - } else { - struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); - } - - const field_is_comptime = field_is_comptime_val.toBool(); - if (field_is_comptime) { - assert(any_comptime_fields); - switch (layout) { - .@"extern" => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}), - .@"packed" => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}), - .auto => struct_type.setFieldComptime(ip, field_idx), - } - } - - const field_default: InternPool.Index = d: { - if (!any_default_inits) break :d .none; - const ptr_val = field_default_value_val.optionalValue(zcu) orelse break :d .none; - const ptr_ty = try pt.singleConstPtrType(field_ty); - // Asserted comptime-dereferencable above. - const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?; - // We already resolved this for deduplication, so we may as well do it now. - break :d (try sema.resolveLazyValue(val)).toIntern(); - }; - - if (field_is_comptime and field_default == .none) { - return sema.fail(block, src, "comptime field without default initialization value", .{}); - } - - struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern(); - if (field_default != .none) { - struct_type.field_inits.get(ip)[field_idx] = field_default; - } - - if (field_ty.zigTypeTag(zcu) == .@"opaque") { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); - errdefer msg.destroy(gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } - if (field_ty.zigTypeTag(zcu) == .noreturn) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "struct fields cannot be 'noreturn'", .{}); - errdefer msg.destroy(gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } - if (layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .struct_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { - return sema.failWithOwnedErrorMsg(block, msg: { - const msg = try sema.errMsg(src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(gpa); - - try sema.explainWhyTypeIsNotPacked(msg, src, field_ty); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }); - } - } - - if (layout == .@"packed") { - var fields_bit_sum: u64 = 0; - for (0..struct_type.field_types.len) |field_idx| { - const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_idx]); - field_ty.resolveLayout(pt) catch |err| switch (err) { - error.AnalysisFail => { - const msg = sema.err orelse return err; - try sema.errNote(src, msg, "while checking a field of this struct", .{}); - return err; - }, - else => return err, - }; - fields_bit_sum += field_ty.bitSize(zcu); - } - - if (opt_backing_int_val.optionalValue(zcu)) |backing_int_val| { - const backing_int_ty = backing_int_val.toType(); - try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); - struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); - } else { - const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum)); - struct_type.setBackingIntType(ip, backing_int_ty.toIntern()); - } - } - const new_namespace_index = try pt.createNamespace(.{ .parent = block.namespace.toOptional(), .owner_type = wip_ty.index, @@ -21799,7 +21700,44 @@ fn reifyStruct( .generation = zcu.generation, }); - try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); + try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); + if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); + wip_ty.prepare(ip, new_namespace_index); + wip_ty.setTagTy(ip, tag_ty.toIntern()); + done = true; + + for (0..fields_len) |field_idx| { + const field_name_val = try field_names_arr.elemValue(pt, field_idx); + // Don't pass a reason; first loop acts as a check that this is valid. + const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, undefined); + + const field_val = try field_values_arr.elemValue(pt, field_idx); + + if (wip_ty.nextField(ip, field_name, field_val.toIntern())) |conflict| { + return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { + .name => msg: { + const msg = try sema.errMsg(field_names_src, "duplicate enum field '{f}'", .{field_name.fmt(ip)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(field_names_src, msg, "other field here", .{}); + break :msg msg; + }, + .value => msg: { + const msg = try sema.errMsg(field_values_src, "enum tag value {f} already taken", .{field_val.fmtValueSema(pt, sema)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(field_values_src, msg, "other enum tag value here", .{}); + break :msg msg; + }, + }); + } + } + + if (nonexhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) { + return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); + } + codegen_type: { if (zcu.comp.config.use_llvm) break :codegen_type; if (block.ownerModule().strip) break :codegen_type; @@ -21807,10 +21745,7 @@ fn reifyStruct( zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); } - try sema.declareDependency(.{ .interned = wip_ty.index }); - try sema.addTypeReferenceEntry(src, wip_ty.index); - if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); - return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); + return Air.internedToRef(wip_ty.index); } fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { @@ -25541,7 +25476,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; - const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .function_ret_ty }); + const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .fn_ret_ty }); const ty = val.toType(); break :blk ty; } else if (extra.data.bits.has_ret_ty_ref) blk: { @@ -25968,21 +25903,26 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD const src = block.nodeOffset(src_node); const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small); - const ty = switch (value) { + const builtin_type: Zcu.BuiltinDecl = switch (value) { // zig fmt: off - .atomic_order => try sema.getBuiltinType(src, .AtomicOrder), - .atomic_rmw_op => try sema.getBuiltinType(src, .AtomicRmwOp), - .calling_convention => try sema.getBuiltinType(src, .CallingConvention), - .address_space => try sema.getBuiltinType(src, .AddressSpace), - .float_mode => try sema.getBuiltinType(src, .FloatMode), - .reduce_op => try sema.getBuiltinType(src, .ReduceOp), - .call_modifier => try sema.getBuiltinType(src, .CallModifier), - .prefetch_options => try sema.getBuiltinType(src, .PrefetchOptions), - .export_options => try sema.getBuiltinType(src, .ExportOptions), - .extern_options => try sema.getBuiltinType(src, .ExternOptions), - .type_info => try sema.getBuiltinType(src, .Type), - .branch_hint => try sema.getBuiltinType(src, .BranchHint), - .clobbers => try sema.getBuiltinType(src, .@"assembly.Clobbers"), + .atomic_order => .AtomicOrder, + .atomic_rmw_op => .AtomicRmwOp, + .calling_convention => .CallingConvention, + .address_space => .AddressSpace, + .float_mode => .FloatMode, + .signedness => .Signedness, + .reduce_op => .ReduceOp, + .call_modifier => .CallModifier, + .prefetch_options => .PrefetchOptions, + .export_options => .ExportOptions, + .extern_options => .ExternOptions, + .branch_hint => .BranchHint, + .clobbers => .@"assembly.Clobbers", + .pointer_size => .@"Type.Pointer.Size", + .pointer_attributes => .@"Type.Pointer.Attributes", + .fn_attributes, => .@"Type.Fn.Attributes", + .container_layout => .@"Type.ContainerLayout", + .enum_mode => .@"Type.Enum.Mode", // zig fmt: on // Values are handled here. @@ -26009,7 +25949,7 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src); }, }; - return Air.internedToRef(ty.toIntern()); + return .fromType(try sema.getBuiltinType(src, builtin_type)); } fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -35259,7 +35199,7 @@ fn structFields( .base_node_inst = struct_type.zir_index, .offset = .nodeOffset(.zero), }, - .r = .{ .simple = .struct_fields }, + .r = .{ .simple = .type }, } }, .src_base_inst = struct_type.zir_index, .type_name_ctx = struct_type.name, @@ -35614,7 +35554,7 @@ fn unionFields( .inlining = null, .comptime_reason = .{ .reason = .{ .src = src, - .r = .{ .simple = .union_fields }, + .r = .{ .simple = .type }, } }, .src_base_inst = union_type.zir_index, .type_name_ctx = union_type.name, @@ -36077,8 +36017,13 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .manyptr_u8_type, .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, + .manyptr_const_slice_const_u8_type, .slice_const_u8_type, .slice_const_u8_sentinel_0_type, + .slice_const_slice_const_u8_type, + .optional_type_type, + .manyptr_const_type_type, + .slice_const_type_type, .vector_8_i8_type, .vector_16_i8_type, .vector_32_i8_type, @@ -37230,7 +37175,7 @@ fn sliceToIpString( /// Given a slice value, attempts to dereference it into a comptime-known array. /// Emits a compile error if the contents of the slice are not comptime-known. -/// Asserts that `slice_val` is a slice. +/// Asserts that `slice_val` is a slice or a pointer to an array. fn derefSliceAsArray( sema: *Sema, block: *Block, @@ -37247,7 +37192,7 @@ fn derefSliceAsArray( /// Given a slice value, attempts to dereference it into a comptime-known array. /// Returns `null` if the contents of the slice are not comptime-known. -/// Asserts that `slice_val` is a slice. +/// Asserts that `slice_val` is a slice or a pointer to an array. fn maybeDerefSliceAsArray( sema: *Sema, block: *Block, @@ -37257,7 +37202,13 @@ fn maybeDerefSliceAsArray( const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - assert(slice_val.typeOf(zcu).isSlice(zcu)); + const slice_ty = slice_val.typeOf(zcu); + assert(slice_ty.zigTypeTag(zcu) == .pointer); + switch (slice_ty.ptrInfo(zcu).flags.size) { + .slice => {}, + .one => return sema.pointerDeref(block, src, slice_val, slice_ty), + .many, .c => unreachable, + } const slice = switch (ip.indexToKey(slice_val.toIntern())) { .undef => return sema.failWithUseOfUndef(block, src, null), .slice => |slice| slice, @@ -37393,7 +37344,7 @@ pub fn resolveDeclaredEnum( .inlining = null, .comptime_reason = .{ .reason = .{ .src = src, - .r = .{ .simple = .enum_fields }, + .r = .{ .simple = .enum_field_values }, } }, .src_base_inst = tracked_inst, .type_name_ctx = type_name, diff --git a/src/Type.zig b/src/Type.zig index 74a540298c..3d3b36640c 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -317,7 +317,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari .undefined, => try writer.print("@TypeOf({s})", .{@tagName(s)}), - .enum_literal => try writer.writeAll("@Type(.enum_literal)"), + .enum_literal => try writer.writeAll("@EnumLiteral()"), .generic_poison => unreachable, }, @@ -3509,7 +3509,9 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_line, .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_line, .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_line, - .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.src_line, + .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.src_line, + .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.src_line, + .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.src_line, else => unreachable, }, else => unreachable, @@ -4280,6 +4282,10 @@ pub const manyptr_const_u8: Type = .{ .ip_index = .manyptr_const_u8_type }; pub const manyptr_const_u8_sentinel_0: Type = .{ .ip_index = .manyptr_const_u8_sentinel_0_type }; pub const slice_const_u8: Type = .{ .ip_index = .slice_const_u8_type }; pub const slice_const_u8_sentinel_0: Type = .{ .ip_index = .slice_const_u8_sentinel_0_type }; +pub const slice_const_slice_const_u8: Type = .{ .ip_index = .slice_const_slice_const_u8_type }; +pub const slice_const_type: Type = .{ .ip_index = .slice_const_type_type }; +pub const optional_type: Type = .{ .ip_index = .optional_type_type }; +pub const optional_noreturn: Type = .{ .ip_index = .optional_noreturn_type }; pub const vector_8_i8: Type = .{ .ip_index = .vector_8_i8_type }; pub const vector_16_i8: Type = .{ .ip_index = .vector_16_i8_type }; diff --git a/src/Value.zig b/src/Value.zig index 9ced6f1074..930f94a5a3 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -2824,6 +2824,29 @@ pub fn resolveLazy( .val = resolved_val, })); }, + .error_union => |eu| switch (eu.val) { + .err_name => return val, + .payload => |payload| { + const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt); + if (resolved_payload.toIntern() == payload) return val; + return .fromInterned(try pt.intern(.{ .error_union = .{ + .ty = eu.ty, + .val = .{ .payload = resolved_payload.toIntern() }, + } })); + }, + }, + .opt => |opt| switch (opt.val) { + .none => return val, + else => |payload| { + const resolved_payload = try Value.fromInterned(payload).resolveLazy(arena, pt); + if (resolved_payload.toIntern() == payload) return val; + return .fromInterned(try pt.intern(.{ .opt = .{ + .ty = opt.ty, + .val = resolved_payload.toIntern(), + } })); + }, + }, + else => return val, } } diff --git a/src/Zcu.zig b/src/Zcu.zig index f15b03dc1a..c170ef6d71 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -416,10 +416,13 @@ pub const BuiltinDecl = enum { Type, @"Type.Fn", @"Type.Fn.Param", + @"Type.Fn.Param.Attributes", + @"Type.Fn.Attributes", @"Type.Int", @"Type.Float", @"Type.Pointer", @"Type.Pointer.Size", + @"Type.Pointer.Attributes", @"Type.Array", @"Type.Vector", @"Type.Optional", @@ -427,10 +430,13 @@ pub const BuiltinDecl = enum { @"Type.ErrorUnion", @"Type.EnumField", @"Type.Enum", + @"Type.Enum.Mode", @"Type.Union", @"Type.UnionField", + @"Type.UnionField.Attributes", @"Type.Struct", @"Type.StructField", + @"Type.StructField.Attributes", @"Type.ContainerLayout", @"Type.Opaque", @"Type.Declaration", @@ -495,10 +501,13 @@ pub const BuiltinDecl = enum { .Type, .@"Type.Fn", .@"Type.Fn.Param", + .@"Type.Fn.Param.Attributes", + .@"Type.Fn.Attributes", .@"Type.Int", .@"Type.Float", .@"Type.Pointer", .@"Type.Pointer.Size", + .@"Type.Pointer.Attributes", .@"Type.Array", .@"Type.Vector", .@"Type.Optional", @@ -506,10 +515,13 @@ pub const BuiltinDecl = enum { .@"Type.ErrorUnion", .@"Type.EnumField", .@"Type.Enum", + .@"Type.Enum.Mode", .@"Type.Union", .@"Type.UnionField", + .@"Type.UnionField.Attributes", .@"Type.Struct", .@"Type.StructField", + .@"Type.StructField.Attributes", .@"Type.ContainerLayout", .@"Type.Opaque", .@"Type.Declaration", @@ -1745,28 +1757,28 @@ pub const SrcLoc = struct { const node = node_off.toAbsolute(src_loc.base_node); var buf: [1]Ast.Node.Index = undefined; const full = tree.fullFnProto(&buf, node).?; - return tree.nodeToSpan(full.ast.align_expr.unwrap().?); + return tree.nodeToSpan(full.ast.align_expr.unwrap() orelse node); }, .node_offset_fn_type_addrspace => |node_off| { const tree = try src_loc.file_scope.getTree(zcu); const node = node_off.toAbsolute(src_loc.base_node); var buf: [1]Ast.Node.Index = undefined; const full = tree.fullFnProto(&buf, node).?; - return tree.nodeToSpan(full.ast.addrspace_expr.unwrap().?); + return tree.nodeToSpan(full.ast.addrspace_expr.unwrap() orelse node); }, .node_offset_fn_type_section => |node_off| { const tree = try src_loc.file_scope.getTree(zcu); const node = node_off.toAbsolute(src_loc.base_node); var buf: [1]Ast.Node.Index = undefined; const full = tree.fullFnProto(&buf, node).?; - return tree.nodeToSpan(full.ast.section_expr.unwrap().?); + return tree.nodeToSpan(full.ast.section_expr.unwrap() orelse node); }, .node_offset_fn_type_cc => |node_off| { const tree = try src_loc.file_scope.getTree(zcu); const node = node_off.toAbsolute(src_loc.base_node); var buf: [1]Ast.Node.Index = undefined; const full = tree.fullFnProto(&buf, node).?; - return tree.nodeToSpan(full.ast.callconv_expr.unwrap().?); + return tree.nodeToSpan(full.ast.callconv_expr.unwrap() orelse node); }, .node_offset_fn_type_ret_ty => |node_off| { @@ -2684,7 +2696,9 @@ pub const LazySrcLoc = struct { .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node, .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node, .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node, - .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.node, + .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node, + .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node, + .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node, else => unreachable, }, else => unreachable, diff --git a/src/codegen/c/Type.zig b/src/codegen/c/Type.zig index f575a40197..fb37b60580 100644 --- a/src/codegen/c/Type.zig +++ b/src/codegen/c/Type.zig @@ -1416,6 +1416,9 @@ pub const Pool = struct { .null_type, .undefined_type, .enum_literal_type, + .optional_type_type, + .manyptr_const_type_type, + .slice_const_type_type, => return .void, .u1_type, .u8_type => return .u8, .i8_type => return .i8, @@ -1525,6 +1528,73 @@ pub const Pool = struct { return pool.fromFields(allocator, .@"struct", &fields, kind); }, + .manyptr_const_slice_const_u8_type => { + const target = &mod.resolved_target.result; + var fields: [2]Info.Field = .{ + .{ + .name = .{ .index = .ptr }, + .ctype = try pool.getPointer(allocator, .{ + .elem_ctype = .u8, + .@"const" = true, + .nonstring = true, + }), + .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), + }, + .{ + .name = .{ .index = .len }, + .ctype = .usize, + .alignas = AlignAs.fromAbiAlignment( + .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), + ), + }, + }; + const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, kind); + return pool.getPointer(allocator, .{ + .elem_ctype = slice_const_u8, + .@"const" = true, + }); + }, + .slice_const_slice_const_u8_type => { + const target = &mod.resolved_target.result; + var fields: [2]Info.Field = .{ + .{ + .name = .{ .index = .ptr }, + .ctype = try pool.getPointer(allocator, .{ + .elem_ctype = .u8, + .@"const" = true, + .nonstring = true, + }), + .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), + }, + .{ + .name = .{ .index = .len }, + .ctype = .usize, + .alignas = AlignAs.fromAbiAlignment( + .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), + ), + }, + }; + const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, .forward); + fields = .{ + .{ + .name = .{ .index = .ptr }, + .ctype = try pool.getPointer(allocator, .{ + .elem_ctype = slice_const_u8, + .@"const" = true, + }), + .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)), + }, + .{ + .name = .{ .index = .len }, + .ctype = .usize, + .alignas = AlignAs.fromAbiAlignment( + .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())), + ), + }, + }; + return pool.fromFields(allocator, .@"struct", &fields, kind); + }, + .vector_8_i8_type => { const vector_ctype = try pool.getVector(allocator, .{ .elem_ctype = .i8, diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 1fd1840da7..04f3c37b9b 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -4490,7 +4490,12 @@ fn updateContainerTypeWriterError( .enum_decl => @as(Zir.Inst.EnumDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, .union_decl => @as(Zir.Inst.UnionDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, .opaque_decl => @as(Zir.Inst.OpaqueDecl.Small, @bitCast(decl_inst.data.extended.small)).name_strategy, - .reify => @as(Zir.Inst.NameStrategy, @enumFromInt(decl_inst.data.extended.small)), + + .reify_enum, + .reify_struct, + .reify_union, + => @enumFromInt(decl_inst.data.extended.small), + else => unreachable, }, else => unreachable, diff --git a/src/print_zir.zig b/src/print_zir.zig index d263db9f33..316632f2d3 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -399,6 +399,7 @@ const Writer = struct { .splat, .reduce, .bitcast, + .reify_int, .vector_type, .max, .min, @@ -568,6 +569,8 @@ const Writer = struct { .work_group_id, .branch_hint, .float_op_result_ty, + .reify_tuple, + .reify_pointer_sentinel_ty, => { const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; try self.writeInstRef(stream, inst_data.operand); @@ -575,23 +578,13 @@ const Writer = struct { try self.writeSrcNode(stream, inst_data.node); }, - .reify => { - const inst_data = self.code.extraData(Zir.Inst.Reify, extended.operand).data; - try stream.print("line({d}), ", .{inst_data.src_line}); - try self.writeInstRef(stream, inst_data.operand); - try stream.writeAll(")) "); - const prev_parent_decl_node = self.parent_decl_node; - self.parent_decl_node = inst_data.node; - defer self.parent_decl_node = prev_parent_decl_node; - try self.writeSrcNode(stream, .zero); - }, - .builtin_extern, .c_define, .error_cast, .wasm_memory_grow, .prefetch, .c_va_arg, + .reify_enum_value_slice_ty, => { const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; try self.writeInstRef(stream, inst_data.lhs); @@ -601,6 +594,95 @@ const Writer = struct { try self.writeSrcNode(stream, inst_data.node); }, + .reify_slice_arg_ty => { + const reify_slice_arg_info: Zir.Inst.ReifySliceArgInfo = @enumFromInt(extended.operand); + const extra = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; + try stream.print("{t}, ", .{reify_slice_arg_info}); + try self.writeInstRef(stream, extra.operand); + try stream.writeAll(")) "); + try self.writeSrcNode(stream, extra.node); + }, + + .reify_pointer => { + const extra = self.code.extraData(Zir.Inst.ReifyPointer, extended.operand).data; + try self.writeInstRef(stream, extra.size); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.attrs); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.elem_ty); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.sentinel); + try stream.writeAll(")) "); + try self.writeSrcNode(stream, extra.node); + }, + .reify_fn => { + const extra = self.code.extraData(Zir.Inst.ReifyFn, extended.operand).data; + try self.writeInstRef(stream, extra.param_types); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.param_attrs); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.ret_ty); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.fn_attrs); + try stream.writeAll(")) "); + try self.writeSrcNode(stream, extra.node); + }, + .reify_struct => { + const extra = self.code.extraData(Zir.Inst.ReifyStruct, extended.operand).data; + const name_strat: Zir.Inst.NameStrategy = @enumFromInt(extended.small); + try stream.print("line({d}), {t}, ", .{ extra.src_line, name_strat }); + try self.writeInstRef(stream, extra.layout); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.backing_ty); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_names); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_types); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_attrs); + try stream.writeAll(")) "); + const prev_parent_decl_node = self.parent_decl_node; + self.parent_decl_node = extra.node; + defer self.parent_decl_node = prev_parent_decl_node; + try self.writeSrcNode(stream, .zero); + }, + .reify_union => { + const extra = self.code.extraData(Zir.Inst.ReifyUnion, extended.operand).data; + const name_strat: Zir.Inst.NameStrategy = @enumFromInt(extended.small); + try stream.print("line({d}), {t}, ", .{ extra.src_line, name_strat }); + try self.writeInstRef(stream, extra.layout); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.arg_ty); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_names); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_types); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_attrs); + try stream.writeAll(")) "); + const prev_parent_decl_node = self.parent_decl_node; + self.parent_decl_node = extra.node; + defer self.parent_decl_node = prev_parent_decl_node; + try self.writeSrcNode(stream, .zero); + }, + .reify_enum => { + const extra = self.code.extraData(Zir.Inst.ReifyEnum, extended.operand).data; + const name_strat: Zir.Inst.NameStrategy = @enumFromInt(extended.small); + try stream.print("line({d}), {t}, ", .{ extra.src_line, name_strat }); + try self.writeInstRef(stream, extra.tag_ty); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.mode); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_names); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_values); + try stream.writeAll(")) "); + const prev_parent_decl_node = self.parent_decl_node; + self.parent_decl_node = extra.node; + defer self.parent_decl_node = prev_parent_decl_node; + try self.writeSrcNode(stream, .zero); + }, + .cmpxchg => try self.writeCmpxchg(stream, extended), .ptr_cast_full => try self.writePtrCastFull(stream, extended), .ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended),