Commit 6ab0ac161e

Vexu <git@vexu.eu>
2020-08-28 14:51:27
stage2: slice return type analysis
1 parent 2a628fd
Changed files (6)
src-self-hosted/codegen/c.zig
@@ -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
src-self-hosted/codegen.zig
@@ -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;
src-self-hosted/Module.zig
@@ -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,
src-self-hosted/type.zig
@@ -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),
         };
src-self-hosted/zir.zig
@@ -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 = .{
src-self-hosted/zir_sema.zig
@@ -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 {