diff --git a/src/analysis.zig b/src/analysis.zig index 139b05992..aa86abe66 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -326,29 +326,29 @@ pub fn isInstanceCall( call: Ast.full.Call, func_ty: Type, ) error{OutOfMemory}!bool { - std.debug.assert(!func_ty.is_type_val); + std.debug.assert(!func_ty.isTypeVal(analyser)); if (call_handle.tree.nodes.items(.tag)[call.ast.fn_expr] != .field_access) return false; const container_node: NodeWithHandle = .{ .node = call_handle.tree.nodes.items(.data)[call.ast.fn_expr].lhs, .handle = call_handle }; const container_ty = if (try analyser.resolveTypeOfNodeInternal(container_node)) |container_instance| - container_instance.typeOf(analyser) + try container_instance.typeOf(analyser) else blk: { - const func_node = func_ty.data.other; // this assumes that function types can only be Ast nodes + const func_node = func_ty.dynamic.data.other; // this assumes that function types can only be Ast nodes const fn_token = func_node.handle.tree.nodes.items(.main_token)[func_node.node]; break :blk try innermostContainer(func_node.handle, func_node.handle.tree.tokens.items(.start)[fn_token]); }; - std.debug.assert(container_ty.is_type_val); + std.debug.assert(container_ty.isTypeVal(analyser)); return analyser.firstParamIs(func_ty, container_ty); } pub fn hasSelfParam(analyser: *Analyser, func_ty: Type) error{OutOfMemory}!bool { - const func_node = func_ty.data.other; // this assumes that function types can only be Ast nodes + const func_node = func_ty.dynamic.data.other; // this assumes that function types can only be Ast nodes const fn_token = func_node.handle.tree.nodes.items(.main_token)[func_node.node]; const in_container = try innermostContainer(func_node.handle, func_node.handle.tree.tokens.items(.start)[fn_token]); - std.debug.assert(in_container.is_type_val); + std.debug.assert(in_container.isTypeVal(analyser)); if (in_container.isNamespace()) return false; return analyser.firstParamIs(func_ty, in_container); } @@ -359,7 +359,7 @@ pub fn firstParamIs( expected_type: Type, ) error{OutOfMemory}!bool { std.debug.assert(func_type.isFunc()); - const func_handle = func_type.data.other; + const func_handle = func_type.dynamic.data.other; // this assumes that function types can only be Ast nodes var buffer: [1]Ast.Node.Index = undefined; const func = func_handle.handle.tree.fullFnProto(&buffer, func_handle.node).?; @@ -375,20 +375,26 @@ pub fn firstParamIs( .node = param.type_expr, .handle = func_handle.handle, }) orelse return false; - if (!resolved_type.is_type_val) return false; + if (!resolved_type.isTypeVal(analyser)) return false; - const deref_type = switch (resolved_type.data) { - .pointer => |info| switch (info.size) { - .one => info.elem_ty.*, - .many, .slice, .c => return false, + const deref_type = switch (resolved_type) { + .dynamic => |payload| switch (payload.data) { + .pointer => |info| switch (info.size) { + .one => info.elem_ty.*, + .many, .slice, .c => return false, + }, + else => resolved_type, }, else => resolved_type, }; - const deref_expected_type = switch (expected_type.data) { - .pointer => |info| switch (info.size) { - .one => info.elem_ty.*, - .many, .slice, .c => return false, + const deref_expected_type = switch (expected_type) { + .dynamic => |payload| switch (payload.data) { + .pointer => |info| switch (info.size) { + .one => info.elem_ty.*, + .many, .slice, .c => return false, + }, + else => expected_type, }, else => expected_type, }; @@ -722,10 +728,14 @@ fn resolveVarDeclAliasInternal(analyser: *Analyser, node_handle: NodeWithHandle, .field_access => blk: { const lhs = datas[node_handle.node].lhs; const resolved = (try analyser.resolveTypeOfNode(.{ .node = lhs, .handle = handle })) orelse return null; - if (!resolved.is_type_val) + if (!resolved.isTypeVal(analyser)) return null; - const resolved_scope_handle = switch (resolved.data) { + const payload = switch (resolved) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + const resolved_scope_handle = switch (payload.data) { .container => |s| s, else => return null, }; @@ -813,7 +823,7 @@ fn findReturnStatement(tree: Ast, body: Ast.Node.Index) ?Ast.Node.Index { pub fn resolveReturnType(analyser: *Analyser, func_type_param: Type) error{OutOfMemory}!?Type { const func_type = try analyser.resolveFuncProtoOfCallable(func_type_param) orelse return null; - const func_node_handle = func_type.data.other; // this assumes that function types can only be Ast nodes + const func_node_handle = func_type.dynamic.data.other; // this assumes that function types can only be Ast nodes const tree = func_node_handle.handle.tree; const func_node = func_node_handle.node; @@ -839,18 +849,18 @@ pub fn resolveReturnType(analyser: *Analyser, func_type_param: Type) error{OutOf const ret: NodeWithHandle = .{ .node = return_type, .handle = func_node_handle.handle }; const child_type = (try analyser.resolveTypeOfNodeInternal(ret)) orelse return null; - if (!child_type.is_type_val) return null; + if (!child_type.isTypeVal(analyser)) return null; if (ast.hasInferredError(tree, fn_proto)) { const child_type_ptr = try analyser.arena.allocator().create(Type); child_type_ptr.* = child_type; - return .{ + return .{ .dynamic = .{ .data = .{ .error_union = .{ .error_set = null, .payload = child_type_ptr, } }, .is_type_val = false, - }; + } }; } return try child_type.instanceTypeVal(analyser); @@ -858,11 +868,15 @@ pub fn resolveReturnType(analyser: *Analyser, func_type_param: Type) error{OutOf /// `optional.?` pub fn resolveOptionalUnwrap(analyser: *Analyser, optional: Type) error{OutOfMemory}!?Type { - if (optional.is_type_val) return null; + if (optional.isTypeVal(analyser)) return null; - switch (optional.data) { + const payload = switch (optional) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + switch (payload.data) { .optional => |child_ty| { - std.debug.assert(child_ty.is_type_val); + std.debug.assert(child_ty.isTypeVal(analyser)); return try child_ty.instanceTypeVal(analyser); }, .pointer => |ptr| { @@ -874,19 +888,26 @@ pub fn resolveOptionalUnwrap(analyser: *Analyser, optional: Type) error{OutOfMem } pub fn resolveOrelseType(analyser: *Analyser, lhs: Type, rhs: Type) error{OutOfMemory}!?Type { - return switch (rhs.data) { - .optional => rhs, - else => try analyser.resolveOptionalUnwrap(lhs), - }; + switch (rhs) { + .dynamic => |payload| switch (payload.data) { + .optional => return rhs, + else => {}, + }, + .ip_index, .either => {}, + } + return try analyser.resolveOptionalUnwrap(lhs); } /// Resolves the child type of an optional type pub fn resolveOptionalChildType(analyser: *Analyser, optional_type: Type) error{OutOfMemory}!?Type { - _ = analyser; - if (!optional_type.is_type_val) return null; - switch (optional_type.data) { + if (!optional_type.isTypeVal(analyser)) return null; + const payload = switch (optional_type) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + switch (payload.data) { .optional => |child_ty| { - std.debug.assert(child_ty.is_type_val); + std.debug.assert(child_ty.isTypeVal(analyser)); return child_ty.*; }, else => return null, @@ -895,8 +916,8 @@ 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 .{ + base_type_ptr.* = try ty.typeOf(analyser); + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .one, @@ -906,13 +927,17 @@ pub fn resolveAddressOf(analyser: *Analyser, ty: Type) error{OutOfMemory}!?Type }, }, .is_type_val = false, - }; + } }; } pub const ErrorUnionSide = enum { error_set, payload }; pub fn resolveUnwrapErrorUnionType(analyser: *Analyser, ty: Type, side: ErrorUnionSide) error{OutOfMemory}!?Type { - return switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + return switch (payload.data) { .error_union => |info| switch (side) { .error_set => try (info.error_set orelse return null).instanceTypeVal(analyser), .payload => try info.payload.instanceTypeVal(analyser), @@ -922,10 +947,14 @@ pub fn resolveUnwrapErrorUnionType(analyser: *Analyser, ty: Type, side: ErrorUni } fn resolveTaggedUnionFieldType(analyser: *Analyser, ty: Type, symbol: []const u8) error{OutOfMemory}!?Type { - if (!ty.is_type_val) + if (!ty.isTypeVal(analyser)) return null; - const scope_handle = switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + const scope_handle = switch (payload.data) { .container => |s| s, else => return null, }; @@ -951,7 +980,7 @@ fn resolveTaggedUnionFieldType(analyser: *Analyser, ty: Type, symbol: []const u8 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 .{ .dynamic = .{ .data = .{ .union_tag = union_type_ptr }, .is_type_val = false } }; } if (container_decl.ast.arg != 0) { @@ -974,12 +1003,15 @@ pub fn resolveFuncProtoOfCallable(analyser: *Analyser, ty: Type) error{OutOfMemo /// resolve a pointer dereference /// `pointer.*` pub fn resolveDerefType(analyser: *Analyser, pointer: Type) error{OutOfMemory}!?Type { - if (pointer.is_type_val) return null; + if (pointer.isTypeVal(analyser)) return null; - switch (pointer.data) { - .pointer => |info| switch (info.size) { - .one, .c => return try info.elem_ty.instanceTypeVal(analyser), - .many, .slice => return null, + switch (pointer) { + .dynamic => |payload| switch (payload.data) { + .pointer => |info| switch (info.size) { + .one, .c => return try info.elem_ty.instanceTypeVal(analyser), + .many, .slice => return null, + }, + else => return null, }, .ip_index => |payload| { const ty = analyser.ip.typeOf(payload.index); @@ -991,7 +1023,7 @@ pub fn resolveDerefType(analyser: *Analyser, pointer: Type) error{OutOfMemory}!? else => return null, } }, - else => return null, + .either => return null, } } @@ -1009,9 +1041,13 @@ const BracketAccessKind = enum { /// - `lhs[start..]` (Open) /// - `lhs[start..end]` (Range) fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKind) error{OutOfMemory}!?Type { - if (lhs.is_type_val) return null; + if (lhs.isTypeVal(analyser)) return null; - switch (lhs.data) { + const payload = switch (lhs) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + switch (payload.data) { .other => |node_handle| switch (node_handle.handle.tree.nodes.items(.tag)[node_handle.node]) { .for_range => return try Type.typeValFromIP(analyser, .usize_type), else => return null, @@ -1019,7 +1055,7 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi .array => |info| switch (rhs) { .Single => return try info.elem_ty.instanceTypeVal(analyser), .Open => { - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .slice, @@ -1029,10 +1065,10 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi }, }, .is_type_val = false, - }; + } }; }, .Range => { - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .slice, @@ -1042,41 +1078,44 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi }, }, .is_type_val = false, - }; + } }; }, }, .pointer => |info| return switch (info.size) { - .one => switch (info.elem_ty.data) { - .array => |array_info| { - switch (rhs) { - .Single => return try 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, + .one => switch (info.elem_ty.*) { + .dynamic => |elem_payload| switch (elem_payload.data) { + .array => |array_info| { + switch (rhs) { + .Single => return try array_info.elem_ty.instanceTypeVal(analyser), + .Open => { + return .{ .dynamic = .{ + .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, + } }; + }, + .Range => { + return .{ .dynamic = .{ + .data = .{ + .pointer = .{ + .size = .slice, + .sentinel = .none, + .is_const = false, + .elem_ty = array_info.elem_ty, + }, }, - }, - .is_type_val = false, - }; - }, - } + .is_type_val = false, + } }; + }, + } + }, + else => return null, }, else => return null, }, @@ -1084,7 +1123,7 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi .Single => try info.elem_ty.instanceTypeVal(analyser), .Open => lhs, .Range => { - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .slice, @@ -1094,7 +1133,7 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi }, }, .is_type_val = false, - }; + } }; }, }, .slice => switch (rhs) { @@ -1104,7 +1143,7 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi .c => switch (rhs) { .Single => try info.elem_ty.instanceTypeVal(analyser), .Open => lhs, - .Range => .{ + .Range => .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .slice, @@ -1114,7 +1153,7 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi }, }, .is_type_val = false, - }, + } }, }, }, else => return null, @@ -1122,7 +1161,11 @@ fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKi } pub fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) error{OutOfMemory}!?Type { - switch (tuple.data) { + const payload = switch (tuple) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + switch (payload.data) { .tuple => |fields| { if (index >= fields.len) return null; return fields[index].instanceTypeVal(analyser); @@ -1132,17 +1175,24 @@ pub fn resolveTupleFieldType(analyser: *Analyser, tuple: Type, index: usize) err } fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{OutOfMemory}!?Type { - if (ty.is_type_val) + if (ty.isTypeVal(analyser)) return null; - switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + switch (payload.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 try Type.typeValFromIP(analyser, .usize_type); - } + .one => switch (info.elem_ty.*) { + .dynamic => |elem_payload| switch (elem_payload.data) { + .array => { + std.debug.assert(!elem_payload.is_type_val); + if (std.mem.eql(u8, "len", name)) { + return try Type.typeValFromIP(analyser, .usize_type); + } + }, + else => {}, }, else => {}, }, @@ -1152,7 +1202,7 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou } if (std.mem.eql(u8, "ptr", name)) { - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = .many, @@ -1162,7 +1212,7 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou }, }, .is_type_val = false, - }; + } }; } }, .many, .c => {}, @@ -1217,9 +1267,9 @@ fn resolveInternPoolValue(analyser: *Analyser, node_handle: NodeWithHandle) erro defer analyser.resolve_number_literal_values = old_resolve_number_literal_values; const resolved_length = try analyser.resolveTypeOfNode(node_handle) orelse return null; - switch (resolved_length.data) { + switch (resolved_length) { .ip_index => |payload| return payload.index, - else => return null, + .dynamic, .either => return null, } } @@ -1228,14 +1278,20 @@ fn resolveIntegerLiteral(analyser: *Analyser, comptime T: type, node_handle: Nod return analyser.ip.toInt(ip_index, T); } -fn extractArrayData(data: *Type.Data) ?*Type.Data { - return switch (data.*) { - .array => data, - .pointer => |*p| switch (p.elem_ty.data) { - .array => &p.elem_ty.data, +fn extractArrayData(ty: *Type) ?*Type.Data { + return switch (ty.*) { + .dynamic => |*payload| switch (payload.data) { + .array => &payload.data, + .pointer => |*p| switch (p.elem_ty.*) { + .dynamic => |*inner_payload| switch (inner_payload.data) { + .array => &inner_payload.data, + else => null, + }, + .ip_index, .either => null, + }, else => null, }, - else => null, + .ip_index, .either => null, }; } @@ -1440,7 +1496,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e if (var_decl.ast.type_node != 0) blk: { const type_node: NodeWithHandle = .{ .node = var_decl.ast.type_node, .handle = handle }; const decl_type = try analyser.resolveTypeOfNodeInternal(type_node) orelse break :blk; - if (decl_type.isMetaType()) { + if (decl_type.isMetaType(analyser)) { fallback_type = decl_type; break :blk; } @@ -1462,10 +1518,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e if (!is_escaped_identifier) { if (std.mem.eql(u8, name, "_")) return null; if (try analyser.resolvePrimitive(name)) |primitive| { - return .{ - .data = .{ .ip_index = .{ .index = primitive } }, - .is_type_val = analyser.ip.typeOf(primitive) == .type_type, - }; + return .{ .ip_index = .{ .index = primitive } }; } } @@ -1487,9 +1540,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const callee: NodeWithHandle = .{ .node = call.ast.fn_expr, .handle = handle }; const ty = try analyser.resolveTypeOfNodeInternal(callee) orelse return null; const func_ty = try analyser.resolveFuncProtoOfCallable(ty) orelse return null; - if (func_ty.is_type_val) return null; + if (func_ty.isTypeVal(analyser)) return null; - const func_node_handle = func_ty.data.other; // this assumes that function types can only be Ast nodes + const func_node_handle = func_ty.dynamic.data.other; // this assumes that function types can only be Ast nodes const func_node = func_node_handle.node; const func_handle = func_node_handle.handle; const func_tree = func_handle.tree; @@ -1514,7 +1567,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e if (!isMetaType(func_tree, param.type_expr)) continue; const argument_type = (try analyser.resolveTypeOfNodeInternal(.{ .node = arg, .handle = handle })) orelse continue; - if (!argument_type.is_type_val) continue; + if (!argument_type.isTypeVal(analyser)) continue; try analyser.bound_type_params.put(analyser.gpa, .{ .func = func_node, @@ -1609,12 +1662,12 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, .optional_type => { const child_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].lhs, .handle = handle }) orelse return null; - if (!child_ty.is_type_val) return null; + if (!child_ty.isTypeVal(analyser)) 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 .{ .dynamic = .{ .data = .{ .optional = child_ty_ptr }, .is_type_val = true } }; }, .ptr_type_aligned, .ptr_type_sentinel, @@ -1626,11 +1679,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const sentinel = try analyser.resolveInternPoolValue(.{ .node = ptr_info.ast.sentinel, .handle = handle }) orelse .none; 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; + if (!elem_ty.isTypeVal(analyser)) return null; const elem_ty_ptr = try analyser.arena.allocator().create(Type); elem_ty_ptr.* = elem_ty; - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .size = ptr_info.size, @@ -1640,7 +1693,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, }, .is_type_val = true, - }; + } }; }, .array_type, .array_type_sentinel, @@ -1651,19 +1704,19 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const sentinel = try analyser.resolveInternPoolValue(.{ .node = array_info.ast.sentinel, .handle = handle }) orelse .none; 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; + if (!elem_ty.isTypeVal(analyser)) return null; const elem_ty_ptr = try analyser.arena.allocator().create(Type); elem_ty_ptr.* = elem_ty; - return .{ + return .{ .dynamic = .{ .data = .{ .array = .{ .elem_count = elem_count, .sentinel = sentinel, .elem_ty = elem_ty_ptr, } }, .is_type_val = true, - }; + } }; }, .array_init_one, .array_init_one_comma, @@ -1685,19 +1738,19 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const elem_ty_slice = try analyser.arena.allocator().alloc(Type, array_init_info.ast.elements.len); for (elem_ty_slice, array_init_info.ast.elements) |*elem_ty, element| { elem_ty.* = try analyser.resolveTypeOfNodeInternal(.{ .node = element, .handle = handle }) orelse return null; - elem_ty.* = elem_ty.typeOf(analyser); + elem_ty.* = try elem_ty.typeOf(analyser); } - return .{ + return .{ .dynamic = .{ .data = .{ .tuple = elem_ty_slice }, .is_type_val = false, - }; + } }; }, .error_union => { const error_set = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].lhs, .handle = handle }) orelse return null; - if (!error_set.is_type_val) return null; + if (!error_set.isTypeVal(analyser)) return null; const payload = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].rhs, .handle = handle }) orelse return null; - if (!payload.is_type_val) return null; + if (!payload.isTypeVal(analyser)) return null; const error_set_ptr = try analyser.arena.allocator().create(Type); error_set_ptr.* = error_set; @@ -1705,13 +1758,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const payload_ptr = try analyser.arena.allocator().create(Type); payload_ptr.* = payload; - return .{ + return .{ .dynamic = .{ .data = .{ .error_union = .{ .error_set = error_set_ptr, .payload = payload_ptr, } }, .is_type_val = true, - }; + } }; }, // TODO represent through InternPool @@ -1758,30 +1811,32 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e } if (has_unresolved_fields) return null; - return .{ + return .{ .dynamic = .{ .data = .{ .tuple = elem_ty_slice }, .is_type_val = true, - }; + } }; } // TODO: use map? idk const document_scope = try handle.getDocumentScope(); return .{ - .data = .{ - .container = .{ - .handle = handle, - .scope = for (0..document_scope.scopes.len) |scope_index| { - switch (document_scope.getScopeTag(@enumFromInt(scope_index))) { - .container, .container_usingnamespace => if (document_scope.getScopeAstNode(@enumFromInt(scope_index)).? == node) { - break @enumFromInt(scope_index); - }, - else => {}, - } - } else unreachable, // is this safe? idk + .dynamic = .{ + .data = .{ + .container = .{ + .handle = handle, + .scope = for (0..document_scope.scopes.len) |scope_index| { + switch (document_scope.getScopeTag(@enumFromInt(scope_index))) { + .container, .container_usingnamespace => if (document_scope.getScopeAstNode(@enumFromInt(scope_index)).? == node) { + break @enumFromInt(scope_index); + }, + else => {}, + } + } else unreachable, // is this safe? idk + }, }, + .is_type_val = true, }, - .is_type_val = true, }; }, .builtin_call, @@ -1824,7 +1879,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .node = params[0], .handle = handle, })) orelse return null; - return resolved_type.typeOf(analyser); + return try resolved_type.typeOf(analyser); } if (std.mem.eql(u8, call_name, "@typeInfo")) { @@ -1849,7 +1904,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const new_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null; - return .{ + return .{ .dynamic = .{ .data = .{ .container = .{ .handle = new_handle, @@ -1857,7 +1912,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, }, .is_type_val = true, - }; + } }; } if (std.mem.eql(u8, call_name, "@cImport")) { @@ -1865,7 +1920,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const new_handle = analyser.store.getOrLoadHandle(cimport_uri) orelse return null; - return .{ + return .{ .dynamic = .{ .data = .{ .container = .{ .handle = new_handle, @@ -1873,7 +1928,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, }, .is_type_val = true, - }; + } }; } if (std.mem.eql(u8, call_name, "@FieldType")) { @@ -1883,16 +1938,20 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .node = params[0], .handle = handle, })) orelse return null; - if (container_type.data != .container) return null; + const payload = switch (container_type) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + if (payload.data != .container) return null; const field_name = try analyser.resolveStringLiteral(.{ .node = params[1], .handle = handle, }) orelse return null; - const field = try analyser.lookupSymbolContainer(container_type.data.container, field_name, .field) orelse return null; + const field = try analyser.lookupSymbolContainer(payload.data.container, field_name, .field) orelse return null; const result = try field.resolveType(analyser) orelse return null; - return result.typeOf(analyser); + return try result.typeOf(analyser); } if (std.mem.eql(u8, call_name, "@field")) { @@ -1915,18 +1974,18 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e return analyser.instanceStdBuiltinType("SourceLocation"); } if (std.mem.eql(u8, call_name, "@compileError")) { - return .{ .data = .{ .compile_error = node_handle }, .is_type_val = false }; + return .{ .dynamic = .{ .data = .{ .compile_error = node_handle }, .is_type_val = false } }; } if (std.mem.eql(u8, call_name, "@Vector")) { if (params.len != 2) return null; const child_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = params[1], .handle = handle }) orelse return null; - if (!child_ty.is_type_val) return null; + if (!child_ty.isTypeVal(analyser)) return null; - const child_ty_ip_index = switch (child_ty.data) { + const child_ty_ip_index = switch (child_ty) { .ip_index => |payload| payload.index, - else => return null, + .dynamic, .either => return null, }; const len = try analyser.resolveIntegerLiteral(u32, .{ .node = params[0], .handle = handle }) orelse @@ -1939,10 +1998,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }, }); - return .{ - .data = .{ .ip_index = .{ .index = vector_ty_ip_index } }, - .is_type_val = true, - }; + return .{ .ip_index = .{ .index = vector_ty_ip_index } }; } }, .fn_proto, @@ -1957,7 +2013,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e return Type.typeVal(node_handle); } - return .{ .data = .{ .other = .{ .node = node, .handle = handle } }, .is_type_val = false }; + return .{ .dynamic = .{ .data = .{ .other = .{ .node = node, .handle = handle } }, .is_type_val = false } }; }, .@"if", .if_simple => { const if_node = ast.fullIf(tree, node).?; @@ -1979,7 +2035,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e if (try analyser.resolveTypeOfNodeInternal(.{ .handle = handle, .node = if_node.ast.else_expr })) |t| either.appendAssumeCapacity(.{ .type = t, .descriptor = try std.fmt.allocPrint(analyser.arena.allocator(), "!({s})", .{offsets.nodeToSlice(tree, if_node.ast.cond_expr)}) }); - return Type.fromEither(analyser.arena.allocator(), either.constSlice()); + return Type.fromEither(analyser, either.constSlice()); }, .@"switch", .switch_comma, @@ -2005,7 +2061,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }); } - return Type.fromEither(analyser.arena.allocator(), either.items); + return Type.fromEither(analyser, either.items); }, .@"while", .while_simple, @@ -2062,7 +2118,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e else => unreachable, }; if (has_zero_statements) { - return .{ .data = .{ .ip_index = .{ .index = .void_value } }, .is_type_val = false }; + return .{ .ip_index = .{ .index = .void_value } }; } const label_token = ast.blockLabel(tree, node) orelse return null; @@ -2082,7 +2138,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e } }, - .for_range => return .{ .data = .{ .other = .{ .node = node, .handle = handle } }, .is_type_val = false }, + .for_range => return .{ .dynamic = .{ .data = .{ .other = .{ .node = node, .handle = handle } }, .is_type_val = false } }, .equal_equal, .bang_equal, @@ -2094,10 +2150,10 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const ty = try analyser.resolveTypeOfNodeInternal( .{ .node = datas[node].lhs, .handle = handle }, ) orelse return try Type.typeValFromIP(analyser, .bool_type); - const typeof = ty.typeOf(analyser); + const typeof = try ty.typeOf(analyser); - if (typeof.data == .ip_index) { - const key = analyser.ip.indexToKey(typeof.data.ip_index.index); + if (typeof == .ip_index) { + const key = analyser.ip.indexToKey(typeof.ip_index.index); if (key == .vector_type) { const vector_ty_ip_index = try analyser.ip.get(analyser.gpa, .{ .vector_type = .{ @@ -2181,7 +2237,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .ty = error_set_type, .error_tag_name = name_index, } }); - return .{ .data = .{ .ip_index = .{ .index = error_value } }, .is_type_val = false }; + return .{ .ip_index = .{ .index = error_value } }; }, .char_literal => return try Type.typeValFromIP(analyser, .comptime_int_type), @@ -2232,10 +2288,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .failure => unreachable, // checked above }; - return .{ - .data = .{ .ip_index = .{ .index = value orelse try analyser.ip.getUnknown(analyser.gpa, ty) } }, - .is_type_val = false, - }; + return if (value) |v| .{ .ip_index = .{ .index = v } } else try Type.typeValFromIP(analyser, ty); }, .enum_literal => return try Type.typeValFromIP(analyser, .enum_literal_type), @@ -2268,7 +2321,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e => { const elem_idx = datas[node].lhs; var elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = elem_idx, .handle = handle }) orelse return null; - const arr_data = extractArrayData(&elem_ty.data) orelse return null; + const arr_data = extractArrayData(&elem_ty) orelse return null; const mult_idx = datas[node].rhs; const mult_lit = try analyser.resolveIntegerLiteral(u64, .{ .node = mult_idx, .handle = handle }); @@ -2283,11 +2336,11 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e => { const l_elem_idx = datas[node].lhs; var l_elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = l_elem_idx, .handle = handle }) orelse return null; - const l_arr_data = extractArrayData(&l_elem_ty.data) orelse return null; + const l_arr_data = extractArrayData(&l_elem_ty) orelse return null; const r_elem_idx = datas[node].rhs; var r_elem_ty = try analyser.resolveTypeOfNodeInternal(.{ .node = r_elem_idx, .handle = handle }) orelse return null; - const r_arr_data = extractArrayData(&r_elem_ty.data) orelse return null; + const r_arr_data = extractArrayData(&r_elem_ty) orelse return null; if (l_arr_data.array.elem_count != null) { if (r_arr_data.array.elem_count) |right_count| { @@ -2361,15 +2414,28 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e /// Represents a resolved Zig type. /// This is the return type of `resolveTypeOfNode`. -pub const Type = struct { - data: Data, - /// If true, the type `type`, the attached data is the value of the type value. - /// ```zig - /// const foo = u32; // is_type_val == true - /// const bar = @as(u32, ...); // is_type_val == false - /// ``` - /// if `data == .ip_index` then this field is equivalent to `typeOf(index) == .type_type` - is_type_val: bool, +pub const Type = union(enum) { + dynamic: struct { + data: Data, + /// If true, the type `type`, the attached data is the value of the type value. + /// ```zig + /// const foo = u32; // is_type_val == true + /// const bar = @as(u32, ...); // is_type_val == false + /// ``` + /// if `data == .ip_index` then this field is equivalent to `typeOf(index) == .type_type` + is_type_val: bool, + }, + + /// Primitive type: `u8`, `bool`, `type`, etc. + /// Primitive value: `true`, `false`, `null`, `undefined` + ip_index: struct { + node: ?NodeWithHandle = null, + /// this stores both the type and the value + index: InternPool.Index, + }, + + /// Branching types + either: []const TypeWithDescriptor, pub const Data = union(enum) { /// - `*const T` @@ -2422,25 +2488,23 @@ pub const Type = struct { /// - `@compileError("")` compile_error: NodeWithHandle, - - /// Branching types - either: []const EitherEntry, - - /// Primitive type: `u8`, `bool`, `type`, etc. - /// Primitive value: `true`, `false`, `null`, `undefined` - ip_index: struct { - node: ?NodeWithHandle = null, - /// this stores both the type and the value - index: InternPool.Index, - }, - - pub const EitherEntry = struct { - /// the `is_type_val` property is inherited from the containing `Type` - type_data: Data, - descriptor: []const u8, - }; }; + pub fn isTypeVal(self: Type, analyser: *Analyser) bool { + switch (self) { + .dynamic => |payload| return payload.is_type_val, + .ip_index => |payload| return analyser.ip.isType(payload.index), + .either => |entries| { + for (entries) |entry| { + if (entry.type.isTypeVal(analyser)) { + return true; + } + } + return false; + }, + } + } + pub fn hash32(self: Type) u32 { return @truncate(self.hash64()); } @@ -2452,9 +2516,25 @@ pub const Type = struct { } pub fn hashWithHasher(self: Type, hasher: anytype) void { - hasher.update(&.{ @intFromBool(self.is_type_val), @intFromEnum(self.data) }); + hasher.update(&.{@intFromEnum(self)}); + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index => |payload| { + std.hash.autoHash(hasher, payload.node); + std.hash.autoHash(hasher, payload.index); + return; + }, + .either => |entries| { + for (entries) |entry| { + hasher.update(entry.descriptor); + entry.type.hashWithHasher(hasher); + } + return; + }, + }; + hasher.update(&.{ @intFromBool(payload.is_type_val), @intFromEnum(payload.data) }); - switch (self.data) { + switch (payload.data) { .pointer => |info| { std.hash.autoHash(hasher, info.size); std.hash.autoHash(hasher, info.sentinel); @@ -2486,39 +2566,48 @@ pub const Type = struct { std.hash.autoHash(hasher, node_handle.node); hasher.update(node_handle.handle.uri); }, - .either => |entries| { - for (entries) |entry| { - hasher.update(entry.descriptor); - const entry_ty: Type = .{ .data = entry.type_data, .is_type_val = self.is_type_val }; - entry_ty.hashWithHasher(hasher); - } - }, - .ip_index => |payload| { - std.hash.autoHash(hasher, payload.node); - std.hash.autoHash(hasher, payload.index); - }, } } pub fn eql(a: Type, b: Type) bool { - if (a.is_type_val != b.is_type_val) return false; - if (@intFromEnum(a.data) != @intFromEnum(b.data)) return false; + if (@intFromEnum(a) != @intFromEnum(b)) return false; + const a_payload = switch (a) { + .dynamic => |a_payload| a_payload, + .ip_index => |a_payload| { + const b_payload = b.ip_index; + if (a_payload.index != b_payload.index) return false; + if (!std.meta.eql(a_payload.node, b_payload.node)) return false; + return true; + }, + .either => |a_entries| { + const b_entries = b.either; + if (a_entries.len != b_entries.len) return false; + for (a_entries, b_entries) |a_entry, b_entry| { + if (!std.mem.eql(u8, a_entry.descriptor, b_entry.descriptor)) return false; + if (!a_entry.type.eql(b_entry.type)) return false; + } + return true; + }, + }; + const b_payload = b.dynamic; + if (a_payload.is_type_val != b_payload.is_type_val) return false; + if (@intFromEnum(a_payload.data) != @intFromEnum(b_payload.data)) return false; - switch (a.data) { + switch (a_payload.data) { .pointer => |a_type| { - const b_type = b.data.pointer; + const b_type = b_payload.data.pointer; if (a_type.size != b_type.size) return false; if (a_type.sentinel != b_type.sentinel) return false; if (!a_type.elem_ty.eql(b_type.elem_ty.*)) return false; }, .array => |a_type| { - const b_type = b.data.array; + const b_type = b_payload.data.array; if (std.meta.eql(a_type.elem_count, b_type.elem_count)) return false; if (a_type.sentinel != b_type.sentinel) return false; if (!a_type.elem_ty.eql(b_type.elem_ty.*)) return false; }, .tuple => |a_slice| { - const b_slice = b.data.tuple; + const b_slice = b_payload.data.tuple; for (a_slice, b_slice) |a_type, b_type| { if (!a_type.eql(b_type)) return false; } @@ -2526,11 +2615,11 @@ pub const Type = struct { inline .optional, .union_tag, => |a_type, name| { - const b_type = @field(b.data, @tagName(name)); + const b_type = @field(b_payload.data, @tagName(name)); if (!a_type.eql(b_type.*)) return false; }, .error_union => |info| { - const b_info = b.data.error_union; + const b_info = b_payload.data.error_union; if (!info.payload.eql(b_info.payload.*)) return false; if ((info.error_set == null) != (b_info.error_set == null)) return false; if (info.error_set) |a_error_set| { @@ -2538,47 +2627,27 @@ pub const Type = struct { } }, .container => |a_scope_handle| { - const b_scope_handle = b.data.container; + const b_scope_handle = b_payload.data.container; if (a_scope_handle.scope != b_scope_handle.scope) return false; if (!std.mem.eql(u8, a_scope_handle.handle.uri, b_scope_handle.handle.uri)) return false; }, - .other => |a_node_handle| return a_node_handle.eql(b.data.other), - .compile_error => |a_node_handle| return a_node_handle.eql(b.data.compile_error), - .either => |a_entries| { - const b_entries = b.data.either; - - if (a_entries.len != b_entries.len) return false; - for (a_entries, b_entries) |a_entry, b_entry| { - if (!std.mem.eql(u8, a_entry.descriptor, b_entry.descriptor)) return false; - const a_entry_ty: Type = .{ .data = a_entry.type_data, .is_type_val = a.is_type_val }; - const b_entry_ty: Type = .{ .data = b_entry.type_data, .is_type_val = b.is_type_val }; - if (!a_entry_ty.eql(b_entry_ty)) return false; - } - }, - .ip_index => |a_payload| { - const b_payload = b.data.ip_index; - - if (a_payload.index != b_payload.index) return false; - if (!std.meta.eql(a_payload.node, b_payload.node)) return false; - }, + .other => |a_node_handle| return a_node_handle.eql(b_payload.data.other), + .compile_error => |a_node_handle| return a_node_handle.eql(b_payload.data.compile_error), } return true; } pub fn typeVal(node_handle: NodeWithHandle) Type { - return .{ + return .{ .dynamic = .{ .data = .{ .other = node_handle }, .is_type_val = true, - }; + } }; } pub fn typeValFromIP(analyser: *Analyser, ty: InternPool.Index) error{OutOfMemory}!Type { std.debug.assert(analyser.ip.isType(ty)); - return .{ - .data = .{ .ip_index = .{ .index = try analyser.ip.getUnknown(analyser.gpa, ty) } }, - .is_type_val = ty == .type_type, - }; + return .{ .ip_index = .{ .index = try analyser.ip.getUnknown(analyser.gpa, ty) } }; } pub const TypeWithDescriptor = struct { @@ -2586,7 +2655,8 @@ pub const Type = struct { descriptor: []const u8, }; - pub fn fromEither(arena: std.mem.Allocator, entries: []const TypeWithDescriptor) error{OutOfMemory}!?Type { + pub fn fromEither(analyser: *Analyser, entries: []const TypeWithDescriptor) error{OutOfMemory}!?Type { + const arena = analyser.arena.allocator(); if (entries.len == 0) return null; @@ -2597,120 +2667,119 @@ pub const Type = struct { // duplicates const DeduplicatorContext = struct { - pub fn hash(self: @This(), item: Type.Data.EitherEntry) u32 { + pub fn hash(self: @This(), item: TypeWithDescriptor) u32 { _ = self; - const ty: Type = .{ .data = item.type_data, .is_type_val = true }; - return ty.hash32(); + return item.type.hash32(); } - pub fn eql(self: @This(), a: Type.Data.EitherEntry, b: Type.Data.EitherEntry, b_index: usize) bool { + pub fn eql(self: @This(), a: TypeWithDescriptor, b: TypeWithDescriptor, b_index: usize) bool { _ = b_index; _ = self; - const a_ty: Type = .{ .data = a.type_data, .is_type_val = true }; - const b_ty: Type = .{ .data = b.type_data, .is_type_val = true }; - return a_ty.eql(b_ty); + return a.type.eql(b.type); } }; - const Deduplicator = std.ArrayHashMapUnmanaged(Type.Data.EitherEntry, void, DeduplicatorContext, true); + const Deduplicator = std.ArrayHashMapUnmanaged(TypeWithDescriptor, void, DeduplicatorContext, true); var deduplicator: Deduplicator = .empty; defer deduplicator.deinit(arena); - var has_type_val: bool = false; - for (entries) |entry| { - try deduplicator.put( - arena, - .{ .type_data = entry.type.data, .descriptor = entry.descriptor }, - {}, - ); - if (entry.type.is_type_val) { - has_type_val = true; - } + try deduplicator.put(arena, entry, {}); } if (deduplicator.count() == 1) return entries[0].type; - return .{ - .data = .{ .either = try arena.dupe(Type.Data.EitherEntry, deduplicator.keys()) }, - .is_type_val = has_type_val, - }; + return .{ .either = try arena.dupe(TypeWithDescriptor, deduplicator.keys()) }; } /// Resolves possible types of a type (single for all except either) /// Drops duplicates - pub fn getAllTypesWithHandles(ty: Type, arena: std.mem.Allocator) ![]const Type { + pub fn getAllTypesWithHandles(ty: Type, analyser: *Analyser) ![]const Type { + const arena = analyser.arena.allocator(); var all_types: std.ArrayListUnmanaged(Type) = .empty; - try ty.getAllTypesWithHandlesArrayList(arena, &all_types); + try ty.getAllTypesWithHandlesArrayList(analyser, &all_types); return try all_types.toOwnedSlice(arena); } - pub fn getAllTypesWithHandlesArrayList(ty: Type, arena: std.mem.Allocator, all_types: *std.ArrayListUnmanaged(Type)) !void { - switch (ty.data) { + pub fn getAllTypesWithHandlesArrayList(ty: Type, analyser: *Analyser, all_types: *std.ArrayListUnmanaged(Type)) !void { + const arena = analyser.arena.allocator(); + switch (ty) { .either => |entries| { for (entries) |entry| { - const entry_ty: Type = .{ .data = entry.type_data, .is_type_val = ty.is_type_val }; - try entry_ty.getAllTypesWithHandlesArrayList(arena, all_types); + try entry.type.getAllTypesWithHandlesArrayList(analyser, all_types); } }, - else => try all_types.append(arena, ty), + .dynamic, .ip_index => try all_types.append(arena, ty), } } pub fn instanceTypeVal(self: Type, analyser: *Analyser) error{OutOfMemory}!?Type { - if (!self.is_type_val) return null; - return switch (self.data) { + switch (self) { + .dynamic => |payload| { + if (!payload.is_type_val) return null; + return .{ .dynamic = .{ .data = payload.data, .is_type_val = false } }; + }, .ip_index => |payload| { if (payload.index == .unknown_type) return null; - return .{ - .data = .{ - .ip_index = .{ - .index = try analyser.ip.getUnknown(analyser.gpa, payload.index), - .node = payload.node, - }, - }, - .is_type_val = payload.index == .type_type, - }; + if (!analyser.ip.isType(payload.index)) return null; + return try typeValFromIP(analyser, payload.index); }, - else => .{ .data = self.data, .is_type_val = false }, - }; + .either => |entries| { + var either = try std.ArrayListUnmanaged(Type.TypeWithDescriptor).initCapacity(analyser.arena.allocator(), entries.len); + for (entries) |entry| { + either.appendAssumeCapacity(.{ + .type = try entry.type.instanceTypeVal(analyser) orelse continue, + .descriptor = entry.descriptor, + }); + } + return try fromEither(analyser, either.items); + }, + } } - pub fn typeOf(self: Type, analyser: *Analyser) Type { - if (self.is_type_val) { - return .{ - .data = .{ .ip_index = .{ .index = .type_type } }, - .is_type_val = true, - }; + pub fn typeOf(self: Type, analyser: *Analyser) error{OutOfMemory}!Type { + if (self.isTypeVal(analyser)) { + return .{ .ip_index = .{ .index = .type_type } }; } - if (self.data == .ip_index) { - return .{ - .data = .{ .ip_index = .{ .index = analyser.ip.typeOf(self.data.ip_index.index) } }, - .is_type_val = true, - }; + switch (self) { + .dynamic => |payload| return .{ .dynamic = .{ .data = payload.data, .is_type_val = true } }, + .ip_index => |payload| return .{ .ip_index = .{ .index = analyser.ip.typeOf(payload.index) } }, + .either => |entries| { + var either = try std.ArrayListUnmanaged(Type.TypeWithDescriptor).initCapacity(analyser.arena.allocator(), entries.len); + for (entries) |entry| { + either.appendAssumeCapacity(.{ .type = try entry.type.typeOf(analyser), .descriptor = entry.descriptor }); + } + return try fromEither(analyser, either.items) orelse .{ .either = either.items }; + }, } - - return .{ - .data = self.data, - .is_type_val = true, - }; } fn isRoot(self: Type) bool { - switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; + switch (payload.data) { .container => |container_scope_handle| return container_scope_handle.scope == Scope.Index.root, else => return false, } } pub fn isContainerType(self: Type) bool { - return self.data == .container; + return switch (self) { + .dynamic => |payload| payload.data == .container, + .ip_index => false, + }; } fn getContainerKind(self: Type) ?std.zig.Token.Tag { - const scope_handle = switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + const scope_handle = switch (payload.data) { .container => |s| s, else => return null, }; @@ -2729,11 +2798,19 @@ pub const Type = struct { } pub fn isStructType(self: Type) bool { - return self.data == .tuple or self.isContainerKind(.keyword_struct) or self.isRoot(); + if (self.isContainerKind(.keyword_struct) or self.isRoot()) return true; + return switch (self) { + .dynamic => |payload| payload.data == .tuple, + .ip_index, .either => false, + }; } pub fn isNamespace(self: Type) bool { - const scope_handle = switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; + const scope_handle = switch (payload.data) { .tuple => |fields| return fields.len == 0, .container => |scope_handle| scope_handle, else => return false, @@ -2763,33 +2840,45 @@ pub const Type = struct { } pub fn isTaggedUnion(self: Type) bool { - return switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; + return switch (payload.data) { .container => |scope_handle| ast.isTaggedUnion(scope_handle.handle.tree, scope_handle.toNode()), else => false, }; } /// returns whether the given type is of type `type`. - pub fn isMetaType(self: Type) bool { - if (!self.is_type_val) return false; - switch (self.data) { - .other => |node_handle| return Analyser.isMetaType(node_handle.handle.tree, node_handle.node), + pub fn isMetaType(self: Type, analyser: *Analyser) bool { + if (!self.isTypeVal(analyser)) return false; + const payload = switch (self) { + .dynamic => |payload| payload, .ip_index => |payload| return payload.index == .type_type, + .either => return false, + }; + switch (payload.data) { + .other => |node_handle| return Analyser.isMetaType(node_handle.handle.tree, node_handle.node), else => return false, } } pub fn isEnumLiteral(self: Type, analyser: *Analyser) bool { - switch (self.data) { + switch (self) { .ip_index => |payload| return analyser.ip.typeOf(payload.index) == .enum_literal_type, - else => return false, + .dynamic, .either => return false, } } pub fn resolveDeclLiteralResultType(ty: Type) Type { var result_type = ty; while (true) { - result_type = switch (result_type.data) { + const payload = switch (result_type) { + .dynamic => |payload| payload, + .ip_index, .either => return result_type, + }; + result_type = switch (payload.data) { .optional => |child_ty| child_ty.*, .error_union => |info| info.payload.*, .pointer => |child_ty| child_ty.elem_ty.*, @@ -2799,8 +2888,12 @@ pub const Type = struct { } pub fn isTypeFunc(self: Type) bool { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; var buf: [1]Ast.Node.Index = undefined; - return switch (self.data) { + return switch (payload.data) { .other => |node_handle| if (node_handle.handle.tree.fullFnProto(&buf, node_handle.node)) |fn_proto| blk: { break :blk isTypeFunction(node_handle.handle.tree, fn_proto); } else false, @@ -2810,7 +2903,11 @@ pub const Type = struct { /// Returns whether the given function has a `anytype` parameter. pub fn isGenericFunc(self: Type) bool { - return switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; + return switch (payload.data) { .other => |node_handle| { var buf: [1]Ast.Node.Index = undefined; const fn_proto = node_handle.handle.tree.fullFnProto(&buf, node_handle.node) orelse return false; @@ -2827,7 +2924,11 @@ pub const Type = struct { } pub fn isFunc(self: Type) bool { - return switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return false, + }; + return switch (payload.data) { .other => |node_handle| switch (node_handle.handle.tree.nodes.items(.tag)[node_handle.node]) { .fn_proto, .fn_proto_multi, @@ -2842,7 +2943,11 @@ pub const Type = struct { } pub fn typeDefinitionToken(self: Type) !?TokenWithHandle { - return switch (self.data) { + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + return switch (payload.data) { .container => |scope_handle| .{ .token = scope_handle.handle.tree.firstToken(scope_handle.toNode()), .handle = scope_handle.handle, @@ -2855,9 +2960,14 @@ pub const Type = struct { }; } - pub fn docComments(self: Type, allocator: std.mem.Allocator) error{OutOfMemory}!?[]const u8 { - if (self.is_type_val) { - switch (self.data) { + pub fn docComments(self: Type, analyser: *Analyser) error{OutOfMemory}!?[]const u8 { + const allocator = analyser.arena.allocator(); + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + if (payload.is_type_val) { + switch (payload.data) { .container => |scope_handle| return try getDocComments(allocator, scope_handle.handle.tree, scope_handle.toNode()), .other => |node_handle| return try getDocComments(allocator, node_handle.handle.tree, node_handle.node), else => {}, @@ -2871,21 +2981,24 @@ pub const Type = struct { analyser: *Analyser, symbol: []const u8, ) error{OutOfMemory}!?DeclWithHandle { - const scope_handle = switch (self.data) { - .container => |s| s, + const payload = switch (self) { + .dynamic => |payload| payload, + .ip_index => return null, .either => |entries| { // TODO: Return all options instead of first valid one for (entries) |entry| { - const entry_ty: Type = .{ .data = entry.type_data, .is_type_val = self.is_type_val }; - if (try entry_ty.lookupSymbol(analyser, symbol)) |decl| { + if (try entry.type.lookupSymbol(analyser, symbol)) |decl| { return decl; } } return null; }, + }; + const scope_handle = switch (payload.data) { + .container => |s| s, else => return null, }; - if (self.is_type_val) { + if (payload.is_type_val) { if (try analyser.lookupSymbolContainer(scope_handle, symbol, .other)) |decl| return decl; if (self.isEnumType() or self.isTaggedUnion()) @@ -2899,13 +3012,13 @@ pub const Type = struct { return analyser.lookupSymbolContainer(scope_handle, symbol, .other); } - pub fn fmt(ty: Type, analyser: *Analyser, options: FormatOptions) std.fmt.Formatter(format) { - const typeof = ty.typeOf(analyser); + pub fn fmt(ty: Type, analyser: *Analyser, options: FormatOptions) error{OutOfMemory}!std.fmt.Formatter(format) { + const typeof = try ty.typeOf(analyser); return .{ .data = .{ .ty = typeof, .analyser = analyser, .options = options } }; } pub fn fmtTypeVal(ty: Type, analyser: *Analyser, options: FormatOptions) std.fmt.Formatter(format) { - std.debug.assert(ty.is_type_val); + std.debug.assert(ty.isTypeVal(analyser)); return .{ .data = .{ .ty = ty, .analyser = analyser, .options = options } }; } @@ -2930,7 +3043,13 @@ pub const Type = struct { const ty = ctx.ty; const analyser = ctx.analyser; - switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index => |payload| return analyser.ip.print(payload.index, writer, .{}), + .either => return writer.writeAll("either type"), // TODO + }; + + switch (payload.data) { .pointer => |info| { switch (info.size) { .one => try writer.writeByte('*'), @@ -3109,8 +3228,6 @@ pub const Type = struct { .merge_error_sets => if (ctx.options.truncate_container_decls) try writer.writeAll("error{...}") else try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)), else => try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)), }, - .ip_index => |payload| try analyser.ip.print(payload.index, writer, .{}), - .either => try writer.writeAll("either type"), // TODO .compile_error => |node_handle| try writer.writeAll(offsets.nodeToSlice(node_handle.handle.tree, node_handle.node)), } } @@ -3135,10 +3252,10 @@ pub fn instanceStdBuiltinType(analyser: *Analyser, type_name: []const u8) error{ const builtin_uri = try URI.fromPath(analyser.arena.allocator(), builtin_path); const builtin_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null; - const builtin_root_struct_type: Type = .{ + const builtin_root_struct_type: Type = .{ .dynamic = .{ .data = .{ .container = .{ .handle = builtin_handle, .scope = .root } }, .is_type_val = true, - }; + } }; const builtin_type_decl = try builtin_root_struct_type.lookupSymbol(analyser, type_name) orelse return null; const builtin_type = try builtin_type_decl.resolveType(analyser) orelse return null; @@ -3321,7 +3438,7 @@ pub fn getFieldAccessType( const ty = try analyser.resolveFuncProtoOfCallable(current_type.?) orelse return null; // Can't call a function type, we need a function type instance. - if (current_type.?.is_type_val) return null; + if (current_type.?.isTypeVal(analyser)) return null; // TODO Actually bind params here when calling functions instead of just skipping args. current_type = try analyser.resolveReturnType(ty) orelse return null; @@ -3387,7 +3504,7 @@ pub fn getFieldAccessType( }); const uri = try analyser.store.uriFromImportStr(analyser.arena.allocator(), handle, import_str) orelse return null; const node_handle = analyser.store.getOrLoadHandle(uri) orelse return null; - current_type = .{ + current_type = .{ .dynamic = .{ .data = .{ .container = .{ .handle = node_handle, @@ -3395,7 +3512,7 @@ pub fn getFieldAccessType( }, }, .is_type_val = true, - }; + } }; _ = tokenizer.next(); // eat the .r_paren continue; // Outermost `while` } @@ -3789,7 +3906,7 @@ pub const DeclWithHandle = struct { else => {}, } if (try self.resolveType(analyser)) |resolved_type| { - if (resolved_type.is_type_val) { + if (resolved_type.isTypeVal(analyser)) { if (try resolved_type.typeDefinitionToken()) |token| { return token; } @@ -4034,7 +4151,7 @@ pub const DeclWithHandle = struct { }); } - const maybe_type = try Type.fromEither(analyser.arena.allocator(), possible.items); + const maybe_type = try Type.fromEither(analyser, possible.items); if (maybe_type) |ty| analyser.resolved_callsites.getPtr(pay).?.* = ty; break :blk maybe_type; } @@ -4043,7 +4160,7 @@ pub const DeclWithHandle = struct { .{ .node = param.type_expr, .handle = self.handle }, ) orelse return null; - if (param_type.isMetaType()) { + if (param_type.isMetaType(analyser)) { if (analyser.bound_type_params.get(.{ .func = pay.func, .param_index = pay.param_index })) |resolved_type| { break :blk resolved_type; } @@ -4091,7 +4208,11 @@ pub const DeclWithHandle = struct { .node = tree.nodes.items(.data)[pay.node].rhs, .handle = self.handle, }) orelse return null; - break :blk switch (node.data) { + const payload = switch (node) { + .dynamic => |payload| payload, + .ip_index, .either => break :blk null, + }; + break :blk switch (payload.data) { .array => |array_info| try array_info.elem_ty.instanceTypeVal(analyser), .tuple => try analyser.resolveTupleFieldType(node, pay.index), else => null, @@ -4129,9 +4250,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); + resolved_ty_ptr.* = try resolved_ty.typeOf(analyser); - return .{ + return .{ .dynamic = .{ .data = .{ .pointer = .{ .elem_ty = resolved_ty_ptr, .sentinel = .none, @@ -4139,7 +4260,7 @@ pub const DeclWithHandle = struct { .size = .one, } }, .is_type_val = false, - }; + } }; } }; @@ -4202,7 +4323,7 @@ pub fn collectDeclarationsOfContainer( const alias_type = try analyser.resolveTypeOfNode(.{ .node = node, .handle = handle }) orelse continue; const func_ty = try analyser.resolveFuncProtoOfCallable(alias_type) orelse continue; - if (!try analyser.firstParamIs(func_ty, .{ + if (!try analyser.firstParamIs(func_ty, .{ .dynamic = .{ .data = .{ .container = .{ .handle = handle, @@ -4210,7 +4331,7 @@ pub fn collectDeclarationsOfContainer( }, }, .is_type_val = true, - })) continue; + } })) continue; } }, else => {}, @@ -4255,31 +4376,37 @@ fn collectUsingnamespaceDeclarationsOfContainer( .handle = handle, })) orelse return; - switch (use_expr.data) { - .container => |container_scope| { - try analyser.collectDeclarationsOfContainer( - container_scope, - original_handle, - instance_access, - decl_collection, - ); + switch (use_expr) { + .dynamic => |payload| switch (payload.data) { + .container => |container_scope| { + try analyser.collectDeclarationsOfContainer( + container_scope, + original_handle, + instance_access, + decl_collection, + ); + }, + else => {}, }, .either => |entries| { for (entries) |entry| { - switch (entry.type_data) { - .container => |container_scope| { - try analyser.collectDeclarationsOfContainer( - container_scope, - original_handle, - instance_access, - decl_collection, - ); + switch (entry.type) { + .dynamic => |payload| switch (payload.data) { + .container => |container_scope| { + try analyser.collectDeclarationsOfContainer( + container_scope, + original_handle, + instance_access, + decl_collection, + ); + }, + else => {}, }, - else => continue, + .ip_index, .either => {}, } } }, - else => return, + .ip_index => {}, } } @@ -4405,7 +4532,7 @@ pub fn innermostBlockScope(document_scope: DocumentScope, source_index: usize) A pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) error{OutOfMemory}!Type { const document_scope = try handle.getDocumentScope(); var current: DocumentScope.Scope.Index = @enumFromInt(0); - if (document_scope.scopes.len == 1) return .{ + if (document_scope.scopes.len == 1) return .{ .dynamic = .{ .data = .{ .container = .{ .handle = handle, @@ -4413,7 +4540,7 @@ pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) er }, }, .is_type_val = true, - }; + } }; var scope_iterator = iterateEnclosingScopes(&document_scope, source_index); while (scope_iterator.next().unwrap()) |scope_index| { @@ -4422,7 +4549,7 @@ pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) er else => {}, } } - return .{ + return .{ .dynamic = .{ .data = .{ .container = .{ .handle = handle, @@ -4430,7 +4557,7 @@ pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) er }, }, .is_type_val = true, - }; + } }; } fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const u8, handle: *DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { @@ -4445,7 +4572,7 @@ fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const const expr_type = (try analyser.resolveTypeOfNodeUncached(expr)) orelse continue; - if (!expr_type.is_type_val) continue; + if (!expr_type.isTypeVal(analyser)) continue; if (try expr_type.lookupSymbol(analyser, symbol)) |candidate| { if (candidate.handle == handle or candidate.isPublic()) { @@ -4577,7 +4704,11 @@ pub fn lookupSymbolFieldInit( if (try analyser.resolveOptionalUnwrap(container_type)) |unwrapped| container_type = unwrapped; - const container_scope = switch (container_type.data) { + const payload = switch (container_type) { + .dynamic => |payload| payload, + .ip_index, .either => return null, + }; + const container_scope = switch (payload.data) { .container => |s| s, else => return null, }; @@ -4592,7 +4723,7 @@ pub fn lookupSymbolFieldInit( var resolved_type = try decl.resolveType(analyser) orelse return null; resolved_type = try analyser.resolveReturnType(resolved_type) orelse resolved_type; resolved_type = resolved_type.resolveDeclLiteralResultType(); - if (resolved_type.eql(container_type) or resolved_type.eql(container_type.typeOf(analyser))) return decl; + if (resolved_type.eql(container_type) or resolved_type.eql(try container_type.typeOf(analyser))) return decl; return null; }, .keyword_enum, .keyword_union => return try analyser.lookupSymbolContainer(container_scope, field_name, .field), @@ -4799,9 +4930,9 @@ pub fn resolveExpressionTypeFromAncestors( const ty = try analyser.resolveTypeOfNode(.{ .node = call.ast.fn_expr, .handle = handle }) orelse return null; const fn_type = try analyser.resolveFuncProtoOfCallable(ty) orelse return null; - if (fn_type.is_type_val) return null; + if (fn_type.isTypeVal(analyser)) return null; - const fn_node_handle = fn_type.data.other; // this assumes that function types can only be Ast nodes + const fn_node_handle = fn_type.dynamic.data.other; // this assumes that function types can only be Ast nodes const param_decl: Declaration.Param = .{ .param_index = @truncate(arg_index + @intFromBool(try analyser.hasSelfParam(fn_type))), .func = fn_node_handle.node, @@ -4934,7 +5065,7 @@ pub fn getSymbolFieldAccesses( if (try analyser.getFieldAccessType(handle, source_index, held_loc)) |ty| { const container_handle = try analyser.resolveDerefType(ty) orelse ty; - const container_handle_nodes = try container_handle.getAllTypesWithHandles(arena); + const container_handle_nodes = try container_handle.getAllTypesWithHandles(analyser); for (container_handle_nodes) |t| { try decls_with_handles.append(arena, (try t.lookupSymbol(analyser, name)) orelse continue); @@ -5024,7 +5155,7 @@ pub fn referencedTypes( resolved_type: Type, collector: *ReferencedType.Collector, ) error{OutOfMemory}!void { - if (resolved_type.is_type_val) return; + if (resolved_type.isTypeVal(analyser)) return; analyser.resolved_nodes.clearRetainingCapacity(); try analyser.addReferencedTypes(resolved_type, collector.*); } @@ -5036,7 +5167,7 @@ fn addReferencedTypesFromNode( ) error{OutOfMemory}!void { if (analyser.resolved_nodes.contains(.{ .node = node_handle.node, .uri = node_handle.handle.uri })) return; const ty = try analyser.resolveTypeOfNodeInternal(node_handle) orelse return; - if (!ty.is_type_val) return; + if (!ty.isTypeVal(analyser)) return; var collector: ReferencedType.Collector = .{ .referenced_types = referenced_types }; try analyser.referencedTypesFromNodeInternal(node_handle, &collector); try analyser.addReferencedTypes(ty, collector); @@ -5051,7 +5182,12 @@ fn addReferencedTypes( const referenced_types = collector.referenced_types; const arena = analyser.arena.allocator(); - switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index, .either => return, // TODO + }; + + switch (payload.data) { .pointer => |info| try analyser.addReferencedTypes(info.elem_ty.*, .{ .referenced_types = referenced_types }), .array => |info| try analyser.addReferencedTypes(info.elem_ty.*, .{ .referenced_types = referenced_types }), .tuple => {}, @@ -5160,7 +5296,6 @@ fn addReferencedTypes( else => {}, // TODO: Implement more "other" type expressions; better safe than sorry }, - .ip_index, .compile_error => {}, - .either => {}, // TODO + .compile_error => {}, } } diff --git a/src/features/completions.zig b/src/features/completions.zig index 6aef29db1..60a587ecd 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -34,10 +34,26 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi try builder.completions.ensureUnusedCapacity(builder.arena, 2); - switch (ty.data) { + const payload = switch (ty) { + .dynamic => |payload| payload, + .ip_index => |payload| return analyser_completions.dotCompletions( + builder.arena, + &builder.completions, + builder.analyser.ip, + payload.index, + ), + .either => |either_entries| { + for (either_entries) |entry| { + try typeToCompletion(builder, entry.type); + } + return; + }, + }; + + switch (payload.data) { .pointer => |info| switch (info.size) { .one, .c => { - if (ty.is_type_val) return; + if (payload.is_type_val) return; builder.completions.appendAssumeCapacity(.{ .label = "*", @@ -56,7 +72,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi } }, .slice => { - if (ty.is_type_val) return; + if (payload.is_type_val) return; builder.completions.appendAssumeCapacity(.{ .label = "len", @@ -64,9 +80,10 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi .kind = .Field, }); - var many_ptr_ty = ty; - many_ptr_ty.is_type_val = true; - many_ptr_ty.data.pointer.size = .many; + var many_ptr_payload = payload; + many_ptr_payload.is_type_val = true; + many_ptr_payload.data.pointer.size = .many; + const many_ptr_ty: Analyser.Type = .{ .dynamic = many_ptr_payload }; builder.completions.appendAssumeCapacity(.{ .label = "ptr", .kind = .Field, @@ -80,7 +97,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi .many => {}, }, .array => |info| { - if (ty.is_type_val) return; + if (payload.is_type_val) return; builder.completions.appendAssumeCapacity(.{ .label = "len", .detail = if (info.elem_count) |count| @@ -91,7 +108,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi }); }, .tuple => |elem_ty_slice| { - if (ty.is_type_val) return; + if (payload.is_type_val) return; try builder.completions.ensureUnusedCapacity(builder.arena, elem_ty_slice.len); for (elem_ty_slice, 0..) |elem_ty, i| { builder.completions.appendAssumeCapacity(.{ @@ -106,7 +123,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi } }, .optional => |child_ty| { - if (ty.is_type_val) return; + if (payload.is_type_val) return; builder.completions.appendAssumeCapacity(.{ .label = "?", .kind = .Operator, @@ -119,7 +136,7 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi }, .container => |scope_handle| { var decls: std.ArrayListUnmanaged(Analyser.DeclWithHandle) = .empty; - try builder.analyser.collectDeclarationsOfContainer(scope_handle, builder.orig_handle, !ty.is_type_val, &decls); + try builder.analyser.collectDeclarationsOfContainer(scope_handle, builder.orig_handle, !payload.is_type_val, &decls); for (decls.items) |decl_with_handle| { try declToCompletion(builder, decl_with_handle, .{ @@ -139,18 +156,6 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) error{OutOfMemory}!voi }, else => {}, }, - .ip_index => |payload| try analyser_completions.dotCompletions( - builder.arena, - &builder.completions, - builder.analyser.ip, - payload.index, - ), - .either => |either_entries| { - for (either_entries) |entry| { - const entry_ty: Analyser.Type = .{ .data = entry.type_data, .is_type_val = ty.is_type_val }; - try typeToCompletion(builder, entry_ty); - } - }, .error_union, .union_tag, .compile_error, @@ -188,7 +193,7 @@ fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle, opt const maybe_resolved_ty = try decl_handle.resolveType(builder.analyser); if (maybe_resolved_ty) |resolve_ty| { - if (try resolve_ty.docComments(builder.arena)) |docs| { + if (try resolve_ty.docComments(builder.analyser)) |docs| { doc_comments.appendAssumeCapacity(docs); } } @@ -208,9 +213,14 @@ fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle, opt !builder.server.client_capabilities.supports_completion_deprecated_tag) break :blk null; const resolved_ty = maybe_resolved_ty orelse break :blk null; - if (resolved_ty.data != .compile_error) break :blk null; - - const node_with_handle = resolved_ty.data.compile_error; + const payload = switch (resolved_ty) { + .dynamic => |payload| payload, + .ip_index, .either => break :blk null, + }; + const node_with_handle = switch (payload.data) { + .compile_error => |node_with_handle| node_with_handle, + else => break :blk null, + }; const tree = node_with_handle.handle.tree; var buffer: [2]Ast.Node.Index = undefined; @@ -235,7 +245,7 @@ fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle, opt .switch_payload, => { var kind: types.CompletionItemKind = blk: { - const parent_is_type_val = if (options.parent_container_ty) |container_ty| container_ty.is_type_val else null; + const parent_is_type_val = if (options.parent_container_ty) |container_ty| container_ty.isTypeVal(builder.analyser) else null; if (!(parent_is_type_val orelse true)) break :blk .Field; break :blk if (decl_handle.isConst()) .Constant else .Variable; }; @@ -248,27 +258,27 @@ fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle, opt builder.completions.appendAssumeCapacity(item); return; } else if (ty.isEnumType()) { - if (ty.is_type_val) { + if (ty.isTypeVal(builder.analyser)) { kind = .Enum; } else { kind = .EnumMember; } } else if (ty.isStructType() or ty.isUnionType()) { kind = .Struct; - } else if (decl_handle.decl == .function_parameter and ty.isMetaType()) { + } else if (decl_handle.decl == .function_parameter and ty.isMetaType(builder.analyser)) { kind = .TypeParameter; } else if (ty.isEnumLiteral(builder.analyser)) { kind = .EnumMember; - } else if (ty.data == .compile_error) { + } else if (ty == .dynamic and ty.dynamic.data == .compile_error) { is_deprecated = true; } } const detail = if (maybe_resolved_ty) |ty| blk: { - if (ty.is_type_val and ty.data == .ip_index and !builder.analyser.ip.isUnknown(ty.data.ip_index.index)) { + if (ty.isTypeVal(builder.analyser) and ty == .ip_index and !builder.analyser.ip.isUnknown(ty.ip_index.index)) { break :blk try std.fmt.allocPrint(builder.arena, "{}", .{ty.fmtTypeVal(builder.analyser, .{ .truncate_container_decls = false })}); } else { - break :blk try std.fmt.allocPrint(builder.arena, "{}", .{ty.fmt(builder.analyser, .{ .truncate_container_decls = false })}); + break :blk try std.fmt.allocPrint(builder.arena, "{}", .{try ty.fmt(builder.analyser, .{ .truncate_container_decls = false })}); } } else null; @@ -318,7 +328,7 @@ fn functionTypeCompletion( ) error{OutOfMemory}!?types.CompletionItem { std.debug.assert(func_ty.isFunc()); - const node_handle = func_ty.data.other; // this assumes that function types can only be Ast nodes + const node_handle = func_ty.dynamic.data.other; // this assumes that function types can only be Ast nodes const tree = node_handle.handle.tree; var buf: [1]Ast.Node.Index = undefined; @@ -327,9 +337,9 @@ fn functionTypeCompletion( const use_snippets = builder.server.config.enable_snippets and builder.server.client_capabilities.supports_snippets; const has_self_param = if (parent_container_ty) |container_ty| blk: { - if (container_ty.is_type_val) break :blk false; + if (container_ty.isTypeVal(builder.analyser)) break :blk false; if (container_ty.isNamespace()) break :blk false; - break :blk try builder.analyser.firstParamIs(func_ty, container_ty.typeOf(builder.analyser)); + break :blk try builder.analyser.firstParamIs(func_ty, try container_ty.typeOf(builder.analyser)); } else false; const insert_range, const replace_range, const new_text_format = prepareFunctionCompletion(builder); @@ -1290,7 +1300,17 @@ fn collectContainerFields( container: Analyser.Type, omit_members: std.BufSet, ) error{OutOfMemory}!void { - const scope_handle = switch (container.data) { + const payload = switch (container) { + .dynamic => |payload| payload, + .ip_index => return, + .either => |either_entries| { + for (either_entries) |entry| { + try collectContainerFields(builder, likely, entry.type, omit_members); + } + return; + }, + }; + const scope_handle = switch (payload.data) { .container => |s| s, else => return, }; @@ -1341,7 +1361,7 @@ fn collectContainerFields( if (likely != .enum_assignment) continue; // decl literal var expected_ty = try decl_handle.resolveType(builder.analyser) orelse continue; - expected_ty = expected_ty.typeOf(builder.analyser).resolveDeclLiteralResultType(); + expected_ty = (try expected_ty.typeOf(builder.analyser)).resolveDeclLiteralResultType(); if (!expected_ty.eql(container)) continue; try declToCompletion(builder, decl_handle, .{ .parent_container_ty = container }); continue; @@ -1357,7 +1377,7 @@ fn collectContainerFields( const resolved_ty = try decl_handle.resolveType(builder.analyser) orelse continue; var expected_ty = try builder.analyser.resolveReturnType(resolved_ty) orelse continue; expected_ty = expected_ty.resolveDeclLiteralResultType(); - if (!expected_ty.eql(container) and !expected_ty.typeOf(builder.analyser).eql(container)) continue; + if (!expected_ty.eql(container) and !(try expected_ty.typeOf(builder.analyser)).eql(container)) continue; break :blk try functionTypeCompletion(builder, name, container, resolved_ty) orelse continue; }, else => continue, @@ -1528,17 +1548,17 @@ fn collectVarAccessContainerNodes( const result = try symbol_decl.resolveType(analyser) orelse return; const type_expr = try analyser.resolveDerefType(result) orelse result; if (!type_expr.isFunc()) { - try type_expr.getAllTypesWithHandlesArrayList(arena, types_with_handles); + try type_expr.getAllTypesWithHandlesArrayList(analyser, types_with_handles); return; } if (dot_context.likely == .enum_comparison or dot_context.need_ret_type) { // => we need f()'s return type var node_type = try analyser.resolveReturnType(type_expr) orelse return; if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + try node_type.getAllTypesWithHandlesArrayList(analyser, types_with_handles); return; } - const func_node_handle = type_expr.data.other; // this assumes that function types can only be Ast nodes + const func_node_handle = type_expr.dynamic.data.other; // this assumes that function types can only be Ast nodes const fn_param_decl: Analyser.Declaration = .{ .function_parameter = .{ .func = func_node_handle.node, .param_index = @intCast(dot_context.fn_arg_index), @@ -1572,7 +1592,7 @@ fn collectFieldAccessContainerNodes( } } // if (dot_context.likely == .enum_literal and !(container.isEnumType() or container.isUnionType())) return; - try container.getAllTypesWithHandlesArrayList(arena, types_with_handles); + try container.getAllTypesWithHandlesArrayList(analyser, types_with_handles); return; }; const name = offsets.locToSlice(handle.tree.source, name_loc); @@ -1584,14 +1604,14 @@ fn collectFieldAccessContainerNodes( if (try analyser.resolveOptionalUnwrap(node_type)) |unwrapped| node_type = unwrapped; } if (!node_type.isFunc()) { - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + try node_type.getAllTypesWithHandlesArrayList(analyser, types_with_handles); continue; } if (dot_context.need_ret_type) { // => we need f()'s return type node_type = try analyser.resolveReturnType(node_type) orelse continue; if (try analyser.resolveUnwrapErrorUnionType(node_type, .payload)) |unwrapped| node_type = unwrapped; - try node_type.getAllTypesWithHandlesArrayList(arena, types_with_handles); + try node_type.getAllTypesWithHandlesArrayList(analyser, types_with_handles); continue; } // don't have the luxury of referencing an `Ast.full.Call` @@ -1604,12 +1624,12 @@ fn collectFieldAccessContainerNodes( const first_symbol = symbol_iter.next() orelse continue; const symbol_decl = try analyser.lookupSymbolGlobal(handle, first_symbol, loc.start) orelse continue; const symbol_type = try symbol_decl.resolveType(analyser) orelse continue; - if (!symbol_type.is_type_val) { // then => instance_of_T + if (!symbol_type.isTypeVal(analyser)) { // then => instance_of_T if (try analyser.hasSelfParam(node_type)) break :blk 1; } break :blk 0; // is `T`, no SelfParam }; - const fn_node_handle = node_type.data.other; // this assumes that function types can only be Ast nodes + const fn_node_handle = node_type.dynamic.data.other; // this assumes that function types can only be Ast nodes const param_decl: Analyser.Declaration.Param = .{ .param_index = @truncate(dot_context.fn_arg_index + additional_index), .func = fn_node_handle.node, diff --git a/src/features/hover.zig b/src/features/hover.zig index a9c611e59..4e2493310 100644 --- a/src/features/hover.zig +++ b/src/features/hover.zig @@ -134,13 +134,13 @@ fn hoverSymbolRecursive( var resolved_type_str: []const u8 = "unknown"; if (try decl_handle.resolveType(analyser)) |resolved_type| { - if (try resolved_type.docComments(arena)) |doc| + if (try resolved_type.docComments(analyser)) |doc| try doc_strings.append(arena, doc); try analyser.referencedTypes( resolved_type, &reference_collector, ); - resolved_type_str = try std.fmt.allocPrint(arena, "{}", .{resolved_type.fmt(analyser, .{ .truncate_container_decls = false })}); + resolved_type_str = try std.fmt.allocPrint(arena, "{}", .{try resolved_type.fmt(analyser, .{ .truncate_container_decls = false })}); } const referenced_types: []const Analyser.ReferencedType = type_references.keys(); diff --git a/src/features/inlay_hints.zig b/src/features/inlay_hints.zig index 53b2294ac..9360cc058 100644 --- a/src/features/inlay_hints.zig +++ b/src/features/inlay_hints.zig @@ -238,7 +238,7 @@ fn writeCallHint( const ty = try builder.analyser.resolveTypeOfNode(.{ .node = call.ast.fn_expr, .handle = handle }) orelse return; const fn_ty = try builder.analyser.resolveFuncProtoOfCallable(ty) orelse return; - const fn_node = fn_ty.data.other; // this assumes that function types can only be Ast nodes + const fn_node = fn_ty.dynamic.data.other; // this assumes that function types can only be Ast nodes var buffer: [1]Ast.Node.Index = undefined; const fn_proto = fn_node.handle.tree.fullFnProto(&buffer, fn_node.node).?; @@ -345,7 +345,7 @@ fn typeStrOfNode(builder: *Builder, node: Ast.Node.Index) !?[]const u8 { const type_str: []const u8 = try std.fmt.allocPrint( builder.arena, "{}", - .{resolved_type.fmt(builder.analyser, .{ .truncate_container_decls = true })}, + .{try resolved_type.fmt(builder.analyser, .{ .truncate_container_decls = true })}, ); if (type_str.len == 0) return null; @@ -363,7 +363,7 @@ fn typeStrOfToken(builder: *Builder, token: Ast.TokenIndex) !?[]const u8 { const type_str: []const u8 = try std.fmt.allocPrint( builder.arena, "{}", - .{resolved_type.fmt(builder.analyser, .{ .truncate_container_decls = true })}, + .{try resolved_type.fmt(builder.analyser, .{ .truncate_container_decls = true })}, ); if (type_str.len == 0) return null; @@ -568,7 +568,7 @@ fn writeNodeInlayHint( const type_str: []const u8 = try std.fmt.allocPrint( builder.arena, "{}", - .{ty.fmt(builder.analyser, .{ .truncate_container_decls = true })}, + .{try ty.fmt(builder.analyser, .{ .truncate_container_decls = true })}, ); if (type_str.len == 0) continue; try appendTypeHintString( diff --git a/src/features/semantic_tokens.zig b/src/features/semantic_tokens.zig index 1e251cb82..19b958496 100644 --- a/src/features/semantic_tokens.zig +++ b/src/features/semantic_tokens.zig @@ -224,7 +224,7 @@ fn colorIdentifierBasedOnType( is_parameter: bool, tok_mod: TokenModifiers, ) !void { - if (type_node.is_type_val) { + if (type_node.isTypeVal(builder.analyser)) { const token_type: TokenType = if (type_node.isNamespace()) .namespace @@ -255,7 +255,7 @@ fn colorIdentifierBasedOnType( try writeTokenMod(builder, target_tok, if (has_self_param) .method else .function, new_tok_mod); } else { var new_tok_mod = tok_mod; - if (type_node.data == .compile_error) { + if (type_node == .dynamic and type_node.dynamic.data == .compile_error) { new_tok_mod.deprecated = true; } try writeTokenMod(builder, target_tok, if (is_parameter) .parameter else .variable, new_tok_mod); @@ -389,8 +389,10 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v try writeToken(builder, fn_proto.ast.fn_token, .keyword); const func_ty = Analyser.Type{ - .data = .{ .other = .{ .node = node, .handle = handle } }, // this assumes that function types can only be Ast nodes - .is_type_val = true, + .dynamic = .{ + .data = .{ .other = .{ .node = node, .handle = handle } }, // this assumes that function types can only be Ast nodes + .is_type_val = true, + }, }; const func_name_tok_type: TokenType = if (func_ty.isTypeFunc()) @@ -581,8 +583,11 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v field_token_type = if (try builder.analyser.resolveTypeOfNode( .{ .node = struct_init.ast.type_expr, .handle = handle }, - )) |struct_type| switch (struct_type.data) { - .container => |scope_handle| fieldTokenType(scope_handle.toNode(), scope_handle.handle, false), + )) |struct_type| switch (struct_type) { + .dynamic => |payload| switch (payload.data) { + .container => |scope_handle| fieldTokenType(scope_handle.toNode(), scope_handle.handle, false), + else => null, + }, else => null, } else null; } @@ -875,8 +880,11 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v switch (decl_type.decl) { .ast_node => |decl_node| { if (decl_type.handle.tree.nodes.items(.tag)[decl_node].isContainerField()) { - const tok_type = switch (lhs_type.data) { - .container => |scope_handle| fieldTokenType(scope_handle.toNode(), scope_handle.handle, lhs_type.is_type_val), + const tok_type = switch (lhs_type) { + .dynamic => |payload| switch (payload.data) { + .container => |scope_handle| fieldTokenType(scope_handle.toNode(), scope_handle.handle, payload.is_type_val), + else => null, + }, else => null, }; diff --git a/src/features/signature_help.zig b/src/features/signature_help.zig index e96bdf6f3..9530a1dcf 100644 --- a/src/features/signature_help.zig +++ b/src/features/signature_help.zig @@ -20,7 +20,7 @@ fn fnProtoToSignatureInfo( func_type: Analyser.Type, markup_kind: types.MarkupKind, ) !types.SignatureInformation { - const fn_node_handle = func_type.data.other; // this assumes that function types can only be Ast nodes + const fn_node_handle = func_type.dynamic.data.other; // this assumes that function types can only be Ast nodes const fn_node = fn_node_handle.node; const fn_handle = fn_node_handle.handle; const tree = fn_handle.tree; @@ -271,7 +271,7 @@ pub fn getSignatureInfo( }; const name = offsets.locToSlice(handle.tree.source, name_loc); - const skip_self_param = !ty.is_type_val; + const skip_self_param = !ty.isTypeVal(analyser); ty = try analyser.resolveFieldAccess(ty, name) orelse { try symbol_stack.append(arena, .l_paren); continue;