| author | |
| committer | |
| log | 8eea73fb922c1c7fab4b8bf1717588464691a66e |
| tree | 6b80dd1b1d2e1993e05e6d3c19cbc5ba4d424c5e |
| parent | 4cea15f12bdaf1603d60cf80e767dd8f2bb48b26 |
14 files changed, 260 insertions(+), 30 deletions(-)
lib/std/zig/parse.zig+1-1| ... | ... | @@ -884,7 +884,7 @@ const Parser = struct { |
| 884 | 884 | |
| 885 | 885 | var align_expr: Node.Index = 0; |
| 886 | 886 | var type_expr: Node.Index = 0; |
| 887 | if (p.eatToken(.colon) != null or tuple_like) |_| { | |
| 887 | if (p.eatToken(.colon) != null or tuple_like) { | |
| 888 | 888 | type_expr = try p.expectTypeExpr(); |
| 889 | 889 | align_expr = try p.parseByteAlign(); |
| 890 | 890 | } |
lib/std/zig/render.zig+19| ... | ... | @@ -1189,6 +1189,16 @@ fn renderContainerField( |
| 1189 | 1189 | try renderToken(ais, tree, t, .space); // comptime |
| 1190 | 1190 | } |
| 1191 | 1191 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { |
| 1192 | if (field.ast.align_expr != 0) { | |
| 1193 | try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name | |
| 1194 | const lparen_token = tree.firstToken(field.ast.align_expr) - 1; | |
| 1195 | const align_kw = lparen_token - 1; | |
| 1196 | const rparen_token = tree.lastToken(field.ast.align_expr) + 1; | |
| 1197 | try renderToken(ais, tree, align_kw, .none); // align | |
| 1198 | try renderToken(ais, tree, lparen_token, .none); // ( | |
| 1199 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | |
| 1200 | return renderToken(ais, tree, rparen_token, .space); // ) | |
| 1201 | } | |
| 1192 | 1202 | return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name |
| 1193 | 1203 | } |
| 1194 | 1204 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { |
| ... | ... | @@ -1211,6 +1221,15 @@ fn renderContainerField( |
| 1211 | 1221 | } |
| 1212 | 1222 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { |
| 1213 | 1223 | try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name |
| 1224 | if (field.ast.align_expr != 0) { | |
| 1225 | const lparen_token = tree.firstToken(field.ast.align_expr) - 1; | |
| 1226 | const align_kw = lparen_token - 1; | |
| 1227 | const rparen_token = tree.lastToken(field.ast.align_expr) + 1; | |
| 1228 | try renderToken(ais, tree, align_kw, .none); // align | |
| 1229 | try renderToken(ais, tree, lparen_token, .none); // ( | |
| 1230 | try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment | |
| 1231 | try renderToken(ais, tree, rparen_token, .space); // ) | |
| 1232 | } | |
| 1214 | 1233 | try renderToken(ais, tree, field.ast.main_token + 1, .space); // = |
| 1215 | 1234 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value |
| 1216 | 1235 | } |
src/AstGen.zig+3-2| ... | ... | @@ -4854,8 +4854,9 @@ fn containerDecl( |
| 4854 | 4854 | }, |
| 4855 | 4855 | ); |
| 4856 | 4856 | } |
| 4857 | // Alignment expressions in enums are caught by the parser. | |
| 4858 | assert(member.ast.align_expr == 0); | |
| 4857 | if (member.ast.align_expr != 0) { | |
| 4858 | return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{}); | |
| 4859 | } | |
| 4859 | 4860 | |
| 4860 | 4861 | const name_token = member.ast.main_token; |
| 4861 | 4862 | if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { |
src/Sema.zig+41-8| ... | ... | @@ -11834,6 +11834,12 @@ fn analyzeTupleCat( |
| 11834 | 11834 | if (dest_fields == 0) { |
| 11835 | 11835 | return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value)); |
| 11836 | 11836 | } |
| 11837 | if (lhs_len == 0) { | |
| 11838 | return rhs; | |
| 11839 | } | |
| 11840 | if (rhs_len == 0) { | |
| 11841 | return lhs; | |
| 11842 | } | |
| 11837 | 11843 | const final_len = try sema.usizeCast(block, rhs_src, dest_fields); |
| 11838 | 11844 | |
| 11839 | 11845 | const types = try sema.arena.alloc(Type, final_len); |
| ... | ... | @@ -11880,13 +11886,13 @@ fn analyzeTupleCat( |
| 11880 | 11886 | var i: u32 = 0; |
| 11881 | 11887 | while (i < lhs_len) : (i += 1) { |
| 11882 | 11888 | const operand_src = lhs_src; // TODO better source location |
| 11883 | element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty); | |
| 11889 | element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, i, lhs_ty); | |
| 11884 | 11890 | } |
| 11885 | 11891 | i = 0; |
| 11886 | 11892 | while (i < rhs_len) : (i += 1) { |
| 11887 | 11893 | const operand_src = rhs_src; // TODO better source location |
| 11888 | 11894 | element_refs[i + lhs_len] = |
| 11889 | try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty); | |
| 11895 | try sema.tupleFieldValByIndex(block, operand_src, rhs, i, rhs_ty); | |
| 11890 | 11896 | } |
| 11891 | 11897 | |
| 11892 | 11898 | return block.addAggregateInit(tuple_ty, element_refs); |
| ... | ... | @@ -18502,8 +18508,12 @@ fn reifyStruct( |
| 18502 | 18508 | } |
| 18503 | 18509 | const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema)).?); |
| 18504 | 18510 | |
| 18505 | if (layout == .Packed and abi_align != 0) { | |
| 18506 | return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); | |
| 18511 | if (layout == .Packed) { | |
| 18512 | if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); | |
| 18513 | if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); | |
| 18514 | } | |
| 18515 | if (layout == .Extern and is_comptime_val.toBool()) { | |
| 18516 | return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); | |
| 18507 | 18517 | } |
| 18508 | 18518 | |
| 18509 | 18519 | const field_name = try name_val.toAllocatedBytes( |
| ... | ... | @@ -18512,6 +18522,25 @@ fn reifyStruct( |
| 18512 | 18522 | mod, |
| 18513 | 18523 | ); |
| 18514 | 18524 | |
| 18525 | if (is_tuple) { | |
| 18526 | const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch { | |
| 18527 | return sema.fail( | |
| 18528 | block, | |
| 18529 | src, | |
| 18530 | "tuple cannot have non-numeric field '{s}'", | |
| 18531 | .{field_name}, | |
| 18532 | ); | |
| 18533 | }; | |
| 18534 | ||
| 18535 | if (field_index >= fields_len) { | |
| 18536 | return sema.fail( | |
| 18537 | block, | |
| 18538 | src, | |
| 18539 | "tuple field {} exceeds tuple field count", | |
| 18540 | .{field_index}, | |
| 18541 | ); | |
| 18542 | } | |
| 18543 | } | |
| 18515 | 18544 | const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); |
| 18516 | 18545 | if (gop.found_existing) { |
| 18517 | 18546 | // TODO: better source location |
| ... | ... | @@ -18525,6 +18554,9 @@ fn reifyStruct( |
| 18525 | 18554 | opt_val; |
| 18526 | 18555 | break :blk try payload_val.copy(new_decl_arena_allocator); |
| 18527 | 18556 | } else Value.initTag(.unreachable_value); |
| 18557 | if (is_comptime_val.toBool() and default_val.tag() == .unreachable_value) { | |
| 18558 | return sema.fail(block, src, "comptime field without default initialization value", .{}); | |
| 18559 | } | |
| 18528 | 18560 | |
| 18529 | 18561 | var buffer: Value.ToTypeBuffer = undefined; |
| 18530 | 18562 | gop.value_ptr.* = .{ |
| ... | ... | @@ -27094,7 +27126,7 @@ fn coerceTupleToStruct( |
| 27094 | 27126 | |
| 27095 | 27127 | const inst_ty = sema.typeOf(inst); |
| 27096 | 27128 | var runtime_src: ?LazySrcLoc = null; |
| 27097 | const field_count = struct_ty.structFieldCount(); | |
| 27129 | const field_count = inst_ty.structFieldCount(); | |
| 27098 | 27130 | var field_i: u32 = 0; |
| 27099 | 27131 | while (field_i < field_count) : (field_i += 1) { |
| 27100 | 27132 | const field_src = inst_src; // TODO better source location |
| ... | ... | @@ -27176,15 +27208,16 @@ fn coerceTupleToTuple( |
| 27176 | 27208 | inst: Air.Inst.Ref, |
| 27177 | 27209 | inst_src: LazySrcLoc, |
| 27178 | 27210 | ) !Air.Inst.Ref { |
| 27179 | const field_count = tuple_ty.structFieldCount(); | |
| 27180 | const field_vals = try sema.arena.alloc(Value, field_count); | |
| 27211 | const dest_field_count = tuple_ty.structFieldCount(); | |
| 27212 | const field_vals = try sema.arena.alloc(Value, dest_field_count); | |
| 27181 | 27213 | const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); |
| 27182 | 27214 | mem.set(Air.Inst.Ref, field_refs, .none); |
| 27183 | 27215 | |
| 27184 | 27216 | const inst_ty = sema.typeOf(inst); |
| 27217 | const inst_field_count = inst_ty.structFieldCount(); | |
| 27185 | 27218 | var runtime_src: ?LazySrcLoc = null; |
| 27186 | 27219 | var field_i: u32 = 0; |
| 27187 | while (field_i < field_count) : (field_i += 1) { | |
| 27220 | while (field_i < inst_field_count) : (field_i += 1) { | |
| 27188 | 27221 | const field_src = inst_src; // TODO better source location |
| 27189 | 27222 | const field_name = if (inst_ty.castTag(.anon_struct)) |payload| |
| 27190 | 27223 | payload.data.names[field_i] |
src/arch/x86_64/abi.zig+1| ... | ... | @@ -552,6 +552,7 @@ test "C_C_D" { |
| 552 | 552 | .layout = .Extern, |
| 553 | 553 | .status = .fully_resolved, |
| 554 | 554 | .known_non_opv = true, |
| 555 | .is_tuple = false, | |
| 555 | 556 | }; |
| 556 | 557 | var C_C_D = Type.Payload.Struct{ .data = &C_C_D_struct }; |
| 557 | 558 |
src/type.zig+6-2| ... | ... | @@ -804,7 +804,7 @@ pub const Type = extern union { |
| 804 | 804 | return a_struct_obj == b_struct_obj; |
| 805 | 805 | }, |
| 806 | 806 | .tuple, .empty_struct_literal => { |
| 807 | if (!b.isTuple()) return false; | |
| 807 | if (!b.isSimpleTuple()) return false; | |
| 808 | 808 | |
| 809 | 809 | const a_tuple = a.tupleFields(); |
| 810 | 810 | const b_tuple = b.tupleFields(); |
| ... | ... | @@ -5572,7 +5572,11 @@ pub const Type = extern union { |
| 5572 | 5572 | |
| 5573 | 5573 | pub fn structFieldCount(ty: Type) usize { |
| 5574 | 5574 | switch (ty.tag()) { |
| 5575 | .@"struct" => return ty.castTag(.@"struct").?.data.fields.count(), | |
| 5575 | .@"struct" => { | |
| 5576 | const struct_obj = ty.castTag(.@"struct").?.data; | |
| 5577 | assert(struct_obj.haveFieldTypes()); | |
| 5578 | return struct_obj.fields.count(); | |
| 5579 | }, | |
| 5576 | 5580 | .empty_struct, .empty_struct_literal => return 0, |
| 5577 | 5581 | .tuple => return ty.castTag(.tuple).?.data.types.len, |
| 5578 | 5582 | .anon_struct => return ty.castTag(.anon_struct).?.data.types.len, |
test/behavior.zig+1| ... | ... | @@ -200,6 +200,7 @@ test { |
| 200 | 200 | _ = @import("behavior/packed_struct_explicit_backing_int.zig"); |
| 201 | 201 | _ = @import("behavior/empty_union.zig"); |
| 202 | 202 | _ = @import("behavior/inline_switch.zig"); |
| 203 | _ = @import("behavior/tuple_declarations.zig"); | |
| 203 | 204 | _ = @import("behavior/bugs/12723.zig"); |
| 204 | 205 | _ = @import("behavior/bugs/12776.zig"); |
| 205 | 206 | } |
test/behavior/tuple_declarations.zig created+79| ... | ... | @@ -0,0 +1,79 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const testing = std.testing; | |
| 4 | const expect = testing.expect; | |
| 5 | const expectEqualStrings = testing.expectEqualStrings; | |
| 6 | ||
| 7 | test "tuple declaration type info" { | |
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 11 | ||
| 12 | { | |
| 13 | const T = struct { comptime u32 align(2) = 1, []const u8 }; | |
| 14 | const info = @typeInfo(T).Struct; | |
| 15 | ||
| 16 | try expect(info.layout == .Auto); | |
| 17 | try expect(info.backing_integer == null); | |
| 18 | try expect(info.fields.len == 2); | |
| 19 | try expect(info.decls.len == 0); | |
| 20 | try expect(info.is_tuple); | |
| 21 | ||
| 22 | try expectEqualStrings(info.fields[0].name, "0"); | |
| 23 | try expect(info.fields[0].field_type == u32); | |
| 24 | try expect(@ptrCast(*const u32, @alignCast(@alignOf(u32), info.fields[0].default_value)).* == 1); | |
| 25 | try expect(info.fields[0].is_comptime); | |
| 26 | try expect(info.fields[0].alignment == 2); | |
| 27 | ||
| 28 | try expectEqualStrings(info.fields[1].name, "1"); | |
| 29 | try expect(info.fields[1].field_type == []const u8); | |
| 30 | try expect(info.fields[1].default_value == null); | |
| 31 | try expect(!info.fields[1].is_comptime); | |
| 32 | try expect(info.fields[1].alignment == @alignOf([]const u8)); | |
| 33 | } | |
| 34 | { | |
| 35 | const T = packed struct(u32) { u1, u30, u1 }; | |
| 36 | const info = @typeInfo(T).Struct; | |
| 37 | ||
| 38 | try expect(std.mem.endsWith(u8, @typeName(T), "test.tuple declaration type info.T")); | |
| 39 | ||
| 40 | try expect(info.layout == .Packed); | |
| 41 | try expect(info.backing_integer == u32); | |
| 42 | try expect(info.fields.len == 3); | |
| 43 | try expect(info.decls.len == 0); | |
| 44 | try expect(info.is_tuple); | |
| 45 | ||
| 46 | try expectEqualStrings(info.fields[0].name, "0"); | |
| 47 | try expect(info.fields[0].field_type == u1); | |
| 48 | ||
| 49 | try expectEqualStrings(info.fields[1].name, "1"); | |
| 50 | try expect(info.fields[1].field_type == u30); | |
| 51 | ||
| 52 | try expectEqualStrings(info.fields[2].name, "2"); | |
| 53 | try expect(info.fields[2].field_type == u1); | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | test "Tuple declaration usage" { | |
| 58 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 60 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 61 | ||
| 62 | const T = struct { u32, []const u8 }; | |
| 63 | var t: T = .{ 1, "foo" }; | |
| 64 | try expect(t[0] == 1); | |
| 65 | try expectEqualStrings(t[1], "foo"); | |
| 66 | ||
| 67 | var mul = t ** 3; | |
| 68 | try expect(@TypeOf(mul) != T); | |
| 69 | try expect(mul.len == 6); | |
| 70 | try expect(mul[2] == 1); | |
| 71 | try expectEqualStrings(mul[3], "foo"); | |
| 72 | ||
| 73 | var t2: T = .{ 2, "bar" }; | |
| 74 | var cat = t ++ t2; | |
| 75 | try expect(@TypeOf(cat) != T); | |
| 76 | try expect(cat.len == 4); | |
| 77 | try expect(cat[2] == 2); | |
| 78 | try expectEqualStrings(cat[3], "bar"); | |
| 79 | } |
test/cases/compile_errors/alignment_of_enum_field_specified.zig+1-1| ... | ... | @@ -11,4 +11,4 @@ export fn entry1() void { |
| 11 | 11 | // backend=stage2 |
| 12 | 12 | // target=native |
| 13 | 13 | // |
| 14 | // :3:7: error: expected ',' after field | |
| 14 | // :3:13: error: enum fields cannot be aligned |
test/cases/compile_errors/reify_struct.zig created+80| ... | ... | @@ -0,0 +1,80 @@ |
| 1 | comptime { | |
| 2 | @Type(.{ .Struct = .{ | |
| 3 | .layout = .Auto, | |
| 4 | .fields = &.{.{ | |
| 5 | .name = "foo", | |
| 6 | .field_type = u32, | |
| 7 | .default_value = null, | |
| 8 | .is_comptime = false, | |
| 9 | .alignment = 4, | |
| 10 | }}, | |
| 11 | .decls = &.{}, | |
| 12 | .is_tuple = true, | |
| 13 | } }); | |
| 14 | } | |
| 15 | comptime { | |
| 16 | @Type(.{ .Struct = .{ | |
| 17 | .layout = .Auto, | |
| 18 | .fields = &.{.{ | |
| 19 | .name = "3", | |
| 20 | .field_type = u32, | |
| 21 | .default_value = null, | |
| 22 | .is_comptime = false, | |
| 23 | .alignment = 4, | |
| 24 | }}, | |
| 25 | .decls = &.{}, | |
| 26 | .is_tuple = true, | |
| 27 | } }); | |
| 28 | } | |
| 29 | comptime { | |
| 30 | @Type(.{ .Struct = .{ | |
| 31 | .layout = .Auto, | |
| 32 | .fields = &.{.{ | |
| 33 | .name = "0", | |
| 34 | .field_type = u32, | |
| 35 | .default_value = null, | |
| 36 | .is_comptime = true, | |
| 37 | .alignment = 4, | |
| 38 | }}, | |
| 39 | .decls = &.{}, | |
| 40 | .is_tuple = true, | |
| 41 | } }); | |
| 42 | } | |
| 43 | comptime { | |
| 44 | @Type(.{ .Struct = .{ | |
| 45 | .layout = .Extern, | |
| 46 | .fields = &.{.{ | |
| 47 | .name = "0", | |
| 48 | .field_type = u32, | |
| 49 | .default_value = null, | |
| 50 | .is_comptime = true, | |
| 51 | .alignment = 4, | |
| 52 | }}, | |
| 53 | .decls = &.{}, | |
| 54 | .is_tuple = true, | |
| 55 | } }); | |
| 56 | } | |
| 57 | comptime { | |
| 58 | @Type(.{ .Struct = .{ | |
| 59 | .layout = .Packed, | |
| 60 | .fields = &.{.{ | |
| 61 | .name = "0", | |
| 62 | .field_type = u32, | |
| 63 | .default_value = null, | |
| 64 | .is_comptime = true, | |
| 65 | .alignment = 4, | |
| 66 | }}, | |
| 67 | .decls = &.{}, | |
| 68 | .is_tuple = true, | |
| 69 | } }); | |
| 70 | } | |
| 71 | ||
| 72 | // error | |
| 73 | // backend=stage2 | |
| 74 | // target=native | |
| 75 | // | |
| 76 | // :2:5: error: tuple cannot have non-numeric field 'foo' | |
| 77 | // :16:5: error: tuple field 3 exceeds tuple field count | |
| 78 | // :30:5: error: comptime field without default initialization value | |
| 79 | // :44:5: error: extern struct fields cannot be marked comptime | |
| 80 | // :58:5: error: alignment in a packed struct field must be set to 0 |
test/cases/compile_errors/struct_field_missing_type.zig deleted-13| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | const Letter = struct { | |
| 2 | A, | |
| 3 | }; | |
| 4 | export fn entry() void { | |
| 5 | var a = Letter { .A = {} }; | |
| 6 | _ = a; | |
| 7 | } | |
| 8 | ||
| 9 | // error | |
| 10 | // backend=stage2 | |
| 11 | // target=native | |
| 12 | // | |
| 13 | // :2:5: error: struct field missing type |
test/cases/compile_errors/tuple_declarations.zig created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | const E = enum { | |
| 2 | *u32, | |
| 3 | }; | |
| 4 | const U = union { | |
| 5 | *u32, | |
| 6 | }; | |
| 7 | const S = struct { | |
| 8 | a: u32, | |
| 9 | *u32, | |
| 10 | }; | |
| 11 | const T = struct { | |
| 12 | u32, | |
| 13 | []const u8, | |
| 14 | ||
| 15 | const a = 1; | |
| 16 | }; | |
| 17 | ||
| 18 | // error | |
| 19 | // backend=stage2 | |
| 20 | // target=native | |
| 21 | // | |
| 22 | // :2:5: error: enum field missing name | |
| 23 | // :5:5: error: union field missing name | |
| 24 | // :8:5: error: tuple field has a name | |
| 25 | // :15:5: error: tuple declarations cannot contain declarations |
test/compile_errors.zig+2-2| ... | ... | @@ -213,7 +213,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 213 | 213 | case.backend = .stage2; |
| 214 | 214 | |
| 215 | 215 | case.addSourceFile("b.zig", |
| 216 | \\bad | |
| 216 | \\+ | |
| 217 | 217 | ); |
| 218 | 218 | |
| 219 | 219 | case.addError( |
| ... | ... | @@ -221,7 +221,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 221 | 221 | \\ _ = (@sizeOf(@import("b.zig"))); |
| 222 | 222 | \\} |
| 223 | 223 | , &[_][]const u8{ |
| 224 | ":1:1: error: struct field missing type", | |
| 224 | ":1:1: error: expected type expression, found '+'", | |
| 225 | 225 | }); |
| 226 | 226 | } |
| 227 | 227 |
test/stage2/cbe.zig+1-1| ... | ... | @@ -670,7 +670,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 670 | 670 | \\ _ = E1.a; |
| 671 | 671 | \\} |
| 672 | 672 | , &.{ |
| 673 | ":3:7: error: expected ',' after field", | |
| 673 | ":3:13: error: enum fields cannot be aligned", | |
| 674 | 674 | }); |
| 675 | 675 | |
| 676 | 676 | // Redundant non-exhaustive enum mark. |