Skip to content

Commit b82a130

Browse files
Intern tuple types when possible
1 parent 1e72ef2 commit b82a130

6 files changed

Lines changed: 117 additions & 32 deletions

File tree

src/analyser/InternPool.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,7 +4080,7 @@ fn printInternal(ip: *InternPool, ty: Index, writer: *std.Io.Writer, options: Fo
40804080
.union_type => return panicOrElse(?Index, "TODO", null),
40814081
.tuple_type => |tuple_info| {
40824082
assert(tuple_info.types.len == tuple_info.values.len);
4083-
try writer.writeAll("tuple{");
4083+
try writer.writeAll("struct { ");
40844084

40854085
for (0..tuple_info.types.len) |i| {
40864086
const field_ty = tuple_info.types.at(@intCast(i), ip);
@@ -4096,7 +4096,7 @@ fn printInternal(ip: *InternPool, ty: Index, writer: *std.Io.Writer, options: Fo
40964096
try ip.print(field_val, writer, options);
40974097
}
40984098
}
4099-
try writer.writeByte('}');
4099+
try writer.writeAll(" }");
41004100
},
41014101
.vector_type => |vector_info| {
41024102
try writer.print("@Vector({d},{f})", .{

src/analyser/completions.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ pub fn dotCompletions(
170170
try completions.ensureUnusedCapacity(arena, tuple_info.types.len);
171171
for (tuple_types, 0..) |tuple_ty, i| {
172172
completions.appendAssumeCapacity(.{
173-
.label = try std.fmt.allocPrint(arena, "{d}", .{i}),
173+
.label = try std.fmt.allocPrint(arena, "@\"{d}\"", .{i}),
174174
.kind = .Field,
175-
.detail = try std.fmt.allocPrint(arena, "{d}: {f}", .{ i, tuple_ty.fmt(ip) }),
175+
.detail = try std.fmt.allocPrint(arena, "{f}", .{tuple_ty.fmt(ip)}),
176176
});
177177
}
178178
},

src/analysis.zig

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,19 @@ pub fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAcce
10801080
fn bracketAccessTypeFromIPIndex(analyser: *Analyser, ip_index: InternPool.Index) error{OutOfMemory}!Type {
10811081
std.debug.assert(analyser.ip.typeOf(ip_index) == .type_type);
10821082
return switch (analyser.ip.indexToKey(ip_index)) {
1083+
.tuple_type => |info| {
1084+
const types = try info.types.dupe(analyser.gpa, analyser.ip);
1085+
defer analyser.gpa.free(types);
1086+
1087+
const elem_ty_slice = try analyser.arena.alloc(Type, types.len);
1088+
for (elem_ty_slice, types) |*elem_ty, ty|
1089+
elem_ty.* = try analyser.bracketAccessTypeFromIPIndex(ty);
1090+
1091+
return .{
1092+
.data = .{ .tuple = elem_ty_slice },
1093+
.is_type_val = true,
1094+
};
1095+
},
10831096
.vector_type => |info| .{
10841097
.data = .{
10851098
.array = .{
@@ -1307,7 +1320,20 @@ pub fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) erro
13071320
}
13081321
},
13091322

1310-
// TODO: tuple_type
1323+
.tuple_type => |tuple_info| {
1324+
if (std.mem.eql(u8, "len", name)) {
1325+
const index = try analyser.ip.get(.{
1326+
.int_u64_value = .{
1327+
.ty = .usize_type,
1328+
.int = tuple_info.types.len,
1329+
},
1330+
});
1331+
return Type.fromIP(analyser, .usize_type, index);
1332+
}
1333+
if (!allDigits(name)) return null;
1334+
const index = std.fmt.parseInt(u16, name, 10) catch return null;
1335+
return try analyser.resolveBracketAccessType(ty, .{ .single = index });
1336+
},
13111337

13121338
.optional_type => |optional_info| {
13131339
if (std.mem.eql(u8, "?", name)) {
@@ -1962,10 +1988,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
19621988
if (param.type.data == .anytype_parameter) return .unknown_type;
19631989
elem_ty.* = param.type;
19641990
}
1965-
return .{
1966-
.data = .{ .tuple = elem_ty_slice },
1967-
.is_type_val = true,
1968-
};
1991+
return try Type.createTupleType(analyser, elem_ty_slice);
19691992
}
19701993

19711994
if (std.mem.eql(u8, func_name, "Tag")) {
@@ -2117,10 +2140,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
21172140
elem_ty.* = try analyser.resolveTypeOfNodeInternal(.of(element, handle)) orelse return null;
21182141
elem_ty.* = try elem_ty.typeOf(analyser);
21192142
}
2120-
return .{
2121-
.data = .{ .tuple = elem_ty_slice },
2122-
.is_type_val = false,
2123-
};
2143+
const tuple_ty = try Type.createTupleType(analyser, elem_ty_slice);
2144+
return try tuple_ty.instanceUnchecked(analyser);
21242145
},
21252146
.error_union => {
21262147
const lhs, const rhs = tree.nodeData(node).node_and_node;
@@ -2201,10 +2222,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
22012222
}
22022223

22032224
if (has_unresolved_fields) return null;
2204-
return .{
2205-
.data = .{ .tuple = elem_ty_slice },
2206-
.is_type_val = true,
2207-
};
2225+
return try Type.createTupleType(analyser, elem_ty_slice);
22082226
}
22092227

22102228
return try analyser.innermostContainer(handle, tree.tokenStart(tree.firstToken(node)));
@@ -3268,6 +3286,26 @@ pub const Type = struct {
32683286
};
32693287
}
32703288

3289+
fn createTuple(analyser: *Analyser, elem_tys: []Type) !Data {
3290+
const tys = try analyser.gpa.alloc(InternPool.Index, elem_tys.len);
3291+
defer analyser.gpa.free(tys);
3292+
3293+
const vals = try analyser.gpa.alloc(InternPool.Index, elem_tys.len);
3294+
defer analyser.gpa.free(vals);
3295+
3296+
for (tys, vals, elem_tys) |*ty, *val, elem_ty| {
3297+
ty.* = elem_ty.ipIndex() orelse break;
3298+
val.* = .none;
3299+
} else {
3300+
const types = try analyser.ip.getIndexSlice(tys);
3301+
const values = try analyser.ip.getIndexSlice(vals);
3302+
const index = try analyser.ip.get(.{ .tuple_type = .{ .types = types, .values = values } });
3303+
return .{ .ip_index = .{ .type = .type_type, .index = index } };
3304+
}
3305+
3306+
return .{ .tuple = elem_tys };
3307+
}
3308+
32713309
fn createOptional(analyser: *Analyser, child_ty: Type) !Data {
32723310
std.debug.assert(child_ty.is_type_val);
32733311
if (child_ty.ipIndex()) |payload_type| {
@@ -3581,14 +3619,15 @@ pub const Type = struct {
35813619
const elem_ty = try analyser.resolveGenericTypeInternal(info.elem_ty.*, bound_params, visiting);
35823620
return try createArray(analyser, elem_count, sentinel, elem_ty);
35833621
},
3584-
.tuple => |info| return .{
3585-
.tuple = blk: {
3622+
.tuple => |info| {
3623+
const elem_tys = blk: {
35863624
const types = try analyser.arena.alloc(Type, info.len);
35873625
for (info, types) |old, *new| {
35883626
new.* = try analyser.resolveGenericTypeInternal(old, bound_params, visiting);
35893627
}
35903628
break :blk types;
3591-
},
3629+
};
3630+
return try createTuple(analyser, elem_tys);
35923631
},
35933632
.optional => |info| {
35943633
const child_ty = try analyser.resolveGenericTypeInternal(info.*, bound_params, visiting);
@@ -3691,6 +3730,13 @@ pub const Type = struct {
36913730
};
36923731
}
36933732

3733+
fn createTupleType(analyser: *Analyser, elem_tys: []Type) !Type {
3734+
return .{
3735+
.data = try Data.createTuple(analyser, elem_tys),
3736+
.is_type_val = true,
3737+
};
3738+
}
3739+
36943740
fn createOptionalType(analyser: *Analyser, child_ty: Type) !Type {
36953741
return .{
36963742
.data = try Data.createOptional(analyser, child_ty),
@@ -4164,8 +4210,16 @@ pub const Type = struct {
41644210
return self.getContainerKind() == container_kind_tok;
41654211
}
41664212

4167-
pub fn isStructType(self: Type) bool {
4168-
return self.data == .tuple or self.isContainerKind(.keyword_struct) or self.isRoot();
4213+
pub fn isStructType(self: Type, analyser: *Analyser) bool {
4214+
if (!self.is_type_val) return false;
4215+
return switch (self.data) {
4216+
.tuple => true,
4217+
.ip_index => |payload| {
4218+
const index = payload.index orelse return false;
4219+
return analyser.ip.zigTypeTag(index) == .@"struct";
4220+
},
4221+
else => self.isContainerKind(.keyword_struct) or self.isRoot(),
4222+
};
41694223
}
41704224

41714225
pub fn isNamespace(self: Type) bool {
@@ -5052,7 +5106,7 @@ pub fn getFieldAccessType(
50525106
}
50535107
} else return null;
50545108
if (current_type) |ct| {
5055-
if (ct.isStructType() or ct.isUnionType()) {
5109+
if (ct.isStructType(analyser) or ct.isUnionType()) {
50565110
// struct initialization
50575111
current_type = try ct.instanceTypeVal(analyser);
50585112
}
@@ -5832,9 +5886,16 @@ pub const DeclWithHandle = struct {
58325886

58335887
const init_node = tree.nodeData(pay.node).extra_and_node[1];
58345888
const node = try analyser.resolveTypeOfNode(.of(init_node, self.handle)) orelse return null;
5889+
if (node.is_type_val) return null;
58355890
break :blk switch (node.data) {
58365891
.array => |array_info| try array_info.elem_ty.instanceTypeVal(analyser),
58375892
.tuple => try analyser.resolveBracketAccessType(node, .{ .single = pay.index }),
5893+
.ip_index => |payload| switch (analyser.ip.indexToKey(payload.type)) {
5894+
.vector_type => |vector_info| Type.fromIP(analyser, vector_info.child, null),
5895+
.array_type => |array_info| Type.fromIP(analyser, array_info.child, null),
5896+
.tuple_type => try analyser.resolveBracketAccessType(node, .{ .single = pay.index }),
5897+
else => null,
5898+
},
58385899
else => null,
58395900
};
58405901
},

src/features/completions.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle) Ana
313313
} else {
314314
kind = .EnumMember;
315315
}
316-
} else if (ty.is_type_val and ty.isStructType() or ty.isUnionType()) {
316+
} else if (ty.is_type_val and ty.isStructType(builder.analyser) or ty.isUnionType()) {
317317
kind = .Struct;
318318
} else if (decl_handle.decl == .function_parameter and ty.isMetaType()) {
319319
kind = .TypeParameter;

src/features/semantic_tokens.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ fn colorIdentifierBasedOnType(
247247
if (type_node.is_type_val) {
248248
const token_type: TokenType = if (type_node.isNamespace())
249249
.namespace
250-
else if (type_node.isStructType())
250+
else if (type_node.isStructType(builder.analyser))
251251
.@"struct"
252252
else if (type_node.isEnumType())
253253
.@"enum"

tests/analysis/assign_destructure.zig

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@ fn assign_destructure_0() void {
33
// ^^^ (u8)()
44
// ^^^ (u16)()
55
// ^^^ (u24)()
6-
_ = foo;
7-
_ = bar;
8-
_ = baz;
6+
_ = .{ foo, bar, baz };
97
}
108

119
fn assign_destructure_1() void {
1210
const foo, const bar: u32 = .{ 1, 2 };
1311
// ^^^ (comptime_int)()
1412
// ^^^ (u32)()
15-
_ = foo;
16-
_ = bar;
13+
_ = .{ foo, bar };
1714
}
1815

1916
fn assign_destructure_2() void {
@@ -23,8 +20,7 @@ fn assign_destructure_2() void {
2320
const foo, const bar = try S.thing();
2421
// ^^^ (usize)()
2522
// ^^^ (isize)()
26-
_ = foo;
27-
_ = bar;
23+
_ = .{ foo, bar };
2824
}
2925

3026
fn assign_destructure_3() void {
@@ -36,3 +32,31 @@ fn assign_destructure_3() void {
3632
// ^^^ (u64)()
3733
// zig fmt: on
3834
}
35+
36+
fn assign_destructure_int_vector() void {
37+
const vector: @Vector(3, i32) = .{ 1, 2, 3 };
38+
const foo, const bar, const baz = vector;
39+
// ^^^ (i32)()
40+
// ^^^ (i32)()
41+
// ^^^ (i32)()
42+
_ = .{ foo, bar, baz };
43+
}
44+
45+
fn assign_destructure_int_array() void {
46+
const array = [3]i32{ 1, 2, 3 };
47+
const foo, const bar, const baz = array;
48+
// ^^^ (i32)()
49+
// ^^^ (i32)()
50+
// ^^^ (i32)()
51+
_ = .{ foo, bar, baz };
52+
}
53+
54+
fn assign_destructure_struct_array() void {
55+
const S = struct { x: i32 = 0 };
56+
const array = [3]S{ .{}, .{}, .{} };
57+
const foo, const bar, const baz = array;
58+
// ^^^ (S)()
59+
// ^^^ (S)()
60+
// ^^^ (S)()
61+
_ = .{ foo, bar, baz };
62+
}

0 commit comments

Comments
 (0)