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(...@@ -7983,12 +7983,25 @@ fn analyzePtrArithmetic(
7983 const runtime_src = rs: {7983 const runtime_src = rs: {
7984 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {7984 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
7985 if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {7985 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 }
7986 if (air_tag == .ptr_sub) {8001 if (air_tag == .ptr_sub) {
7987 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});8002 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
7988 }8003 }
7989 const offset_int = offset_val.toUnsignedInt();
7990 const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);8004 const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);
7991 const new_ptr_ty = sema.typeOf(ptr);
7992 return sema.addConstant(new_ptr_ty, new_ptr_val);8005 return sema.addConstant(new_ptr_ty, new_ptr_val);
7993 } else break :rs offset_src;8006 } else break :rs offset_src;
7994 } else break :rs ptr_src;8007 } else break :rs ptr_src;
...@@ -11979,6 +11992,8 @@ fn coerce(...@@ -11979,6 +11992,8 @@ fn coerce(
11979 return sema.wrapOptional(block, dest_ty, intermediate, inst_src);11992 return sema.wrapOptional(block, dest_ty, intermediate, inst_src);
11980 },11993 },
11981 .Pointer => {11994 .Pointer => {
11995 const dest_info = dest_ty.ptrInfo().data;
11996
11982 // Function body to function pointer.11997 // Function body to function pointer.
11983 if (inst_ty.zigTypeTag() == .Fn) {11998 if (inst_ty.zigTypeTag() == .Fn) {
11984 const fn_val = try sema.resolveConstValue(block, inst_src, inst);11999 const fn_val = try sema.resolveConstValue(block, inst_src, inst);
...@@ -11989,16 +12004,16 @@ fn coerce(...@@ -11989,16 +12004,16 @@ fn coerce(
1198912004
11990 // *T to *[1]T12005 // *T to *[1]T
11991 single_item: {12006 single_item: {
11992 if (!dest_ty.isSinglePointer()) break :single_item;12007 if (dest_info.size != .One) break :single_item;
11993 if (!inst_ty.isSinglePointer()) break :single_item;12008 if (!inst_ty.isSinglePointer()) break :single_item;
11994 const ptr_elem_ty = inst_ty.childType();12009 const ptr_elem_ty = inst_ty.childType();
11995 const array_ty = dest_ty.childType();12010 const array_ty = dest_info.pointee_type;
11996 if (array_ty.zigTypeTag() != .Array) break :single_item;12011 if (array_ty.zigTypeTag() != .Array) break :single_item;
11997 const array_elem_ty = array_ty.childType();12012 const array_elem_ty = array_ty.childType();
11998 const dest_is_mut = !dest_ty.isConstPtr();12013 const dest_is_mut = dest_info.mutable;
11999 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;12014 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
12000 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :single_item;12015 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item;
12001 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :single_item;12016 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
12002 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {12017 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
12003 .ok => {},12018 .ok => {},
12004 .no_match => break :single_item,12019 .no_match => break :single_item,
...@@ -12012,18 +12027,18 @@ fn coerce(...@@ -12012,18 +12027,18 @@ fn coerce(
12012 const array_ty = inst_ty.childType();12027 const array_ty = inst_ty.childType();
12013 if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;12028 if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;
12014 const array_elem_type = array_ty.childType();12029 const array_elem_type = array_ty.childType();
12015 const dest_is_mut = !dest_ty.isConstPtr();12030 const dest_is_mut = dest_info.mutable;
12016 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;12031 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;12032 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr;
12018 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) 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;
12021 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {12036 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
12022 .ok => {},12037 .ok => {},
12023 .no_match => break :src_array_ptr,12038 .no_match => break :src_array_ptr,
12024 }12039 }
1202512040
12026 switch (dest_ty.ptrSize()) {12041 switch (dest_info.size) {
12027 .Slice => {12042 .Slice => {
12028 // *[N]T to []T12043 // *[N]T to []T
12029 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);12044 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
...@@ -12036,7 +12051,7 @@ fn coerce(...@@ -12036,7 +12051,7 @@ fn coerce(
12036 // *[N]T to [*]T12051 // *[N]T to [*]T
12037 // *[N:s]T to [*:s]T12052 // *[N:s]T to [*:s]T
12038 // *[N:s]T to [*]T12053 // *[N:s]T to [*]T
12039 if (dest_ty.sentinel()) |dst_sentinel| {12054 if (dest_info.sentinel) |dst_sentinel| {
12040 if (array_ty.sentinel()) |src_sentinel| {12055 if (array_ty.sentinel()) |src_sentinel| {
12041 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {12056 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
12042 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);12057 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
...@@ -12051,9 +12066,24 @@ fn coerce(...@@ -12051,9 +12066,24 @@ fn coerce(
12051 }12066 }
1205212067
12053 // coercion to C pointer12068 // coercion to C pointer
12054 if (dest_ty.ptrSize() == .C) {12069 if (dest_info.size == .C) {
12055 if (inst_ty.zigTypeTag() == .Null) {12070 switch (inst_ty.zigTypeTag()) {
12056 return sema.addConstant(dest_ty, Value.@"null");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 => {},
12057 }12087 }
12058 }12088 }
12059 },12089 },
...@@ -13632,6 +13662,19 @@ fn resolvePeerTypes(...@@ -13632,6 +13662,19 @@ fn resolvePeerTypes(
13632 continue;13662 continue;
13633 }13663 }
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
13635 if (chosen_ty_tag == .ComptimeFloat and candidate_ty_tag == .ComptimeInt)13678 if (chosen_ty_tag == .ComptimeFloat and candidate_ty_tag == .ComptimeInt)
13636 continue;13679 continue;
13637 if (chosen_ty_tag == .ComptimeInt and candidate_ty_tag == .ComptimeFloat) {13680 if (chosen_ty_tag == .ComptimeInt and candidate_ty_tag == .ComptimeFloat) {
src/codegen/llvm.zig+4-3
...@@ -1106,7 +1106,7 @@ pub const DeclGen = struct {...@@ -1106,7 +1106,7 @@ pub const DeclGen = struct {
1106 return parent_ptr.constInBoundsGEP(&indices, indices.len);1106 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1107 }1107 }
1108 },1108 },
1109 .null_value => {1109 .null_value, .zero => {
1110 const llvm_type = try self.llvmType(tv.ty);1110 const llvm_type = try self.llvmType(tv.ty);
1111 return llvm_type.constNull();1111 return llvm_type.constNull();
1112 },1112 },
...@@ -3180,8 +3180,9 @@ pub const FuncGen = struct {...@@ -3180,8 +3180,9 @@ pub const FuncGen = struct {
3180 const inst_ty = self.air.typeOfIndex(inst);3180 const inst_ty = self.air.typeOfIndex(inst);
3181 const llvm_dest_ty = try self.dg.llvmType(inst_ty);3181 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 bitcast3183 if (operand_ty.zigTypeTag() == .Int and inst_ty.zigTypeTag() == .Pointer) {
3184 if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {3184 return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");
3185 } else if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
3185 const target = self.dg.module.getTarget();3186 const target = self.dg.module.getTarget();
3186 const elem_ty = operand_ty.childType();3187 const elem_ty = operand_ty.childType();
3187 if (!isByRef(inst_ty)) {3188 if (!isByRef(inst_ty)) {
src/type.zig+1
...@@ -4006,6 +4006,7 @@ pub const Type = extern union {...@@ -4006,6 +4006,7 @@ pub const Type = extern union {
4006 pub const @"u8" = initTag(.u8);4006 pub const @"u8" = initTag(.u8);
4007 pub const @"bool" = initTag(.bool);4007 pub const @"bool" = initTag(.bool);
4008 pub const @"usize" = initTag(.usize);4008 pub const @"usize" = initTag(.usize);
4009 pub const @"isize" = initTag(.isize);
4009 pub const @"comptime_int" = initTag(.comptime_int);4010 pub const @"comptime_int" = initTag(.comptime_int);
4010 pub const @"void" = initTag(.void);4011 pub const @"void" = initTag(.void);
4011 pub const @"type" = initTag(.type);4012 pub const @"type" = initTag(.type);
src/value.zig+14-8
...@@ -937,9 +937,10 @@ pub const Value = extern union {...@@ -937,9 +937,10 @@ pub const Value = extern union {
937 }937 }
938 }938 }
939939
940 /// Asserts the value is an integer and it fits in a u64940 /// If the value fits in a u64, return it, otherwise null.
941 pub fn toUnsignedInt(self: Value) u64 {941 /// Asserts not undefined.
942 switch (self.tag()) {942 pub fn getUnsignedInt(val: Value) ?u64 {
943 switch (val.tag()) {
943 .zero,944 .zero,
944 .bool_false,945 .bool_false,
945 .the_only_possible_value, // i0, u0946 .the_only_possible_value, // i0, u0
...@@ -949,16 +950,21 @@ pub const Value = extern union {...@@ -949,16 +950,21 @@ pub const Value = extern union {
949 .bool_true,950 .bool_true,
950 => return 1,951 => return 1,
951952
952 .int_u64 => return self.castTag(.int_u64).?.data,953 .int_u64 => return val.castTag(.int_u64).?.data,
953 .int_i64 => return @intCast(u64, self.castTag(.int_i64).?.data),954 .int_i64 => return @intCast(u64, val.castTag(.int_i64).?.data),
954 .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(u64) catch unreachable,955 .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null,
955 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(u64) catch unreachable,956 .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null,
956957
957 .undef => unreachable,958 .undef => unreachable,
958 else => unreachable,959 else => return null,
959 }960 }
960 }961 }
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
962 /// Asserts the value is an integer and it fits in a i64968 /// Asserts the value is an integer and it fits in a i64
963 pub fn toSignedInt(self: Value) i64 {969 pub fn toSignedInt(self: Value) i64 {
964 switch (self.tag()) {970 switch (self.tag()) {
test/behavior/pointers.zig+35
...@@ -58,3 +58,38 @@ test "initialize const optional C pointer to null" {...@@ -58,3 +58,38 @@ test "initialize const optional C pointer to null" {
58 try expect(a == null);58 try expect(a == null);
59 comptime try expect(a == null);59 comptime try expect(a == null);
60}60}
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 {...@@ -19,41 +19,6 @@ fn testDerefPtrOneVal() !void {
19 try expect(@TypeOf(y.x) == void);19 try expect(@TypeOf(y.x) == void);
20}20}
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
57test "peer type resolution with C pointers" {22test "peer type resolution with C pointers" {
58 var ptr_one: *u8 = undefined;23 var ptr_one: *u8 = undefined;
59 var ptr_many: [*]u8 = undefined;24 var ptr_many: [*]u8 = undefined;