From 888a7096b26b7de8f0453baad836e98470e42f22 Mon Sep 17 00:00:00 2001 From: barry3406 Date: Sat, 11 Apr 2026 01:48:32 -0700 Subject: [PATCH 1/3] preserve allowzero, align, volatile, and addrspace on pointer types The `ptr_type` resolver in analysis.zig only read `const_token` from `ptr_info`, and `Data.createPointer` only set `is_const` on the `InternPool.pointer_type` flags. As a result, types like `*allowzero u8`, `*align(4) u8`, `*volatile u8` were collapsed to `*u8` in inlay hints (and any other surface that runs through `stringifyTypeOf`). Read `volatile_token`, `allowzero_token`, and the integer literal in `ast.align_node` from `ptr_info`, plumb them through `createPointerType` / `Data.createPointer` into the IP flags and the `.pointer` Data fallback, and update `rawStringify` so the fallback path matches the InternPool printer. Address-space resolution from arbitrary expressions still defers to `.generic`; only the literal alignment integer is read for now. Fixes #2603. --- src/analysis.zig | 85 +++++++++++++++++++++++++----- tests/lsp_features/inlay_hints.zig | 27 ++++++++++ 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index e18b438d8..ff5f04120 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -910,7 +910,7 @@ pub fn resolveOrelseType(analyser: *Analyser, lhs: Type, rhs: Type) error{OutOfM pub fn resolveAddressOf(analyser: *Analyser, is_const: bool, ty: Type) error{OutOfMemory}!Type { const elem_ty = try ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, elem_ty); return try pointer_ty.instanceUnchecked(analyser); } @@ -1233,12 +1233,12 @@ pub fn resolveBracketAccess(analyser: *Analyser, lhs_binding: Binding, rhs: Brac switch (result) { .array => |elem_count| { const array_ty = try Type.createArrayType(analyser, elem_count, sentinel, elem_ty.*); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, array_ty); + const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, array_ty); const pointer_instance = try pointer_ty.instanceUnchecked(analyser); return .{ .type = pointer_instance, .is_const = true }; }, .slice => { - const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, elem_ty.*); + const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, false, false, 0, .generic, elem_ty.*); const slice_instance = try slice_ty.instanceUnchecked(analyser); return .{ .type = slice_instance, .is_const = true }; }, @@ -2157,13 +2157,30 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error const ptr_info = ast.fullPtrType(tree, node).?; const size = ptr_info.size; const is_const = ptr_info.const_token != null; + const is_volatile = ptr_info.volatile_token != null; + const is_allowzero = ptr_info.allowzero_token != null; const sentinel = try analyser.resolveOptionalIPValue(ptr_info.ast.sentinel, handle); + const alignment: u16 = if (ptr_info.ast.align_node.unwrap()) |align_node| + try analyser.resolveIntegerLiteral(u16, .of(align_node, handle)) orelse 0 + else + 0; + const elem_ty = try analyser.resolveTypeOfNodeInternal(.of(ptr_info.ast.child_type, handle)) orelse return null; if (!elem_ty.is_type_val) return null; - return try Type.createPointerType(analyser, size, sentinel, is_const, elem_ty); + return try Type.createPointerType( + analyser, + size, + sentinel, + is_const, + is_volatile, + is_allowzero, + alignment, + .generic, + elem_ty, + ); }, .array_type, .array_type_sentinel, @@ -2964,7 +2981,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error r_elem_ty = try r_elem_ty.instanceUnchecked(analyser); var elem_ty = try analyser.resolveArrayCat(l_elem_ty, r_elem_ty) orelse return null; elem_ty = try elem_ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, true, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, .one, .none, true, false, false, 0, .generic, elem_ty); return try pointer_ty.instanceUnchecked(analyser); } @@ -3169,6 +3186,11 @@ pub const Type = struct { /// `.none` means no sentinel, `.unknown_unknown` means unknown sentinel sentinel: InternPool.Index, is_const: bool, + is_volatile: bool = false, + is_allowzero: bool = false, + /// `0` means default alignment. + alignment: u16 = 0, + address_space: std.builtin.AddressSpace = .generic, elem_ty: *Type, }, @@ -3275,6 +3297,10 @@ pub const Type = struct { size: std.lang.Type.Pointer.Size, sentinel: InternPool.Index, is_const: bool, + is_volatile: bool, + is_allowzero: bool, + alignment: u16, + address_space: std.builtin.AddressSpace, elem_ty: Type, ) !Data { std.debug.assert(elem_ty.is_type_val); @@ -3287,6 +3313,10 @@ pub const Type = struct { .flags = .{ .size = size, .is_const = is_const, + .is_volatile = is_volatile, + .is_allowzero = is_allowzero, + .alignment = alignment, + .address_space = address_space, }, }, }); @@ -3297,6 +3327,10 @@ pub const Type = struct { .size = size, .sentinel = sentinel, .is_const = is_const, + .is_volatile = is_volatile, + .is_allowzero = is_allowzero, + .alignment = alignment, + .address_space = address_space, .elem_ty = try analyser.allocType(elem_ty), }, }; @@ -3651,11 +3685,18 @@ pub const Type = struct { return t.data.resolveGeneric(analyser, bound_params, visiting); }, .pointer => |info| { - const size = info.size; - const sentinel = info.sentinel; - const is_const = info.is_const; const elem_ty = try analyser.resolveGenericTypeInternal(info.elem_ty.*, bound_params, visiting); - return try createPointer(analyser, size, sentinel, is_const, elem_ty); + return try createPointer( + analyser, + info.size, + info.sentinel, + info.is_const, + info.is_volatile, + info.is_allowzero, + info.alignment, + info.address_space, + elem_ty, + ); }, .array => |info| { const elem_count = info.elem_count; @@ -3754,10 +3795,24 @@ pub const Type = struct { size: std.lang.Type.Pointer.Size, sentinel: InternPool.Index, is_const: bool, + is_volatile: bool, + is_allowzero: bool, + alignment: u16, + address_space: std.builtin.AddressSpace, elem_ty: Type, ) !Type { return .{ - .data = try Data.createPointer(analyser, size, sentinel, is_const, elem_ty), + .data = try Data.createPointer( + analyser, + size, + sentinel, + is_const, + is_volatile, + is_allowzero, + alignment, + address_space, + elem_ty, + ), .is_type_val = true, }; } @@ -4602,7 +4657,11 @@ pub const Type = struct { }, .c => try writer.writeAll("[*c]"), } + if (info.is_allowzero and info.size != .c) try writer.writeAll("allowzero "); + if (info.alignment != 0) try writer.print("align({d}) ", .{info.alignment}); + if (info.address_space != .generic) try writer.print("addrspace(.{t}) ", .{info.address_space}); if (info.is_const) try writer.writeAll("const "); + if (info.is_volatile) try writer.writeAll("volatile "); try info.elem_ty.rawStringify(writer, analyser, options); }, .array => |info| { @@ -4875,7 +4934,7 @@ fn resolveLangrefType(analyser: *Analyser, type_str: []const u8) Error!?Type { elem_str = elem_str[6..]; const elem_instance = try analyser.resolveLangrefType(elem_str) orelse return null; const elem_ty = try elem_instance.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, elem_ty); return try pointer_ty.instanceUnchecked(analyser); } @@ -4911,7 +4970,7 @@ fn resolveLangrefType(analyser: *Analyser, type_str: []const u8) Error!?Type { elem_str = elem_str[6..]; const elem_instance = try analyser.resolveLangrefType(elem_str) orelse return null; const elem_ty = try elem_instance.typeOf(analyser); - const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, elem_ty); + const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, false, false, 0, .generic, elem_ty); return try slice_ty.instanceUnchecked(analyser); } @@ -6013,7 +6072,7 @@ pub const DeclWithHandle = struct { if (!self.isCaptureByRef()) return resolved_ty; const elem_ty = try resolved_ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, false, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, .one, .none, false, false, false, 0, .generic, elem_ty); return try pointer_ty.instanceUnchecked(analyser); } }; diff --git a/tests/lsp_features/inlay_hints.zig b/tests/lsp_features/inlay_hints.zig index b161fb3b4..b976153c5 100644 --- a/tests/lsp_features/inlay_hints.zig +++ b/tests/lsp_features/inlay_hints.zig @@ -286,6 +286,33 @@ test "var decl" { , .{ .kind = .Type }); } +test "var decl - pointer modifiers" { + try testInlayHints( + \\const a: *u8 = undefined; + \\const b<*u8> = a; + , .{ .kind = .Type }); + try testInlayHints( + \\const a: *allowzero u8 = undefined; + \\const b<*allowzero u8> = a; + , .{ .kind = .Type }); + try testInlayHints( + \\const a: *align(4) u8 = undefined; + \\const b<*align(4) u8> = a; + , .{ .kind = .Type }); + try testInlayHints( + \\const a: *volatile u8 = undefined; + \\const b<*volatile u8> = a; + , .{ .kind = .Type }); + try testInlayHints( + \\const a: *allowzero align(8) const volatile u8 = undefined; + \\const b<*allowzero align(8) const volatile u8> = a; + , .{ .kind = .Type }); + try testInlayHints( + \\const a: [*]allowzero u8 = undefined; + \\const b<[*]allowzero u8> = a; + , .{ .kind = .Type }); +} + test "comptime return types" { try testInlayHints( \\fn Box(comptime T: type) type { From 92638b0812ec6477f74364b49c909f19d41325fb Mon Sep 17 00:00:00 2001 From: ilovapples Date: Mon, 9 Nov 2026 16:44:38 -0600 Subject: [PATCH 2/3] analysis: replace pointer attr arguments with InternPool.Key.Pointer.Flags, fix type of `slice.ptr`, use different tests --- src/analysis.zig | 163 +++++++++++------------------ src/features/completions.zig | 6 +- tests/analysis/pointer.zig | 12 +++ tests/lsp_features/inlay_hints.zig | 27 ----- 4 files changed, 75 insertions(+), 133 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index ff5f04120..09fc5eea9 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -469,7 +469,7 @@ pub fn firstParamIs( if (resolved_type.isMetaType()) return false; const deref_type = deref: switch (resolved_type.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one, .c => info.elem_ty.*, .many, .slice => return false, }, @@ -481,7 +481,7 @@ pub fn firstParamIs( }; const deref_expected_type = switch (expected_type.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one, .c => info.elem_ty.*, .many, .slice => return false, }, @@ -881,7 +881,7 @@ pub fn resolveOptionalUnwrap(analyser: *Analyser, optional: Type) error{OutOfMem switch (optional.data) { .optional => |child_ty| return try child_ty.instanceUnchecked(analyser), .pointer => |ptr| { - if (ptr.size == .c) return optional; + if (ptr.flags.size == .c) return optional; return null; }, .ip_index => |payload| switch (analyser.ip.indexToKey(payload.type)) { @@ -910,7 +910,7 @@ pub fn resolveOrelseType(analyser: *Analyser, lhs: Type, rhs: Type) error{OutOfM pub fn resolveAddressOf(analyser: *Analyser, is_const: bool, ty: Type) error{OutOfMemory}!Type { const elem_ty = try ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, elem_ty, .none, .{ .size = .one, .is_const = is_const }); return try pointer_ty.instanceUnchecked(analyser); } @@ -1000,10 +1000,10 @@ pub fn resolveDerefBinding(analyser: *Analyser, pointer: Type) error{OutOfMemory if (pointer.is_type_val) return null; switch (pointer.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one, .c => return .{ .type = try info.elem_ty.instanceTypeVal(analyser) orelse return null, - .is_const = info.is_const, + .is_const = info.flags.is_const, }, .many, .slice => return null, }, @@ -1120,10 +1120,9 @@ fn bracketAccessTypeFromIPIndex(analyser: *Analyser, ip_index: InternPool.Index) .pointer_type => |info| .{ .data = .{ .pointer = .{ - .size = info.flags.size, - .sentinel = info.sentinel, - .is_const = info.flags.is_const, .elem_ty = try analyser.allocType(try analyser.bracketAccessTypeFromIPIndex(info.elem_type)), + .sentinel = info.sentinel, + .flags = info.flags, }, }, .is_type_val = true, @@ -1137,7 +1136,7 @@ pub fn resolveBracketAccess(analyser: *Analyser, lhs_binding: Binding, rhs: Brac if (lhs.is_type_val) return null; const is_const = switch (lhs.data) { - .pointer => |info| info.is_const, + .pointer => |info| info.flags.is_const, else => lhs_binding.is_const, }; @@ -1185,7 +1184,7 @@ pub fn resolveBracketAccess(analyser: *Analyser, lhs_binding: Binding, rhs: Brac break :elem info.elem_ty; }, }, - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one => switch (info.elem_ty.data) { .tuple, .array => continue :elem info.elem_ty.data, else => switch (rhs) { @@ -1233,12 +1232,12 @@ pub fn resolveBracketAccess(analyser: *Analyser, lhs_binding: Binding, rhs: Brac switch (result) { .array => |elem_count| { const array_ty = try Type.createArrayType(analyser, elem_count, sentinel, elem_ty.*); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, array_ty); + const pointer_ty = try Type.createPointerType(analyser, array_ty, .none, .{ .size = .one, .is_const = is_const }); const pointer_instance = try pointer_ty.instanceUnchecked(analyser); return .{ .type = pointer_instance, .is_const = true }; }, .slice => { - const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, false, false, 0, .generic, elem_ty.*); + const slice_ty = try Type.createPointerType(analyser, elem_ty.*, sentinel, .{ .size = .slice, .is_const = is_const }); const slice_instance = try slice_ty.instanceUnchecked(analyser); return .{ .type = slice_instance, .is_const = true }; }, @@ -1250,7 +1249,7 @@ pub fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) erro return null; switch (ty.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one => { if (std.mem.eql(u8, "*", name)) { return info.elem_ty.instanceTypeVal(analyser); @@ -1263,13 +1262,14 @@ pub fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) erro } if (std.mem.eql(u8, "ptr", name)) { + var flags = info.flags; + flags.size = .many; return .{ .data = .{ .pointer = .{ - .size = .many, - .sentinel = info.sentinel, - .is_const = info.is_const, .elem_ty = info.elem_ty, + .sentinel = info.sentinel, + .flags = flags, }, }, .is_type_val = false, @@ -2170,17 +2170,14 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error const elem_ty = try analyser.resolveTypeOfNodeInternal(.of(ptr_info.ast.child_type, handle)) orelse return null; if (!elem_ty.is_type_val) return null; - return try Type.createPointerType( - analyser, - size, - sentinel, - is_const, - is_volatile, - is_allowzero, - alignment, - .generic, - elem_ty, - ); + return try Type.createPointerType(analyser, elem_ty, sentinel, .{ + .size = size, + .is_const = is_const, + .is_volatile = is_volatile, + .is_allowzero = is_allowzero, + .alignment = alignment, + .address_space = .generic, + }); }, .array_type, .array_type_sentinel, @@ -2981,7 +2978,10 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error r_elem_ty = try r_elem_ty.instanceUnchecked(analyser); var elem_ty = try analyser.resolveArrayCat(l_elem_ty, r_elem_ty) orelse return null; elem_ty = try elem_ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, true, false, false, 0, .generic, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, elem_ty, .none, .{ + .size = .one, + .is_const = true, + }); return try pointer_ty.instanceUnchecked(analyser); } @@ -3182,16 +3182,10 @@ pub const Type = struct { /// - `[]const T` /// - `[*c]T` pointer: struct { - size: std.lang.Type.Pointer.Size, + elem_ty: *Type, /// `.none` means no sentinel, `.unknown_unknown` means unknown sentinel sentinel: InternPool.Index, - is_const: bool, - is_volatile: bool = false, - is_allowzero: bool = false, - /// `0` means default alignment. - alignment: u16 = 0, - address_space: std.builtin.AddressSpace = .generic, - elem_ty: *Type, + flags: InternPool.Key.Pointer.Flags, }, /// `[elem_count :sentinel]elem_ty` @@ -3294,14 +3288,9 @@ pub const Type = struct { fn createPointer( analyser: *Analyser, - size: std.lang.Type.Pointer.Size, - sentinel: InternPool.Index, - is_const: bool, - is_volatile: bool, - is_allowzero: bool, - alignment: u16, - address_space: std.builtin.AddressSpace, elem_ty: Type, + sentinel: InternPool.Index, + flags: InternPool.Key.Pointer.Flags, ) !Data { std.debug.assert(elem_ty.is_type_val); blk: { @@ -3310,28 +3299,16 @@ pub const Type = struct { .pointer_type = .{ .elem_type = elem_type, .sentinel = try analyser.coerceIP(elem_type, sentinel) orelse break :blk, - .flags = .{ - .size = size, - .is_const = is_const, - .is_volatile = is_volatile, - .is_allowzero = is_allowzero, - .alignment = alignment, - .address_space = address_space, - }, + .flags = flags, }, }); return .{ .ip_index = .{ .type = .type_type, .index = index } }; } return .{ .pointer = .{ - .size = size, - .sentinel = sentinel, - .is_const = is_const, - .is_volatile = is_volatile, - .is_allowzero = is_allowzero, - .alignment = alignment, - .address_space = address_space, .elem_ty = try analyser.allocType(elem_ty), + .sentinel = sentinel, + .flags = flags, }, }; } @@ -3427,9 +3404,8 @@ pub const Type = struct { hasher.update(&.{@backingInt(data)}); switch (data) { .pointer => |info| { - std.hash.autoHash(hasher, info.size); + std.hash.autoHash(hasher, info.flags); std.hash.autoHash(hasher, info.sentinel); - std.hash.autoHash(hasher, info.is_const); info.elem_ty.hashWithHasher(hasher); }, .array => |info| { @@ -3495,7 +3471,7 @@ pub const Type = struct { switch (a) { .pointer => |a_type| { const b_type = b.pointer; - if (a_type.size != b_type.size) return false; + if (a_type.flags != b_type.flags) return false; if (a_type.sentinel != b_type.sentinel) return false; if (!a_type.elem_ty.eql(b_type.elem_ty.*)) return false; }, @@ -3686,17 +3662,7 @@ pub const Type = struct { }, .pointer => |info| { const elem_ty = try analyser.resolveGenericTypeInternal(info.elem_ty.*, bound_params, visiting); - return try createPointer( - analyser, - info.size, - info.sentinel, - info.is_const, - info.is_volatile, - info.is_allowzero, - info.alignment, - info.address_space, - elem_ty, - ); + return try createPointer(analyser, elem_ty, info.sentinel, info.flags); }, .array => |info| { const elem_count = info.elem_count; @@ -3792,27 +3758,12 @@ pub const Type = struct { fn createPointerType( analyser: *Analyser, - size: std.lang.Type.Pointer.Size, - sentinel: InternPool.Index, - is_const: bool, - is_volatile: bool, - is_allowzero: bool, - alignment: u16, - address_space: std.builtin.AddressSpace, elem_ty: Type, + sentinel: InternPool.Index, + flags: InternPool.Key.Pointer.Flags, ) !Type { return .{ - .data = try Data.createPointer( - analyser, - size, - sentinel, - is_const, - is_volatile, - is_allowzero, - alignment, - address_space, - elem_ty, - ), + .data = try Data.createPointer(analyser, elem_ty, sentinel, flags), .is_type_val = true, }; } @@ -4448,7 +4399,7 @@ pub const Type = struct { fn pointerSize(self: Type, analyser: *Analyser) ?std.lang.Type.Pointer.Size { if (self.is_type_val) return null; return switch (self.data) { - .pointer => |info| info.size, + .pointer => |info| info.flags.size, .ip_index => |payload| switch (analyser.ip.indexToKey(payload.type)) { .pointer_type => |pointer_info| pointer_info.flags.size, else => null, @@ -4465,7 +4416,7 @@ pub const Type = struct { if (self.is_type_val) return null; return switch (self.data) { .pointer => |info| { - if (info.size != size) return null; + if (info.flags.size != size) return null; return info.elem_ty.*; }, .ip_index => |payload| switch (analyser.ip.indexToKey(payload.type)) { @@ -4629,7 +4580,7 @@ pub const Type = struct { switch (ty.data) { .pointer => |info| { - switch (info.size) { + switch (info.flags.size) { .one => try writer.writeByte('*'), .many => { try writer.writeAll("[*"); @@ -4657,11 +4608,11 @@ pub const Type = struct { }, .c => try writer.writeAll("[*c]"), } - if (info.is_allowzero and info.size != .c) try writer.writeAll("allowzero "); - if (info.alignment != 0) try writer.print("align({d}) ", .{info.alignment}); - if (info.address_space != .generic) try writer.print("addrspace(.{t}) ", .{info.address_space}); - if (info.is_const) try writer.writeAll("const "); - if (info.is_volatile) try writer.writeAll("volatile "); + if (info.flags.is_allowzero and info.flags.size != .c) try writer.writeAll("allowzero "); + if (info.flags.alignment != 0) try writer.print("align({d}) ", .{info.flags.alignment}); + if (info.flags.address_space != .generic) try writer.print("addrspace(.{t}) ", .{info.flags.address_space}); + if (info.flags.is_const) try writer.writeAll("const "); + if (info.flags.is_volatile) try writer.writeAll("volatile "); try info.elem_ty.rawStringify(writer, analyser, options); }, .array => |info| { @@ -4934,7 +4885,10 @@ fn resolveLangrefType(analyser: *Analyser, type_str: []const u8) Error!?Type { elem_str = elem_str[6..]; const elem_instance = try analyser.resolveLangrefType(elem_str) orelse return null; const elem_ty = try elem_instance.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, is_const, false, false, 0, .generic, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, elem_ty, .none, .{ + .size = .one, + .is_const = is_const, + }); return try pointer_ty.instanceUnchecked(analyser); } @@ -4970,7 +4924,10 @@ fn resolveLangrefType(analyser: *Analyser, type_str: []const u8) Error!?Type { elem_str = elem_str[6..]; const elem_instance = try analyser.resolveLangrefType(elem_str) orelse return null; const elem_ty = try elem_instance.typeOf(analyser); - const slice_ty = try Type.createPointerType(analyser, .slice, sentinel, is_const, false, false, 0, .generic, elem_ty); + const slice_ty = try Type.createPointerType(analyser, elem_ty, sentinel, .{ + .size = .slice, + .is_const = is_const, + }); return try slice_ty.instanceUnchecked(analyser); } @@ -6072,7 +6029,7 @@ pub const DeclWithHandle = struct { if (!self.isCaptureByRef()) return resolved_ty; const elem_ty = try resolved_ty.typeOf(analyser); - const pointer_ty = try Type.createPointerType(analyser, .one, .none, false, false, false, 0, .generic, elem_ty); + const pointer_ty = try Type.createPointerType(analyser, elem_ty, .none, .{ .size = .one }); return try pointer_ty.instanceUnchecked(analyser); } }; @@ -6855,7 +6812,7 @@ pub fn resolveExpressionTypeFromAncestors( } switch (expr_ty.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .slice => { var buffer: [2]Ast.Node.Index = undefined; const array_init = tree.fullArrayInit(&buffer, node) orelse return null; diff --git a/src/features/completions.zig b/src/features/completions.zig index 673d4cf4f..d140aef65 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -108,7 +108,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) Analyser.Error!void { try builder.completions.ensureUnusedCapacity(builder.arena, 2); switch (ty.data) { - .pointer => |info| switch (info.size) { + .pointer => |info| switch (info.flags.size) { .one, .c => { if (ty.is_type_val) return; @@ -118,7 +118,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) Analyser.Error!void { .detail = try info.elem_ty.stringifyTypeVal(builder.analyser, .{ .truncate_container_decls = false }), }); - if (info.size == .c) { + if (info.flags.size == .c) { builder.completions.appendAssumeCapacity(.{ .label = "?", .kind = .Operator, @@ -142,7 +142,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) Analyser.Error!void { var many_ptr_ty = ty; many_ptr_ty.is_type_val = true; - many_ptr_ty.data.pointer.size = .many; + many_ptr_ty.data.pointer.flags.size = .many; builder.completions.appendAssumeCapacity(.{ .label = "ptr", .kind = .Field, diff --git a/tests/analysis/pointer.zig b/tests/analysis/pointer.zig index f795f24d7..99a39d8aa 100644 --- a/tests/analysis/pointer.zig +++ b/tests/analysis/pointer.zig @@ -270,6 +270,18 @@ const ManyPointerWithSentinel = [*:.{}]addrspace(.generic) const bitpack struct // ^ (bitpack struct {})() // ^^^^^^^^ (AddressSpace)() +const VolatilePointer = *volatile u32; +// ^^^^^^^^^^^^^^^ (type)(*volatile u32) + +const AlignConstVolatilePointer = *align(8) const volatile u32; +// ^^^^^^^^^^^^^^^^^^^^^^^^^ (type)(*align(8) const volatile u32) + +const AllowZeroConstPointer = *allowzero const u32; +// ^^^^^^^^^^^^^^^^^^^^^ (type)(*allowzero const u32) + +const AddrspacePointer = *addrspace(.uniform) u32; +// ^^^^^^^^^^^^^^^^ (type)(*u32) + var runtime_index: usize = 5; var runtime_u8: u8 = 1; var runtime_i8: i8 = -1; diff --git a/tests/lsp_features/inlay_hints.zig b/tests/lsp_features/inlay_hints.zig index b976153c5..b161fb3b4 100644 --- a/tests/lsp_features/inlay_hints.zig +++ b/tests/lsp_features/inlay_hints.zig @@ -286,33 +286,6 @@ test "var decl" { , .{ .kind = .Type }); } -test "var decl - pointer modifiers" { - try testInlayHints( - \\const a: *u8 = undefined; - \\const b<*u8> = a; - , .{ .kind = .Type }); - try testInlayHints( - \\const a: *allowzero u8 = undefined; - \\const b<*allowzero u8> = a; - , .{ .kind = .Type }); - try testInlayHints( - \\const a: *align(4) u8 = undefined; - \\const b<*align(4) u8> = a; - , .{ .kind = .Type }); - try testInlayHints( - \\const a: *volatile u8 = undefined; - \\const b<*volatile u8> = a; - , .{ .kind = .Type }); - try testInlayHints( - \\const a: *allowzero align(8) const volatile u8 = undefined; - \\const b<*allowzero align(8) const volatile u8> = a; - , .{ .kind = .Type }); - try testInlayHints( - \\const a: [*]allowzero u8 = undefined; - \\const b<[*]allowzero u8> = a; - , .{ .kind = .Type }); -} - test "comptime return types" { try testInlayHints( \\fn Box(comptime T: type) type { From e219d947be13927cc521b6a25ca42a3452374a46 Mon Sep 17 00:00:00 2001 From: ilovapples Date: Fri, 13 Nov 2026 17:13:08 -0600 Subject: [PATCH 3/3] analysis: resolve pointer type addrspace attribute --- src/analysis.zig | 8 +++++++- tests/analysis/pointer.zig | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 09fc5eea9..fbfac8d93 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2162,6 +2162,12 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error const sentinel = try analyser.resolveOptionalIPValue(ptr_info.ast.sentinel, handle); + const address_space: std.lang.AddressSpace = if (ptr_info.ast.addrspace_node.unwrap()) |addrspace_node| blk: { + if (tree.nodeTag(addrspace_node) != .enum_literal) break :blk .generic; + const name = offsets.identifierTokenToNameSlice(&handle.tree, tree.nodeMainToken(addrspace_node)); + break :blk std.meta.stringToEnum(std.lang.AddressSpace, name) orelse .generic; + } else .generic; + const alignment: u16 = if (ptr_info.ast.align_node.unwrap()) |align_node| try analyser.resolveIntegerLiteral(u16, .of(align_node, handle)) orelse 0 else @@ -2176,7 +2182,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error .is_volatile = is_volatile, .is_allowzero = is_allowzero, .alignment = alignment, - .address_space = .generic, + .address_space = address_space, }); }, .array_type, diff --git a/tests/analysis/pointer.zig b/tests/analysis/pointer.zig index 99a39d8aa..64706db11 100644 --- a/tests/analysis/pointer.zig +++ b/tests/analysis/pointer.zig @@ -280,7 +280,7 @@ const AllowZeroConstPointer = *allowzero const u32; // ^^^^^^^^^^^^^^^^^^^^^ (type)(*allowzero const u32) const AddrspacePointer = *addrspace(.uniform) u32; -// ^^^^^^^^^^^^^^^^ (type)(*u32) +// ^^^^^^^^^^^^^^^^ (type)(*addrspace(.uniform) u32) var runtime_index: usize = 5; var runtime_u8: u8 = 1;