mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 12:27:41 +00:00
stage2: fix AstGen for some struct syntaxes
* AstGen: fix not emitting `struct_init_empty` when an explicit type is
present in struct initialization syntax.
* AstGen: these two syntaxes now lower to identical ZIR:
- `var a = A{ .b = c };`
- `var a = @as(A, .{ .b = c });`
* Zir: clarify `auto_enum_tag` in the doc comments.
* LLVM Backend: fix lowering of function return types when the type has
0 bits.
This commit is contained in:
parent
0ec01e58b4
commit
736d14fd5f
110
src/AstGen.zig
110
src/AstGen.zig
@ -1357,7 +1357,14 @@ fn structInitExpr(
|
||||
const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
|
||||
.array_type => tree.arrayType(struct_init.ast.type_expr),
|
||||
.array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),
|
||||
else => break :array,
|
||||
else => {
|
||||
if (struct_init.ast.fields.len == 0) {
|
||||
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
||||
const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
|
||||
return rvalue(gz, rl, result, node);
|
||||
}
|
||||
break :array;
|
||||
},
|
||||
};
|
||||
const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
|
||||
// This intentionally does not support `@"_"` syntax.
|
||||
@ -1419,8 +1426,8 @@ fn structInitExpr(
|
||||
const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
|
||||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
.ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst),
|
||||
.block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr),
|
||||
.ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
|
||||
.block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1459,6 +1466,26 @@ fn structInitExprRlNone(
|
||||
}
|
||||
|
||||
fn structInitExprRlPtr(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
node: Ast.Node.Index,
|
||||
struct_init: Ast.full.StructInit,
|
||||
result_ptr: Zir.Inst.Ref,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
if (struct_init.ast.type_expr == 0) {
|
||||
return structInitExprRlPtrInner(gz, scope, node, struct_init, result_ptr);
|
||||
}
|
||||
const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
||||
|
||||
var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr);
|
||||
defer as_scope.instructions.deinit(gz.astgen.gpa);
|
||||
|
||||
const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr);
|
||||
return as_scope.finishCoercion(gz, rl, node, result, ty_inst);
|
||||
}
|
||||
|
||||
fn structInitExprRlPtrInner(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
node: Ast.Node.Index,
|
||||
@ -1472,9 +1499,6 @@ fn structInitExprRlPtr(
|
||||
const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len);
|
||||
defer gpa.free(field_ptr_list);
|
||||
|
||||
if (struct_init.ast.type_expr != 0)
|
||||
_ = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
||||
|
||||
for (struct_init.ast.fields) |field_init, i| {
|
||||
const name_token = tree.firstToken(field_init) - 2;
|
||||
const str_index = try astgen.identAsString(name_token);
|
||||
@ -1489,7 +1513,7 @@ fn structInitExprRlPtr(
|
||||
.body_len = @intCast(u32, field_ptr_list.len),
|
||||
});
|
||||
try astgen.extra.appendSlice(gpa, field_ptr_list);
|
||||
return .void_value;
|
||||
return Zir.Inst.Ref.void_value;
|
||||
}
|
||||
|
||||
fn structInitExprRlTy(
|
||||
@ -6902,35 +6926,13 @@ fn asRlPtr(
|
||||
operand_node: Ast.Node.Index,
|
||||
dest_type: Zir.Inst.Ref,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
// Detect whether this expr() call goes into rvalue() to store the result into the
|
||||
// result location. If it does, elide the coerce_result_ptr instruction
|
||||
// as well as the store instruction, instead passing the result as an rvalue.
|
||||
const astgen = parent_gz.astgen;
|
||||
|
||||
var as_scope = parent_gz.makeSubBlock(scope);
|
||||
var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr);
|
||||
defer as_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
|
||||
const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
|
||||
const parent_zir = &parent_gz.instructions;
|
||||
if (as_scope.rvalue_rl_count == 1) {
|
||||
// Busted! This expression didn't actually need a pointer.
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);
|
||||
for (as_scope.instructions.items) |src_inst| {
|
||||
if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
|
||||
if (zir_tags[src_inst] == .store_to_block_ptr) {
|
||||
if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
|
||||
}
|
||||
parent_zir.appendAssumeCapacity(src_inst);
|
||||
}
|
||||
const casted_result = try parent_gz.addBin(.as, dest_type, result);
|
||||
return rvalue(parent_gz, rl, casted_result, operand_node);
|
||||
} else {
|
||||
try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items);
|
||||
return result;
|
||||
}
|
||||
return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type);
|
||||
}
|
||||
|
||||
fn bitCast(
|
||||
@ -9108,6 +9110,52 @@ const GenZir = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn makeCoercionScope(
|
||||
parent_gz: *GenZir,
|
||||
scope: *Scope,
|
||||
dest_type: Zir.Inst.Ref,
|
||||
result_ptr: Zir.Inst.Ref,
|
||||
) !GenZir {
|
||||
// Detect whether this expr() call goes into rvalue() to store the result into the
|
||||
// result location. If it does, elide the coerce_result_ptr instruction
|
||||
// as well as the store instruction, instead passing the result as an rvalue.
|
||||
var as_scope = parent_gz.makeSubBlock(scope);
|
||||
errdefer as_scope.instructions.deinit(parent_gz.astgen.gpa);
|
||||
as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
|
||||
|
||||
return as_scope;
|
||||
}
|
||||
|
||||
fn finishCoercion(
|
||||
as_scope: *GenZir,
|
||||
parent_gz: *GenZir,
|
||||
rl: ResultLoc,
|
||||
src_node: Ast.Node.Index,
|
||||
result: Zir.Inst.Ref,
|
||||
dest_type: Zir.Inst.Ref,
|
||||
) !Zir.Inst.Ref {
|
||||
const astgen = as_scope.astgen;
|
||||
const parent_zir = &parent_gz.instructions;
|
||||
if (as_scope.rvalue_rl_count == 1) {
|
||||
// Busted! This expression didn't actually need a pointer.
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);
|
||||
for (as_scope.instructions.items) |src_inst| {
|
||||
if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
|
||||
if (zir_tags[src_inst] == .store_to_block_ptr) {
|
||||
if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
|
||||
}
|
||||
parent_zir.appendAssumeCapacity(src_inst);
|
||||
}
|
||||
const casted_result = try parent_gz.addBin(.as, dest_type, result);
|
||||
return rvalue(parent_gz, rl, casted_result, src_node);
|
||||
} else {
|
||||
try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const Label = struct {
|
||||
token: Ast.TokenIndex,
|
||||
block_inst: Zir.Inst.Index,
|
||||
|
||||
@ -2650,8 +2650,12 @@ pub const Inst = struct {
|
||||
has_decls_len: bool,
|
||||
name_strategy: NameStrategy,
|
||||
layout: std.builtin.TypeInfo.ContainerLayout,
|
||||
/// false: union(tag_type)
|
||||
/// true: union(enum(tag_type))
|
||||
/// has_tag_type | auto_enum_tag | result
|
||||
/// -------------------------------------
|
||||
/// false | false | union { }
|
||||
/// false | true | union(enum) { }
|
||||
/// true | true | union(enum(T)) { }
|
||||
/// true | false | union(T) { }
|
||||
auto_enum_tag: bool,
|
||||
_: u6 = undefined,
|
||||
};
|
||||
|
||||
@ -563,8 +563,13 @@ pub const DeclGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const llvm_ret_ty = if (!return_type.hasCodeGenBits())
|
||||
self.context.voidType()
|
||||
else
|
||||
try self.llvmType(return_type);
|
||||
|
||||
const fn_type = llvm.functionType(
|
||||
try self.llvmType(return_type),
|
||||
llvm_ret_ty,
|
||||
llvm_param_buffer.ptr,
|
||||
llvm_params_len,
|
||||
.False,
|
||||
|
||||
@ -13,6 +13,7 @@ test {
|
||||
_ = @import("behavior/atomics.zig");
|
||||
_ = @import("behavior/sizeof_and_typeof.zig");
|
||||
_ = @import("behavior/translate_c_macros.zig");
|
||||
_ = @import("behavior/struct.zig");
|
||||
_ = @import("behavior/union.zig");
|
||||
_ = @import("behavior/widening.zig");
|
||||
|
||||
@ -135,7 +136,7 @@ test {
|
||||
_ = @import("behavior/sizeof_and_typeof_stage1.zig");
|
||||
_ = @import("behavior/slice.zig");
|
||||
_ = @import("behavior/slice_sentinel_comptime.zig");
|
||||
_ = @import("behavior/struct.zig");
|
||||
_ = @import("behavior/struct_stage1.zig");
|
||||
_ = @import("behavior/struct_contains_null_ptr_itself.zig");
|
||||
_ = @import("behavior/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("behavior/switch.zig");
|
||||
|
||||
@ -5,949 +5,29 @@ const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const expectEqualSlices = std.testing.expectEqualSlices;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
const StructWithNoFields = struct {
|
||||
fn add(a: i32, b: i32) i32 {
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
const empty_global_instance = StructWithNoFields{};
|
||||
|
||||
top_level_field: i32,
|
||||
|
||||
test "top level fields" {
|
||||
var instance = @This(){
|
||||
.top_level_field = 1234,
|
||||
};
|
||||
instance.top_level_field += 1;
|
||||
try expectEqual(@as(i32, 1235), instance.top_level_field);
|
||||
}
|
||||
|
||||
test "call struct static method" {
|
||||
const result = StructWithNoFields.add(3, 4);
|
||||
try expect(result == 7);
|
||||
}
|
||||
|
||||
test "return empty struct instance" {
|
||||
_ = returnEmptyStructInstance();
|
||||
}
|
||||
fn returnEmptyStructInstance() StructWithNoFields {
|
||||
return empty_global_instance;
|
||||
}
|
||||
|
||||
const should_be_11 = StructWithNoFields.add(5, 6);
|
||||
|
||||
test "invoke static method in global scope" {
|
||||
try expect(should_be_11 == 11);
|
||||
}
|
||||
|
||||
test "void struct fields" {
|
||||
const foo = VoidStructFieldsFoo{
|
||||
.a = void{},
|
||||
.b = 1,
|
||||
.c = void{},
|
||||
};
|
||||
try expect(foo.b == 1);
|
||||
try expect(@sizeOf(VoidStructFieldsFoo) == 4);
|
||||
const empty_global_instance = StructWithNoFields{};
|
||||
|
||||
test "return empty struct instance" {
|
||||
_ = returnEmptyStructInstance();
|
||||
}
|
||||
const VoidStructFieldsFoo = struct {
|
||||
a: void,
|
||||
b: i32,
|
||||
c: void,
|
||||
};
|
||||
|
||||
test "structs" {
|
||||
var foo: StructFoo = undefined;
|
||||
@memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
|
||||
foo.a += 1;
|
||||
foo.b = foo.a == 1;
|
||||
try testFoo(foo);
|
||||
testMutation(&foo);
|
||||
try expect(foo.c == 100);
|
||||
}
|
||||
const StructFoo = struct {
|
||||
a: i32,
|
||||
b: bool,
|
||||
c: f32,
|
||||
};
|
||||
fn testFoo(foo: StructFoo) !void {
|
||||
try expect(foo.b);
|
||||
}
|
||||
fn testMutation(foo: *StructFoo) void {
|
||||
foo.c = 100;
|
||||
}
|
||||
|
||||
const Node = struct {
|
||||
val: Val,
|
||||
next: *Node,
|
||||
};
|
||||
|
||||
const Val = struct {
|
||||
x: i32,
|
||||
};
|
||||
|
||||
test "struct point to self" {
|
||||
var root: Node = undefined;
|
||||
root.val.x = 1;
|
||||
|
||||
var node: Node = undefined;
|
||||
node.next = &root;
|
||||
node.val.x = 2;
|
||||
|
||||
root.next = &node;
|
||||
|
||||
try expect(node.next.next.next.val.x == 1);
|
||||
}
|
||||
|
||||
test "struct byval assign" {
|
||||
var foo1: StructFoo = undefined;
|
||||
var foo2: StructFoo = undefined;
|
||||
|
||||
foo1.a = 1234;
|
||||
foo2.a = 0;
|
||||
try expect(foo2.a == 0);
|
||||
foo2 = foo1;
|
||||
try expect(foo2.a == 1234);
|
||||
}
|
||||
|
||||
fn structInitializer() void {
|
||||
const val = Val{ .x = 42 };
|
||||
try expect(val.x == 42);
|
||||
}
|
||||
|
||||
test "fn call of struct field" {
|
||||
const Foo = struct {
|
||||
ptr: fn () i32,
|
||||
};
|
||||
const S = struct {
|
||||
fn aFunc() i32 {
|
||||
return 13;
|
||||
}
|
||||
|
||||
fn callStructField(foo: Foo) i32 {
|
||||
return foo.ptr();
|
||||
}
|
||||
};
|
||||
|
||||
try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);
|
||||
}
|
||||
|
||||
test "store member function in variable" {
|
||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||
const memberFn = MemberFnTestFoo.member;
|
||||
const result = memberFn(instance);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
const MemberFnTestFoo = struct {
|
||||
x: i32,
|
||||
fn member(foo: MemberFnTestFoo) i32 {
|
||||
return foo.x;
|
||||
}
|
||||
};
|
||||
|
||||
test "call member function directly" {
|
||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||
const result = MemberFnTestFoo.member(instance);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
|
||||
test "member functions" {
|
||||
const r = MemberFnRand{ .seed = 1234 };
|
||||
try expect(r.getSeed() == 1234);
|
||||
}
|
||||
const MemberFnRand = struct {
|
||||
seed: u32,
|
||||
pub fn getSeed(r: *const MemberFnRand) u32 {
|
||||
return r.seed;
|
||||
}
|
||||
};
|
||||
|
||||
test "return struct byval from function" {
|
||||
const bar = makeBar2(1234, 5678);
|
||||
try expect(bar.y == 5678);
|
||||
}
|
||||
const Bar = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
fn makeBar2(x: i32, y: i32) Bar {
|
||||
return Bar{
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
}
|
||||
|
||||
test "empty struct method call" {
|
||||
const es = EmptyStruct{};
|
||||
try expect(es.method() == 1234);
|
||||
}
|
||||
const EmptyStruct = struct {
|
||||
fn method(es: *const EmptyStruct) i32 {
|
||||
_ = es;
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
test "return empty struct from fn" {
|
||||
_ = testReturnEmptyStructFromFn();
|
||||
}
|
||||
const EmptyStruct2 = struct {};
|
||||
fn testReturnEmptyStructFromFn() EmptyStruct2 {
|
||||
return EmptyStruct2{};
|
||||
}
|
||||
|
||||
test "pass slice of empty struct to fn" {
|
||||
try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1);
|
||||
}
|
||||
fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
const APackedStruct = packed struct {
|
||||
x: u8,
|
||||
y: u8,
|
||||
};
|
||||
|
||||
test "packed struct" {
|
||||
var foo = APackedStruct{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
foo.y += 1;
|
||||
const four = foo.x + foo.y;
|
||||
try expect(four == 4);
|
||||
}
|
||||
|
||||
const BitField1 = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
const bit_field_1 = BitField1{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
};
|
||||
|
||||
test "bit field access" {
|
||||
var data = bit_field_1;
|
||||
try expect(getA(&data) == 1);
|
||||
try expect(getB(&data) == 2);
|
||||
try expect(getC(&data) == 3);
|
||||
comptime try expect(@sizeOf(BitField1) == 1);
|
||||
|
||||
data.b += 1;
|
||||
try expect(data.b == 3);
|
||||
|
||||
data.a += 1;
|
||||
try expect(data.a == 2);
|
||||
try expect(data.b == 3);
|
||||
}
|
||||
|
||||
fn getA(data: *const BitField1) u3 {
|
||||
return data.a;
|
||||
}
|
||||
|
||||
fn getB(data: *const BitField1) u3 {
|
||||
return data.b;
|
||||
}
|
||||
|
||||
fn getC(data: *const BitField1) u2 {
|
||||
return data.c;
|
||||
}
|
||||
|
||||
const Foo24Bits = packed struct {
|
||||
field: u24,
|
||||
};
|
||||
const Foo96Bits = packed struct {
|
||||
a: u24,
|
||||
b: u24,
|
||||
c: u24,
|
||||
d: u24,
|
||||
};
|
||||
|
||||
test "packed struct 24bits" {
|
||||
comptime {
|
||||
try expect(@sizeOf(Foo24Bits) == 4);
|
||||
if (@sizeOf(usize) == 4) {
|
||||
try expect(@sizeOf(Foo96Bits) == 12);
|
||||
} else {
|
||||
try expect(@sizeOf(Foo96Bits) == 16);
|
||||
}
|
||||
}
|
||||
|
||||
var value = Foo96Bits{
|
||||
.a = 0,
|
||||
.b = 0,
|
||||
.c = 0,
|
||||
.d = 0,
|
||||
};
|
||||
value.a += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 0);
|
||||
try expect(value.c == 0);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.b += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 0);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.c += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 1);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.d += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 1);
|
||||
try expect(value.d == 1);
|
||||
}
|
||||
|
||||
const Foo32Bits = packed struct {
|
||||
field: u24,
|
||||
pad: u8,
|
||||
};
|
||||
|
||||
const FooArray24Bits = packed struct {
|
||||
a: u16,
|
||||
b: [2]Foo32Bits,
|
||||
c: u16,
|
||||
};
|
||||
|
||||
// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512
|
||||
test "packed array 24bits" {
|
||||
comptime {
|
||||
try expect(@sizeOf([9]Foo32Bits) == 9 * 4);
|
||||
try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2);
|
||||
}
|
||||
|
||||
var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1);
|
||||
bytes[bytes.len - 1] = 0xaa;
|
||||
const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0];
|
||||
try expect(ptr.a == 0);
|
||||
try expect(ptr.b[0].field == 0);
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.a = maxInt(u16);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == 0);
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.b[0].field = maxInt(u24);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.b[1].field = maxInt(u24);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == maxInt(u24));
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.c = maxInt(u16);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == maxInt(u24));
|
||||
try expect(ptr.c == maxInt(u16));
|
||||
|
||||
try expect(bytes[bytes.len - 1] == 0xaa);
|
||||
}
|
||||
|
||||
const FooStructAligned = packed struct {
|
||||
a: u8,
|
||||
b: u8,
|
||||
};
|
||||
|
||||
const FooArrayOfAligned = packed struct {
|
||||
a: [2]FooStructAligned,
|
||||
};
|
||||
|
||||
test "aligned array of packed struct" {
|
||||
comptime {
|
||||
try expect(@sizeOf(FooStructAligned) == 2);
|
||||
try expect(@sizeOf(FooArrayOfAligned) == 2 * 2);
|
||||
}
|
||||
|
||||
var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned);
|
||||
const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0];
|
||||
|
||||
try expect(ptr.a[0].a == 0xbb);
|
||||
try expect(ptr.a[0].b == 0xbb);
|
||||
try expect(ptr.a[1].a == 0xbb);
|
||||
try expect(ptr.a[1].b == 0xbb);
|
||||
}
|
||||
|
||||
test "runtime struct initialization of bitfield" {
|
||||
const s1 = Nibbles{
|
||||
.x = x1,
|
||||
.y = x1,
|
||||
};
|
||||
const s2 = Nibbles{
|
||||
.x = @intCast(u4, x2),
|
||||
.y = @intCast(u4, x2),
|
||||
};
|
||||
|
||||
try expect(s1.x == x1);
|
||||
try expect(s1.y == x1);
|
||||
try expect(s2.x == @intCast(u4, x2));
|
||||
try expect(s2.y == @intCast(u4, x2));
|
||||
}
|
||||
|
||||
var x1 = @as(u4, 1);
|
||||
var x2 = @as(u8, 2);
|
||||
|
||||
const Nibbles = packed struct {
|
||||
x: u4,
|
||||
y: u4,
|
||||
};
|
||||
|
||||
const Bitfields = packed struct {
|
||||
f1: u16,
|
||||
f2: u16,
|
||||
f3: u8,
|
||||
f4: u8,
|
||||
f5: u4,
|
||||
f6: u4,
|
||||
f7: u8,
|
||||
};
|
||||
|
||||
test "native bit field understands endianness" {
|
||||
var all: u64 = if (native_endian != .Little)
|
||||
0x1111222233445677
|
||||
else
|
||||
0x7765443322221111;
|
||||
var bytes: [8]u8 = undefined;
|
||||
@memcpy(&bytes, @ptrCast([*]u8, &all), 8);
|
||||
var bitfields = @ptrCast(*Bitfields, &bytes).*;
|
||||
|
||||
try expect(bitfields.f1 == 0x1111);
|
||||
try expect(bitfields.f2 == 0x2222);
|
||||
try expect(bitfields.f3 == 0x33);
|
||||
try expect(bitfields.f4 == 0x44);
|
||||
try expect(bitfields.f5 == 0x5);
|
||||
try expect(bitfields.f6 == 0x6);
|
||||
try expect(bitfields.f7 == 0x77);
|
||||
}
|
||||
|
||||
test "align 1 field before self referential align 8 field as slice return type" {
|
||||
const result = alloc(Expr);
|
||||
try expect(result.len == 0);
|
||||
}
|
||||
|
||||
const Expr = union(enum) {
|
||||
Literal: u8,
|
||||
Question: *Expr,
|
||||
};
|
||||
|
||||
fn alloc(comptime T: type) []T {
|
||||
return &[_]T{};
|
||||
}
|
||||
|
||||
test "call method with mutable reference to struct with no fields" {
|
||||
const S = struct {
|
||||
fn doC(s: *const @This()) bool {
|
||||
_ = s;
|
||||
return true;
|
||||
}
|
||||
fn do(s: *@This()) bool {
|
||||
_ = s;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var s = S{};
|
||||
try expect(S.doC(&s));
|
||||
try expect(s.doC());
|
||||
try expect(S.do(&s));
|
||||
try expect(s.do());
|
||||
}
|
||||
|
||||
test "implicit cast packed struct field to const ptr" {
|
||||
const LevelUpMove = packed struct {
|
||||
move_id: u9,
|
||||
level: u7,
|
||||
|
||||
fn toInt(value: u7) u7 {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
var lup: LevelUpMove = undefined;
|
||||
lup.level = 12;
|
||||
const res = LevelUpMove.toInt(lup.level);
|
||||
try expect(res == 12);
|
||||
}
|
||||
|
||||
test "pointer to packed struct member in a stack variable" {
|
||||
const S = packed struct {
|
||||
a: u2,
|
||||
b: u2,
|
||||
};
|
||||
|
||||
var s = S{ .a = 2, .b = 0 };
|
||||
var b_ptr = &s.b;
|
||||
try expect(s.b == 0);
|
||||
b_ptr.* = 2;
|
||||
try expect(s.b == 2);
|
||||
}
|
||||
|
||||
test "non-byte-aligned array inside packed struct" {
|
||||
const Foo = packed struct {
|
||||
a: bool,
|
||||
b: [0x16]u8,
|
||||
};
|
||||
const S = struct {
|
||||
fn bar(slice: []const u8) !void {
|
||||
try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu");
|
||||
}
|
||||
fn doTheTest() !void {
|
||||
var foo = Foo{
|
||||
.a = true,
|
||||
.b = "abcdefghijklmnopqurstu".*,
|
||||
};
|
||||
const value = foo.b;
|
||||
try bar(&value);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with u0 field access" {
|
||||
const S = packed struct {
|
||||
f0: u0,
|
||||
};
|
||||
var s = S{ .f0 = 0 };
|
||||
comptime try expect(s.f0 == 0);
|
||||
}
|
||||
|
||||
const S0 = struct {
|
||||
bar: S1,
|
||||
|
||||
pub const S1 = struct {
|
||||
value: u8,
|
||||
};
|
||||
|
||||
fn init() @This() {
|
||||
return S0{ .bar = S1{ .value = 123 } };
|
||||
}
|
||||
};
|
||||
|
||||
var g_foo: S0 = S0.init();
|
||||
|
||||
test "access to global struct fields" {
|
||||
g_foo.bar.value = 42;
|
||||
try expect(g_foo.bar.value == 42);
|
||||
}
|
||||
|
||||
test "packed struct with fp fields" {
|
||||
const S = packed struct {
|
||||
data: [3]f32,
|
||||
|
||||
pub fn frob(self: *@This()) void {
|
||||
self.data[0] += self.data[1] + self.data[2];
|
||||
self.data[1] += self.data[0] + self.data[2];
|
||||
self.data[2] += self.data[0] + self.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.data[0] = 1.0;
|
||||
s.data[1] = 2.0;
|
||||
s.data[2] = 3.0;
|
||||
s.frob();
|
||||
try expectEqual(@as(f32, 6.0), s.data[0]);
|
||||
try expectEqual(@as(f32, 11.0), s.data[1]);
|
||||
try expectEqual(@as(f32, 20.0), s.data[2]);
|
||||
}
|
||||
|
||||
test "use within struct scope" {
|
||||
const S = struct {
|
||||
usingnamespace struct {
|
||||
pub fn inner() i32 {
|
||||
return 42;
|
||||
}
|
||||
};
|
||||
};
|
||||
try expectEqual(@as(i32, 42), S.inner());
|
||||
}
|
||||
|
||||
test "default struct initialization fields" {
|
||||
const S = struct {
|
||||
a: i32 = 1234,
|
||||
b: i32,
|
||||
};
|
||||
const x = S{
|
||||
.b = 5,
|
||||
};
|
||||
var five: i32 = 5;
|
||||
const y = S{
|
||||
.b = five,
|
||||
};
|
||||
if (x.a + x.b != 1239) {
|
||||
@compileError("it should be comptime known");
|
||||
}
|
||||
try expectEqual(y, x);
|
||||
try expectEqual(1239, x.a + x.b);
|
||||
}
|
||||
|
||||
test "fn with C calling convention returns struct by value" {
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
var x = makeBar(10);
|
||||
try expectEqual(@as(i32, 10), x.handle);
|
||||
}
|
||||
|
||||
const ExternBar = extern struct {
|
||||
handle: i32,
|
||||
};
|
||||
|
||||
fn makeBar(t: i32) callconv(.C) ExternBar {
|
||||
return ExternBar{
|
||||
.handle = t,
|
||||
};
|
||||
}
|
||||
};
|
||||
try S.entry();
|
||||
comptime try S.entry();
|
||||
}
|
||||
|
||||
test "for loop over pointers to struct, getting field from struct pointer" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
var ok = true;
|
||||
|
||||
fn eql(a: []const u8) bool {
|
||||
_ = a;
|
||||
return true;
|
||||
}
|
||||
|
||||
const ArrayList = struct {
|
||||
fn toSlice(self: *ArrayList) []*Foo {
|
||||
_ = self;
|
||||
return @as([*]*Foo, undefined)[0..0];
|
||||
}
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var objects: ArrayList = undefined;
|
||||
|
||||
for (objects.toSlice()) |obj| {
|
||||
if (eql(obj.name)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
try expect(ok);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "zero-bit field in packed struct" {
|
||||
const S = packed struct {
|
||||
x: u10,
|
||||
y: void,
|
||||
};
|
||||
var x: S = undefined;
|
||||
_ = x;
|
||||
}
|
||||
|
||||
test "struct field init with catch" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: anyerror!isize = 1;
|
||||
var req = Foo{
|
||||
.field = x catch undefined,
|
||||
};
|
||||
try expect(req.field == 1);
|
||||
}
|
||||
|
||||
pub const Foo = extern struct {
|
||||
field: isize,
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with non-ABI-aligned field" {
|
||||
const S = packed struct {
|
||||
x: u9,
|
||||
y: u183,
|
||||
};
|
||||
var s: S = undefined;
|
||||
s.x = 1;
|
||||
s.y = 42;
|
||||
try expect(s.x == 1);
|
||||
try expect(s.y == 42);
|
||||
}
|
||||
|
||||
test "non-packed struct with u128 entry in union" {
|
||||
const U = union(enum) {
|
||||
Num: u128,
|
||||
Void,
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
f1: U,
|
||||
f2: U,
|
||||
};
|
||||
|
||||
var sx: S = undefined;
|
||||
var s = &sx;
|
||||
try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2"));
|
||||
var v2 = U{ .Num = 123 };
|
||||
s.f2 = v2;
|
||||
try std.testing.expect(s.f2.Num == 123);
|
||||
}
|
||||
|
||||
test "packed struct field passed to generic function" {
|
||||
const S = struct {
|
||||
const P = packed struct {
|
||||
b: u5,
|
||||
g: u5,
|
||||
r: u5,
|
||||
a: u1,
|
||||
};
|
||||
|
||||
fn genericReadPackedField(ptr: anytype) u5 {
|
||||
return ptr.*;
|
||||
}
|
||||
};
|
||||
|
||||
var p: S.P = undefined;
|
||||
p.b = 29;
|
||||
var loaded = S.genericReadPackedField(&p.b);
|
||||
try expect(loaded == 29);
|
||||
}
|
||||
|
||||
test "anonymous struct literal syntax" {
|
||||
const S = struct {
|
||||
const Point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var p: Point = .{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
try expect(p.x == 1);
|
||||
try expect(p.y == 2);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "fully anonymous struct" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try dump(.{
|
||||
.int = @as(u32, 1234),
|
||||
.float = @as(f64, 12.34),
|
||||
.b = true,
|
||||
.s = "hi",
|
||||
});
|
||||
}
|
||||
fn dump(args: anytype) !void {
|
||||
try expect(args.int == 1234);
|
||||
try expect(args.float == 12.34);
|
||||
try expect(args.b);
|
||||
try expect(args.s[0] == 'h');
|
||||
try expect(args.s[1] == 'i');
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "fully anonymous list literal" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });
|
||||
}
|
||||
fn dump(args: anytype) !void {
|
||||
try expect(args.@"0" == 1234);
|
||||
try expect(args.@"1" == 12.34);
|
||||
try expect(args.@"2");
|
||||
try expect(args.@"3"[0] == 'h');
|
||||
try expect(args.@"3"[1] == 'i');
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "anonymous struct literal assigned to variable" {
|
||||
var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) };
|
||||
try expect(vec.@"0" == 22);
|
||||
try expect(vec.@"1" == 55);
|
||||
try expect(vec.@"2" == 99);
|
||||
}
|
||||
|
||||
test "struct with var field" {
|
||||
const Point = struct {
|
||||
x: anytype,
|
||||
y: anytype,
|
||||
};
|
||||
const pt = Point{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
try expect(pt.x == 1);
|
||||
try expect(pt.y == 2);
|
||||
}
|
||||
|
||||
test "comptime struct field" {
|
||||
const T = struct {
|
||||
a: i32,
|
||||
comptime b: i32 = 1234,
|
||||
};
|
||||
|
||||
var foo: T = undefined;
|
||||
comptime try expect(foo.b == 1234);
|
||||
}
|
||||
|
||||
test "anon struct literal field value initialized with fn call" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x = .{foo()};
|
||||
try expectEqualSlices(u8, x[0], "hi");
|
||||
}
|
||||
fn foo() []const u8 {
|
||||
return "hi";
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "self-referencing struct via array member" {
|
||||
const T = struct {
|
||||
children: [1]*@This(),
|
||||
};
|
||||
var x: T = undefined;
|
||||
x = T{ .children = .{&x} };
|
||||
try expect(x.children[0] == &x);
|
||||
}
|
||||
|
||||
test "struct with union field" {
|
||||
const Value = struct {
|
||||
ref: u32 = 2,
|
||||
kind: union(enum) {
|
||||
None: usize,
|
||||
Bool: bool,
|
||||
},
|
||||
};
|
||||
|
||||
var True = Value{
|
||||
.kind = .{ .Bool = true },
|
||||
};
|
||||
try expectEqual(@as(u32, 2), True.ref);
|
||||
try expectEqual(true, True.kind.Bool);
|
||||
}
|
||||
|
||||
test "type coercion of anon struct literal to struct" {
|
||||
const S = struct {
|
||||
const S2 = struct {
|
||||
A: u32,
|
||||
B: []const u8,
|
||||
C: void,
|
||||
D: Foo = .{},
|
||||
};
|
||||
|
||||
const Foo = struct {
|
||||
field: i32 = 1234,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var y: u32 = 42;
|
||||
const t0 = .{ .A = 123, .B = "foo", .C = {} };
|
||||
const t1 = .{ .A = y, .B = "foo", .C = {} };
|
||||
const y0: S2 = t0;
|
||||
var y1: S2 = t1;
|
||||
try expect(y0.A == 123);
|
||||
try expect(std.mem.eql(u8, y0.B, "foo"));
|
||||
try expect(y0.C == {});
|
||||
try expect(y0.D.field == 1234);
|
||||
try expect(y1.A == y);
|
||||
try expect(std.mem.eql(u8, y1.B, "foo"));
|
||||
try expect(y1.C == {});
|
||||
try expect(y1.D.field == 1234);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type coercion of pointer to anon struct literal to pointer to struct" {
|
||||
const S = struct {
|
||||
const S2 = struct {
|
||||
A: u32,
|
||||
B: []const u8,
|
||||
C: void,
|
||||
D: Foo = .{},
|
||||
};
|
||||
|
||||
const Foo = struct {
|
||||
field: i32 = 1234,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var y: u32 = 42;
|
||||
const t0 = &.{ .A = 123, .B = "foo", .C = {} };
|
||||
const t1 = &.{ .A = y, .B = "foo", .C = {} };
|
||||
const y0: *const S2 = t0;
|
||||
var y1: *const S2 = t1;
|
||||
try expect(y0.A == 123);
|
||||
try expect(std.mem.eql(u8, y0.B, "foo"));
|
||||
try expect(y0.C == {});
|
||||
try expect(y0.D.field == 1234);
|
||||
try expect(y1.A == y);
|
||||
try expect(std.mem.eql(u8, y1.B, "foo"));
|
||||
try expect(y1.C == {});
|
||||
try expect(y1.D.field == 1234);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with undefined initializers" {
|
||||
const S = struct {
|
||||
const P = packed struct {
|
||||
a: u3,
|
||||
_a: u3 = undefined,
|
||||
b: u3,
|
||||
_b: u3 = undefined,
|
||||
c: u3,
|
||||
_c: u3 = undefined,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var p: P = undefined;
|
||||
p = P{ .a = 2, .b = 4, .c = 6 };
|
||||
// Make sure the compiler doesn't touch the unprefixed fields.
|
||||
// Use expect since i386-linux doesn't like expectEqual
|
||||
try expect(p.a == 2);
|
||||
try expect(p.b == 4);
|
||||
try expect(p.c == 6);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
fn returnEmptyStructInstance() StructWithNoFields {
|
||||
return empty_global_instance;
|
||||
}
|
||||
|
||||
928
test/behavior/struct_stage1.zig
Normal file
928
test/behavior/struct_stage1.zig
Normal file
@ -0,0 +1,928 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const expectEqualSlices = std.testing.expectEqualSlices;
|
||||
const maxInt = std.math.maxInt;
|
||||
top_level_field: i32,
|
||||
|
||||
test "top level fields" {
|
||||
var instance = @This(){
|
||||
.top_level_field = 1234,
|
||||
};
|
||||
instance.top_level_field += 1;
|
||||
try expectEqual(@as(i32, 1235), instance.top_level_field);
|
||||
}
|
||||
|
||||
test "void struct fields" {
|
||||
const foo = VoidStructFieldsFoo{
|
||||
.a = void{},
|
||||
.b = 1,
|
||||
.c = void{},
|
||||
};
|
||||
try expect(foo.b == 1);
|
||||
try expect(@sizeOf(VoidStructFieldsFoo) == 4);
|
||||
}
|
||||
const VoidStructFieldsFoo = struct {
|
||||
a: void,
|
||||
b: i32,
|
||||
c: void,
|
||||
};
|
||||
|
||||
test "structs" {
|
||||
var foo: StructFoo = undefined;
|
||||
@memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
|
||||
foo.a += 1;
|
||||
foo.b = foo.a == 1;
|
||||
try testFoo(foo);
|
||||
testMutation(&foo);
|
||||
try expect(foo.c == 100);
|
||||
}
|
||||
const StructFoo = struct {
|
||||
a: i32,
|
||||
b: bool,
|
||||
c: f32,
|
||||
};
|
||||
fn testFoo(foo: StructFoo) !void {
|
||||
try expect(foo.b);
|
||||
}
|
||||
fn testMutation(foo: *StructFoo) void {
|
||||
foo.c = 100;
|
||||
}
|
||||
|
||||
const Node = struct {
|
||||
val: Val,
|
||||
next: *Node,
|
||||
};
|
||||
|
||||
const Val = struct {
|
||||
x: i32,
|
||||
};
|
||||
|
||||
test "struct point to self" {
|
||||
var root: Node = undefined;
|
||||
root.val.x = 1;
|
||||
|
||||
var node: Node = undefined;
|
||||
node.next = &root;
|
||||
node.val.x = 2;
|
||||
|
||||
root.next = &node;
|
||||
|
||||
try expect(node.next.next.next.val.x == 1);
|
||||
}
|
||||
|
||||
test "struct byval assign" {
|
||||
var foo1: StructFoo = undefined;
|
||||
var foo2: StructFoo = undefined;
|
||||
|
||||
foo1.a = 1234;
|
||||
foo2.a = 0;
|
||||
try expect(foo2.a == 0);
|
||||
foo2 = foo1;
|
||||
try expect(foo2.a == 1234);
|
||||
}
|
||||
|
||||
fn structInitializer() void {
|
||||
const val = Val{ .x = 42 };
|
||||
try expect(val.x == 42);
|
||||
}
|
||||
|
||||
test "fn call of struct field" {
|
||||
const Foo = struct {
|
||||
ptr: fn () i32,
|
||||
};
|
||||
const S = struct {
|
||||
fn aFunc() i32 {
|
||||
return 13;
|
||||
}
|
||||
|
||||
fn callStructField(foo: Foo) i32 {
|
||||
return foo.ptr();
|
||||
}
|
||||
};
|
||||
|
||||
try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);
|
||||
}
|
||||
|
||||
test "store member function in variable" {
|
||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||
const memberFn = MemberFnTestFoo.member;
|
||||
const result = memberFn(instance);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
const MemberFnTestFoo = struct {
|
||||
x: i32,
|
||||
fn member(foo: MemberFnTestFoo) i32 {
|
||||
return foo.x;
|
||||
}
|
||||
};
|
||||
|
||||
test "call member function directly" {
|
||||
const instance = MemberFnTestFoo{ .x = 1234 };
|
||||
const result = MemberFnTestFoo.member(instance);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
|
||||
test "member functions" {
|
||||
const r = MemberFnRand{ .seed = 1234 };
|
||||
try expect(r.getSeed() == 1234);
|
||||
}
|
||||
const MemberFnRand = struct {
|
||||
seed: u32,
|
||||
pub fn getSeed(r: *const MemberFnRand) u32 {
|
||||
return r.seed;
|
||||
}
|
||||
};
|
||||
|
||||
test "return struct byval from function" {
|
||||
const bar = makeBar2(1234, 5678);
|
||||
try expect(bar.y == 5678);
|
||||
}
|
||||
const Bar = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
fn makeBar2(x: i32, y: i32) Bar {
|
||||
return Bar{
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
}
|
||||
|
||||
test "empty struct method call" {
|
||||
const es = EmptyStruct{};
|
||||
try expect(es.method() == 1234);
|
||||
}
|
||||
const EmptyStruct = struct {
|
||||
fn method(es: *const EmptyStruct) i32 {
|
||||
_ = es;
|
||||
return 1234;
|
||||
}
|
||||
};
|
||||
|
||||
test "return empty struct from fn" {
|
||||
_ = testReturnEmptyStructFromFn();
|
||||
}
|
||||
const EmptyStruct2 = struct {};
|
||||
fn testReturnEmptyStructFromFn() EmptyStruct2 {
|
||||
return EmptyStruct2{};
|
||||
}
|
||||
|
||||
test "pass slice of empty struct to fn" {
|
||||
try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1);
|
||||
}
|
||||
fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
const APackedStruct = packed struct {
|
||||
x: u8,
|
||||
y: u8,
|
||||
};
|
||||
|
||||
test "packed struct" {
|
||||
var foo = APackedStruct{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
foo.y += 1;
|
||||
const four = foo.x + foo.y;
|
||||
try expect(four == 4);
|
||||
}
|
||||
|
||||
const BitField1 = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
const bit_field_1 = BitField1{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
};
|
||||
|
||||
test "bit field access" {
|
||||
var data = bit_field_1;
|
||||
try expect(getA(&data) == 1);
|
||||
try expect(getB(&data) == 2);
|
||||
try expect(getC(&data) == 3);
|
||||
comptime try expect(@sizeOf(BitField1) == 1);
|
||||
|
||||
data.b += 1;
|
||||
try expect(data.b == 3);
|
||||
|
||||
data.a += 1;
|
||||
try expect(data.a == 2);
|
||||
try expect(data.b == 3);
|
||||
}
|
||||
|
||||
fn getA(data: *const BitField1) u3 {
|
||||
return data.a;
|
||||
}
|
||||
|
||||
fn getB(data: *const BitField1) u3 {
|
||||
return data.b;
|
||||
}
|
||||
|
||||
fn getC(data: *const BitField1) u2 {
|
||||
return data.c;
|
||||
}
|
||||
|
||||
const Foo24Bits = packed struct {
|
||||
field: u24,
|
||||
};
|
||||
const Foo96Bits = packed struct {
|
||||
a: u24,
|
||||
b: u24,
|
||||
c: u24,
|
||||
d: u24,
|
||||
};
|
||||
|
||||
test "packed struct 24bits" {
|
||||
comptime {
|
||||
try expect(@sizeOf(Foo24Bits) == 4);
|
||||
if (@sizeOf(usize) == 4) {
|
||||
try expect(@sizeOf(Foo96Bits) == 12);
|
||||
} else {
|
||||
try expect(@sizeOf(Foo96Bits) == 16);
|
||||
}
|
||||
}
|
||||
|
||||
var value = Foo96Bits{
|
||||
.a = 0,
|
||||
.b = 0,
|
||||
.c = 0,
|
||||
.d = 0,
|
||||
};
|
||||
value.a += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 0);
|
||||
try expect(value.c == 0);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.b += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 0);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.c += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 1);
|
||||
try expect(value.d == 0);
|
||||
|
||||
value.d += 1;
|
||||
try expect(value.a == 1);
|
||||
try expect(value.b == 1);
|
||||
try expect(value.c == 1);
|
||||
try expect(value.d == 1);
|
||||
}
|
||||
|
||||
const Foo32Bits = packed struct {
|
||||
field: u24,
|
||||
pad: u8,
|
||||
};
|
||||
|
||||
const FooArray24Bits = packed struct {
|
||||
a: u16,
|
||||
b: [2]Foo32Bits,
|
||||
c: u16,
|
||||
};
|
||||
|
||||
// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512
|
||||
test "packed array 24bits" {
|
||||
comptime {
|
||||
try expect(@sizeOf([9]Foo32Bits) == 9 * 4);
|
||||
try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2);
|
||||
}
|
||||
|
||||
var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1);
|
||||
bytes[bytes.len - 1] = 0xaa;
|
||||
const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0];
|
||||
try expect(ptr.a == 0);
|
||||
try expect(ptr.b[0].field == 0);
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.a = maxInt(u16);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == 0);
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.b[0].field = maxInt(u24);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == 0);
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.b[1].field = maxInt(u24);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == maxInt(u24));
|
||||
try expect(ptr.c == 0);
|
||||
|
||||
ptr.c = maxInt(u16);
|
||||
try expect(ptr.a == maxInt(u16));
|
||||
try expect(ptr.b[0].field == maxInt(u24));
|
||||
try expect(ptr.b[1].field == maxInt(u24));
|
||||
try expect(ptr.c == maxInt(u16));
|
||||
|
||||
try expect(bytes[bytes.len - 1] == 0xaa);
|
||||
}
|
||||
|
||||
const FooStructAligned = packed struct {
|
||||
a: u8,
|
||||
b: u8,
|
||||
};
|
||||
|
||||
const FooArrayOfAligned = packed struct {
|
||||
a: [2]FooStructAligned,
|
||||
};
|
||||
|
||||
test "aligned array of packed struct" {
|
||||
comptime {
|
||||
try expect(@sizeOf(FooStructAligned) == 2);
|
||||
try expect(@sizeOf(FooArrayOfAligned) == 2 * 2);
|
||||
}
|
||||
|
||||
var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned);
|
||||
const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0];
|
||||
|
||||
try expect(ptr.a[0].a == 0xbb);
|
||||
try expect(ptr.a[0].b == 0xbb);
|
||||
try expect(ptr.a[1].a == 0xbb);
|
||||
try expect(ptr.a[1].b == 0xbb);
|
||||
}
|
||||
|
||||
test "runtime struct initialization of bitfield" {
|
||||
const s1 = Nibbles{
|
||||
.x = x1,
|
||||
.y = x1,
|
||||
};
|
||||
const s2 = Nibbles{
|
||||
.x = @intCast(u4, x2),
|
||||
.y = @intCast(u4, x2),
|
||||
};
|
||||
|
||||
try expect(s1.x == x1);
|
||||
try expect(s1.y == x1);
|
||||
try expect(s2.x == @intCast(u4, x2));
|
||||
try expect(s2.y == @intCast(u4, x2));
|
||||
}
|
||||
|
||||
var x1 = @as(u4, 1);
|
||||
var x2 = @as(u8, 2);
|
||||
|
||||
const Nibbles = packed struct {
|
||||
x: u4,
|
||||
y: u4,
|
||||
};
|
||||
|
||||
const Bitfields = packed struct {
|
||||
f1: u16,
|
||||
f2: u16,
|
||||
f3: u8,
|
||||
f4: u8,
|
||||
f5: u4,
|
||||
f6: u4,
|
||||
f7: u8,
|
||||
};
|
||||
|
||||
test "native bit field understands endianness" {
|
||||
var all: u64 = if (native_endian != .Little)
|
||||
0x1111222233445677
|
||||
else
|
||||
0x7765443322221111;
|
||||
var bytes: [8]u8 = undefined;
|
||||
@memcpy(&bytes, @ptrCast([*]u8, &all), 8);
|
||||
var bitfields = @ptrCast(*Bitfields, &bytes).*;
|
||||
|
||||
try expect(bitfields.f1 == 0x1111);
|
||||
try expect(bitfields.f2 == 0x2222);
|
||||
try expect(bitfields.f3 == 0x33);
|
||||
try expect(bitfields.f4 == 0x44);
|
||||
try expect(bitfields.f5 == 0x5);
|
||||
try expect(bitfields.f6 == 0x6);
|
||||
try expect(bitfields.f7 == 0x77);
|
||||
}
|
||||
|
||||
test "align 1 field before self referential align 8 field as slice return type" {
|
||||
const result = alloc(Expr);
|
||||
try expect(result.len == 0);
|
||||
}
|
||||
|
||||
const Expr = union(enum) {
|
||||
Literal: u8,
|
||||
Question: *Expr,
|
||||
};
|
||||
|
||||
fn alloc(comptime T: type) []T {
|
||||
return &[_]T{};
|
||||
}
|
||||
|
||||
test "call method with mutable reference to struct with no fields" {
|
||||
const S = struct {
|
||||
fn doC(s: *const @This()) bool {
|
||||
_ = s;
|
||||
return true;
|
||||
}
|
||||
fn do(s: *@This()) bool {
|
||||
_ = s;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var s = S{};
|
||||
try expect(S.doC(&s));
|
||||
try expect(s.doC());
|
||||
try expect(S.do(&s));
|
||||
try expect(s.do());
|
||||
}
|
||||
|
||||
test "implicit cast packed struct field to const ptr" {
|
||||
const LevelUpMove = packed struct {
|
||||
move_id: u9,
|
||||
level: u7,
|
||||
|
||||
fn toInt(value: u7) u7 {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
var lup: LevelUpMove = undefined;
|
||||
lup.level = 12;
|
||||
const res = LevelUpMove.toInt(lup.level);
|
||||
try expect(res == 12);
|
||||
}
|
||||
|
||||
test "pointer to packed struct member in a stack variable" {
|
||||
const S = packed struct {
|
||||
a: u2,
|
||||
b: u2,
|
||||
};
|
||||
|
||||
var s = S{ .a = 2, .b = 0 };
|
||||
var b_ptr = &s.b;
|
||||
try expect(s.b == 0);
|
||||
b_ptr.* = 2;
|
||||
try expect(s.b == 2);
|
||||
}
|
||||
|
||||
test "non-byte-aligned array inside packed struct" {
|
||||
const Foo = packed struct {
|
||||
a: bool,
|
||||
b: [0x16]u8,
|
||||
};
|
||||
const S = struct {
|
||||
fn bar(slice: []const u8) !void {
|
||||
try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu");
|
||||
}
|
||||
fn doTheTest() !void {
|
||||
var foo = Foo{
|
||||
.a = true,
|
||||
.b = "abcdefghijklmnopqurstu".*,
|
||||
};
|
||||
const value = foo.b;
|
||||
try bar(&value);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with u0 field access" {
|
||||
const S = packed struct {
|
||||
f0: u0,
|
||||
};
|
||||
var s = S{ .f0 = 0 };
|
||||
comptime try expect(s.f0 == 0);
|
||||
}
|
||||
|
||||
const S0 = struct {
|
||||
bar: S1,
|
||||
|
||||
pub const S1 = struct {
|
||||
value: u8,
|
||||
};
|
||||
|
||||
fn init() @This() {
|
||||
return S0{ .bar = S1{ .value = 123 } };
|
||||
}
|
||||
};
|
||||
|
||||
var g_foo: S0 = S0.init();
|
||||
|
||||
test "access to global struct fields" {
|
||||
g_foo.bar.value = 42;
|
||||
try expect(g_foo.bar.value == 42);
|
||||
}
|
||||
|
||||
test "packed struct with fp fields" {
|
||||
const S = packed struct {
|
||||
data: [3]f32,
|
||||
|
||||
pub fn frob(self: *@This()) void {
|
||||
self.data[0] += self.data[1] + self.data[2];
|
||||
self.data[1] += self.data[0] + self.data[2];
|
||||
self.data[2] += self.data[0] + self.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
var s: S = undefined;
|
||||
s.data[0] = 1.0;
|
||||
s.data[1] = 2.0;
|
||||
s.data[2] = 3.0;
|
||||
s.frob();
|
||||
try expectEqual(@as(f32, 6.0), s.data[0]);
|
||||
try expectEqual(@as(f32, 11.0), s.data[1]);
|
||||
try expectEqual(@as(f32, 20.0), s.data[2]);
|
||||
}
|
||||
|
||||
test "use within struct scope" {
|
||||
const S = struct {
|
||||
usingnamespace struct {
|
||||
pub fn inner() i32 {
|
||||
return 42;
|
||||
}
|
||||
};
|
||||
};
|
||||
try expectEqual(@as(i32, 42), S.inner());
|
||||
}
|
||||
|
||||
test "default struct initialization fields" {
|
||||
const S = struct {
|
||||
a: i32 = 1234,
|
||||
b: i32,
|
||||
};
|
||||
const x = S{
|
||||
.b = 5,
|
||||
};
|
||||
var five: i32 = 5;
|
||||
const y = S{
|
||||
.b = five,
|
||||
};
|
||||
if (x.a + x.b != 1239) {
|
||||
@compileError("it should be comptime known");
|
||||
}
|
||||
try expectEqual(y, x);
|
||||
try expectEqual(1239, x.a + x.b);
|
||||
}
|
||||
|
||||
test "fn with C calling convention returns struct by value" {
|
||||
const S = struct {
|
||||
fn entry() !void {
|
||||
var x = makeBar(10);
|
||||
try expectEqual(@as(i32, 10), x.handle);
|
||||
}
|
||||
|
||||
const ExternBar = extern struct {
|
||||
handle: i32,
|
||||
};
|
||||
|
||||
fn makeBar(t: i32) callconv(.C) ExternBar {
|
||||
return ExternBar{
|
||||
.handle = t,
|
||||
};
|
||||
}
|
||||
};
|
||||
try S.entry();
|
||||
comptime try S.entry();
|
||||
}
|
||||
|
||||
test "for loop over pointers to struct, getting field from struct pointer" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
var ok = true;
|
||||
|
||||
fn eql(a: []const u8) bool {
|
||||
_ = a;
|
||||
return true;
|
||||
}
|
||||
|
||||
const ArrayList = struct {
|
||||
fn toSlice(self: *ArrayList) []*Foo {
|
||||
_ = self;
|
||||
return @as([*]*Foo, undefined)[0..0];
|
||||
}
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var objects: ArrayList = undefined;
|
||||
|
||||
for (objects.toSlice()) |obj| {
|
||||
if (eql(obj.name)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
try expect(ok);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "zero-bit field in packed struct" {
|
||||
const S = packed struct {
|
||||
x: u10,
|
||||
y: void,
|
||||
};
|
||||
var x: S = undefined;
|
||||
_ = x;
|
||||
}
|
||||
|
||||
test "struct field init with catch" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x: anyerror!isize = 1;
|
||||
var req = Foo{
|
||||
.field = x catch undefined,
|
||||
};
|
||||
try expect(req.field == 1);
|
||||
}
|
||||
|
||||
pub const Foo = extern struct {
|
||||
field: isize,
|
||||
};
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with non-ABI-aligned field" {
|
||||
const S = packed struct {
|
||||
x: u9,
|
||||
y: u183,
|
||||
};
|
||||
var s: S = undefined;
|
||||
s.x = 1;
|
||||
s.y = 42;
|
||||
try expect(s.x == 1);
|
||||
try expect(s.y == 42);
|
||||
}
|
||||
|
||||
test "non-packed struct with u128 entry in union" {
|
||||
const U = union(enum) {
|
||||
Num: u128,
|
||||
Void,
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
f1: U,
|
||||
f2: U,
|
||||
};
|
||||
|
||||
var sx: S = undefined;
|
||||
var s = &sx;
|
||||
try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2"));
|
||||
var v2 = U{ .Num = 123 };
|
||||
s.f2 = v2;
|
||||
try std.testing.expect(s.f2.Num == 123);
|
||||
}
|
||||
|
||||
test "packed struct field passed to generic function" {
|
||||
const S = struct {
|
||||
const P = packed struct {
|
||||
b: u5,
|
||||
g: u5,
|
||||
r: u5,
|
||||
a: u1,
|
||||
};
|
||||
|
||||
fn genericReadPackedField(ptr: anytype) u5 {
|
||||
return ptr.*;
|
||||
}
|
||||
};
|
||||
|
||||
var p: S.P = undefined;
|
||||
p.b = 29;
|
||||
var loaded = S.genericReadPackedField(&p.b);
|
||||
try expect(loaded == 29);
|
||||
}
|
||||
|
||||
test "anonymous struct literal syntax" {
|
||||
const S = struct {
|
||||
const Point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var p: Point = .{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
try expect(p.x == 1);
|
||||
try expect(p.y == 2);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "fully anonymous struct" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try dump(.{
|
||||
.int = @as(u32, 1234),
|
||||
.float = @as(f64, 12.34),
|
||||
.b = true,
|
||||
.s = "hi",
|
||||
});
|
||||
}
|
||||
fn dump(args: anytype) !void {
|
||||
try expect(args.int == 1234);
|
||||
try expect(args.float == 12.34);
|
||||
try expect(args.b);
|
||||
try expect(args.s[0] == 'h');
|
||||
try expect(args.s[1] == 'i');
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "fully anonymous list literal" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });
|
||||
}
|
||||
fn dump(args: anytype) !void {
|
||||
try expect(args.@"0" == 1234);
|
||||
try expect(args.@"1" == 12.34);
|
||||
try expect(args.@"2");
|
||||
try expect(args.@"3"[0] == 'h');
|
||||
try expect(args.@"3"[1] == 'i');
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "anonymous struct literal assigned to variable" {
|
||||
var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) };
|
||||
try expect(vec.@"0" == 22);
|
||||
try expect(vec.@"1" == 55);
|
||||
try expect(vec.@"2" == 99);
|
||||
}
|
||||
|
||||
test "struct with var field" {
|
||||
const Point = struct {
|
||||
x: anytype,
|
||||
y: anytype,
|
||||
};
|
||||
const pt = Point{
|
||||
.x = 1,
|
||||
.y = 2,
|
||||
};
|
||||
try expect(pt.x == 1);
|
||||
try expect(pt.y == 2);
|
||||
}
|
||||
|
||||
test "comptime struct field" {
|
||||
const T = struct {
|
||||
a: i32,
|
||||
comptime b: i32 = 1234,
|
||||
};
|
||||
|
||||
var foo: T = undefined;
|
||||
comptime try expect(foo.b == 1234);
|
||||
}
|
||||
|
||||
test "anon struct literal field value initialized with fn call" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var x = .{foo()};
|
||||
try expectEqualSlices(u8, x[0], "hi");
|
||||
}
|
||||
fn foo() []const u8 {
|
||||
return "hi";
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "self-referencing struct via array member" {
|
||||
const T = struct {
|
||||
children: [1]*@This(),
|
||||
};
|
||||
var x: T = undefined;
|
||||
x = T{ .children = .{&x} };
|
||||
try expect(x.children[0] == &x);
|
||||
}
|
||||
|
||||
test "struct with union field" {
|
||||
const Value = struct {
|
||||
ref: u32 = 2,
|
||||
kind: union(enum) {
|
||||
None: usize,
|
||||
Bool: bool,
|
||||
},
|
||||
};
|
||||
|
||||
var True = Value{
|
||||
.kind = .{ .Bool = true },
|
||||
};
|
||||
try expectEqual(@as(u32, 2), True.ref);
|
||||
try expectEqual(true, True.kind.Bool);
|
||||
}
|
||||
|
||||
test "type coercion of anon struct literal to struct" {
|
||||
const S = struct {
|
||||
const S2 = struct {
|
||||
A: u32,
|
||||
B: []const u8,
|
||||
C: void,
|
||||
D: Foo = .{},
|
||||
};
|
||||
|
||||
const Foo = struct {
|
||||
field: i32 = 1234,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var y: u32 = 42;
|
||||
const t0 = .{ .A = 123, .B = "foo", .C = {} };
|
||||
const t1 = .{ .A = y, .B = "foo", .C = {} };
|
||||
const y0: S2 = t0;
|
||||
var y1: S2 = t1;
|
||||
try expect(y0.A == 123);
|
||||
try expect(std.mem.eql(u8, y0.B, "foo"));
|
||||
try expect(y0.C == {});
|
||||
try expect(y0.D.field == 1234);
|
||||
try expect(y1.A == y);
|
||||
try expect(std.mem.eql(u8, y1.B, "foo"));
|
||||
try expect(y1.C == {});
|
||||
try expect(y1.D.field == 1234);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type coercion of pointer to anon struct literal to pointer to struct" {
|
||||
const S = struct {
|
||||
const S2 = struct {
|
||||
A: u32,
|
||||
B: []const u8,
|
||||
C: void,
|
||||
D: Foo = .{},
|
||||
};
|
||||
|
||||
const Foo = struct {
|
||||
field: i32 = 1234,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var y: u32 = 42;
|
||||
const t0 = &.{ .A = 123, .B = "foo", .C = {} };
|
||||
const t1 = &.{ .A = y, .B = "foo", .C = {} };
|
||||
const y0: *const S2 = t0;
|
||||
var y1: *const S2 = t1;
|
||||
try expect(y0.A == 123);
|
||||
try expect(std.mem.eql(u8, y0.B, "foo"));
|
||||
try expect(y0.C == {});
|
||||
try expect(y0.D.field == 1234);
|
||||
try expect(y1.A == y);
|
||||
try expect(std.mem.eql(u8, y1.B, "foo"));
|
||||
try expect(y1.C == {});
|
||||
try expect(y1.D.field == 1234);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "packed struct with undefined initializers" {
|
||||
const S = struct {
|
||||
const P = packed struct {
|
||||
a: u3,
|
||||
_a: u3 = undefined,
|
||||
b: u3,
|
||||
_b: u3 = undefined,
|
||||
c: u3,
|
||||
_c: u3 = undefined,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var p: P = undefined;
|
||||
p = P{ .a = 2, .b = 4, .c = 6 };
|
||||
// Make sure the compiler doesn't touch the unprefixed fields.
|
||||
// Use expect since i386-linux doesn't like expectEqual
|
||||
try expect(p.a == 2);
|
||||
try expect(p.b == 4);
|
||||
try expect(p.c == 6);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user