Skip to content

Commit

Permalink
resolve peer types of optionals and interned values (#2193)
Browse files Browse the repository at this point in the history
  • Loading branch information
FnControlOption authored Feb 23, 2025
1 parent 43bece5 commit 2078220
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 4 deletions.
70 changes: 66 additions & 4 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! - `lookupSymbolContainer`
//!

const builtin = @import("builtin");
const std = @import("std");
const DocumentStore = @import("DocumentStore.zig");
const Ast = std.zig.Ast;
Expand Down Expand Up @@ -2118,7 +2119,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,
Expand All @@ -2144,7 +2145,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,
Expand Down Expand Up @@ -2722,13 +2723,23 @@ 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;

if (entries.len == 1)
return entries[0].type;

peer_type_resolution: {
var chosen = entries[0].type;
for (entries[1..]) |entry| {
const candidate = entry.type;
chosen = try resolvePeerTypes(analyser, chosen, candidate) orelse break :peer_type_resolution;
}
return chosen;
}

// Note that we don't hash/equate descriptors to remove
// duplicates

Expand Down Expand Up @@ -2774,6 +2785,57 @@ pub const Type = struct {
};
}

fn resolvePeerTypes(analyser: *Analyser, a: Type, b: Type) error{OutOfMemory}!?Type {
if (a.is_type_val or b.is_type_val) return null;
if (a.eql(b)) return a;

if (a.data == .ip_index and b.data == .ip_index) {
const types = [_]InternPool.Index{ a.data.ip_index.type, b.data.ip_index.type };
const resolved_type = try analyser.ip.resolvePeerTypes(analyser.gpa, &types, builtin.target);
return fromIP(analyser, resolved_type, null);
}

switch (a.data) {
.optional => |a_type| {
if (a_type.eql(b.typeOf(analyser))) {
return a;
}
},
.ip_index => |a_payload| switch (a_payload.type) {
.null_type => switch (b.data) {
.optional => return b,
else => return .{
.data = .{ .optional = try analyser.allocType(b.typeOf(analyser)) },
.is_type_val = false,
},
},
else => {},
},
else => {},
}

switch (b.data) {
.optional => |b_type| {
if (b_type.eql(a.typeOf(analyser))) {
return b;
}
},
.ip_index => |b_payload| switch (b_payload.type) {
.null_type => switch (a.data) {
.optional => return a,
else => return .{
.data = .{ .optional = try analyser.allocType(a.typeOf(analyser)) },
.is_type_val = false,
},
},
else => {},
},
else => {},
}

return null;
}

/// Resolves possible types of a type (single for all except either)
/// Drops duplicates
pub fn getAllTypesWithHandles(ty: Type, arena: std.mem.Allocator) ![]const Type {
Expand Down Expand Up @@ -4153,7 +4215,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;
}
Expand Down
44 changes: 44 additions & 0 deletions tests/analysis/peer_type_resolution.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const S = struct {
int: i64,
float: f32,
};

pub fn main() void {
var runtime_bool: bool = undefined;

const widened_int_0 = if (runtime_bool) @as(i8, undefined) else @as(i16, undefined);
// ^^^^^^^^^^^^^ (i16)()

const widened_int_1 = if (runtime_bool) @as(i16, undefined) else @as(i8, undefined);
// ^^^^^^^^^^^^^ (i16)()

const optional_0 = if (runtime_bool) @as(S, undefined) else @as(?S, undefined);
// ^^^^^^^^^^ (?S)()

const optional_1 = if (runtime_bool) @as(?S, undefined) else @as(S, undefined);
// ^^^^^^^^^^ (?S)()

const optional_2 = if (runtime_bool) null else @as(S, undefined);
// ^^^^^^^^^^ (?S)()

const optional_3 = if (runtime_bool) @as(S, undefined) else null;
// ^^^^^^^^^^ (?S)()

const optional_4 = if (runtime_bool) null else @as(?S, undefined);
// ^^^^^^^^^^ (?S)()

const optional_5 = if (runtime_bool) @as(?S, undefined) else null;
// ^^^^^^^^^^ (?S)()

// Use @compileLog to verify the expected type with the compiler:
@compileLog(widened_int_0);
@compileLog(widened_int_1);
@compileLog(optional_0);
@compileLog(optional_1);
@compileLog(optional_2);
@compileLog(optional_3);
@compileLog(optional_4);
@compileLog(optional_5);

runtime_bool = undefined;
}

0 comments on commit 2078220

Please sign in to comment.