| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const testing = std.testing; |
| 4 | const assert = std.debug.assert; |
| 5 | const expect = testing.expect; |
| 6 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 7 | const expectEqual = std.testing.expectEqual; |
| 8 | |
| 9 | test "tuple concatenation" { |
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 12 | |
| 13 | const S = struct { |
| 14 | fn doTheTest() !void { |
| 15 | var a: i32 = 1; |
| 16 | var b: i32 = 2; |
| 17 | _ = .{ &a, &b }; |
| 18 | const x = .{a}; |
| 19 | const y = .{b}; |
| 20 | const c = x ++ y; |
| 21 | try expect(@as(i32, 1) == c[0]); |
| 22 | try expect(@as(i32, 2) == c[1]); |
| 23 | } |
| 24 | }; |
| 25 | try S.doTheTest(); |
| 26 | try comptime S.doTheTest(); |
| 27 | } |
| 28 | |
| 29 | test "more tuple concatenation" { |
| 30 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 31 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 32 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 33 | |
| 34 | const T = struct { |
| 35 | fn consume_tuple(tuple: anytype, len: usize) !void { |
| 36 | try expect(tuple.len == len); |
| 37 | } |
| 38 | |
| 39 | fn doTheTest() !void { |
| 40 | const t1 = .{}; |
| 41 | |
| 42 | var rt_var: u8 = 42; |
| 43 | const t2 = .{rt_var} ++ .{}; |
| 44 | |
| 45 | try expect(t2.len == 1); |
| 46 | try expect(t2.@"0" == rt_var); |
| 47 | try expect(t2.@"0" == 42); |
| 48 | try expect(&t2.@"0" != &rt_var); |
| 49 | |
| 50 | try consume_tuple(t1 ++ t1, 0); |
| 51 | try consume_tuple(.{} ++ .{}, 0); |
| 52 | try consume_tuple(.{0} ++ .{}, 1); |
| 53 | try consume_tuple(.{0} ++ .{1}, 2); |
| 54 | try consume_tuple(.{ 0, 1, 2 } ++ .{ u8, 1, noreturn }, 6); |
| 55 | try consume_tuple(t2 ++ t1, 1); |
| 56 | try consume_tuple(t1 ++ t2, 1); |
| 57 | try consume_tuple(t2 ++ t2, 2); |
| 58 | try consume_tuple(.{rt_var} ++ .{}, 1); |
| 59 | try consume_tuple(.{rt_var} ++ t1, 1); |
| 60 | try consume_tuple(.{} ++ .{rt_var}, 1); |
| 61 | try consume_tuple(t2 ++ .{void}, 2); |
| 62 | try consume_tuple(t2 ++ .{0}, 2); |
| 63 | try consume_tuple(.{0} ++ t2, 2); |
| 64 | try consume_tuple(.{void} ++ t2, 2); |
| 65 | try consume_tuple(.{u8} ++ .{rt_var} ++ .{true}, 3); |
| 66 | } |
| 67 | }; |
| 68 | |
| 69 | try T.doTheTest(); |
| 70 | try comptime T.doTheTest(); |
| 71 | } |
| 72 | |
| 73 | test "pass tuple to comptime var parameter" { |
| 74 | const S = struct { |
| 75 | fn Foo(comptime args: anytype) !void { |
| 76 | try expect(args[0] == 1); |
| 77 | } |
| 78 | |
| 79 | fn doTheTest() !void { |
| 80 | try Foo(.{1}); |
| 81 | } |
| 82 | }; |
| 83 | try S.doTheTest(); |
| 84 | try comptime S.doTheTest(); |
| 85 | } |
| 86 | |
| 87 | test "tuple initializer for var" { |
| 88 | const S = struct { |
| 89 | fn doTheTest() void { |
| 90 | const Bytes = struct { |
| 91 | id: usize, |
| 92 | }; |
| 93 | |
| 94 | var tmp = .{ |
| 95 | .id = @as(usize, 2), |
| 96 | .name = Bytes{ .id = 20 }, |
| 97 | }; |
| 98 | _ = &tmp; |
| 99 | } |
| 100 | }; |
| 101 | |
| 102 | S.doTheTest(); |
| 103 | comptime S.doTheTest(); |
| 104 | } |
| 105 | |
| 106 | test "array-like initializer for tuple types" { |
| 107 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 108 | const T = @Tuple(&.{ i32, u8 }); |
| 109 | const S = struct { |
| 110 | fn doTheTest() !void { |
| 111 | var obj: T = .{ -1234, 128 }; |
| 112 | _ = &obj; |
| 113 | try expect(@as(i32, -1234) == obj[0]); |
| 114 | try expect(@as(u8, 128) == obj[1]); |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | try S.doTheTest(); |
| 119 | try comptime S.doTheTest(); |
| 120 | } |
| 121 | |
| 122 | test "anon struct as the result from a labeled block" { |
| 123 | const S = struct { |
| 124 | fn doTheTest() !void { |
| 125 | const precomputed = comptime blk: { |
| 126 | var x: i32 = 1234; |
| 127 | _ = &x; |
| 128 | break :blk .{ |
| 129 | .x = x, |
| 130 | }; |
| 131 | }; |
| 132 | try expect(precomputed.x == 1234); |
| 133 | } |
| 134 | }; |
| 135 | |
| 136 | try S.doTheTest(); |
| 137 | try comptime S.doTheTest(); |
| 138 | } |
| 139 | |
| 140 | test "tuple as the result from a labeled block" { |
| 141 | const S = struct { |
| 142 | fn doTheTest() !void { |
| 143 | const precomputed = comptime blk: { |
| 144 | var x: i32 = 1234; |
| 145 | _ = &x; |
| 146 | break :blk .{x}; |
| 147 | }; |
| 148 | try expect(precomputed[0] == 1234); |
| 149 | } |
| 150 | }; |
| 151 | |
| 152 | try S.doTheTest(); |
| 153 | try comptime S.doTheTest(); |
| 154 | } |
| 155 | |
| 156 | test "initializing tuple with explicit type" { |
| 157 | const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) }); |
| 158 | var a = T{ 0, 0 }; |
| 159 | _ = &a; |
| 160 | } |
| 161 | |
| 162 | test "initializing anon struct with explicit type" { |
| 163 | const T = @TypeOf(.{ .foo = @as(i32, 1), .bar = @as(i32, 2) }); |
| 164 | var a = T{ .foo = 1, .bar = 2 }; |
| 165 | _ = &a; |
| 166 | } |
| 167 | |
| 168 | test "fieldParentPtr of tuple" { |
| 169 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 170 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 171 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 172 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 173 | var x: u32 = 0; |
| 174 | _ = &x; |
| 175 | const tuple = .{ x, x }; |
| 176 | try testing.expect(&tuple == @as(@TypeOf(&tuple), @fieldParentPtr("1", &tuple[1]))); |
| 177 | } |
| 178 | |
| 179 | test "fieldParentPtr of anon struct" { |
| 180 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 181 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 182 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 183 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 184 | |
| 185 | var x: u32 = 0; |
| 186 | _ = &x; |
| 187 | const anon_st = .{ .foo = x, .bar = x }; |
| 188 | try testing.expect(&anon_st == @as(@TypeOf(&anon_st), @fieldParentPtr("bar", &anon_st.bar))); |
| 189 | } |
| 190 | |
| 191 | test "offsetOf tuple" { |
| 192 | var x: u32 = 0; |
| 193 | _ = &x; |
| 194 | const T = @TypeOf(.{ x, x }); |
| 195 | try expect(@offsetOf(T, "1") == @sizeOf(u32)); |
| 196 | } |
| 197 | |
| 198 | test "offsetOf anon struct" { |
| 199 | var x: u32 = 0; |
| 200 | _ = &x; |
| 201 | const T = @TypeOf(.{ .foo = x, .bar = x }); |
| 202 | try expect(@offsetOf(T, "bar") == @sizeOf(u32)); |
| 203 | } |
| 204 | |
| 205 | test "initializing tuple with mixed comptime-runtime fields" { |
| 206 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 207 | |
| 208 | var x: u32 = 15; |
| 209 | _ = &x; |
| 210 | const T = @TypeOf(.{ @as(i32, -1234), @as(u32, 5678), x }); |
| 211 | var a: T = .{ -1234, 5678, x + 1 }; |
| 212 | _ = &a; |
| 213 | try expect(a[2] == 16); |
| 214 | } |
| 215 | |
| 216 | test "initializing anon struct with mixed comptime-runtime fields" { |
| 217 | var x: u32 = 15; |
| 218 | _ = &x; |
| 219 | const T = @TypeOf(.{ .foo = @as(i32, -1234), .bar = x }); |
| 220 | var a: T = .{ .foo = -1234, .bar = x + 1 }; |
| 221 | _ = &a; |
| 222 | try expect(a.bar == 16); |
| 223 | } |
| 224 | |
| 225 | test "tuple in tuple passed to generic function" { |
| 226 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 227 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 228 | const S = struct { |
| 229 | fn pair(x: f32, y: f32) @Tuple(&.{ f32, f32 }) { |
| 230 | return .{ x, y }; |
| 231 | } |
| 232 | |
| 233 | fn foo(x: anytype) !void { |
| 234 | try expect(x[0][0] == 1.5); |
| 235 | try expect(x[0][1] == 2.5); |
| 236 | } |
| 237 | }; |
| 238 | const x = comptime S.pair(1.5, 2.5); |
| 239 | try S.foo(.{x}); |
| 240 | } |
| 241 | |
| 242 | test "coerce tuple to tuple" { |
| 243 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 244 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 245 | |
| 246 | const T = @Tuple(&.{u8}); |
| 247 | const S = struct { |
| 248 | fn foo(x: T) !void { |
| 249 | try expect(x[0] == 123); |
| 250 | } |
| 251 | }; |
| 252 | try S.foo(.{123}); |
| 253 | } |
| 254 | |
| 255 | test "tuple type with void field" { |
| 256 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 257 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 258 | |
| 259 | const T = @Tuple(&.{void}); |
| 260 | const x = T{{}}; |
| 261 | try expect(@TypeOf(x[0]) == void); |
| 262 | } |
| 263 | |
| 264 | test "zero sized struct in tuple handled correctly" { |
| 265 | const State = struct { |
| 266 | const Self = @This(); |
| 267 | const Inner = struct {}; |
| 268 | |
| 269 | data: @Tuple(&.{Inner}), |
| 270 | |
| 271 | pub fn do(this: Self) usize { |
| 272 | return @sizeOf(@TypeOf(this)); |
| 273 | } |
| 274 | }; |
| 275 | |
| 276 | var s: State = undefined; |
| 277 | try expect(s.do() == 0); |
| 278 | } |
| 279 | |
| 280 | test "tuple type with void field and a runtime field" { |
| 281 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 282 | |
| 283 | const T = @Tuple(&.{ usize, void }); |
| 284 | var t: T = .{ 5, {} }; |
| 285 | _ = &t; |
| 286 | try expect(t[0] == 5); |
| 287 | } |
| 288 | |
| 289 | test "branching inside tuple literal" { |
| 290 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 291 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 292 | |
| 293 | const S = struct { |
| 294 | fn foo(a: anytype) !void { |
| 295 | try expect(a[0] == 1234); |
| 296 | } |
| 297 | }; |
| 298 | var a = false; |
| 299 | _ = &a; |
| 300 | try S.foo(.{if (a) @as(u32, 5678) else @as(u32, 1234)}); |
| 301 | } |
| 302 | |
| 303 | test "tuple initialized with a runtime known value" { |
| 304 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 305 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 306 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 307 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 308 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 309 | |
| 310 | const E = union(enum) { e: []const u8 }; |
| 311 | const W = union(enum) { w: E }; |
| 312 | var e = E{ .e = "test" }; |
| 313 | _ = &e; |
| 314 | const w = .{W{ .w = e }}; |
| 315 | try expectEqualStrings(w[0].w.e, "test"); |
| 316 | } |
| 317 | |
| 318 | test "tuple of struct concatenation and coercion to array" { |
| 319 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 320 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 321 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 322 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 323 | |
| 324 | const StructWithDefault = struct { value: f32 = 42 }; |
| 325 | const SomeStruct = struct { array: [4]StructWithDefault }; |
| 326 | |
| 327 | const value1 = SomeStruct{ .array = .{StructWithDefault{}} ++ @as([3]StructWithDefault, @splat(.{})) }; |
| 328 | const value2 = SomeStruct{ .array = .{ .{}, .{}, .{}, .{} } }; |
| 329 | |
| 330 | try expectEqual(value1, value2); |
| 331 | } |
| 332 | |
| 333 | test "nested runtime conditionals in tuple initializer" { |
| 334 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 335 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 336 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 337 | |
| 338 | var data: u8 = 0; |
| 339 | _ = &data; |
| 340 | const x = .{ |
| 341 | if (data != 0) "" else switch (@as(u1, @truncate(data))) { |
| 342 | 0 => "up", |
| 343 | 1 => "down", |
| 344 | }, |
| 345 | }; |
| 346 | try expectEqualStrings("up", x[0]); |
| 347 | } |
| 348 | |
| 349 | test "sentinel slice in tuple with other fields" { |
| 350 | const S = struct { |
| 351 | a: u32, |
| 352 | b: u32, |
| 353 | }; |
| 354 | |
| 355 | const Submission = union(enum) { |
| 356 | open: struct { *S, [:0]const u8, u32 }, |
| 357 | }; |
| 358 | |
| 359 | _ = Submission; |
| 360 | } |
| 361 | |
| 362 | test "sentinel slice in tuple" { |
| 363 | const S = struct { [:0]const u8 }; |
| 364 | |
| 365 | _ = S; |
| 366 | } |
| 367 | |
| 368 | test "tuple pointer is indexable" { |
| 369 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 370 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 371 | |
| 372 | const S = struct { u32, bool }; |
| 373 | |
| 374 | const x: S = .{ 123, true }; |
| 375 | comptime assert(@TypeOf(&(&x)[0]) == *const u32); // validate constness |
| 376 | try expectEqual(@as(u32, 123), (&x)[0]); |
| 377 | try expectEqual(true, (&x)[1]); |
| 378 | |
| 379 | var y: S = .{ 123, true }; |
| 380 | comptime assert(@TypeOf(&(&y)[0]) == *u32); // validate constness |
| 381 | try expectEqual(@as(u32, 123), (&y)[0]); |
| 382 | try expectEqual(true, (&y)[1]); |
| 383 | |
| 384 | (&y)[0] = 100; |
| 385 | (&y)[1] = false; |
| 386 | try expectEqual(@as(u32, 100), (&y)[0]); |
| 387 | try expectEqual(false, (&y)[1]); |
| 388 | } |
| 389 | |
| 390 | test "coerce anon tuple to tuple" { |
| 391 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 392 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 393 | |
| 394 | var x: u8 = 1; |
| 395 | var y: u16 = 2; |
| 396 | _ = .{ &x, &y }; |
| 397 | const t = .{ x, y }; |
| 398 | const s: struct { u8, u16 } = t; |
| 399 | try expectEqual(x, s[0]); |
| 400 | try expectEqual(y, s[1]); |
| 401 | } |
| 402 | |
| 403 | test "empty tuple type" { |
| 404 | const S = @Tuple(&.{}); |
| 405 | |
| 406 | const s: S = .{}; |
| 407 | try expect(s.len == 0); |
| 408 | } |
| 409 | |
| 410 | test "tuple with comptime fields with non empty initializer" { |
| 411 | const a: struct { comptime comptime_int = 0 } = .{0}; |
| 412 | _ = a; |
| 413 | } |
| 414 | |
| 415 | test "anon tuple field referencing comptime var isn't comptime" { |
| 416 | comptime var a: u8 = 0; |
| 417 | const tuple = .{&a}; |
| 418 | // field isn't comptime but tuple is still comptime-known |
| 419 | comptime assert(@TypeOf(tuple) == struct { *u8 }); |
| 420 | a = 1; |
| 421 | comptime assert(tuple[0].* == 1); |
| 422 | } |
| 423 | |
| 424 | test "tuple with runtime value coerced into a slice with a sentinel" { |
| 425 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 426 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 427 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 428 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 429 | |
| 430 | const S = struct { |
| 431 | fn f(a: [:null]const ?u8) !void { |
| 432 | try expect(a[0] == 42); |
| 433 | } |
| 434 | }; |
| 435 | |
| 436 | const c: u8 = 42; |
| 437 | try S.f(&[_:null]?u8{c}); |
| 438 | try S.f(&.{c}); |
| 439 | |
| 440 | var v: u8 = 42; |
| 441 | _ = &v; |
| 442 | try S.f(&[_:null]?u8{v}); |
| 443 | try S.f(&.{v}); |
| 444 | } |
| 445 | |
| 446 | test "tuple implicitly coerced to optional/error union struct/union" { |
| 447 | const SomeUnion = union(enum) { |
| 448 | variant: u8, |
| 449 | }; |
| 450 | const SomeStruct = struct { |
| 451 | struct_field: u8, |
| 452 | }; |
| 453 | const OptEnum = struct { |
| 454 | opt_union: ?SomeUnion, |
| 455 | }; |
| 456 | const ErrEnum = struct { |
| 457 | err_union: anyerror!SomeUnion, |
| 458 | }; |
| 459 | const OptStruct = struct { |
| 460 | opt_struct: ?SomeStruct, |
| 461 | }; |
| 462 | const ErrStruct = struct { |
| 463 | err_struct: anyerror!SomeStruct, |
| 464 | }; |
| 465 | |
| 466 | try expect((OptEnum{ |
| 467 | .opt_union = .{ |
| 468 | .variant = 1, |
| 469 | }, |
| 470 | }).opt_union.?.variant == 1); |
| 471 | |
| 472 | try expect(((ErrEnum{ |
| 473 | .err_union = .{ |
| 474 | .variant = 1, |
| 475 | }, |
| 476 | }).err_union catch unreachable).variant == 1); |
| 477 | |
| 478 | try expect((OptStruct{ |
| 479 | .opt_struct = .{ |
| 480 | .struct_field = 1, |
| 481 | }, |
| 482 | }).opt_struct.?.struct_field == 1); |
| 483 | |
| 484 | try expect(((ErrStruct{ |
| 485 | .err_struct = .{ |
| 486 | .struct_field = 1, |
| 487 | }, |
| 488 | }).err_struct catch unreachable).struct_field == 1); |
| 489 | } |
| 490 | |
| 491 | test "comptime fields in tuple can be initialized" { |
| 492 | const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) }); |
| 493 | var a: T = .{ 0, 0 }; |
| 494 | _ = &a; |
| 495 | } |
| 496 | |
| 497 | test "empty struct in tuple" { |
| 498 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 499 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 500 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 501 | |
| 502 | const T = struct { struct {} }; |
| 503 | const info = @typeInfo(T); |
| 504 | try std.testing.expectEqual(@as(usize, 1), info.@"struct".field_names.len); |
| 505 | try std.testing.expectEqualStrings("0", info.@"struct".field_names[0]); |
| 506 | try std.testing.expect(@typeInfo(info.@"struct".field_types[0]) == .@"struct"); |
| 507 | } |
| 508 | |
| 509 | test "empty union in tuple" { |
| 510 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 511 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 512 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 513 | |
| 514 | const T = struct { union {} }; |
| 515 | const info = @typeInfo(T); |
| 516 | try std.testing.expectEqual(@as(usize, 1), info.@"struct".field_names.len); |
| 517 | try std.testing.expectEqualStrings("0", info.@"struct".field_names[0]); |
| 518 | try std.testing.expect(@typeInfo(info.@"struct".field_types[0]) == .@"union"); |
| 519 | } |
| 520 | |
| 521 | test "field pointer of underaligned tuple" { |
| 522 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 523 | |
| 524 | const S = struct { |
| 525 | fn doTheTest() !void { |
| 526 | const T = struct { u8, u32 }; |
| 527 | var val: T align(2) = .{ 1, 2 }; |
| 528 | |
| 529 | comptime assert(@TypeOf(&val[0]) == *u8); // `u8` field pointer isn't overaligned |
| 530 | comptime assert(@TypeOf(&val[1]) == *align(2) u32); // `u32` field pointer is correctly underaligned |
| 531 | |
| 532 | try expect(val[0] == 1); |
| 533 | try expect(val[1] == 2); |
| 534 | } |
| 535 | }; |
| 536 | try S.doTheTest(); |
| 537 | try comptime S.doTheTest(); |
| 538 | } |
| 539 | |
| 540 | test "OPV tuple fields aren't comptime" { |
| 541 | const T = struct { void }; |
| 542 | const t_info = @typeInfo(T); |
| 543 | try expect(!t_info.@"struct".field_attrs[0].@"comptime"); |
| 544 | |
| 545 | const T2 = @Tuple(&.{void}); |
| 546 | const t2_info = @typeInfo(T2); |
| 547 | try expect(!t2_info.@"struct".field_attrs[0].@"comptime"); |
| 548 | } |
| 549 | |
| 550 | test "array of tuples that end with a zero-bit field followed by padding" { |
| 551 | const S = struct { |
| 552 | var foo: [2]struct { u32, u8, void } = .{ .{ 1, 2, {} }, .{ 3, 4, {} } }; |
| 553 | }; |
| 554 | try expect(S.foo[0][0] == 1); |
| 555 | try expect(S.foo[0][1] == 2); |
| 556 | try expect(S.foo[0][2] == {}); |
| 557 | try expect(S.foo[1][0] == 3); |
| 558 | try expect(S.foo[1][1] == 4); |
| 559 | try expect(S.foo[1][2] == {}); |
| 560 | } |
| 561 | |
| 562 | test "call function at comptime through container-level const tuple" { |
| 563 | const static = struct { |
| 564 | const MyTuple = struct { (fn () u32) }; |
| 565 | const val: MyTuple = .{foo}; |
| 566 | fn foo() u32 { |
| 567 | return 1234; |
| 568 | } |
| 569 | }; |
| 570 | comptime assert(static.val[0]() == 1234); |
| 571 | } |