| author | |
| committer | |
| log | 1e5a494603d287ea3005dc35f0528c0311f43515 |
| tree | 7e5d6799a59ca44dc11d6b280dcd4f622ef0ca42 |
| parent | 7f0cf395aa74eb5ea250bd28f7525b3036790a6a |
and error union payload5 files changed, 232 insertions(+), 140 deletions(-)
src/Sema.zig+68-2| ... | ... | @@ -15069,8 +15069,74 @@ fn beginComptimePtrMutation( |
| 15069 | 15069 | else => unreachable, |
| 15070 | 15070 | } |
| 15071 | 15071 | }, |
| 15072 | .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}), | |
| 15073 | .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}), | |
| 15072 | .eu_payload_ptr => { | |
| 15073 | const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data; | |
| 15074 | var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val); | |
| 15075 | const payload_ty = parent.ty.errorUnionPayload(); | |
| 15076 | switch (parent.val.tag()) { | |
| 15077 | else => { | |
| 15078 | // An error union has been initialized to undefined at comptime and now we | |
| 15079 | // are for the first time setting the payload. We must change the | |
| 15080 | // representation of the error union from `undef` to `opt_payload`. | |
| 15081 | const arena = parent.beginArena(sema.gpa); | |
| 15082 | defer parent.finishArena(); | |
| 15083 | ||
| 15084 | const payload = try arena.create(Value.Payload.SubValue); | |
| 15085 | payload.* = .{ | |
| 15086 | .base = .{ .tag = .eu_payload }, | |
| 15087 | .data = Value.undef, | |
| 15088 | }; | |
| 15089 | ||
| 15090 | parent.val.* = Value.initPayload(&payload.base); | |
| 15091 | ||
| 15092 | return ComptimePtrMutationKit{ | |
| 15093 | .decl_ref_mut = parent.decl_ref_mut, | |
| 15094 | .val = &payload.data, | |
| 15095 | .ty = payload_ty, | |
| 15096 | }; | |
| 15097 | }, | |
| 15098 | .eu_payload => return ComptimePtrMutationKit{ | |
| 15099 | .decl_ref_mut = parent.decl_ref_mut, | |
| 15100 | .val = &parent.val.castTag(.eu_payload).?.data, | |
| 15101 | .ty = payload_ty, | |
| 15102 | }, | |
| 15103 | } | |
| 15104 | }, | |
| 15105 | .opt_payload_ptr => { | |
| 15106 | const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data; | |
| 15107 | var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val); | |
| 15108 | const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); | |
| 15109 | switch (parent.val.tag()) { | |
| 15110 | .undef, .null_value => { | |
| 15111 | // An optional has been initialized to undefined at comptime and now we | |
| 15112 | // are for the first time setting the payload. We must change the | |
| 15113 | // representation of the optional from `undef` to `opt_payload`. | |
| 15114 | const arena = parent.beginArena(sema.gpa); | |
| 15115 | defer parent.finishArena(); | |
| 15116 | ||
| 15117 | const payload = try arena.create(Value.Payload.SubValue); | |
| 15118 | payload.* = .{ | |
| 15119 | .base = .{ .tag = .opt_payload }, | |
| 15120 | .data = Value.undef, | |
| 15121 | }; | |
| 15122 | ||
| 15123 | parent.val.* = Value.initPayload(&payload.base); | |
| 15124 | ||
| 15125 | return ComptimePtrMutationKit{ | |
| 15126 | .decl_ref_mut = parent.decl_ref_mut, | |
| 15127 | .val = &payload.data, | |
| 15128 | .ty = payload_ty, | |
| 15129 | }; | |
| 15130 | }, | |
| 15131 | .opt_payload => return ComptimePtrMutationKit{ | |
| 15132 | .decl_ref_mut = parent.decl_ref_mut, | |
| 15133 | .val = &parent.val.castTag(.opt_payload).?.data, | |
| 15134 | .ty = payload_ty, | |
| 15135 | }, | |
| 15136 | ||
| 15137 | else => unreachable, | |
| 15138 | } | |
| 15139 | }, | |
| 15074 | 15140 | .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already |
| 15075 | 15141 | else => unreachable, |
| 15076 | 15142 | } |
test/behavior.zig+1-3| ... | ... | @@ -33,7 +33,7 @@ test { |
| 33 | 33 | _ = @import("behavior/hasdecl.zig"); |
| 34 | 34 | _ = @import("behavior/hasfield.zig"); |
| 35 | 35 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 36 | _ = @import("behavior/optional_llvm.zig"); | |
| 36 | _ = @import("behavior/optional.zig"); | |
| 37 | 37 | _ = @import("behavior/prefetch.zig"); |
| 38 | 38 | _ = @import("behavior/pub_enum.zig"); |
| 39 | 39 | _ = @import("behavior/slice_sentinel_comptime.zig"); |
| ... | ... | @@ -69,7 +69,6 @@ test { |
| 69 | 69 | _ = @import("behavior/inttoptr.zig"); |
| 70 | 70 | _ = @import("behavior/member_func.zig"); |
| 71 | 71 | _ = @import("behavior/null.zig"); |
| 72 | _ = @import("behavior/optional.zig"); | |
| 73 | 72 | _ = @import("behavior/pointers.zig"); |
| 74 | 73 | _ = @import("behavior/ptrcast.zig"); |
| 75 | 74 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| ... | ... | @@ -154,7 +153,6 @@ test { |
| 154 | 153 | _ = @import("behavior/ir_block_deps.zig"); |
| 155 | 154 | _ = @import("behavior/misc.zig"); |
| 156 | 155 | _ = @import("behavior/muladd.zig"); |
| 157 | _ = @import("behavior/optional_stage1.zig"); | |
| 158 | 156 | _ = @import("behavior/popcount_stage1.zig"); |
| 159 | 157 | _ = @import("behavior/reflection.zig"); |
| 160 | 158 | _ = @import("behavior/select.zig"); |
test/behavior/optional.zig+163| ... | ... | @@ -1,9 +1,13 @@ |
| 1 | const builtin = @import("builtin"); | |
| 1 | 2 | const std = @import("std"); |
| 2 | 3 | const testing = std.testing; |
| 3 | 4 | const expect = testing.expect; |
| 4 | 5 | const expectEqual = testing.expectEqual; |
| 5 | 6 | |
| 6 | 7 | test "passing an optional integer as a parameter" { |
| 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 10 | ||
| 7 | 11 | const S = struct { |
| 8 | 12 | fn entry() bool { |
| 9 | 13 | var x: i32 = 1234; |
| ... | ... | @@ -21,12 +25,18 @@ test "passing an optional integer as a parameter" { |
| 21 | 25 | pub const EmptyStruct = struct {}; |
| 22 | 26 | |
| 23 | 27 | test "optional pointer to size zero struct" { |
| 28 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 29 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 30 | ||
| 24 | 31 | var e = EmptyStruct{}; |
| 25 | 32 | var o: ?*EmptyStruct = &e; |
| 26 | 33 | try expect(o != null); |
| 27 | 34 | } |
| 28 | 35 | |
| 29 | 36 | test "equality compare optional pointers" { |
| 37 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 38 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 39 | ||
| 30 | 40 | try testNullPtrsEql(); |
| 31 | 41 | comptime try testNullPtrsEql(); |
| 32 | 42 | } |
| ... | ... | @@ -48,6 +58,9 @@ fn testNullPtrsEql() !void { |
| 48 | 58 | } |
| 49 | 59 | |
| 50 | 60 | test "optional with void type" { |
| 61 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 62 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 63 | ||
| 51 | 64 | const Foo = struct { |
| 52 | 65 | x: ?void, |
| 53 | 66 | }; |
| ... | ... | @@ -56,6 +69,9 @@ test "optional with void type" { |
| 56 | 69 | } |
| 57 | 70 | |
| 58 | 71 | test "address of unwrap optional" { |
| 72 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 73 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 74 | ||
| 59 | 75 | const S = struct { |
| 60 | 76 | const Foo = struct { |
| 61 | 77 | a: i32, |
| ... | ... | @@ -73,6 +89,9 @@ test "address of unwrap optional" { |
| 73 | 89 | } |
| 74 | 90 | |
| 75 | 91 | test "nested optional field in struct" { |
| 92 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 93 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 94 | ||
| 76 | 95 | const S2 = struct { |
| 77 | 96 | y: u8, |
| 78 | 97 | }; |
| ... | ... | @@ -86,6 +105,9 @@ test "nested optional field in struct" { |
| 86 | 105 | } |
| 87 | 106 | |
| 88 | 107 | test "equality compare optional with non-optional" { |
| 108 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 109 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 110 | ||
| 89 | 111 | try test_cmp_optional_non_optional(); |
| 90 | 112 | comptime try test_cmp_optional_non_optional(); |
| 91 | 113 | } |
| ... | ... | @@ -120,6 +142,9 @@ fn test_cmp_optional_non_optional() !void { |
| 120 | 142 | } |
| 121 | 143 | |
| 122 | 144 | test "unwrap function call with optional pointer return value" { |
| 145 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 146 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 147 | ||
| 123 | 148 | const S = struct { |
| 124 | 149 | fn entry() !void { |
| 125 | 150 | try expect(foo().?.* == 1234); |
| ... | ... | @@ -138,6 +163,9 @@ test "unwrap function call with optional pointer return value" { |
| 138 | 163 | } |
| 139 | 164 | |
| 140 | 165 | test "nested orelse" { |
| 166 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 167 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 168 | ||
| 141 | 169 | const S = struct { |
| 142 | 170 | fn entry() !void { |
| 143 | 171 | try expect(func() == null); |
| ... | ... | @@ -159,3 +187,138 @@ test "nested orelse" { |
| 159 | 187 | try S.entry(); |
| 160 | 188 | comptime try S.entry(); |
| 161 | 189 | } |
| 190 | ||
| 191 | test "self-referential struct through a slice of optional" { | |
| 192 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 193 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 194 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 195 | ||
| 196 | const S = struct { | |
| 197 | const Node = struct { | |
| 198 | children: []?Node, | |
| 199 | data: ?u8, | |
| 200 | ||
| 201 | fn new() Node { | |
| 202 | return Node{ | |
| 203 | .children = undefined, | |
| 204 | .data = null, | |
| 205 | }; | |
| 206 | } | |
| 207 | }; | |
| 208 | }; | |
| 209 | ||
| 210 | var n = S.Node.new(); | |
| 211 | try expect(n.data == null); | |
| 212 | } | |
| 213 | ||
| 214 | test "assigning to an unwrapped optional field in an inline loop" { | |
| 215 | comptime var maybe_pos_arg: ?comptime_int = null; | |
| 216 | inline for ("ab") |x| { | |
| 217 | _ = x; | |
| 218 | maybe_pos_arg = 0; | |
| 219 | if (maybe_pos_arg.? != 0) { | |
| 220 | @compileError("bad"); | |
| 221 | } | |
| 222 | maybe_pos_arg.? = 10; | |
| 223 | } | |
| 224 | } | |
| 225 | ||
| 226 | test "coerce an anon struct literal to optional struct" { | |
| 227 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 228 | ||
| 229 | const S = struct { | |
| 230 | const Struct = struct { | |
| 231 | field: u32, | |
| 232 | }; | |
| 233 | fn doTheTest() !void { | |
| 234 | var maybe_dims: ?Struct = null; | |
| 235 | maybe_dims = .{ .field = 1 }; | |
| 236 | try expect(maybe_dims.?.field == 1); | |
| 237 | } | |
| 238 | }; | |
| 239 | try S.doTheTest(); | |
| 240 | comptime try S.doTheTest(); | |
| 241 | } | |
| 242 | ||
| 243 | test "0-bit child type coerced to optional return ptr result location" { | |
| 244 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 245 | ||
| 246 | const S = struct { | |
| 247 | fn doTheTest() !void { | |
| 248 | var y = Foo{}; | |
| 249 | var z = y.thing(); | |
| 250 | try expect(z != null); | |
| 251 | } | |
| 252 | ||
| 253 | const Foo = struct { | |
| 254 | pub const Bar = struct { | |
| 255 | field: *Foo, | |
| 256 | }; | |
| 257 | ||
| 258 | pub fn thing(self: *Foo) ?Bar { | |
| 259 | return Bar{ .field = self }; | |
| 260 | } | |
| 261 | }; | |
| 262 | }; | |
| 263 | try S.doTheTest(); | |
| 264 | comptime try S.doTheTest(); | |
| 265 | } | |
| 266 | ||
| 267 | test "0-bit child type coerced to optional" { | |
| 268 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 269 | ||
| 270 | const S = struct { | |
| 271 | fn doTheTest() !void { | |
| 272 | var it: Foo = .{ | |
| 273 | .list = undefined, | |
| 274 | }; | |
| 275 | try expect(it.foo() != null); | |
| 276 | } | |
| 277 | ||
| 278 | const Empty = struct {}; | |
| 279 | const Foo = struct { | |
| 280 | list: [10]Empty, | |
| 281 | ||
| 282 | fn foo(self: *Foo) ?*Empty { | |
| 283 | const data = &self.list[0]; | |
| 284 | return data; | |
| 285 | } | |
| 286 | }; | |
| 287 | }; | |
| 288 | try S.doTheTest(); | |
| 289 | comptime try S.doTheTest(); | |
| 290 | } | |
| 291 | ||
| 292 | test "array of optional unaligned types" { | |
| 293 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 294 | ||
| 295 | const Enum = enum { one, two, three }; | |
| 296 | ||
| 297 | const SomeUnion = union(enum) { | |
| 298 | Num: Enum, | |
| 299 | Other: u32, | |
| 300 | }; | |
| 301 | ||
| 302 | const values = [_]?SomeUnion{ | |
| 303 | SomeUnion{ .Num = .one }, | |
| 304 | SomeUnion{ .Num = .two }, | |
| 305 | SomeUnion{ .Num = .three }, | |
| 306 | SomeUnion{ .Num = .one }, | |
| 307 | SomeUnion{ .Num = .two }, | |
| 308 | SomeUnion{ .Num = .three }, | |
| 309 | }; | |
| 310 | ||
| 311 | // The index must be a runtime value | |
| 312 | var i: usize = 0; | |
| 313 | try expectEqual(Enum.one, values[i].?.Num); | |
| 314 | i += 1; | |
| 315 | try expectEqual(Enum.two, values[i].?.Num); | |
| 316 | i += 1; | |
| 317 | try expectEqual(Enum.three, values[i].?.Num); | |
| 318 | i += 1; | |
| 319 | try expectEqual(Enum.one, values[i].?.Num); | |
| 320 | i += 1; | |
| 321 | try expectEqual(Enum.two, values[i].?.Num); | |
| 322 | i += 1; | |
| 323 | try expectEqual(Enum.three, values[i].?.Num); | |
| 324 | } |
test/behavior/optional_llvm.zig deleted-27| ... | ... | @@ -1,27 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const expect = testing.expect; | |
| 4 | const expectEqual = testing.expectEqual; | |
| 5 | const builtin = @import("builtin"); | |
| 6 | ||
| 7 | test "self-referential struct through a slice of optional" { | |
| 8 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 11 | const S = struct { | |
| 12 | const Node = struct { | |
| 13 | children: []?Node, | |
| 14 | data: ?u8, | |
| 15 | ||
| 16 | fn new() Node { | |
| 17 | return Node{ | |
| 18 | .children = undefined, | |
| 19 | .data = null, | |
| 20 | }; | |
| 21 | } | |
| 22 | }; | |
| 23 | }; | |
| 24 | ||
| 25 | var n = S.Node.new(); | |
| 26 | try expect(n.data == null); | |
| 27 | } |
test/behavior/optional_stage1.zig deleted-108| ... | ... | @@ -1,108 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const expect = testing.expect; | |
| 4 | const expectEqual = testing.expectEqual; | |
| 5 | ||
| 6 | test "assigning to an unwrapped optional field in an inline loop" { | |
| 7 | comptime var maybe_pos_arg: ?comptime_int = null; | |
| 8 | inline for ("ab") |x| { | |
| 9 | _ = x; | |
| 10 | maybe_pos_arg = 0; | |
| 11 | if (maybe_pos_arg.? != 0) { | |
| 12 | @compileError("bad"); | |
| 13 | } | |
| 14 | maybe_pos_arg.? = 10; | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | test "coerce an anon struct literal to optional struct" { | |
| 19 | const S = struct { | |
| 20 | const Struct = struct { | |
| 21 | field: u32, | |
| 22 | }; | |
| 23 | fn doTheTest() !void { | |
| 24 | var maybe_dims: ?Struct = null; | |
| 25 | maybe_dims = .{ .field = 1 }; | |
| 26 | try expect(maybe_dims.?.field == 1); | |
| 27 | } | |
| 28 | }; | |
| 29 | try S.doTheTest(); | |
| 30 | comptime try S.doTheTest(); | |
| 31 | } | |
| 32 | ||
| 33 | test "0-bit child type coerced to optional return ptr result location" { | |
| 34 | const S = struct { | |
| 35 | fn doTheTest() !void { | |
| 36 | var y = Foo{}; | |
| 37 | var z = y.thing(); | |
| 38 | try expect(z != null); | |
| 39 | } | |
| 40 | ||
| 41 | const Foo = struct { | |
| 42 | pub const Bar = struct { | |
| 43 | field: *Foo, | |
| 44 | }; | |
| 45 | ||
| 46 | pub fn thing(self: *Foo) ?Bar { | |
| 47 | return Bar{ .field = self }; | |
| 48 | } | |
| 49 | }; | |
| 50 | }; | |
| 51 | try S.doTheTest(); | |
| 52 | comptime try S.doTheTest(); | |
| 53 | } | |
| 54 | ||
| 55 | test "0-bit child type coerced to optional" { | |
| 56 | const S = struct { | |
| 57 | fn doTheTest() !void { | |
| 58 | var it: Foo = .{ | |
| 59 | .list = undefined, | |
| 60 | }; | |
| 61 | try expect(it.foo() != null); | |
| 62 | } | |
| 63 | ||
| 64 | const Empty = struct {}; | |
| 65 | const Foo = struct { | |
| 66 | list: [10]Empty, | |
| 67 | ||
| 68 | fn foo(self: *Foo) ?*Empty { | |
| 69 | const data = &self.list[0]; | |
| 70 | return data; | |
| 71 | } | |
| 72 | }; | |
| 73 | }; | |
| 74 | try S.doTheTest(); | |
| 75 | comptime try S.doTheTest(); | |
| 76 | } | |
| 77 | ||
| 78 | test "array of optional unaligned types" { | |
| 79 | const Enum = enum { one, two, three }; | |
| 80 | ||
| 81 | const SomeUnion = union(enum) { | |
| 82 | Num: Enum, | |
| 83 | Other: u32, | |
| 84 | }; | |
| 85 | ||
| 86 | const values = [_]?SomeUnion{ | |
| 87 | SomeUnion{ .Num = .one }, | |
| 88 | SomeUnion{ .Num = .two }, | |
| 89 | SomeUnion{ .Num = .three }, | |
| 90 | SomeUnion{ .Num = .one }, | |
| 91 | SomeUnion{ .Num = .two }, | |
| 92 | SomeUnion{ .Num = .three }, | |
| 93 | }; | |
| 94 | ||
| 95 | // The index must be a runtime value | |
| 96 | var i: usize = 0; | |
| 97 | try expectEqual(Enum.one, values[i].?.Num); | |
| 98 | i += 1; | |
| 99 | try expectEqual(Enum.two, values[i].?.Num); | |
| 100 | i += 1; | |
| 101 | try expectEqual(Enum.three, values[i].?.Num); | |
| 102 | i += 1; | |
| 103 | try expectEqual(Enum.one, values[i].?.Num); | |
| 104 | i += 1; | |
| 105 | try expectEqual(Enum.two, values[i].?.Num); | |
| 106 | i += 1; | |
| 107 | try expectEqual(Enum.three, values[i].?.Num); | |
| 108 | } |