Skip to content

Commit b39329d

Browse files
Resolve peer types for array and tuple
1 parent 432db35 commit b39329d

File tree

2 files changed

+136
-5
lines changed

2 files changed

+136
-5
lines changed

src/analysis.zig

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6298,6 +6298,47 @@ pub const ReferencedType = struct {
62986298
};
62996299
};
63006300

6301+
const ArrayLike = struct {
6302+
len: u64,
6303+
/// `noreturn` indicates that this type is `struct{}` so can coerce to anything
6304+
elem_ty: Type,
6305+
};
6306+
fn typeIsArrayLike(analyser: *Analyser, ty: Type) ?ArrayLike {
6307+
std.debug.assert(ty.is_type_val);
6308+
return switch (ty.data) {
6309+
.array => |info| .{
6310+
.len = info.elem_count orelse return null,
6311+
.elem_ty = info.elem_ty.*,
6312+
},
6313+
.ip_index => |payload| switch (payload.index orelse .unknown_type) {
6314+
.empty_struct_type => .{
6315+
.len = 0,
6316+
.elem_ty = Type.fromIP(analyser, .type_type, .noreturn_type),
6317+
},
6318+
else => |ip_index| switch (analyser.ip.indexToKey(ip_index)) {
6319+
.array_type => |info| .{
6320+
.len = info.len,
6321+
.elem_ty = Type.fromIP(analyser, .type_type, info.child),
6322+
},
6323+
else => null,
6324+
},
6325+
},
6326+
.tuple => |field_tys| {
6327+
const elem_ty = field_tys[0];
6328+
for (field_tys[1..]) |field_ty| {
6329+
if (!field_ty.eql(elem_ty)) {
6330+
return null;
6331+
}
6332+
}
6333+
return .{
6334+
.len = field_tys.len,
6335+
.elem_ty = elem_ty,
6336+
};
6337+
},
6338+
else => null,
6339+
};
6340+
}
6341+
63016342
// Based on src/Sema.zig from the zig codebase
63026343
// https://github.com/ziglang/zig/blob/master/src/Sema.zig
63036344
fn resolvePeerTypesInner(analyser: *Analyser, peer_tys: []?Type) !?Type {
@@ -6544,7 +6585,78 @@ fn resolvePeerTypesInner(analyser: *Analyser, peer_tys: []?Type) !?Type {
65446585
};
65456586
},
65466587

6547-
.array => return null, // TODO
6588+
.array => {
6589+
var len: ?u64 = null;
6590+
var sentinel: InternPool.Index = .none;
6591+
var elem_ty: ?Type = null;
6592+
6593+
for (peer_tys) |*ty_ptr| {
6594+
const ty = ty_ptr.* orelse continue;
6595+
6596+
if (ty.data != .array) {
6597+
const arr_like = analyser.typeIsArrayLike(ty) orelse {
6598+
return null;
6599+
};
6600+
6601+
if (len) |cur_len| {
6602+
if (arr_like.len != cur_len) return null;
6603+
} else {
6604+
len = arr_like.len;
6605+
}
6606+
6607+
sentinel = .none;
6608+
6609+
continue;
6610+
}
6611+
6612+
const arr_info = ty.data.array;
6613+
const arr_len = arr_info.elem_count orelse {
6614+
return null;
6615+
};
6616+
6617+
const cur_elem_ty = elem_ty orelse {
6618+
if (len) |cur_len| {
6619+
if (arr_len != cur_len) return null;
6620+
} else {
6621+
len = arr_len;
6622+
sentinel = arr_info.sentinel;
6623+
}
6624+
elem_ty = arr_info.elem_ty.*;
6625+
continue;
6626+
};
6627+
6628+
if (arr_info.elem_count != len) {
6629+
return null;
6630+
}
6631+
6632+
const peer_elem_ty = arr_info.elem_ty.*;
6633+
if (!peer_elem_ty.eql(cur_elem_ty)) {
6634+
// TODO: check if coercible
6635+
return null;
6636+
}
6637+
6638+
if (sentinel != .none) {
6639+
if (arr_info.sentinel != .none) {
6640+
if (arr_info.sentinel != sentinel) sentinel = .none;
6641+
} else {
6642+
sentinel = .none;
6643+
}
6644+
}
6645+
}
6646+
6647+
std.debug.assert(elem_ty != null);
6648+
6649+
return .{
6650+
.data = .{
6651+
.array = .{
6652+
.elem_count = len,
6653+
.sentinel = sentinel,
6654+
.elem_ty = try analyser.allocType(elem_ty.?),
6655+
},
6656+
},
6657+
.is_type_val = true,
6658+
};
6659+
},
65486660

65496661
.vector => return null, // TODO
65506662

tests/analysis/peer_type_resolution.zig

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ const s: S = .{
77
.float = 1.2,
88
};
99

10-
pub fn main() !void {
11-
var runtime_bool: bool = true;
10+
var runtime_bool: bool = true;
11+
var runtime_int: i32 = 0;
1212

13+
pub fn main() !void {
1314
const widened_int_0 = if (runtime_bool) @as(i8, 0) else @as(i16, 0);
1415
_ = widened_int_0;
1516
// ^^^^^^^^^^^^^ (i16)()
@@ -218,10 +219,16 @@ pub fn main() !void {
218219
_ = noreturn_1;
219220
// ^^^^^^^^^^ (S)()
220221

222+
const coercible_array_0 = if (runtime_bool) [2]*i32{ &runtime_int, &runtime_int } else [2]?*i32{ &runtime_int, null };
223+
_ = coercible_array_0;
224+
// ^^^^^^^^^^^^^^^^^ (either type)() TODO this should be `[2]?*i32`
225+
226+
const coercible_array_1 = if (runtime_bool) [2]?*i32{ &runtime_int, null } else [2]*i32{ &runtime_int, &runtime_int };
227+
_ = coercible_array_1;
228+
// ^^^^^^^^^^^^^^^^^ (either type)() TODO this should be `[2]?*i32`
229+
221230
// Use @compileLog to verify the expected type with the compiler:
222231
// @compileLog(error_union_0);
223-
224-
_ = &runtime_bool;
225232
}
226233

227234
const comptime_bool: bool = true;
@@ -244,6 +251,18 @@ const f32_and_u32 = if (comptime_bool) @as(f32, 0) else @as(i32, 0);
244251
const u32_and_f32 = if (comptime_bool) @as(u32, 0) else @as(f32, 0);
245252
// ^^^^^^^^^^^ (either type)()
246253

254+
const array_2_and_array_3 = if (comptime_bool) [2]S{ s, s } else [3]S{ s, s, s };
255+
// ^^^^^^^^^^^^^^^^^^^ (either type)()
256+
257+
const tuple_2_and_array_3 = if (comptime_bool) @as(struct { S, S }, .{ s, s }) else [3]S{ s, s, s };
258+
// ^^^^^^^^^^^^^^^^^^^ (either type)()
259+
260+
const array_3_and_tuple_2 = if (comptime_bool) [3]S{ s, s, s } else @as(struct { S, S }, .{ s, s });
261+
// ^^^^^^^^^^^^^^^^^^^ (either type)()
262+
263+
const array_3_and_tuple_3 = if (comptime_bool) [3]S{ s, s, s } else @as(struct { S, S, S }, .{ s, s, s });
264+
// ^^^^^^^^^^^^^^^^^^^ ([3]S)()
265+
247266
const compile_error_0 = if (comptime_bool) s else @compileError("Foo");
248267
// ^^^^^^^^^^^^^^^ (S)()
249268

0 commit comments

Comments
 (0)