From 8a78e9fa01458d67ed18dd858314abd7cdc0d3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Wed, 12 Feb 2025 21:58:06 -0800 Subject: [PATCH 1/6] Add helper method --- src/analysis.zig | 47 +++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index deb608e5d..6ac7ec28d 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -67,6 +67,12 @@ pub fn deinit(self: *Analyser) void { self.arena.deinit(); } +fn allocType(analyser: *Analyser, ty: Type) error{OutOfMemory}!*Type { + const ptr = try analyser.arena.allocator().create(Type); + ptr.* = ty; + return ptr; +} + pub fn getDocCommentsBeforeToken(allocator: std.mem.Allocator, tree: Ast, base: Ast.TokenIndex) error{OutOfMemory}!?[]const u8 { const tokens = tree.tokens.items(.tag); const doc_comment_index = getDocCommentTokenIndex(tokens, base) orelse return null; @@ -842,12 +848,10 @@ pub fn resolveReturnType(analyser: *Analyser, func_type_param: Type) error{OutOf if (!child_type.is_type_val) return null; if (ast.hasInferredError(tree, fn_proto)) { - const child_type_ptr = try analyser.arena.allocator().create(Type); - child_type_ptr.* = child_type; return .{ .data = .{ .error_union = .{ .error_set = null, - .payload = child_type_ptr, + .payload = try analyser.allocType(child_type), } }, .is_type_val = false, }; @@ -894,15 +898,13 @@ pub fn resolveOptionalChildType(analyser: *Analyser, optional_type: Type) error{ } pub fn resolveAddressOf(analyser: *Analyser, ty: Type) error{OutOfMemory}!?Type { - const base_type_ptr = try analyser.arena.allocator().create(Type); - base_type_ptr.* = ty.typeOf(analyser); return .{ .data = .{ .pointer = .{ .size = .one, .sentinel = .none, .is_const = false, - .elem_ty = base_type_ptr, + .elem_ty = try analyser.allocType(ty.typeOf(analyser)), }, }, .is_type_val = false, @@ -949,9 +951,7 @@ fn resolveTaggedUnionFieldType(analyser: *Analyser, ty: Type, symbol: []const u8 return try child.resolveType(analyser); if (container_decl.ast.enum_token != null) { - const union_type_ptr = try analyser.arena.allocator().create(Type); - union_type_ptr.* = ty; - return .{ .data = .{ .union_tag = union_type_ptr }, .is_type_val = false }; + return .{ .data = .{ .union_tag = try analyser.allocType(ty) }, .is_type_val = false }; } if (container_decl.ast.arg != 0) { @@ -1608,10 +1608,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const child_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].lhs, .handle = handle }) orelse return null; if (!child_ty.is_type_val) return null; - const child_ty_ptr = try analyser.arena.allocator().create(Type); - child_ty_ptr.* = child_ty; - - return .{ .data = .{ .optional = child_ty_ptr }, .is_type_val = true }; + return .{ .data = .{ .optional = try analyser.allocType(child_ty) }, .is_type_val = true }; }, .ptr_type_aligned, .ptr_type_sentinel, @@ -1625,15 +1622,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = ptr_info.ast.child_type, .handle = handle }) orelse return null; if (!elem_ty.is_type_val) return null; - const elem_ty_ptr = try analyser.arena.allocator().create(Type); - elem_ty_ptr.* = elem_ty; return .{ .data = .{ .pointer = .{ .size = ptr_info.size, .sentinel = sentinel, .is_const = ptr_info.const_token != null, - .elem_ty = elem_ty_ptr, + .elem_ty = try analyser.allocType(elem_ty), }, }, .is_type_val = true, @@ -1650,14 +1645,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = array_info.ast.elem_type, .handle = handle }) orelse return null; if (!elem_ty.is_type_val) return null; - const elem_ty_ptr = try analyser.arena.allocator().create(Type); - elem_ty_ptr.* = elem_ty; - return .{ .data = .{ .array = .{ .elem_count = elem_count, .sentinel = sentinel, - .elem_ty = elem_ty_ptr, + .elem_ty = try analyser.allocType(elem_ty), } }, .is_type_val = true, }; @@ -1696,16 +1688,10 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const payload = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].rhs, .handle = handle }) orelse return null; if (!payload.is_type_val) return null; - const error_set_ptr = try analyser.arena.allocator().create(Type); - error_set_ptr.* = error_set; - - const payload_ptr = try analyser.arena.allocator().create(Type); - payload_ptr.* = payload; - return .{ .data = .{ .error_union = .{ - .error_set = error_set_ptr, - .payload = payload_ptr, + .error_set = try analyser.allocType(error_set), + .payload = try analyser.allocType(payload), } }, .is_type_val = true, }; @@ -4102,12 +4088,9 @@ pub const DeclWithHandle = struct { if (!self.isCaptureByRef()) return resolved_ty; - const resolved_ty_ptr = try analyser.arena.allocator().create(Type); - resolved_ty_ptr.* = resolved_ty.typeOf(analyser); - return .{ .data = .{ .pointer = .{ - .elem_ty = resolved_ty_ptr, + .elem_ty = try analyser.allocType(resolved_ty.typeOf(analyser)), .sentinel = .none, .is_const = false, .size = .one, From 65f858a4766a6c2812fc54e29f9119a5dbf0f031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 15 Feb 2025 00:57:31 -0800 Subject: [PATCH 2/6] Fix type resolution of slice with integer literals --- src/analysis.zig | 225 ++++++++++++++++++++++++++--------- tests/analysis/array.zig | 12 +- tests/analysis/pointer.zig | 9 +- tests/lsp_features/hover.zig | 4 +- 4 files changed, 179 insertions(+), 71 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 6ac7ec28d..2138d04b6 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -995,20 +995,20 @@ pub fn resolveDerefType(analyser: *Analyser, pointer: Type) error{OutOfMemory}!? } } -const BracketAccessKind = enum { +const BracketAccess = union(enum) { /// `lhs[index]` - Single, + single: ?u64, /// `lhs[start..]` - Open, + open: ?u64, /// `lhs[start..end]` - Range, + range: ?struct { u64, u64 }, }; /// Resolves slicing and array access -/// - `lhs[index]` (Single) -/// - `lhs[start..]` (Open) -/// - `lhs[start..end]` (Range) -fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKind) error{OutOfMemory}!?Type { +/// - `lhs[index]` (single) +/// - `lhs[start..]` (open) +/// - `lhs[start..end]` (range) +fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccess) error{OutOfMemory}!?Type { if (lhs.is_type_val) return null; switch (lhs.data) { @@ -1017,8 +1017,35 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi else => return null, }, .array => |info| switch (rhs) { - .Single => return info.elem_ty.instanceTypeVal(analyser), - .Open => { + .single => return info.elem_ty.instanceTypeVal(analyser), + .open => |start_maybe| { + if (start_maybe) |start| { + const elem_count = blk: { + const elem_count = info.elem_count orelse break :blk null; + if (start > elem_count) break :blk null; + break :blk elem_count - start; + }; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = false, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = info.sentinel, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + } return .{ .data = .{ .pointer = .{ @@ -1031,7 +1058,35 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi .is_type_val = false, }; }, - .Range => { + .range => |range_maybe| { + if (range_maybe) |range| { + const start, const end = range; + const elem_count = blk: { + const elem_count = info.elem_count orelse break :blk null; + if (start > end or start > elem_count or end > elem_count) break :blk null; + break :blk end - start; + }; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = false, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = .none, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + } return .{ .data = .{ .pointer = .{ @@ -1048,42 +1103,39 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi .pointer => |info| return switch (info.size) { .one => switch (info.elem_ty.data) { .array => |array_info| { - switch (rhs) { - .Single => return array_info.elem_ty.instanceTypeVal(analyser), - .Open => { - return .{ - .data = .{ - .pointer = .{ - .size = .slice, - .sentinel = array_info.sentinel, - .is_const = false, - .elem_ty = array_info.elem_ty, - }, - }, - .is_type_val = false, - }; - }, - .Range => { - return .{ - .data = .{ - .pointer = .{ - .size = .slice, - .sentinel = .none, - .is_const = false, - .elem_ty = array_info.elem_ty, - }, - }, - .is_type_val = false, - }; - }, - } + const inner_ty: Type = .{ .data = .{ .array = array_info }, .is_type_val = false }; + return analyser.resolveBracketAccessType(inner_ty, rhs); }, else => return null, }, .many => switch (rhs) { - .Single => info.elem_ty.instanceTypeVal(analyser), - .Open => lhs, - .Range => { + .single => info.elem_ty.instanceTypeVal(analyser), + .open => lhs, + .range => |range_maybe| { + if (range_maybe) |range| { + const start, const end = range; + const elem_count = if (start > end) null else end - start; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = info.is_const, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = .none, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + } return .{ .data = .{ .pointer = .{ @@ -1098,13 +1150,60 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi }, }, .slice => switch (rhs) { - .Single => info.elem_ty.instanceTypeVal(analyser), - .Open, .Range => lhs, + .single => info.elem_ty.instanceTypeVal(analyser), + .open => lhs, + .range => |range_maybe| { + const start, const end = range_maybe orelse return lhs; + const elem_count = if (start > end) null else end - start; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = info.is_const, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = .none, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + }, }, .c => switch (rhs) { - .Single => info.elem_ty.instanceTypeVal(analyser), - .Open => lhs, - .Range => .{ + .single => info.elem_ty.instanceTypeVal(analyser), + .open => lhs, + .range => |range_maybe| if (range_maybe) |range| { + const start, const end = range; + const elem_count = if (start > end) null else end - start; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = info.is_const, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = .none, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + } else .{ .data = .{ .pointer = .{ .size = .slice, @@ -1576,12 +1675,24 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .slice_open, => { const slice_info = tree.fullSlice(node).?; - const kind: BracketAccessKind = if (slice_info.ast.end == 0) .Open else .Range; + const kind: BracketAccess = if (slice_info.ast.end == 0) + .{ .open = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.start, .handle = handle }) } + else + .{ .range = blk: { + const start = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.start, .handle = handle }) orelse + break :blk null; + const end = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.end, .handle = handle }) orelse + break :blk null; + break :blk .{ start, end }; + } }; return try analyser.resolveBracketAccessType(base_type, kind); }, .deref => try analyser.resolveDerefType(base_type), .unwrap_optional => try analyser.resolveOptionalUnwrap(base_type), - .array_access => try analyser.resolveBracketAccessType(base_type, .Single), + .array_access => try analyser.resolveBracketAccessType( + base_type, + .{ .single = try analyser.resolveIntegerLiteral(u64, .{ .node = datas[node].rhs, .handle = handle }) }, + ), .@"orelse" => { const type_right = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].rhs, .handle = handle }) orelse return try analyser.resolveOptionalUnwrap(base_type); return try analyser.resolveOrelseType(base_type, type_right); @@ -3305,7 +3416,7 @@ pub fn getFieldAccessType( }, .l_bracket => { var bracket_count: usize = 1; - var kind: BracketAccessKind = .Single; + var kind: BracketAccess = .{ .single = null }; while (true) { const token = tokenizer.next(); @@ -3320,12 +3431,12 @@ pub fn getFieldAccessType( }, .ellipsis2 => { if (bracket_count == 1) { - kind = .Open; + kind = .{ .open = null }; } }, else => { - if (bracket_count == 1 and kind == .Open) { - kind = .Range; + if (bracket_count == 1 and kind == .open) { + kind = .{ .range = null }; } }, } @@ -4037,7 +4148,7 @@ pub const DeclWithHandle = struct { .node = pay.condition, .handle = self.handle, })) orelse return null, - .Single, + .{ .single = null }, ), .assign_destructure => |pay| blk: { const type_node = pay.getFullVarDecl(tree).ast.type_node; @@ -4626,7 +4737,7 @@ pub fn resolveExpressionTypeFromAncestors( ancestors[0], ancestors[1..], )) |array_type| { - return (try analyser.resolveBracketAccessType(array_type, .Single)) orelse + return (try analyser.resolveBracketAccessType(array_type, .{ .single = null })) orelse (try analyser.resolveTupleFieldType(array_type, element_index)); } @@ -4636,7 +4747,7 @@ pub fn resolveExpressionTypeFromAncestors( ancestors[1], ancestors[2..], )) |slice_type| { - return try analyser.resolveBracketAccessType(slice_type, .Single); + return try analyser.resolveBracketAccessType(slice_type, .{ .single = null }); } } }, diff --git a/tests/analysis/array.zig b/tests/analysis/array.zig index 45e2a3a91..c567e553f 100644 --- a/tests/analysis/array.zig +++ b/tests/analysis/array.zig @@ -29,22 +29,22 @@ const array_indexing = some_array[0]; // TODO this should be `*const [2]u8` const array_slice_open_1 = some_array[1..]; -// ^^^^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^^^^ (*[2]u8)() // TODO this should be `*const [0]u8` const array_slice_open_3 = some_array[3..]; -// ^^^^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^^^^ (*[0]u8)() // TODO this should be `*const [?]u8` const array_slice_open_4 = some_array[4..]; -// ^^^^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^^^^ (*[?]u8)() const array_slice_open_runtime = some_array[runtime_index..]; // ^^^^^^^^^^^^^^^^^^^^^^^^ ([]u8)() // TODO this should be `*const [2]u8` const array_slice_0_2 = some_array[0..2]; -// ^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^ (*[2]u8)() // TODO this should be `*const [2 :0]u8` const array_slice_0_2_sentinel = some_array[0..2 :0]; @@ -52,11 +52,11 @@ const array_slice_0_2_sentinel = some_array[0..2 :0]; // TODO this should be `*const [?]u8` const array_slice_0_5 = some_array[0..5]; -// ^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^ (*[?]u8)() // TODO this should be `*const [?]u8` const array_slice_3_2 = some_array[3..2]; -// ^^^^^^^^^^^^^^^ ([]u8)() +// ^^^^^^^^^^^^^^^ (*[?]u8)() const array_slice_0_runtime = some_array[0..runtime_index]; // ^^^^^^^^^^^^^^^^^^^^^ ([]u8)() diff --git a/tests/analysis/pointer.zig b/tests/analysis/pointer.zig index ee87e3951..9dd16a698 100644 --- a/tests/analysis/pointer.zig +++ b/tests/analysis/pointer.zig @@ -45,9 +45,8 @@ const many_u32_deref = many_u32.*; const many_u32_indexing = many_u32[0]; // ^^^^^^^^^^^^^^^^^ (u32)() -// TODO this should be `*const [2]u32` const many_u32_slice_len_comptime = many_u32[0..2]; -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ (*const [2]u32)() const many_u32_slice_len_runtime = many_u32[0..runtime_index]; // ^^^^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() @@ -74,9 +73,8 @@ const slice_u32_deref = slice_u32.*; const slice_u32_indexing = slice_u32[0]; // ^^^^^^^^^^^^^^^^^^ (u32)() -// TODO this should be `*const [2]u32` const slice_u32_slice_len_comptime = slice_u32[0..2]; -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (*const [2]u32)() const slice_u32_slice_len_runtime = slice_u32[0..runtime_index]; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() @@ -104,9 +102,8 @@ const c_u32_deref = c_u32.*; const c_u32_indexing = c_u32[0]; // ^^^^^^^^^^^^^^ (u32)() -// TODO this should be `*const [2]u32` const c_u32_slice_len_comptime = c_u32[0..2]; -// ^^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() +// ^^^^^^^^^^^^^^^^^^^^^^^^ (*const [2]u32)() const c_u32_slice_len_runtime = c_u32[0..runtime_index]; // ^^^^^^^^^^^^^^^^^^^^^^^ ([]const u32)() diff --git a/tests/lsp_features/hover.zig b/tests/lsp_features/hover.zig index ab0454d4f..17131f215 100644 --- a/tests/lsp_features/hover.zig +++ b/tests/lsp_features/hover.zig @@ -706,7 +706,7 @@ test "sentinel values" { \\const range = array[0..2] \\``` \\```zig - \\([]u8) + \\(*[?]u8) \\``` ); try testHover( @@ -717,7 +717,7 @@ test "sentinel values" { \\const open = array[1..] \\``` \\```zig - \\([:0]u8) + \\(*[?:0]u8) \\``` ); // try testHover( From 86576a0b8be81cee41abdc5762833a0213003170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 15 Feb 2025 01:15:03 -0800 Subject: [PATCH 3/6] Resolve type of slice on single-item pointer --- src/analysis.zig | 29 ++++++++++++++++++++++++++++- tests/analysis/pointer.zig | 6 +++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 2138d04b6..30a12b3cc 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1106,7 +1106,34 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccess) const inner_ty: Type = .{ .data = .{ .array = array_info }, .is_type_val = false }; return analyser.resolveBracketAccessType(inner_ty, rhs); }, - else => return null, + else => switch (rhs) { + .single, .open => return null, + .range => |range_maybe| { + const start, const end = range_maybe orelse return null; + if (start > end or start > 1 or end > 1) return null; + const elem_count = end - start; + return .{ + .data = .{ + .pointer = .{ + .size = .one, + .sentinel = .none, + .is_const = info.is_const, + .elem_ty = try analyser.allocType(.{ + .data = .{ + .array = .{ + .elem_count = elem_count, + .sentinel = .none, + .elem_ty = info.elem_ty, + }, + }, + .is_type_val = true, + }), + }, + }, + .is_type_val = false, + }; + }, + }, }, .many => switch (rhs) { .single => info.elem_ty.instanceTypeVal(analyser), diff --git a/tests/analysis/pointer.zig b/tests/analysis/pointer.zig index 9dd16a698..09156b849 100644 --- a/tests/analysis/pointer.zig +++ b/tests/analysis/pointer.zig @@ -15,13 +15,13 @@ const one_u32_slice_len_0_5 = one_u32[0..5]; // ^^^^^^^^^^^^^^^^^^^^^ (unknown)() const one_u32_slice_len_0_0 = one_u32[0..0]; -// TODO ^^^^^^^^^^^^^^^^^^^^^ (*const [0]u32)() +// ^^^^^^^^^^^^^^^^^^^^^ (*const [0]u32)() const one_u32_slice_len_0_1 = one_u32[0..1]; -// TODO ^^^^^^^^^^^^^^^^^^^^^ (*const [1]u32)() +// ^^^^^^^^^^^^^^^^^^^^^ (*const [1]u32)() const one_u32_slice_len_1_1 = one_u32[1..1]; -// TODO ^^^^^^^^^^^^^^^^^^^^^ (*const [0]u32)() +// ^^^^^^^^^^^^^^^^^^^^^ (*const [0]u32)() const one_u32_slice_open = one_u32[1..]; // ^^^^^^^^^^^^^^^^^^ (unknown)() From a49641f2afbb866f4072d02da61503466c213c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 15 Feb 2025 01:11:43 -0800 Subject: [PATCH 4/6] Resolve type of tuple field accessed with brackets --- src/analysis.zig | 8 ++++++++ tests/analysis/tuple.zig | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/analysis/tuple.zig diff --git a/src/analysis.zig b/src/analysis.zig index 30a12b3cc..7c93e0933 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1016,6 +1016,14 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccess) .for_range => return Type.fromIP(analyser, .usize_type, null), else => return null, }, + .tuple => |fields| switch (rhs) { + .single => |index_maybe| { + const index = index_maybe orelse return null; + if (index >= fields.len) return null; + return fields[index]; + }, + .open, .range => return null, + }, .array => |info| switch (rhs) { .single => return info.elem_ty.instanceTypeVal(analyser), .open => |start_maybe| { diff --git a/tests/analysis/tuple.zig b/tests/analysis/tuple.zig new file mode 100644 index 000000000..3ea73d6d7 --- /dev/null +++ b/tests/analysis/tuple.zig @@ -0,0 +1,31 @@ +const TupleType = struct { i64, f32 }; +// ^^^^^^^^^ (type)(struct { i64, f32 }) + +const some_tuple: struct { i64, f32 } = undefined; +// ^^^^^^^^^^ (struct { i64, f32 })() + +comptime { + const some_tuple_0, const some_tuple_1 = some_tuple; + // ^^^^^^^^^^^^ (i64)() + // ^^^^^^^^^^^^ (f32)() + _ = some_tuple_0; + _ = some_tuple_1; +} + +const int: i64 = undefined; +const float: f32 = undefined; +const inferred_tuple = .{ int, float }; +// ^^^^^^^^^^^^^^ (struct { i64, f32 })() + +comptime { + const inferred_tuple_0, const inferred_tuple_1 = inferred_tuple; + // ^^^^^^^^^^^^^^^^ (i64)() + // ^^^^^^^^^^^^^^^^ (f32)() + _ = inferred_tuple_0; + _ = inferred_tuple_1; +} + +comptime { + // Use @compileLog to verify the expected type with the compiler: + // @compileLog(some_tuple); +} From 95388ab2c202ed72aa6097b47c99fc717461cc77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 15 Feb 2025 01:28:48 -0800 Subject: [PATCH 5/6] Fix resolved type of inferred-size array --- src/analysis.zig | 16 +++++++++++++++- tests/analysis/array.zig | 4 ++-- tests/lsp_features/hover.zig | 14 +++++++------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 7c93e0933..7a54c20fb 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1704,7 +1704,16 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .struct_init_comma, .struct_init_one, .struct_init_one_comma, - => base_type.instanceTypeVal(analyser), + => { + var buffer: [2]Ast.Node.Index = undefined; + const struct_init_info = tree.fullStructInit(&buffer, node).?; + if (base_type.data == .array and base_type.data.array.elem_count == null) { + var ty = base_type; + ty.data.array.elem_count = struct_init_info.ast.fields.len; + return ty.instanceTypeVal(analyser); + } + return base_type.instanceTypeVal(analyser); + }, .slice, .slice_sentinel, .slice_open, @@ -1814,6 +1823,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e if (array_init_info.ast.type_expr != 0) blk: { const array_ty = try analyser.resolveTypeOfNode(.{ .node = array_init_info.ast.type_expr, .handle = handle }) orelse break :blk; + if (array_ty.data == .array and array_ty.data.array.elem_count == null) { + var ty = array_ty; + ty.data.array.elem_count = array_init_info.ast.elements.len; + return ty.instanceTypeVal(analyser); + } return array_ty.instanceTypeVal(analyser); } diff --git a/tests/analysis/array.zig b/tests/analysis/array.zig index c567e553f..78a707216 100644 --- a/tests/analysis/array.zig +++ b/tests/analysis/array.zig @@ -71,9 +71,9 @@ const array_slice_with_sentinel = some_array[0..runtime_index :0]; const array_init = [length]u8{}; // ^^^^^^^^^^ ([3]u8)() const array_init_inferred_len_0 = [_]u8{}; -// TODO ^^^^^^^^^^^^^^^^^^^^^^^^^ ([0]u8)() +// ^^^^^^^^^^^^^^^^^^^^^^^^^ ([0]u8)() const array_init_inferred_len_3 = [_]u8{ 1, 2, 3 }; -// TODO ^^^^^^^^^^^^^^^^^^^^^^^^^ ([0]u8)() +// ^^^^^^^^^^^^^^^^^^^^^^^^^ ([3]u8)() comptime { // Use @compileLog to verify the expected type with the compiler: diff --git a/tests/lsp_features/hover.zig b/tests/lsp_features/hover.zig index 17131f215..18449565a 100644 --- a/tests/lsp_features/hover.zig +++ b/tests/lsp_features/hover.zig @@ -571,7 +571,7 @@ test "array cat and mult" { \\const a = [_]u8{0} ++ [_]u8{1} \\``` \\```zig - \\([?]u8) + \\([2]u8) \\``` ); try testHover( @@ -581,7 +581,7 @@ test "array cat and mult" { \\const a = [1]u8{0} ++ [_]u8{1} \\``` \\```zig - \\([?]u8) + \\([2]u8) \\``` ); try testHover( @@ -591,7 +591,7 @@ test "array cat and mult" { \\const a = [_]u8{0} ++ [1]u8{1} \\``` \\```zig - \\([?]u8) + \\([2]u8) \\``` ); try testHover( @@ -621,7 +621,7 @@ test "array cat and mult" { \\const a = [_]u8{0} ** 2 \\``` \\```zig - \\([?]u8) + \\([2]u8) \\``` ); try testHover( @@ -684,7 +684,7 @@ test "sentinel values" { \\const array = [_:0]u8{ 1, 2, 3, 4 } \\``` \\```zig - \\([?:0]u8) + \\([4:0]u8) \\``` ); try testHover( @@ -706,7 +706,7 @@ test "sentinel values" { \\const range = array[0..2] \\``` \\```zig - \\(*[?]u8) + \\(*[2]u8) \\``` ); try testHover( @@ -717,7 +717,7 @@ test "sentinel values" { \\const open = array[1..] \\``` \\```zig - \\(*[?:0]u8) + \\(*[3:0]u8) \\``` ); // try testHover( From cea41168396f3616c174b2c07c28e7969c1eff8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Sat, 15 Feb 2025 01:35:03 -0800 Subject: [PATCH 6/6] Resolve value of `array.len` --- src/analysis.zig | 19 +++++++++---------- tests/analysis/array.zig | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 7a54c20fb..e9c78536c 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1271,15 +1271,7 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou switch (ty.data) { .pointer => |info| switch (info.size) { - .one => switch (info.elem_ty.data) { - .array => { - std.debug.assert(!info.elem_ty.is_type_val); - if (std.mem.eql(u8, "len", name)) { - return Type.fromIP(analyser, .usize_type, null); - } - }, - else => {}, - }, + .one => {}, // One level of indirection is handled by resolveDerefType .slice => { if (std.mem.eql(u8, "len", name)) { return Type.fromIP(analyser, .usize_type, null); @@ -1302,8 +1294,15 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou .many, .c => {}, }, - .array => { + .array => |info| { if (std.mem.eql(u8, "len", name)) { + if (info.elem_count) |elem_count| { + const index = try analyser.ip.get( + analyser.gpa, + .{ .int_u64_value = .{ .ty = .usize_type, .int = elem_count } }, + ); + return Type.fromIP(analyser, .usize_type, index); + } return Type.fromIP(analyser, .usize_type, null); } }, diff --git a/tests/analysis/array.zig b/tests/analysis/array.zig index 78a707216..3afc6108d 100644 --- a/tests/analysis/array.zig +++ b/tests/analysis/array.zig @@ -19,7 +19,7 @@ const some_unsized_array: [unknown_length]u8 = undefined; // ^^^^^^^^^^^^^^^^^^ ([?]u8)() const some_array_len = some_array.len; -// ^^^^^^^^^^^^^^ (usize)() +// ^^^^^^^^^^^^^^ (usize)(3) const some_unsized_array_len = some_unsized_array.len; // ^^^^^^^^^^^^^^^^^^^^^^ (usize)()