Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix type resolution of slice with integer literals #2181

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 138 additions & 108 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -995,129 +995,122 @@ pub fn resolveDerefType(analyser: *Analyser, pointer: Type) error{OutOfMemory}!?
}
}

const BracketAccessKind = enum {
const BracketAccess = union(enum) {
/// `lhs[index]`
Single,
single: ?u64,
/// `lhs[start..]`
Open,
open: ?u64,
/// `lhs[start..end]`
Range,
range: ?struct { u64, u64 },
};

/// Resolves slicing and array access
/// - `lhs[index]` (Single)
/// - `lhs[start..]` (Open)
/// - `lhs[start..end]` (Range)
fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccessKind) error{OutOfMemory}!?Type {
/// - `lhs[index]` (single)
/// - `lhs[start..]` (open)
/// - `lhs[start..end]` (range)
fn resolveBracketAccessType(analyser: *Analyser, lhs: Type, rhs: BracketAccess) error{OutOfMemory}!?Type {
if (lhs.is_type_val) return null;

switch (lhs.data) {
.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,
},
.array => |info| switch (rhs) {
.Single => return try info.elem_ty.instanceTypeVal(analyser),
.Open => {
else => switch (rhs) {
.single => |index_maybe| {
const elem_ty = lhs.bracketAccessElemType(index_maybe) orelse return null;
return try elem_ty.instanceTypeVal(analyser);
},
.open => |start_maybe| {
const is_const, const array_info = switch (lhs.data) {
.array => |info| .{ false, info }, // TODO: should only be false for `var`
.pointer => |info| switch (info.size) {
.one => switch (info.elem_ty.data) {
.array => |array_info| .{ info.is_const, array_info },
else => return null,
},
.many,
.slice,
.c,
=> return lhs,
},
else => return null,
};
const elem_count = blk: {
const start = start_maybe orelse break :blk null;
const elem_count = array_info.elem_count orelse break :blk null;
if (start > elem_count) break :blk null;
break :blk elem_count - start;
};
return .{
.data = .{
.pointer = .{
.size = .slice,
.sentinel = info.sentinel,
.is_const = false,
.elem_ty = info.elem_ty,
.size = .one,
.sentinel = .none,
.is_const = is_const,
.elem_ty = blk: {
const array_ty = try analyser.arena.allocator().create(Type);
array_ty.* = .{
.data = .{
.array = .{
.elem_count = elem_count,
.sentinel = array_info.sentinel,
.elem_ty = array_info.elem_ty,
},
},
.is_type_val = true,
};
break :blk array_ty;
},
},
},
.is_type_val = false,
};
},
.Range => {
return .{
.range => |range_maybe| {
const elem_ty = lhs.bracketAccessElemType(null) orelse return null;
const is_const = switch (lhs.data) {
.array => false, // TODO: should only be false for `var`
.pointer => |info| info.is_const,
else => return null,
};
const start, const end = range_maybe orelse return .{
.data = .{
.pointer = .{
.size = .slice,
.sentinel = .none,
.is_const = false,
.elem_ty = info.elem_ty,
.is_const = is_const,
.elem_ty = elem_ty,
},
},
.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,
},
},
.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,
};
},
}
},
else => return null,
},
.many => switch (rhs) {
.Single => try info.elem_ty.instanceTypeVal(analyser),
.Open => lhs,
.Range => {
return .{
.data = .{
.pointer = .{
.size = .slice,
.sentinel = .none,
.is_const = info.is_const,
.elem_ty = info.elem_ty,
},
},
.is_type_val = false,
};
},
},
.slice => switch (rhs) {
.Single => try info.elem_ty.instanceTypeVal(analyser),
.Open, .Range => lhs,
},
.c => switch (rhs) {
.Single => try info.elem_ty.instanceTypeVal(analyser),
.Open => lhs,
.Range => .{
return .{
.data = .{
.pointer = .{
.size = .slice,
.size = .one,
.sentinel = .none,
.is_const = info.is_const,
.elem_ty = info.elem_ty,
.is_const = is_const,
.elem_ty = blk: {
const array_ty = try analyser.arena.allocator().create(Type);
array_ty.* = .{
.data = .{
.array = .{
.elem_count = end - start,
.sentinel = .none,
.elem_ty = elem_ty,
},
},
.is_type_val = true,
};
break :blk array_ty;
},
},
},
.is_type_val = false,
},
};
},
},
else => return null,
}
}

Expand All @@ -1137,15 +1130,7 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou

switch (ty.data) {
.pointer => |info| switch (info.size) {
.one => switch (info.elem_ty.data) {
.array => {
std.debug.assert(!info.elem_ty.is_type_val);
if (std.mem.eql(u8, "len", name)) {
return try Type.typeValFromIP(analyser, .usize_type);
}
},
else => {},
},
.one => unreachable, // handled by resolveDerefType
.slice => {
if (std.mem.eql(u8, "len", name)) {
return try Type.typeValFromIP(analyser, .usize_type);
Expand All @@ -1168,8 +1153,18 @@ fn resolvePropertyType(analyser: *Analyser, ty: Type, name: []const u8) error{Ou
.many, .c => {},
},

.array => {
.array => |info| {
if (std.mem.eql(u8, "len", name)) {
if (info.elem_count) |elem_count| {
const index = try analyser.ip.get(
analyser.gpa,
.{ .int_u64_value = .{ .ty = .usize_type, .int = elem_count } },
);
return .{
.data = .{ .ip_index = .{ .index = index } },
.is_type_val = false,
};
}
return try Type.typeValFromIP(analyser, .usize_type);
}
},
Expand Down Expand Up @@ -1579,12 +1574,24 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
.slice_open,
=> {
const slice_info = tree.fullSlice(node).?;
const kind: BracketAccessKind = if (slice_info.ast.end == 0) .Open else .Range;
const kind: BracketAccess = if (slice_info.ast.end == 0) .{
.open = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.start, .handle = handle }),
} else .{
.range = blk: {
const start = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.start, .handle = handle }) orelse
break :blk null;
const end = try analyser.resolveIntegerLiteral(u64, .{ .node = slice_info.ast.end, .handle = handle }) orelse
break :blk null;
break :blk .{ start, end };
},
};
return try analyser.resolveBracketAccessType(base_type, kind);
},
.deref => try analyser.resolveDerefType(base_type),
.unwrap_optional => try analyser.resolveOptionalUnwrap(base_type),
.array_access => try analyser.resolveBracketAccessType(base_type, .Single),
.array_access => try analyser.resolveBracketAccessType(base_type, .{
.single = try analyser.resolveIntegerLiteral(u64, .{ .node = datas[node].rhs, .handle = handle }),
}),
.@"orelse" => {
const type_right = try analyser.resolveTypeOfNodeInternal(.{ .node = datas[node].rhs, .handle = handle }) orelse return try analyser.resolveOptionalUnwrap(base_type);
return try analyser.resolveOrelseType(base_type, type_right);
Expand Down Expand Up @@ -1677,9 +1684,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
var buffer: [2]Ast.Node.Index = undefined;
const array_init_info = tree.fullArrayInit(&buffer, node).?;

if (array_init_info.ast.type_expr != 0) blk: {
const array_ty = try analyser.resolveTypeOfNode(.{ .node = array_init_info.ast.type_expr, .handle = handle }) orelse break :blk;
return try array_ty.instanceTypeVal(analyser);
if (array_init_info.ast.type_expr != 0) {
var array_ty = try analyser.resolveTypeOfNode(.{ .node = array_init_info.ast.type_expr, .handle = handle }) orelse return null;
array_ty = try array_ty.instanceTypeVal(analyser) orelse return null;
if (array_ty.data == .array and array_ty.data.array.elem_count == null) {
array_ty.data.array.elem_count = array_init_info.ast.elements.len;
}
return array_ty;
}

const elem_ty_slice = try analyser.arena.allocator().alloc(Type, array_init_info.ast.elements.len);
Expand Down Expand Up @@ -2899,6 +2910,25 @@ pub const Type = struct {
return analyser.lookupSymbolContainer(scope_handle, symbol, .other);
}

fn bracketAccessElemType(self: Type, index_maybe: ?u64) ?*Type {
return switch (self.data) {
.tuple => |fields| {
const index = index_maybe orelse return null;
if (index >= fields.len) return null;
return &fields[index];
},
.array => |info| info.elem_ty,
.pointer => |info| switch (info.size) {
.one => switch (info.elem_ty.data) {
.array => |array_info| array_info.elem_ty,
else => null,
},
else => info.elem_ty,
},
else => null,
};
}

pub fn fmt(ty: Type, analyser: *Analyser, options: FormatOptions) std.fmt.Formatter(format) {
const typeof = ty.typeOf(analyser);
return .{ .data = .{ .ty = typeof, .analyser = analyser, .options = options } };
Expand Down Expand Up @@ -3345,7 +3375,7 @@ pub fn getFieldAccessType(
},
.l_bracket => {
var bracket_count: usize = 1;
var kind: BracketAccessKind = .Single;
var kind: BracketAccess = .{ .single = null };

while (true) {
const token = tokenizer.next();
Expand All @@ -3360,12 +3390,12 @@ pub fn getFieldAccessType(
},
.ellipsis2 => {
if (bracket_count == 1) {
kind = .Open;
kind = .{ .open = null };
}
},
else => {
if (bracket_count == 1 and kind == .Open) {
kind = .Range;
if (bracket_count == 1 and kind == .open) {
kind = .{ .range = null };
}
},
}
Expand Down Expand Up @@ -4077,7 +4107,7 @@ pub const DeclWithHandle = struct {
.node = pay.condition,
.handle = self.handle,
})) orelse return null,
.Single,
.{ .single = null },
),
.assign_destructure => |pay| blk: {
const type_node = pay.getFullVarDecl(tree).ast.type_node;
Expand Down Expand Up @@ -4669,7 +4699,7 @@ pub fn resolveExpressionTypeFromAncestors(
ancestors[0],
ancestors[1..],
)) |array_type| {
return (try analyser.resolveBracketAccessType(array_type, .Single)) orelse
return (try analyser.resolveBracketAccessType(array_type, .{ .single = null })) orelse
(try analyser.resolveTupleFieldType(array_type, element_index));
}

Expand All @@ -4679,7 +4709,7 @@ pub fn resolveExpressionTypeFromAncestors(
ancestors[1],
ancestors[2..],
)) |slice_type| {
return try analyser.resolveBracketAccessType(slice_type, .Single);
return try analyser.resolveBracketAccessType(slice_type, .{ .single = null });
}
}
},
Expand Down
Loading