| author | |
| committer | |
| log | df198ea60e05664b5b72a43aae815fa06d94c19c |
| tree | edf13ce2d73ab17a56d6b07b4734b97260bde3eb |
| parent | 30d01c8fea68baab396081da040f49defa494088 |
| parent | 21bf3b80666c14c9b2a2e1ec984a6b4bb23a5bb7 |
| signature |
stage2: slice and optional improvements7 files changed, 127 insertions(+), 77 deletions(-)
src/Sema.zig+43-11| ... | ... | @@ -8013,14 +8013,24 @@ fn analyzePtrArithmetic( |
| 8013 | 8013 | const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); |
| 8014 | 8014 | // TODO adjust the return type according to alignment and other factors |
| 8015 | 8015 | const runtime_src = rs: { |
| 8016 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { | |
| 8017 | if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| { | |
| 8016 | if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { | |
| 8017 | if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| { | |
| 8018 | 8018 | const ptr_ty = sema.typeOf(ptr); |
| 8019 | const offset_int = offset_val.toUnsignedInt(); | |
| 8020 | 8019 | const new_ptr_ty = ptr_ty; // TODO modify alignment |
| 8020 | ||
| 8021 | if (ptr_val.isUndef() or offset_val.isUndef()) { | |
| 8022 | return sema.addConstUndef(new_ptr_ty); | |
| 8023 | } | |
| 8024 | ||
| 8025 | const offset_int = offset_val.toUnsignedInt(); | |
| 8021 | 8026 | if (ptr_val.getUnsignedInt()) |addr| { |
| 8022 | 8027 | const target = sema.mod.getTarget(); |
| 8023 | const elem_ty = ptr_ty.childType(); | |
| 8028 | const ptr_child_ty = ptr_ty.childType(); | |
| 8029 | const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) | |
| 8030 | ptr_child_ty.childType() | |
| 8031 | else | |
| 8032 | ptr_child_ty; | |
| 8033 | ||
| 8024 | 8034 | const elem_size = elem_ty.abiSize(target); |
| 8025 | 8035 | const new_addr = switch (air_tag) { |
| 8026 | 8036 | .ptr_add => addr + elem_size * offset_int, |
| ... | ... | @@ -13217,8 +13227,8 @@ fn analyzeSlice( |
| 13217 | 13227 | var elem_ty = ptr_ptr_child_ty.childType(); |
| 13218 | 13228 | switch (ptr_ptr_child_ty.zigTypeTag()) { |
| 13219 | 13229 | .Array => {}, |
| 13220 | .Pointer => { | |
| 13221 | if (ptr_ptr_child_ty.isSinglePointer()) { | |
| 13230 | .Pointer => switch (ptr_ptr_child_ty.ptrSize()) { | |
| 13231 | .One => { | |
| 13222 | 13232 | const double_child_ty = ptr_ptr_child_ty.childType(); |
| 13223 | 13233 | if (double_child_ty.zigTypeTag() == .Array) { |
| 13224 | 13234 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); |
| ... | ... | @@ -13228,10 +13238,23 @@ fn analyzeSlice( |
| 13228 | 13238 | } else { |
| 13229 | 13239 | return sema.fail(block, ptr_src, "slice of single-item pointer", .{}); |
| 13230 | 13240 | } |
| 13231 | } | |
| 13241 | }, | |
| 13242 | .Many, .C => { | |
| 13243 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); | |
| 13244 | slice_ty = ptr_ptr_child_ty; | |
| 13245 | array_ty = ptr_ptr_child_ty; | |
| 13246 | elem_ty = ptr_ptr_child_ty.childType(); | |
| 13247 | }, | |
| 13248 | .Slice => { | |
| 13249 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); | |
| 13250 | slice_ty = ptr_ptr_child_ty; | |
| 13251 | array_ty = ptr_ptr_child_ty; | |
| 13252 | elem_ty = ptr_ptr_child_ty.childType(); | |
| 13253 | }, | |
| 13232 | 13254 | }, |
| 13233 | 13255 | else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}), |
| 13234 | 13256 | } |
| 13257 | ||
| 13235 | 13258 | const ptr = if (slice_ty.isSlice()) |
| 13236 | 13259 | try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src) |
| 13237 | 13260 | else |
| ... | ... | @@ -13263,7 +13286,6 @@ fn analyzeSlice( |
| 13263 | 13286 | |
| 13264 | 13287 | const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); |
| 13265 | 13288 | |
| 13266 | const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr); | |
| 13267 | 13289 | const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); |
| 13268 | 13290 | |
| 13269 | 13291 | const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; |
| ... | ... | @@ -13287,11 +13309,21 @@ fn analyzeSlice( |
| 13287 | 13309 | .size = .One, |
| 13288 | 13310 | }); |
| 13289 | 13311 | |
| 13290 | if (opt_new_ptr_val) |new_ptr_val| { | |
| 13291 | return sema.addConstant(return_ty, new_ptr_val); | |
| 13292 | } else { | |
| 13312 | const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr); | |
| 13313 | const new_ptr_val = opt_new_ptr_val orelse { | |
| 13293 | 13314 | return block.addBitCast(return_ty, new_ptr); |
| 13315 | }; | |
| 13316 | ||
| 13317 | if (!new_ptr_val.isUndef()) { | |
| 13318 | return sema.addConstant(return_ty, new_ptr_val); | |
| 13294 | 13319 | } |
| 13320 | ||
| 13321 | // Special case: @as([]i32, undefined)[x..x] | |
| 13322 | if (new_len_int == 0) { | |
| 13323 | return sema.addConstUndef(return_ty); | |
| 13324 | } | |
| 13325 | ||
| 13326 | return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{}); | |
| 13295 | 13327 | } |
| 13296 | 13328 | |
| 13297 | 13329 | const return_ty = try Type.ptr(sema.arena, .{ |
src/codegen/llvm.zig+9-8| ... | ... | @@ -767,7 +767,7 @@ pub const DeclGen = struct { |
| 767 | 767 | } |
| 768 | 768 | const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); |
| 769 | 769 | const elem_ty = t.childType(); |
| 770 | const llvm_elem_ty = if (elem_ty.hasCodeGenBits()) | |
| 770 | const llvm_elem_ty = if (elem_ty.hasCodeGenBits() or elem_ty.zigTypeTag() == .Array) | |
| 771 | 771 | try dg.llvmType(elem_ty) |
| 772 | 772 | else |
| 773 | 773 | dg.context.intType(8); |
| ... | ... | @@ -1480,7 +1480,7 @@ pub const DeclGen = struct { |
| 1480 | 1480 | } |
| 1481 | 1481 | |
| 1482 | 1482 | const llvm_type = try self.llvmType(tv.ty); |
| 1483 | if (!tv.ty.childType().hasCodeGenBits()) { | |
| 1483 | if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) { | |
| 1484 | 1484 | return self.lowerPtrToVoid(tv.ty); |
| 1485 | 1485 | } |
| 1486 | 1486 | |
| ... | ... | @@ -1502,7 +1502,7 @@ pub const DeclGen = struct { |
| 1502 | 1502 | // for non-optional pointers. We also need to respect the alignment, even though |
| 1503 | 1503 | // the address will never be dereferenced. |
| 1504 | 1504 | const llvm_usize = try dg.llvmType(Type.usize); |
| 1505 | const llvm_ptr_ty = dg.context.intType(8).pointerType(0); | |
| 1505 | const llvm_ptr_ty = try dg.llvmType(ptr_ty); | |
| 1506 | 1506 | if (alignment != 0) { |
| 1507 | 1507 | return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty); |
| 1508 | 1508 | } |
| ... | ... | @@ -2475,6 +2475,12 @@ pub const FuncGen = struct { |
| 2475 | 2475 | const operand = try self.resolveInst(un_op); |
| 2476 | 2476 | const operand_ty = self.air.typeOf(un_op); |
| 2477 | 2477 | const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; |
| 2478 | if (optional_ty.isPtrLikeOptional()) { | |
| 2479 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); | |
| 2480 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; | |
| 2481 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); | |
| 2482 | } | |
| 2483 | ||
| 2478 | 2484 | var buf: Type.Payload.ElemType = undefined; |
| 2479 | 2485 | const payload_ty = optional_ty.optionalChild(&buf); |
| 2480 | 2486 | if (!payload_ty.hasCodeGenBits()) { |
| ... | ... | @@ -2484,11 +2490,6 @@ pub const FuncGen = struct { |
| 2484 | 2490 | return operand; |
| 2485 | 2491 | } |
| 2486 | 2492 | } |
| 2487 | if (optional_ty.isPtrLikeOptional()) { | |
| 2488 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); | |
| 2489 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; | |
| 2490 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); | |
| 2491 | } | |
| 2492 | 2493 | |
| 2493 | 2494 | if (operand_is_ptr or isByRef(optional_ty)) { |
| 2494 | 2495 | const index_type = self.context.intType(32); |
src/type.zig+5-1| ... | ... | @@ -2347,11 +2347,13 @@ pub const Type = extern union { |
| 2347 | 2347 | } |
| 2348 | 2348 | } |
| 2349 | 2349 | |
| 2350 | /// Asserts that the type is an optional | |
| 2350 | /// Asserts that the type is an optional or a pointer that can be null. | |
| 2351 | 2351 | pub fn isPtrLikeOptional(self: Type) bool { |
| 2352 | 2352 | switch (self.tag()) { |
| 2353 | 2353 | .optional_single_const_pointer, |
| 2354 | 2354 | .optional_single_mut_pointer, |
| 2355 | .c_const_pointer, | |
| 2356 | .c_mut_pointer, | |
| 2355 | 2357 | => return true, |
| 2356 | 2358 | |
| 2357 | 2359 | .optional => { |
| ... | ... | @@ -2367,6 +2369,8 @@ pub const Type = extern union { |
| 2367 | 2369 | .Many, .One => return !info.@"allowzero", |
| 2368 | 2370 | } |
| 2369 | 2371 | }, |
| 2372 | ||
| 2373 | .pointer => return self.castTag(.pointer).?.data.size == .C, | |
| 2370 | 2374 | else => unreachable, |
| 2371 | 2375 | } |
| 2372 | 2376 | } |
test/behavior.zig+1| ... | ... | @@ -65,6 +65,7 @@ test { |
| 65 | 65 | if (builtin.zig_is_stage2) { |
| 66 | 66 | // When all comptime_memory.zig tests pass, #9646 can be closed. |
| 67 | 67 | // _ = @import("behavior/comptime_memory.zig"); |
| 68 | _ = @import("behavior/slice_stage2.zig"); | |
| 68 | 69 | } else { |
| 69 | 70 | _ = @import("behavior/align_stage1.zig"); |
| 70 | 71 | _ = @import("behavior/alignof.zig"); |
test/behavior/slice.zig+57| ... | ... | @@ -109,3 +109,60 @@ test "slice of type" { |
| 109 | 109 | } |
| 110 | 110 | } |
| 111 | 111 | } |
| 112 | ||
| 113 | test "generic malloc free" { | |
| 114 | const a = memAlloc(u8, 10) catch unreachable; | |
| 115 | memFree(u8, a); | |
| 116 | } | |
| 117 | var some_mem: [100]u8 = undefined; | |
| 118 | fn memAlloc(comptime T: type, n: usize) anyerror![]T { | |
| 119 | return @ptrCast([*]T, &some_mem[0])[0..n]; | |
| 120 | } | |
| 121 | fn memFree(comptime T: type, memory: []T) void { | |
| 122 | _ = memory; | |
| 123 | } | |
| 124 | ||
| 125 | test "slice of hardcoded address to pointer" { | |
| 126 | const S = struct { | |
| 127 | fn doTheTest() !void { | |
| 128 | const pointer = @intToPtr([*]u8, 0x04)[0..2]; | |
| 129 | comptime try expect(@TypeOf(pointer) == *[2]u8); | |
| 130 | const slice: []const u8 = pointer; | |
| 131 | try expect(@ptrToInt(slice.ptr) == 4); | |
| 132 | try expect(slice.len == 2); | |
| 133 | } | |
| 134 | }; | |
| 135 | ||
| 136 | try S.doTheTest(); | |
| 137 | } | |
| 138 | ||
| 139 | test "comptime slice of pointer preserves comptime var" { | |
| 140 | comptime { | |
| 141 | var buff: [10]u8 = undefined; | |
| 142 | var a = @ptrCast([*]u8, &buff); | |
| 143 | a[0..1][0] = 1; | |
| 144 | try expect(buff[0..][0..][0] == 1); | |
| 145 | } | |
| 146 | } | |
| 147 | ||
| 148 | test "comptime pointer cast array and then slice" { | |
| 149 | const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; | |
| 150 | ||
| 151 | const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); | |
| 152 | const sliceA: []const u8 = ptrA[0..2]; | |
| 153 | ||
| 154 | const ptrB: [*]const u8 = &array; | |
| 155 | const sliceB: []const u8 = ptrB[0..2]; | |
| 156 | ||
| 157 | try expect(sliceA[1] == 2); | |
| 158 | try expect(sliceB[1] == 2); | |
| 159 | } | |
| 160 | ||
| 161 | test "slicing zero length array" { | |
| 162 | const s1 = ""[0..]; | |
| 163 | const s2 = ([_]u32{})[0..]; | |
| 164 | try expect(s1.len == 0); | |
| 165 | try expect(s2.len == 0); | |
| 166 | try expect(mem.eql(u8, s1, "")); | |
| 167 | try expect(mem.eql(u32, s2, &[_]u32{})); | |
| 168 | } |
test/behavior/slice_stage1.zig-57| ... | ... | @@ -4,15 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | test "slicing zero length array" { | |
| 8 | const s1 = ""[0..]; | |
| 9 | const s2 = ([_]u32{})[0..]; | |
| 10 | try expect(s1.len == 0); | |
| 11 | try expect(s2.len == 0); | |
| 12 | try expect(mem.eql(u8, s1, "")); | |
| 13 | try expect(mem.eql(u32, s2, &[_]u32{})); | |
| 14 | } | |
| 15 | ||
| 16 | 7 | test "slice string literal has correct type" { |
| 17 | 8 | comptime { |
| 18 | 9 | try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); |
| ... | ... | @@ -25,18 +16,6 @@ test "slice string literal has correct type" { |
| 25 | 16 | comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32); |
| 26 | 17 | } |
| 27 | 18 | |
| 28 | test "generic malloc free" { | |
| 29 | const a = memAlloc(u8, 10) catch unreachable; | |
| 30 | memFree(u8, a); | |
| 31 | } | |
| 32 | var some_mem: [100]u8 = undefined; | |
| 33 | fn memAlloc(comptime T: type, n: usize) anyerror![]T { | |
| 34 | return @ptrCast([*]T, &some_mem[0])[0..n]; | |
| 35 | } | |
| 36 | fn memFree(comptime T: type, memory: []T) void { | |
| 37 | _ = memory; | |
| 38 | } | |
| 39 | ||
| 40 | 19 | test "result location zero sized array inside struct field implicit cast to slice" { |
| 41 | 20 | const E = struct { |
| 42 | 21 | entries: []u32, |
| ... | ... | @@ -307,20 +286,6 @@ test "slice syntax resulting in pointer-to-array" { |
| 307 | 286 | comptime try S.doTheTest(); |
| 308 | 287 | } |
| 309 | 288 | |
| 310 | test "slice of hardcoded address to pointer" { | |
| 311 | const S = struct { | |
| 312 | fn doTheTest() !void { | |
| 313 | const pointer = @intToPtr([*]u8, 0x04)[0..2]; | |
| 314 | comptime try expect(@TypeOf(pointer) == *[2]u8); | |
| 315 | const slice: []const u8 = pointer; | |
| 316 | try expect(@ptrToInt(slice.ptr) == 4); | |
| 317 | try expect(slice.len == 2); | |
| 318 | } | |
| 319 | }; | |
| 320 | ||
| 321 | try S.doTheTest(); | |
| 322 | } | |
| 323 | ||
| 324 | 289 | test "type coercion of pointer to anon struct literal to pointer to slice" { |
| 325 | 290 | const S = struct { |
| 326 | 291 | const U = union { |
| ... | ... | @@ -352,15 +317,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { |
| 352 | 317 | comptime try S.doTheTest(); |
| 353 | 318 | } |
| 354 | 319 | |
| 355 | test "comptime slice of pointer preserves comptime var" { | |
| 356 | comptime { | |
| 357 | var buff: [10]u8 = undefined; | |
| 358 | var a = @ptrCast([*]u8, &buff); | |
| 359 | a[0..1][0] = 1; | |
| 360 | try expect(buff[0..][0..][0] == 1); | |
| 361 | } | |
| 362 | } | |
| 363 | ||
| 364 | 320 | test "array concat of slices gives slice" { |
| 365 | 321 | comptime { |
| 366 | 322 | var a: []const u8 = "aoeu"; |
| ... | ... | @@ -370,19 +326,6 @@ test "array concat of slices gives slice" { |
| 370 | 326 | } |
| 371 | 327 | } |
| 372 | 328 | |
| 373 | test "comptime pointer cast array and then slice" { | |
| 374 | const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; | |
| 375 | ||
| 376 | const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); | |
| 377 | const sliceA: []const u8 = ptrA[0..2]; | |
| 378 | ||
| 379 | const ptrB: [*]const u8 = &array; | |
| 380 | const sliceB: []const u8 = ptrB[0..2]; | |
| 381 | ||
| 382 | try expect(sliceA[1] == 2); | |
| 383 | try expect(sliceB[1] == 2); | |
| 384 | } | |
| 385 | ||
| 386 | 329 | test "slice bounds in comptime concatenation" { |
| 387 | 330 | const bs = comptime blk: { |
| 388 | 331 | const b = "........1........"; |
test/behavior/slice_stage2.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; | |
| 5 | const y = x[0x100..]; | |
| 6 | test "compile time slice of pointer to hard coded address" { | |
| 7 | try expect(@ptrToInt(x) == 0x1000); | |
| 8 | try expect(x.len == 0x500); | |
| 9 | ||
| 10 | try expect(@ptrToInt(y) == 0x1400); | |
| 11 | try expect(y.len == 0x400); | |
| 12 | } |