mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 15:13:08 +00:00
Merge pull request #10034 from Snektron/stage2-slice
stage2: slice and optional improvements
This commit is contained in:
commit
df198ea60e
54
src/Sema.zig
54
src/Sema.zig
@ -8013,14 +8013,24 @@ fn analyzePtrArithmetic(
|
||||
const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
|
||||
// TODO adjust the return type according to alignment and other factors
|
||||
const runtime_src = rs: {
|
||||
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
|
||||
if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| {
|
||||
const ptr_ty = sema.typeOf(ptr);
|
||||
const offset_int = offset_val.toUnsignedInt();
|
||||
const new_ptr_ty = ptr_ty; // TODO modify alignment
|
||||
|
||||
if (ptr_val.isUndef() or offset_val.isUndef()) {
|
||||
return sema.addConstUndef(new_ptr_ty);
|
||||
}
|
||||
|
||||
const offset_int = offset_val.toUnsignedInt();
|
||||
if (ptr_val.getUnsignedInt()) |addr| {
|
||||
const target = sema.mod.getTarget();
|
||||
const elem_ty = ptr_ty.childType();
|
||||
const ptr_child_ty = ptr_ty.childType();
|
||||
const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array)
|
||||
ptr_child_ty.childType()
|
||||
else
|
||||
ptr_child_ty;
|
||||
|
||||
const elem_size = elem_ty.abiSize(target);
|
||||
const new_addr = switch (air_tag) {
|
||||
.ptr_add => addr + elem_size * offset_int,
|
||||
@ -13217,8 +13227,8 @@ fn analyzeSlice(
|
||||
var elem_ty = ptr_ptr_child_ty.childType();
|
||||
switch (ptr_ptr_child_ty.zigTypeTag()) {
|
||||
.Array => {},
|
||||
.Pointer => {
|
||||
if (ptr_ptr_child_ty.isSinglePointer()) {
|
||||
.Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
|
||||
.One => {
|
||||
const double_child_ty = ptr_ptr_child_ty.childType();
|
||||
if (double_child_ty.zigTypeTag() == .Array) {
|
||||
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
|
||||
@ -13228,10 +13238,23 @@ fn analyzeSlice(
|
||||
} else {
|
||||
return sema.fail(block, ptr_src, "slice of single-item pointer", .{});
|
||||
}
|
||||
}
|
||||
},
|
||||
.Many, .C => {
|
||||
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
|
||||
slice_ty = ptr_ptr_child_ty;
|
||||
array_ty = ptr_ptr_child_ty;
|
||||
elem_ty = ptr_ptr_child_ty.childType();
|
||||
},
|
||||
.Slice => {
|
||||
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
|
||||
slice_ty = ptr_ptr_child_ty;
|
||||
array_ty = ptr_ptr_child_ty;
|
||||
elem_ty = ptr_ptr_child_ty.childType();
|
||||
},
|
||||
},
|
||||
else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),
|
||||
}
|
||||
|
||||
const ptr = if (slice_ty.isSlice())
|
||||
try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)
|
||||
else
|
||||
@ -13263,7 +13286,6 @@ fn analyzeSlice(
|
||||
|
||||
const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
|
||||
|
||||
const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr);
|
||||
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
|
||||
|
||||
const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
|
||||
@ -13287,11 +13309,21 @@ fn analyzeSlice(
|
||||
.size = .One,
|
||||
});
|
||||
|
||||
if (opt_new_ptr_val) |new_ptr_val| {
|
||||
return sema.addConstant(return_ty, new_ptr_val);
|
||||
} else {
|
||||
const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
|
||||
const new_ptr_val = opt_new_ptr_val orelse {
|
||||
return block.addBitCast(return_ty, new_ptr);
|
||||
};
|
||||
|
||||
if (!new_ptr_val.isUndef()) {
|
||||
return sema.addConstant(return_ty, new_ptr_val);
|
||||
}
|
||||
|
||||
// Special case: @as([]i32, undefined)[x..x]
|
||||
if (new_len_int == 0) {
|
||||
return sema.addConstUndef(return_ty);
|
||||
}
|
||||
|
||||
return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{});
|
||||
}
|
||||
|
||||
const return_ty = try Type.ptr(sema.arena, .{
|
||||
|
||||
@ -767,7 +767,7 @@ pub const DeclGen = struct {
|
||||
}
|
||||
const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
|
||||
const elem_ty = t.childType();
|
||||
const llvm_elem_ty = if (elem_ty.hasCodeGenBits())
|
||||
const llvm_elem_ty = if (elem_ty.hasCodeGenBits() or elem_ty.zigTypeTag() == .Array)
|
||||
try dg.llvmType(elem_ty)
|
||||
else
|
||||
dg.context.intType(8);
|
||||
@ -1480,7 +1480,7 @@ pub const DeclGen = struct {
|
||||
}
|
||||
|
||||
const llvm_type = try self.llvmType(tv.ty);
|
||||
if (!tv.ty.childType().hasCodeGenBits()) {
|
||||
if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) {
|
||||
return self.lowerPtrToVoid(tv.ty);
|
||||
}
|
||||
|
||||
@ -1502,7 +1502,7 @@ pub const DeclGen = struct {
|
||||
// for non-optional pointers. We also need to respect the alignment, even though
|
||||
// the address will never be dereferenced.
|
||||
const llvm_usize = try dg.llvmType(Type.usize);
|
||||
const llvm_ptr_ty = dg.context.intType(8).pointerType(0);
|
||||
const llvm_ptr_ty = try dg.llvmType(ptr_ty);
|
||||
if (alignment != 0) {
|
||||
return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty);
|
||||
}
|
||||
@ -2475,6 +2475,12 @@ pub const FuncGen = struct {
|
||||
const operand = try self.resolveInst(un_op);
|
||||
const operand_ty = self.air.typeOf(un_op);
|
||||
const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
const optional_llvm_ty = try self.dg.llvmType(optional_ty);
|
||||
const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
|
||||
return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
|
||||
}
|
||||
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = optional_ty.optionalChild(&buf);
|
||||
if (!payload_ty.hasCodeGenBits()) {
|
||||
@ -2484,11 +2490,6 @@ pub const FuncGen = struct {
|
||||
return operand;
|
||||
}
|
||||
}
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
const optional_llvm_ty = try self.dg.llvmType(optional_ty);
|
||||
const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
|
||||
return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
|
||||
}
|
||||
|
||||
if (operand_is_ptr or isByRef(optional_ty)) {
|
||||
const index_type = self.context.intType(32);
|
||||
|
||||
@ -2347,11 +2347,13 @@ pub const Type = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that the type is an optional
|
||||
/// Asserts that the type is an optional or a pointer that can be null.
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
=> return true,
|
||||
|
||||
.optional => {
|
||||
@ -2367,6 +2369,8 @@ pub const Type = extern union {
|
||||
.Many, .One => return !info.@"allowzero",
|
||||
}
|
||||
},
|
||||
|
||||
.pointer => return self.castTag(.pointer).?.data.size == .C,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,7 @@ test {
|
||||
if (builtin.zig_is_stage2) {
|
||||
// When all comptime_memory.zig tests pass, #9646 can be closed.
|
||||
// _ = @import("behavior/comptime_memory.zig");
|
||||
_ = @import("behavior/slice_stage2.zig");
|
||||
} else {
|
||||
_ = @import("behavior/align_stage1.zig");
|
||||
_ = @import("behavior/alignof.zig");
|
||||
|
||||
@ -109,3 +109,60 @@ test "slice of type" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "generic malloc free" {
|
||||
const a = memAlloc(u8, 10) catch unreachable;
|
||||
memFree(u8, a);
|
||||
}
|
||||
var some_mem: [100]u8 = undefined;
|
||||
fn memAlloc(comptime T: type, n: usize) anyerror![]T {
|
||||
return @ptrCast([*]T, &some_mem[0])[0..n];
|
||||
}
|
||||
fn memFree(comptime T: type, memory: []T) void {
|
||||
_ = memory;
|
||||
}
|
||||
|
||||
test "slice of hardcoded address to pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const pointer = @intToPtr([*]u8, 0x04)[0..2];
|
||||
comptime try expect(@TypeOf(pointer) == *[2]u8);
|
||||
const slice: []const u8 = pointer;
|
||||
try expect(@ptrToInt(slice.ptr) == 4);
|
||||
try expect(slice.len == 2);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "comptime slice of pointer preserves comptime var" {
|
||||
comptime {
|
||||
var buff: [10]u8 = undefined;
|
||||
var a = @ptrCast([*]u8, &buff);
|
||||
a[0..1][0] = 1;
|
||||
try expect(buff[0..][0..][0] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
test "comptime pointer cast array and then slice" {
|
||||
const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
|
||||
const sliceA: []const u8 = ptrA[0..2];
|
||||
|
||||
const ptrB: [*]const u8 = &array;
|
||||
const sliceB: []const u8 = ptrB[0..2];
|
||||
|
||||
try expect(sliceA[1] == 2);
|
||||
try expect(sliceB[1] == 2);
|
||||
}
|
||||
|
||||
test "slicing zero length array" {
|
||||
const s1 = ""[0..];
|
||||
const s2 = ([_]u32{})[0..];
|
||||
try expect(s1.len == 0);
|
||||
try expect(s2.len == 0);
|
||||
try expect(mem.eql(u8, s1, ""));
|
||||
try expect(mem.eql(u32, s2, &[_]u32{}));
|
||||
}
|
||||
|
||||
@ -4,15 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const mem = std.mem;
|
||||
|
||||
test "slicing zero length array" {
|
||||
const s1 = ""[0..];
|
||||
const s2 = ([_]u32{})[0..];
|
||||
try expect(s1.len == 0);
|
||||
try expect(s2.len == 0);
|
||||
try expect(mem.eql(u8, s1, ""));
|
||||
try expect(mem.eql(u32, s2, &[_]u32{}));
|
||||
}
|
||||
|
||||
test "slice string literal has correct type" {
|
||||
comptime {
|
||||
try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
|
||||
@ -25,18 +16,6 @@ test "slice string literal has correct type" {
|
||||
comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);
|
||||
}
|
||||
|
||||
test "generic malloc free" {
|
||||
const a = memAlloc(u8, 10) catch unreachable;
|
||||
memFree(u8, a);
|
||||
}
|
||||
var some_mem: [100]u8 = undefined;
|
||||
fn memAlloc(comptime T: type, n: usize) anyerror![]T {
|
||||
return @ptrCast([*]T, &some_mem[0])[0..n];
|
||||
}
|
||||
fn memFree(comptime T: type, memory: []T) void {
|
||||
_ = memory;
|
||||
}
|
||||
|
||||
test "result location zero sized array inside struct field implicit cast to slice" {
|
||||
const E = struct {
|
||||
entries: []u32,
|
||||
@ -307,20 +286,6 @@ test "slice syntax resulting in pointer-to-array" {
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "slice of hardcoded address to pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const pointer = @intToPtr([*]u8, 0x04)[0..2];
|
||||
comptime try expect(@TypeOf(pointer) == *[2]u8);
|
||||
const slice: []const u8 = pointer;
|
||||
try expect(@ptrToInt(slice.ptr) == 4);
|
||||
try expect(slice.len == 2);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type coercion of pointer to anon struct literal to pointer to slice" {
|
||||
const S = struct {
|
||||
const U = union {
|
||||
@ -352,15 +317,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "comptime slice of pointer preserves comptime var" {
|
||||
comptime {
|
||||
var buff: [10]u8 = undefined;
|
||||
var a = @ptrCast([*]u8, &buff);
|
||||
a[0..1][0] = 1;
|
||||
try expect(buff[0..][0..][0] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
test "array concat of slices gives slice" {
|
||||
comptime {
|
||||
var a: []const u8 = "aoeu";
|
||||
@ -370,19 +326,6 @@ test "array concat of slices gives slice" {
|
||||
}
|
||||
}
|
||||
|
||||
test "comptime pointer cast array and then slice" {
|
||||
const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
|
||||
const sliceA: []const u8 = ptrA[0..2];
|
||||
|
||||
const ptrB: [*]const u8 = &array;
|
||||
const sliceB: []const u8 = ptrB[0..2];
|
||||
|
||||
try expect(sliceA[1] == 2);
|
||||
try expect(sliceB[1] == 2);
|
||||
}
|
||||
|
||||
test "slice bounds in comptime concatenation" {
|
||||
const bs = comptime blk: {
|
||||
const b = "........1........";
|
||||
|
||||
12
test/behavior/slice_stage2.zig
Normal file
12
test/behavior/slice_stage2.zig
Normal file
@ -0,0 +1,12 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const x = @intToPtr([*]i32, 0x1000)[0..0x500];
|
||||
const y = x[0x100..];
|
||||
test "compile time slice of pointer to hard coded address" {
|
||||
try expect(@ptrToInt(x) == 0x1000);
|
||||
try expect(x.len == 0x500);
|
||||
|
||||
try expect(@ptrToInt(y) == 0x1400);
|
||||
try expect(y.len == 0x400);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user