authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-23 19:46:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-23 19:47:32-07:00
log22b4c9e1a9595bd94ada4c500a430e2668ffcd07
tree325e312f063422e19b0427373194b703b8cc3b53
parentee98d8700818aa667137e3aa580b16df2ba6d680

stage2: implement more C pointer Sema and comptime ptr arith


6 files changed, 113 insertions(+), 62 deletions(-)

src/Sema.zig+59-16
......@@ -7983,12 +7983,25 @@ fn analyzePtrArithmetic(
79837983 const runtime_src = rs: {
79847984 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
79857985 if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {
7986 const ptr_ty = sema.typeOf(ptr);
7987 const offset_int = offset_val.toUnsignedInt();
7988 const new_ptr_ty = ptr_ty; // TODO modify alignment
7989 if (ptr_val.getUnsignedInt()) |addr| {
7990 const target = sema.mod.getTarget();
7991 const elem_ty = ptr_ty.childType();
7992 const elem_size = elem_ty.abiSize(target);
7993 const new_addr = switch (air_tag) {
7994 .ptr_add => addr + elem_size * offset_int,
7995 .ptr_sub => addr - elem_size * offset_int,
7996 else => unreachable,
7997 };
7998 const new_ptr_val = try Value.Tag.int_u64.create(sema.arena, new_addr);
7999 return sema.addConstant(new_ptr_ty, new_ptr_val);
8000 }
79868001 if (air_tag == .ptr_sub) {
79878002 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
79888003 }
7989 const offset_int = offset_val.toUnsignedInt();
79908004 const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);
7991 const new_ptr_ty = sema.typeOf(ptr);
79928005 return sema.addConstant(new_ptr_ty, new_ptr_val);
79938006 } else break :rs offset_src;
79948007 } else break :rs ptr_src;
......@@ -11979,6 +11992,8 @@ fn coerce(
1197911992 return sema.wrapOptional(block, dest_ty, intermediate, inst_src);
1198011993 },
1198111994 .Pointer => {
11995 const dest_info = dest_ty.ptrInfo().data;
11996
1198211997 // Function body to function pointer.
1198311998 if (inst_ty.zigTypeTag() == .Fn) {
1198411999 const fn_val = try sema.resolveConstValue(block, inst_src, inst);
......@@ -11989,16 +12004,16 @@ fn coerce(
1198912004
1199012005 // *T to *[1]T
1199112006 single_item: {
11992 if (!dest_ty.isSinglePointer()) break :single_item;
12007 if (dest_info.size != .One) break :single_item;
1199312008 if (!inst_ty.isSinglePointer()) break :single_item;
1199412009 const ptr_elem_ty = inst_ty.childType();
11995 const array_ty = dest_ty.childType();
12010 const array_ty = dest_info.pointee_type;
1199612011 if (array_ty.zigTypeTag() != .Array) break :single_item;
1199712012 const array_elem_ty = array_ty.childType();
11998 const dest_is_mut = !dest_ty.isConstPtr();
12013 const dest_is_mut = dest_info.mutable;
1199912014 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
12000 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :single_item;
12001 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :single_item;
12015 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item;
12016 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
1200212017 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
1200312018 .ok => {},
1200412019 .no_match => break :single_item,
......@@ -12012,18 +12027,18 @@ fn coerce(
1201212027 const array_ty = inst_ty.childType();
1201312028 if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;
1201412029 const array_elem_type = array_ty.childType();
12015 const dest_is_mut = !dest_ty.isConstPtr();
12030 const dest_is_mut = dest_info.mutable;
1201612031 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;
12017 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;
12018 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :src_array_ptr;
12032 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr;
12033 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :src_array_ptr;
1201912034
12020 const dst_elem_type = dest_ty.childType();
12035 const dst_elem_type = dest_info.pointee_type;
1202112036 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
1202212037 .ok => {},
1202312038 .no_match => break :src_array_ptr,
1202412039 }
1202512040
12026 switch (dest_ty.ptrSize()) {
12041 switch (dest_info.size) {
1202712042 .Slice => {
1202812043 // *[N]T to []T
1202912044 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
......@@ -12036,7 +12051,7 @@ fn coerce(
1203612051 // *[N]T to [*]T
1203712052 // *[N:s]T to [*:s]T
1203812053 // *[N:s]T to [*]T
12039 if (dest_ty.sentinel()) |dst_sentinel| {
12054 if (dest_info.sentinel) |dst_sentinel| {
1204012055 if (array_ty.sentinel()) |src_sentinel| {
1204112056 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
1204212057 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
......@@ -12051,9 +12066,24 @@ fn coerce(
1205112066 }
1205212067
1205312068 // coercion to C pointer
12054 if (dest_ty.ptrSize() == .C) {
12055 if (inst_ty.zigTypeTag() == .Null) {
12056 return sema.addConstant(dest_ty, Value.@"null");
12069 if (dest_info.size == .C) {
12070 switch (inst_ty.zigTypeTag()) {
12071 .Null => {
12072 return sema.addConstant(dest_ty, Value.@"null");
12073 },
12074 .ComptimeInt => {
12075 const addr = try sema.coerce(block, Type.usize, inst, inst_src);
12076 return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
12077 },
12078 .Int => {
12079 const ptr_size_ty = switch (inst_ty.intInfo(target).signedness) {
12080 .signed => Type.isize,
12081 .unsigned => Type.usize,
12082 };
12083 const addr = try sema.coerce(block, ptr_size_ty, inst, inst_src);
12084 return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
12085 },
12086 else => {},
1205712087 }
1205812088 }
1205912089 },
......@@ -13632,6 +13662,19 @@ fn resolvePeerTypes(
1363213662 continue;
1363313663 }
1363413664
13665 if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() == .C and
13666 (candidate_ty_tag == .Int or candidate_ty_tag == .ComptimeInt))
13667 {
13668 continue;
13669 }
13670 if (candidate_ty_tag == .Pointer and candidate_ty.ptrSize() == .C and
13671 (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt))
13672 {
13673 chosen = candidate;
13674 chosen_i = candidate_i + 1;
13675 continue;
13676 }
13677
1363513678 if (chosen_ty_tag == .ComptimeFloat and candidate_ty_tag == .ComptimeInt)
1363613679 continue;
1363713680 if (chosen_ty_tag == .ComptimeInt and candidate_ty_tag == .ComptimeFloat) {
src/codegen/llvm.zig+4-3
......@@ -1106,7 +1106,7 @@ pub const DeclGen = struct {
11061106 return parent_ptr.constInBoundsGEP(&indices, indices.len);
11071107 }
11081108 },
1109 .null_value => {
1109 .null_value, .zero => {
11101110 const llvm_type = try self.llvmType(tv.ty);
11111111 return llvm_type.constNull();
11121112 },
......@@ -3180,8 +3180,9 @@ pub const FuncGen = struct {
31803180 const inst_ty = self.air.typeOfIndex(inst);
31813181 const llvm_dest_ty = try self.dg.llvmType(inst_ty);
31823182
3183 // TODO look into pulling this logic out into a different AIR instruction than bitcast
3184 if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
3183 if (operand_ty.zigTypeTag() == .Int and inst_ty.zigTypeTag() == .Pointer) {
3184 return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");
3185 } else if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
31853186 const target = self.dg.module.getTarget();
31863187 const elem_ty = operand_ty.childType();
31873188 if (!isByRef(inst_ty)) {
src/type.zig+1
......@@ -4006,6 +4006,7 @@ pub const Type = extern union {
40064006 pub const @"u8" = initTag(.u8);
40074007 pub const @"bool" = initTag(.bool);
40084008 pub const @"usize" = initTag(.usize);
4009 pub const @"isize" = initTag(.isize);
40094010 pub const @"comptime_int" = initTag(.comptime_int);
40104011 pub const @"void" = initTag(.void);
40114012 pub const @"type" = initTag(.type);
src/value.zig+14-8
......@@ -937,9 +937,10 @@ pub const Value = extern union {
937937 }
938938 }
939939
940 /// Asserts the value is an integer and it fits in a u64
941 pub fn toUnsignedInt(self: Value) u64 {
942 switch (self.tag()) {
940 /// If the value fits in a u64, return it, otherwise null.
941 /// Asserts not undefined.
942 pub fn getUnsignedInt(val: Value) ?u64 {
943 switch (val.tag()) {
943944 .zero,
944945 .bool_false,
945946 .the_only_possible_value, // i0, u0
......@@ -949,16 +950,21 @@ pub const Value = extern union {
949950 .bool_true,
950951 => return 1,
951952
952 .int_u64 => return self.castTag(.int_u64).?.data,
953 .int_i64 => return @intCast(u64, self.castTag(.int_i64).?.data),
954 .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(u64) catch unreachable,
955 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(u64) catch unreachable,
953 .int_u64 => return val.castTag(.int_u64).?.data,
954 .int_i64 => return @intCast(u64, val.castTag(.int_i64).?.data),
955 .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null,
956 .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null,
956957
957958 .undef => unreachable,
958 else => unreachable,
959 else => return null,
959960 }
960961 }
961962
963 /// Asserts the value is an integer and it fits in a u64
964 pub fn toUnsignedInt(val: Value) u64 {
965 return getUnsignedInt(val).?;
966 }
967
962968 /// Asserts the value is an integer and it fits in a i64
963969 pub fn toSignedInt(self: Value) i64 {
964970 switch (self.tag()) {
test/behavior/pointers.zig+35
......@@ -58,3 +58,38 @@ test "initialize const optional C pointer to null" {
5858 try expect(a == null);
5959 comptime try expect(a == null);
6060}
61
62test "assigning integer to C pointer" {
63 var x: i32 = 0;
64 var ptr: [*c]u8 = 0;
65 var ptr2: [*c]u8 = x;
66 if (false) {
67 ptr;
68 ptr2;
69 }
70}
71
72test "C pointer comparison and arithmetic" {
73 const S = struct {
74 fn doTheTest() !void {
75 var ptr1: [*c]u32 = 0;
76 var ptr2 = ptr1 + 10;
77 try expect(ptr1 == 0);
78 try expect(ptr1 >= 0);
79 try expect(ptr1 <= 0);
80 // expect(ptr1 < 1);
81 // expect(ptr1 < one);
82 // expect(1 > ptr1);
83 // expect(one > ptr1);
84 try expect(ptr1 < ptr2);
85 try expect(ptr2 > ptr1);
86 try expect(ptr2 >= 40);
87 try expect(ptr2 == 40);
88 try expect(ptr2 <= 40);
89 ptr2 -= 10;
90 try expect(ptr1 == ptr2);
91 }
92 };
93 try S.doTheTest();
94 comptime try S.doTheTest();
95}
test/behavior/pointers_stage1.zig-35
......@@ -19,41 +19,6 @@ fn testDerefPtrOneVal() !void {
1919 try expect(@TypeOf(y.x) == void);
2020}
2121
22test "assigning integer to C pointer" {
23 var x: i32 = 0;
24 var ptr: [*c]u8 = 0;
25 var ptr2: [*c]u8 = x;
26 if (false) {
27 ptr;
28 ptr2;
29 }
30}
31
32test "C pointer comparison and arithmetic" {
33 const S = struct {
34 fn doTheTest() !void {
35 var ptr1: [*c]u32 = 0;
36 var ptr2 = ptr1 + 10;
37 try expect(ptr1 == 0);
38 try expect(ptr1 >= 0);
39 try expect(ptr1 <= 0);
40 // expect(ptr1 < 1);
41 // expect(ptr1 < one);
42 // expect(1 > ptr1);
43 // expect(one > ptr1);
44 try expect(ptr1 < ptr2);
45 try expect(ptr2 > ptr1);
46 try expect(ptr2 >= 40);
47 try expect(ptr2 == 40);
48 try expect(ptr2 <= 40);
49 ptr2 -= 10;
50 try expect(ptr1 == ptr2);
51 }
52 };
53 try S.doTheTest();
54 comptime try S.doTheTest();
55}
56
5722test "peer type resolution with C pointers" {
5823 var ptr_one: *u8 = undefined;
5924 var ptr_many: [*]u8 = undefined;