stage2: slice return type analysis

This commit is contained in:
Vexu
2020-08-28 15:51:27 +03:00
parent 2a628fd401
commit 6ab0ac161e
6 changed files with 172 additions and 20 deletions

View File

@@ -2591,6 +2591,72 @@ pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) In
return self.fail(scope, src, "TODO implement analysis of iserr", .{});
}
pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
.Pointer => array_ptr.ty.elemType(),
else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
};
var array_type = ptr_child;
const elem_type = switch (ptr_child.zigTypeTag()) {
.Array => ptr_child.elemType(),
.Pointer => blk: {
if (ptr_child.isSinglePointer()) {
if (ptr_child.elemType().zigTypeTag() == .Array) {
array_type = ptr_child.elemType();
break :blk ptr_child.elemType().elemType();
}
return self.fail(scope, src, "slice of single-item pointer", .{});
}
break :blk ptr_child.elemType();
},
else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
};
const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
const casted = try self.coerce(scope, elem_type, sentinel);
break :blk try self.resolveConstValue(scope, casted);
} else null;
var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
var return_elem_type = elem_type;
if (end_opt) |end| {
if (end.value()) |end_val| {
if (start.value()) |start_val| {
const start_u64 = start_val.toUnsignedInt();
const end_u64 = end_val.toUnsignedInt();
if (start_u64 > end_u64) {
return self.fail(scope, src, "out of bounds slice", .{});
}
const len = end_u64 - start_u64;
const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
array_type.sentinel()
else
slice_sentinel;
return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
return_ptr_size = .One;
}
}
}
const return_type = try self.ptrType(
scope,
src,
return_elem_type,
if (end_opt == null) slice_sentinel else null,
0, // TODO alignment
0,
0,
!ptr_child.isConstPtr(),
ptr_child.isAllowzeroPtr(),
ptr_child.isVolatilePtr(),
return_ptr_size,
);
return self.fail(scope, src, "TODO implement analysis of slice", .{});
}
/// Asserts that lhs and rhs types are both numeric.
pub fn cmpNumeric(
self: *Module,

View File

@@ -132,7 +132,7 @@ pub fn generateSymbol(
.Array => {
// TODO populate .debug_info for the array
if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
if (typed_value.ty.arraySentinel()) |sentinel| {
if (typed_value.ty.sentinel()) |sentinel| {
try code.ensureCapacity(code.items.len + payload.data.len + 1);
code.appendSliceAssumeCapacity(payload.data);
const prev_len = code.items.len;

View File

@@ -85,7 +85,7 @@ fn genArray(file: *C, decl: *Decl) !void {
const name = try map(file.base.allocator, mem.span(decl.name));
defer file.base.allocator.free(name);
if (tv.val.cast(Value.Payload.Bytes)) |payload|
if (tv.ty.arraySentinel()) |sentinel|
if (tv.ty.sentinel()) |sentinel|
if (sentinel.toUnsignedInt() == 0)
try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
else

View File

@@ -191,8 +191,8 @@ pub const Type = extern union {
return false;
if (!a.elemType().eql(b.elemType()))
return false;
const sentinel_a = a.arraySentinel();
const sentinel_b = b.arraySentinel();
const sentinel_a = a.sentinel();
const sentinel_b = b.sentinel();
if (sentinel_a) |sa| {
if (sentinel_b) |sb| {
return sa.eql(sb);
@@ -630,8 +630,8 @@ pub const Type = extern union {
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
if (payload.sentinel) |some| switch (payload.size) {
.One, .C => unreachable,
.Many => try out_stream.writeAll("[*:{}]"),
.Slice => try out_stream.writeAll("[:{}]"),
.Many => try out_stream.print("[*:{}]", .{some}),
.Slice => try out_stream.print("[:{}]", .{some}),
} else switch (payload.size) {
.One => try out_stream.writeAll("*"),
.Many => try out_stream.writeAll("[*]"),
@@ -1341,6 +1341,81 @@ pub const Type = extern union {
};
}
pub fn isAllowzeroPtr(self: Type) bool {
return switch (self.tag()) {
.u8,
.i8,
.u16,
.i16,
.u32,
.i32,
.u64,
.i64,
.usize,
.isize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.f16,
.f32,
.f64,
.f128,
.c_void,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.array,
.array_sentinel,
.array_u8,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.int_unsigned,
.int_signed,
.single_mut_pointer,
.single_const_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
=> false,
.pointer => {
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
return payload.@"allowzero";
},
};
}
/// Asserts that the type is an optional
pub fn isPtrLikeOptional(self: Type) bool {
switch (self.tag()) {
@@ -1585,8 +1660,8 @@ pub const Type = extern union {
};
}
/// Asserts the type is an array or vector.
pub fn arraySentinel(self: Type) ?Value {
/// Asserts the type is an array, pointer or vector.
pub fn sentinel(self: Type) ?Value {
return switch (self.tag()) {
.u8,
.i8,
@@ -1626,16 +1701,8 @@ pub const Type = extern union {
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.function,
.pointer,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
.int_unsigned,
.int_signed,
@@ -1651,7 +1718,18 @@ pub const Type = extern union {
.error_set_single,
=> unreachable,
.array, .array_u8 => return null,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.single_const_pointer_to_comptime_int,
.array,
.array_u8,
=> return null,
.pointer => return self.cast(Payload.Pointer).?.sentinel,
.array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
.array_u8_sentinel_0 => return Value.initTag(.zero),
};

View File

@@ -2596,7 +2596,7 @@ const EmitZIR = struct {
var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
const len = Value.initPayload(&len_pl.base);
const inst = if (ty.arraySentinel()) |sentinel| blk: {
const inst = if (ty.sentinel()) |sentinel| blk: {
const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
inst.* = .{
.base = .{

View File

@@ -1175,11 +1175,19 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
}
fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstSlice", .{});
const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
const start = try resolveInst(mod, scope, inst.positionals.start);
const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
}
fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstSliceStart", .{});
const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
const start = try resolveInst(mod, scope, inst.positionals.rhs);
return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
}
fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {