Skip to content

Commit 57392a5

Browse files
committed
compiler: remove anonymous struct types, unify all tuples
This commit reworks how anonymous struct literals and tuples work. Previously, an untyped anonymous struct literal (e.g. `const x = .{ .a = 123 }`) was given an "anonymous struct type", which is a special kind of struct which coerces using structural equivalence. This mechanism was a holdover from before we used RLS / result types as the primary mechanism of type inference. This commit changes the language so that the type assigned here is a "normal" struct type. It uses a form of equivalence based on the AST node and the type's structure, much like a reified (`@Type`) type. Additionally, tuples have been simplified. The distinction between "simple" and "complex" tuple types is eliminated. All tuples, even those explicitly declared using `struct { ... }` syntax, use structural equivalence, and do not undergo staged type resolution. Tuples are very restricted: they cannot have non-`auto` layouts, cannot have aligned fields, and cannot have default values with the exception of `comptime` fields. Tuples currently do not have optimized layout, but this can be changed in the future. This change simplifies the language, and fixes some problematic coercions through pointers which led to unintuitive behavior. Resolves: ziglang#16865
1 parent a916bc7 commit 57392a5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1066
-1313
lines changed

lib/compiler/aro/aro/Builtins.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *c
157157
.len = element_count,
158158
.elem = child_ty,
159159
};
160-
const vector_ty = .{ .specifier = .vector, .data = .{ .array = arr_ty } };
160+
const vector_ty: Type = .{ .specifier = .vector, .data = .{ .array = arr_ty } };
161161
builder.specifier = Type.Builder.fromType(vector_ty);
162162
},
163163
.q => {

lib/compiler/aro/aro/Parser.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8095,7 +8095,7 @@ fn primaryExpr(p: *Parser) Error!Result {
80958095

80968096
fn makePredefinedIdentifier(p: *Parser, strings_top: usize) !Result {
80978097
const end: u32 = @intCast(p.strings.items.len);
8098-
const elem_ty = .{ .specifier = .char, .qual = .{ .@"const" = true } };
8098+
const elem_ty: Type = .{ .specifier = .char, .qual = .{ .@"const" = true } };
80998099
const arr_ty = try p.arena.create(Type.Array);
81008100
arr_ty.* = .{ .elem = elem_ty, .len = end - strings_top };
81018101
const ty: Type = .{ .specifier = .array, .data = .{ .array = arr_ty } };

lib/compiler/aro/aro/text_literal.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ pub const Parser = struct {
188188
pub fn err(self: *Parser, tag: Diagnostics.Tag, extra: Diagnostics.Message.Extra) void {
189189
if (self.errored) return;
190190
self.errored = true;
191-
const diagnostic = .{ .tag = tag, .extra = extra };
191+
const diagnostic: CharDiagnostic = .{ .tag = tag, .extra = extra };
192192
if (self.errors_len == self.errors_buffer.len) {
193193
self.errors_buffer[self.errors_buffer.len - 1] = diagnostic;
194194
} else {

lib/compiler/aro_translate_c.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualH
749749
const is_const = is_fn_proto or child_type.isConst();
750750
const is_volatile = child_type.qual.@"volatile";
751751
const elem_type = try transType(c, scope, child_type, qual_handling, source_loc);
752-
const ptr_info = .{
752+
const ptr_info: @FieldType(ast.Payload.Pointer, "data") = .{
753753
.is_const = is_const,
754754
.is_volatile = is_volatile,
755755
.elem_type = elem_type,

lib/compiler/test_runner.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const io = std.io;
66
const testing = std.testing;
77
const assert = std.debug.assert;
88

9-
pub const std_options = .{
9+
pub const std_options: std.Options = .{
1010
.logFn = log,
1111
};
1212

lib/std/SemanticVersion.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ test "precedence" {
299299

300300
test "zig_version" {
301301
// An approximate Zig build that predates this test.
302-
const older_version = .{ .major = 0, .minor = 8, .patch = 0, .pre = "dev.874" };
302+
const older_version: Version = .{ .major = 0, .minor = 8, .patch = 0, .pre = "dev.874" };
303303

304304
// Simulated compatibility check using Zig version.
305305
const compatible = comptime @import("builtin").zig_version.order(older_version) == .gt;

lib/std/Target.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ pub const Os = struct {
509509
.max = .{ .major = 6, .minor = 10, .patch = 3 },
510510
},
511511
.glibc = blk: {
512-
const default_min = .{ .major = 2, .minor = 28, .patch = 0 };
512+
const default_min: std.SemanticVersion = .{ .major = 2, .minor = 28, .patch = 0 };
513513

514514
for (std.zig.target.available_libcs) |libc| {
515515
// We don't know the ABI here. We can get away with not checking it

lib/std/array_list.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
100100
/// of this ArrayList. Empties this ArrayList.
101101
pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
102102
const allocator = self.allocator;
103-
const result = .{ .items = self.items, .capacity = self.capacity };
103+
const result: ArrayListAlignedUnmanaged(T, alignment) = .{ .items = self.items, .capacity = self.capacity };
104104
self.* = init(allocator);
105105
return result;
106106
}

lib/std/crypto/phc_encoding.zig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ fn kvSplit(str: []const u8) !struct { key: []const u8, value: []const u8 } {
258258
var it = mem.splitScalar(u8, str, kv_delimiter_scalar);
259259
const key = it.first();
260260
const value = it.next() orelse return Error.InvalidEncoding;
261-
const ret = .{ .key = key, .value = value };
262-
return ret;
261+
return .{ .key = key, .value = value };
263262
}
264263

265264
test "phc format - encoding/decoding" {

lib/std/meta.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ fn CreateUniqueTuple(comptime N: comptime_int, comptime types: [N]type) type {
10181018
.type = T,
10191019
.default_value = null,
10201020
.is_comptime = false,
1021-
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
1021+
.alignment = 0,
10221022
};
10231023
}
10241024

lib/std/zig/AstGen.zig

Lines changed: 129 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,7 @@ fn structInitExpr(
17111711
return rvalue(gz, ri, val, node);
17121712
},
17131713
.none, .ref, .inferred_ptr => {
1714-
return rvalue(gz, ri, .empty_struct, node);
1714+
return rvalue(gz, ri, .empty_tuple, node);
17151715
},
17161716
.destructure => |destructure| {
17171717
return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
@@ -1888,6 +1888,8 @@ fn structInitExprAnon(
18881888
const tree = astgen.tree;
18891889

18901890
const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
1891+
.abs_node = node,
1892+
.abs_line = astgen.source_line,
18911893
.fields_len = @intCast(struct_init.ast.fields.len),
18921894
});
18931895
const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len;
@@ -1919,6 +1921,8 @@ fn structInitExprTyped(
19191921
const tree = astgen.tree;
19201922

19211923
const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
1924+
.abs_node = node,
1925+
.abs_line = astgen.source_line,
19221926
.fields_len = @intCast(struct_init.ast.fields.len),
19231927
});
19241928
const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len;
@@ -5007,6 +5011,25 @@ fn structDeclInner(
50075011
layout: std.builtin.Type.ContainerLayout,
50085012
backing_int_node: Ast.Node.Index,
50095013
) InnerError!Zir.Inst.Ref {
5014+
const astgen = gz.astgen;
5015+
const gpa = astgen.gpa;
5016+
const tree = astgen.tree;
5017+
5018+
{
5019+
const is_tuple = for (container_decl.ast.members) |member_node| {
5020+
const container_field = tree.fullContainerField(member_node) orelse continue;
5021+
if (container_field.ast.tuple_like) break true;
5022+
} else false;
5023+
5024+
if (is_tuple) {
5025+
if (node == 0) {
5026+
return astgen.failTok(0, "file cannot be a tuple", .{});
5027+
} else {
5028+
return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node);
5029+
}
5030+
}
5031+
}
5032+
50105033
const decl_inst = try gz.reserveInstructionIndex();
50115034

50125035
if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
@@ -5019,7 +5042,6 @@ fn structDeclInner(
50195042
.has_backing_int = false,
50205043
.known_non_opv = false,
50215044
.known_comptime_only = false,
5022-
.is_tuple = false,
50235045
.any_comptime_fields = false,
50245046
.any_default_inits = false,
50255047
.any_aligned_fields = false,
@@ -5028,10 +5050,6 @@ fn structDeclInner(
50285050
return decl_inst.toRef();
50295051
}
50305052

5031-
const astgen = gz.astgen;
5032-
const gpa = astgen.gpa;
5033-
const tree = astgen.tree;
5034-
50355053
var namespace: Scope.Namespace = .{
50365054
.parent = scope,
50375055
.node = node,
@@ -5106,46 +5124,6 @@ fn structDeclInner(
51065124
// No defer needed here because it is handled by `wip_members.deinit()` above.
51075125
const bodies_start = astgen.scratch.items.len;
51085126

5109-
const node_tags = tree.nodes.items(.tag);
5110-
const is_tuple = for (container_decl.ast.members) |member_node| {
5111-
const container_field = tree.fullContainerField(member_node) orelse continue;
5112-
if (container_field.ast.tuple_like) break true;
5113-
} else false;
5114-
5115-
if (is_tuple) switch (layout) {
5116-
.auto => {},
5117-
.@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),
5118-
.@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
5119-
};
5120-
5121-
if (is_tuple) for (container_decl.ast.members) |member_node| {
5122-
switch (node_tags[member_node]) {
5123-
.container_field_init,
5124-
.container_field_align,
5125-
.container_field,
5126-
.@"comptime",
5127-
.test_decl,
5128-
=> continue,
5129-
else => {
5130-
const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
5131-
.container_field_init,
5132-
.container_field_align,
5133-
.container_field,
5134-
=> break maybe_tuple,
5135-
else => {},
5136-
} else unreachable;
5137-
return astgen.failNodeNotes(
5138-
member_node,
5139-
"tuple declarations cannot contain declarations",
5140-
.{},
5141-
&[_]u32{
5142-
try astgen.errNoteNode(tuple_member, "tuple field here", .{}),
5143-
},
5144-
);
5145-
},
5146-
}
5147-
};
5148-
51495127
const old_hasher = astgen.src_hasher;
51505128
defer astgen.src_hasher = old_hasher;
51515129
astgen.src_hasher = std.zig.SrcHasher.init(.{});
@@ -5167,16 +5145,10 @@ fn structDeclInner(
51675145

51685146
astgen.src_hasher.update(tree.getNodeSource(member_node));
51695147

5170-
if (!is_tuple) {
5171-
const field_name = try astgen.identAsString(member.ast.main_token);
5172-
5173-
member.convertToNonTupleLike(astgen.tree.nodes);
5174-
assert(!member.ast.tuple_like);
5175-
5176-
wip_members.appendToField(@intFromEnum(field_name));
5177-
} else if (!member.ast.tuple_like) {
5178-
return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
5179-
}
5148+
const field_name = try astgen.identAsString(member.ast.main_token);
5149+
member.convertToNonTupleLike(astgen.tree.nodes);
5150+
assert(!member.ast.tuple_like);
5151+
wip_members.appendToField(@intFromEnum(field_name));
51805152

51815153
const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
51825154
wip_members.appendToField(@intFromEnum(doc_comment_index));
@@ -5270,7 +5242,6 @@ fn structDeclInner(
52705242
.has_backing_int = backing_int_ref != .none,
52715243
.known_non_opv = known_non_opv,
52725244
.known_comptime_only = known_comptime_only,
5273-
.is_tuple = is_tuple,
52745245
.any_comptime_fields = any_comptime_fields,
52755246
.any_default_inits = any_default_inits,
52765247
.any_aligned_fields = any_aligned_fields,
@@ -5300,6 +5271,106 @@ fn structDeclInner(
53005271
return decl_inst.toRef();
53015272
}
53025273

5274+
fn tupleDecl(
5275+
gz: *GenZir,
5276+
scope: *Scope,
5277+
node: Ast.Node.Index,
5278+
container_decl: Ast.full.ContainerDecl,
5279+
layout: std.builtin.Type.ContainerLayout,
5280+
backing_int_node: Ast.Node.Index,
5281+
) InnerError!Zir.Inst.Ref {
5282+
const astgen = gz.astgen;
5283+
const gpa = astgen.gpa;
5284+
const tree = astgen.tree;
5285+
5286+
const node_tags = tree.nodes.items(.tag);
5287+
5288+
switch (layout) {
5289+
.auto => {},
5290+
.@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),
5291+
.@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
5292+
}
5293+
5294+
if (backing_int_node != 0) {
5295+
return astgen.failNode(backing_int_node, "tuple does not support backing integer type", .{});
5296+
}
5297+
5298+
// We will use the scratch buffer, starting here, for the field data:
5299+
// 1. fields: { // for every `fields_len` (stored in `extended.small`)
5300+
// type: Inst.Ref,
5301+
// init: Inst.Ref, // `.none` for non-`comptime` fields
5302+
// }
5303+
const fields_start = astgen.scratch.items.len;
5304+
defer astgen.scratch.items.len = fields_start;
5305+
5306+
try astgen.scratch.ensureUnusedCapacity(gpa, container_decl.ast.members.len * 2);
5307+
5308+
for (container_decl.ast.members) |member_node| {
5309+
const field = tree.fullContainerField(member_node) orelse {
5310+
const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
5311+
.container_field_init,
5312+
.container_field_align,
5313+
.container_field,
5314+
=> break maybe_tuple,
5315+
else => {},
5316+
} else unreachable;
5317+
return astgen.failNodeNotes(
5318+
member_node,
5319+
"tuple declarations cannot contain declarations",
5320+
.{},
5321+
&.{try astgen.errNoteNode(tuple_member, "tuple field here", .{})},
5322+
);
5323+
};
5324+
5325+
if (!field.ast.tuple_like) {
5326+
return astgen.failTok(field.ast.main_token, "tuple field has a name", .{});
5327+
}
5328+
5329+
if (field.ast.align_expr != 0) {
5330+
return astgen.failTok(field.ast.main_token, "tuple field has alignment", .{});
5331+
}
5332+
5333+
if (field.ast.value_expr != 0 and field.comptime_token == null) {
5334+
return astgen.failTok(field.ast.main_token, "non-comptime tuple field has default initialization value", .{});
5335+
}
5336+
5337+
if (field.ast.value_expr == 0 and field.comptime_token != null) {
5338+
return astgen.failTok(field.comptime_token.?, "comptime field without default initialization value", .{});
5339+
}
5340+
5341+
const field_type_ref = try typeExpr(gz, scope, field.ast.type_expr);
5342+
astgen.scratch.appendAssumeCapacity(@intFromEnum(field_type_ref));
5343+
5344+
if (field.ast.value_expr != 0) {
5345+
const field_init_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_type_ref } }, field.ast.value_expr);
5346+
astgen.scratch.appendAssumeCapacity(@intFromEnum(field_init_ref));
5347+
} else {
5348+
astgen.scratch.appendAssumeCapacity(@intFromEnum(Zir.Inst.Ref.none));
5349+
}
5350+
}
5351+
5352+
const fields_len = std.math.cast(u16, container_decl.ast.members.len) orelse {
5353+
return astgen.failNode(node, "this compiler implementation only supports 65535 tuple fields", .{});
5354+
};
5355+
5356+
const extra_trail = astgen.scratch.items[fields_start..];
5357+
assert(extra_trail.len == fields_len * 2);
5358+
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.TupleDecl).@"struct".fields.len + extra_trail.len);
5359+
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.TupleDecl{
5360+
.src_node = gz.nodeIndexToRelative(node),
5361+
});
5362+
astgen.extra.appendSliceAssumeCapacity(extra_trail);
5363+
5364+
return gz.add(.{
5365+
.tag = .extended,
5366+
.data = .{ .extended = .{
5367+
.opcode = .tuple_decl,
5368+
.small = fields_len,
5369+
.operand = payload_index,
5370+
} },
5371+
});
5372+
}
5373+
53035374
fn unionDeclInner(
53045375
gz: *GenZir,
53055376
scope: *Scope,
@@ -11172,7 +11243,7 @@ fn rvalueInner(
1117211243
as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
1117311244
as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
1117411245
as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
11175-
as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type),
11246+
as_ty | @intFromEnum(Zir.Inst.Ref.empty_tuple_type),
1117611247
as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
1117711248
as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
1117811249
as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one),
@@ -13173,7 +13244,6 @@ const GenZir = struct {
1317313244
layout: std.builtin.Type.ContainerLayout,
1317413245
known_non_opv: bool,
1317513246
known_comptime_only: bool,
13176-
is_tuple: bool,
1317713247
any_comptime_fields: bool,
1317813248
any_default_inits: bool,
1317913249
any_aligned_fields: bool,
@@ -13217,7 +13287,6 @@ const GenZir = struct {
1321713287
.has_backing_int = args.has_backing_int,
1321813288
.known_non_opv = args.known_non_opv,
1321913289
.known_comptime_only = args.known_comptime_only,
13220-
.is_tuple = args.is_tuple,
1322113290
.name_strategy = gz.anon_name_strategy,
1322213291
.layout = args.layout,
1322313292
.any_comptime_fields = args.any_comptime_fields,

lib/std/zig/BuiltinFn.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const std = @import("std");
2-
31
pub const Tag = enum {
42
add_with_overflow,
53
addrspace_cast,
@@ -147,7 +145,7 @@ param_count: ?u8,
147145

148146
pub const list = list: {
149147
@setEvalBranchQuota(3000);
150-
break :list std.StaticStringMap(@This()).initComptime(.{
148+
break :list std.StaticStringMap(BuiltinFn).initComptime([_]struct { []const u8, BuiltinFn }{
151149
.{
152150
"@addWithOverflow",
153151
.{
@@ -1011,3 +1009,6 @@ pub const list = list: {
10111009
},
10121010
});
10131011
};
1012+
1013+
const std = @import("std");
1014+
const BuiltinFn = @This();

0 commit comments

Comments
 (0)