mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
Sema: implement comptime ptr store to optional payload
and error union payload
This commit is contained in:
parent
7f0cf395aa
commit
1e5a494603
70
src/Sema.zig
70
src/Sema.zig
@ -15069,8 +15069,74 @@ fn beginComptimePtrMutation(
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}),
|
||||
.opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}),
|
||||
.eu_payload_ptr => {
|
||||
const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data;
|
||||
var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val);
|
||||
const payload_ty = parent.ty.errorUnionPayload();
|
||||
switch (parent.val.tag()) {
|
||||
else => {
|
||||
// An error union has been initialized to undefined at comptime and now we
|
||||
// are for the first time setting the payload. We must change the
|
||||
// representation of the error union from `undef` to `opt_payload`.
|
||||
const arena = parent.beginArena(sema.gpa);
|
||||
defer parent.finishArena();
|
||||
|
||||
const payload = try arena.create(Value.Payload.SubValue);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .eu_payload },
|
||||
.data = Value.undef,
|
||||
};
|
||||
|
||||
parent.val.* = Value.initPayload(&payload.base);
|
||||
|
||||
return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &payload.data,
|
||||
.ty = payload_ty,
|
||||
};
|
||||
},
|
||||
.eu_payload => return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &parent.val.castTag(.eu_payload).?.data,
|
||||
.ty = payload_ty,
|
||||
},
|
||||
}
|
||||
},
|
||||
.opt_payload_ptr => {
|
||||
const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data;
|
||||
var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val);
|
||||
const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
|
||||
switch (parent.val.tag()) {
|
||||
.undef, .null_value => {
|
||||
// An optional has been initialized to undefined at comptime and now we
|
||||
// are for the first time setting the payload. We must change the
|
||||
// representation of the optional from `undef` to `opt_payload`.
|
||||
const arena = parent.beginArena(sema.gpa);
|
||||
defer parent.finishArena();
|
||||
|
||||
const payload = try arena.create(Value.Payload.SubValue);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .opt_payload },
|
||||
.data = Value.undef,
|
||||
};
|
||||
|
||||
parent.val.* = Value.initPayload(&payload.base);
|
||||
|
||||
return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &payload.data,
|
||||
.ty = payload_ty,
|
||||
};
|
||||
},
|
||||
.opt_payload => return ComptimePtrMutationKit{
|
||||
.decl_ref_mut = parent.decl_ref_mut,
|
||||
.val = &parent.val.castTag(.opt_payload).?.data,
|
||||
.ty = payload_ty,
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ test {
|
||||
_ = @import("behavior/hasdecl.zig");
|
||||
_ = @import("behavior/hasfield.zig");
|
||||
_ = @import("behavior/namespace_depends_on_compile_var.zig");
|
||||
_ = @import("behavior/optional_llvm.zig");
|
||||
_ = @import("behavior/optional.zig");
|
||||
_ = @import("behavior/prefetch.zig");
|
||||
_ = @import("behavior/pub_enum.zig");
|
||||
_ = @import("behavior/slice_sentinel_comptime.zig");
|
||||
@ -69,7 +69,6 @@ test {
|
||||
_ = @import("behavior/inttoptr.zig");
|
||||
_ = @import("behavior/member_func.zig");
|
||||
_ = @import("behavior/null.zig");
|
||||
_ = @import("behavior/optional.zig");
|
||||
_ = @import("behavior/pointers.zig");
|
||||
_ = @import("behavior/ptrcast.zig");
|
||||
_ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
|
||||
@ -154,7 +153,6 @@ test {
|
||||
_ = @import("behavior/ir_block_deps.zig");
|
||||
_ = @import("behavior/misc.zig");
|
||||
_ = @import("behavior/muladd.zig");
|
||||
_ = @import("behavior/optional_stage1.zig");
|
||||
_ = @import("behavior/popcount_stage1.zig");
|
||||
_ = @import("behavior/reflection.zig");
|
||||
_ = @import("behavior/select.zig");
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "passing an optional integer as a parameter" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn entry() bool {
|
||||
var x: i32 = 1234;
|
||||
@ -21,12 +25,18 @@ test "passing an optional integer as a parameter" {
|
||||
pub const EmptyStruct = struct {};
|
||||
|
||||
test "optional pointer to size zero struct" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
var e = EmptyStruct{};
|
||||
var o: ?*EmptyStruct = &e;
|
||||
try expect(o != null);
|
||||
}
|
||||
|
||||
test "equality compare optional pointers" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
try testNullPtrsEql();
|
||||
comptime try testNullPtrsEql();
|
||||
}
|
||||
@ -48,6 +58,9 @@ fn testNullPtrsEql() !void {
|
||||
}
|
||||
|
||||
test "optional with void type" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const Foo = struct {
|
||||
x: ?void,
|
||||
};
|
||||
@ -56,6 +69,9 @@ test "optional with void type" {
|
||||
}
|
||||
|
||||
test "address of unwrap optional" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
a: i32,
|
||||
@ -73,6 +89,9 @@ test "address of unwrap optional" {
|
||||
}
|
||||
|
||||
test "nested optional field in struct" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S2 = struct {
|
||||
y: u8,
|
||||
};
|
||||
@ -86,6 +105,9 @@ test "nested optional field in struct" {
|
||||
}
|
||||
|
||||
test "equality compare optional with non-optional" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
try test_cmp_optional_non_optional();
|
||||
comptime try test_cmp_optional_non_optional();
|
||||
}
|
||||
@ -120,6 +142,9 @@ fn test_cmp_optional_non_optional() !void {
|
||||
}
|
||||
|
||||
test "unwrap function call with optional pointer return value" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
try expect(foo().?.* == 1234);
|
||||
@ -138,6 +163,9 @@ test "unwrap function call with optional pointer return value" {
|
||||
}
|
||||
|
||||
test "nested orelse" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
try expect(func() == null);
|
||||
@ -159,3 +187,138 @@ test "nested orelse" {
|
||||
try S.entry();
|
||||
comptime try S.entry();
|
||||
}
|
||||
|
||||
test "self-referential struct through a slice of optional" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const Node = struct {
|
||||
children: []?Node,
|
||||
data: ?u8,
|
||||
|
||||
fn new() Node {
|
||||
return Node{
|
||||
.children = undefined,
|
||||
.data = null,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var n = S.Node.new();
|
||||
try expect(n.data == null);
|
||||
}
|
||||
|
||||
test "assigning to an unwrapped optional field in an inline loop" {
|
||||
comptime var maybe_pos_arg: ?comptime_int = null;
|
||||
inline for ("ab") |x| {
|
||||
_ = x;
|
||||
maybe_pos_arg = 0;
|
||||
if (maybe_pos_arg.? != 0) {
|
||||
@compileError("bad");
|
||||
}
|
||||
maybe_pos_arg.? = 10;
|
||||
}
|
||||
}
|
||||
|
||||
test "coerce an anon struct literal to optional struct" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const Struct = struct {
|
||||
field: u32,
|
||||
};
|
||||
fn doTheTest() !void {
|
||||
var maybe_dims: ?Struct = null;
|
||||
maybe_dims = .{ .field = 1 };
|
||||
try expect(maybe_dims.?.field == 1);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "0-bit child type coerced to optional return ptr result location" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var y = Foo{};
|
||||
var z = y.thing();
|
||||
try expect(z != null);
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
pub const Bar = struct {
|
||||
field: *Foo,
|
||||
};
|
||||
|
||||
pub fn thing(self: *Foo) ?Bar {
|
||||
return Bar{ .field = self };
|
||||
}
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "0-bit child type coerced to optional" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var it: Foo = .{
|
||||
.list = undefined,
|
||||
};
|
||||
try expect(it.foo() != null);
|
||||
}
|
||||
|
||||
const Empty = struct {};
|
||||
const Foo = struct {
|
||||
list: [10]Empty,
|
||||
|
||||
fn foo(self: *Foo) ?*Empty {
|
||||
const data = &self.list[0];
|
||||
return data;
|
||||
}
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "array of optional unaligned types" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
const Enum = enum { one, two, three };
|
||||
|
||||
const SomeUnion = union(enum) {
|
||||
Num: Enum,
|
||||
Other: u32,
|
||||
};
|
||||
|
||||
const values = [_]?SomeUnion{
|
||||
SomeUnion{ .Num = .one },
|
||||
SomeUnion{ .Num = .two },
|
||||
SomeUnion{ .Num = .three },
|
||||
SomeUnion{ .Num = .one },
|
||||
SomeUnion{ .Num = .two },
|
||||
SomeUnion{ .Num = .three },
|
||||
};
|
||||
|
||||
// The index must be a runtime value
|
||||
var i: usize = 0;
|
||||
try expectEqual(Enum.one, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.two, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.three, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.one, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.two, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.three, values[i].?.Num);
|
||||
}
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "self-referential struct through a slice of optional" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
const S = struct {
|
||||
const Node = struct {
|
||||
children: []?Node,
|
||||
data: ?u8,
|
||||
|
||||
fn new() Node {
|
||||
return Node{
|
||||
.children = undefined,
|
||||
.data = null,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var n = S.Node.new();
|
||||
try expect(n.data == null);
|
||||
}
|
||||
@ -1,108 +0,0 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "assigning to an unwrapped optional field in an inline loop" {
|
||||
comptime var maybe_pos_arg: ?comptime_int = null;
|
||||
inline for ("ab") |x| {
|
||||
_ = x;
|
||||
maybe_pos_arg = 0;
|
||||
if (maybe_pos_arg.? != 0) {
|
||||
@compileError("bad");
|
||||
}
|
||||
maybe_pos_arg.? = 10;
|
||||
}
|
||||
}
|
||||
|
||||
test "coerce an anon struct literal to optional struct" {
|
||||
const S = struct {
|
||||
const Struct = struct {
|
||||
field: u32,
|
||||
};
|
||||
fn doTheTest() !void {
|
||||
var maybe_dims: ?Struct = null;
|
||||
maybe_dims = .{ .field = 1 };
|
||||
try expect(maybe_dims.?.field == 1);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "0-bit child type coerced to optional return ptr result location" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var y = Foo{};
|
||||
var z = y.thing();
|
||||
try expect(z != null);
|
||||
}
|
||||
|
||||
const Foo = struct {
|
||||
pub const Bar = struct {
|
||||
field: *Foo,
|
||||
};
|
||||
|
||||
pub fn thing(self: *Foo) ?Bar {
|
||||
return Bar{ .field = self };
|
||||
}
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "0-bit child type coerced to optional" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var it: Foo = .{
|
||||
.list = undefined,
|
||||
};
|
||||
try expect(it.foo() != null);
|
||||
}
|
||||
|
||||
const Empty = struct {};
|
||||
const Foo = struct {
|
||||
list: [10]Empty,
|
||||
|
||||
fn foo(self: *Foo) ?*Empty {
|
||||
const data = &self.list[0];
|
||||
return data;
|
||||
}
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "array of optional unaligned types" {
|
||||
const Enum = enum { one, two, three };
|
||||
|
||||
const SomeUnion = union(enum) {
|
||||
Num: Enum,
|
||||
Other: u32,
|
||||
};
|
||||
|
||||
const values = [_]?SomeUnion{
|
||||
SomeUnion{ .Num = .one },
|
||||
SomeUnion{ .Num = .two },
|
||||
SomeUnion{ .Num = .three },
|
||||
SomeUnion{ .Num = .one },
|
||||
SomeUnion{ .Num = .two },
|
||||
SomeUnion{ .Num = .three },
|
||||
};
|
||||
|
||||
// The index must be a runtime value
|
||||
var i: usize = 0;
|
||||
try expectEqual(Enum.one, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.two, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.three, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.one, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.two, values[i].?.Num);
|
||||
i += 1;
|
||||
try expectEqual(Enum.three, values[i].?.Num);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user