| ... | @@ -900,9 +900,9 @@ pub const DeclGen = struct { | ... | @@ -900,9 +900,9 @@ pub const DeclGen = struct { |
| 900 | | 900 | |
| 901 | try writer.writeByte('{'); | 901 | try writer.writeByte('{'); |
| 902 | var empty = true; | 902 | var empty = true; |
| 903 | for (0..ty.structFieldCount(mod)) |field_i| { | 903 | for (0..ty.structFieldCount(mod)) |field_index| { |
| 904 | if (ty.structFieldIsComptime(field_i, mod)) continue; | 904 | if (ty.structFieldIsComptime(field_index, mod)) continue; |
| 905 | const field_ty = ty.structFieldType(field_i, mod); | 905 | const field_ty = ty.structFieldType(field_index, mod); |
| 906 | if (!field_ty.hasRuntimeBits(mod)) continue; | 906 | if (!field_ty.hasRuntimeBits(mod)) continue; |
| 907 | | 907 | |
| 908 | if (!empty) try writer.writeByte(','); | 908 | if (!empty) try writer.writeByte(','); |
| ... | @@ -934,9 +934,10 @@ pub const DeclGen = struct { | ... | @@ -934,9 +934,10 @@ pub const DeclGen = struct { |
| 934 | try writer.writeAll(" .payload = {"); | 934 | try writer.writeAll(" .payload = {"); |
| 935 | } | 935 | } |
| 936 | const union_obj = mod.typeToUnion(ty).?; | 936 | const union_obj = mod.typeToUnion(ty).?; |
| 937 | for (union_obj.field_types.get(ip)) |field_ty| { | 937 | for (0..union_obj.field_types.len) |field_index| { |
| 938 | if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; | 938 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 939 | try dg.renderValue(writer, Type.fromInterned(field_ty), val, initializer_type); | 939 | if (!field_ty.hasRuntimeBits(mod)) continue; |
| | 940 | try dg.renderValue(writer, field_ty, val, initializer_type); |
| 940 | break; | 941 | break; |
| 941 | } | 942 | } |
| 942 | if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); | 943 | if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); |
| ... | @@ -1354,25 +1355,23 @@ pub const DeclGen = struct { | ... | @@ -1354,25 +1355,23 @@ pub const DeclGen = struct { |
| 1354 | | 1355 | |
| 1355 | try writer.writeByte('{'); | 1356 | try writer.writeByte('{'); |
| 1356 | var empty = true; | 1357 | var empty = true; |
| 1357 | for ( | 1358 | for (0..tuple.types.len) |field_index| { |
| 1358 | tuple.types.get(ip), | 1359 | const comptime_val = tuple.values.get(ip)[field_index]; |
| 1359 | tuple.values.get(ip), | 1360 | if (comptime_val != .none) continue; |
| 1360 | 0.., | 1361 | const field_ty = Type.fromInterned(tuple.types.get(ip)[field_index]); |
| 1361 | ) |field_ty, comptime_ty, field_i| { | 1362 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 1362 | if (comptime_ty != .none) continue; | | |
| 1363 | if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; | | |
| 1364 | | 1363 | |
| 1365 | if (!empty) try writer.writeByte(','); | 1364 | if (!empty) try writer.writeByte(','); |
| 1366 | | 1365 | |
| 1367 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { | 1366 | const field_val = Value.fromInterned(switch (ip.indexToKey(val.ip_index).aggregate.storage) { |
| 1368 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | 1367 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ |
| 1369 | .ty = field_ty, | 1368 | .ty = field_ty.toIntern(), |
| 1370 | .storage = .{ .u64 = bytes[field_i] }, | 1369 | .storage = .{ .u64 = bytes[field_index] }, |
| 1371 | } }), | 1370 | } }), |
| 1372 | .elems => |elems| elems[field_i], | 1371 | .elems => |elems| elems[field_index], |
| 1373 | .repeated_elem => |elem| elem, | 1372 | .repeated_elem => |elem| elem, |
| 1374 | }; | 1373 | }); |
| 1375 | try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), initializer_type); | 1374 | try dg.renderValue(writer, field_ty, field_val, initializer_type); |
| 1376 | | 1375 | |
| 1377 | empty = false; | 1376 | empty = false; |
| 1378 | } | 1377 | } |
| ... | @@ -1388,18 +1387,18 @@ pub const DeclGen = struct { | ... | @@ -1388,18 +1387,18 @@ pub const DeclGen = struct { |
| 1388 | | 1387 | |
| 1389 | try writer.writeByte('{'); | 1388 | try writer.writeByte('{'); |
| 1390 | var empty = true; | 1389 | var empty = true; |
| 1391 | for (0..struct_type.field_types.len) |field_i| { | 1390 | for (0..struct_type.field_types.len) |field_index| { |
| 1392 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_i]); | 1391 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 1393 | if (struct_type.fieldIsComptime(ip, field_i)) continue; | 1392 | if (struct_type.fieldIsComptime(ip, field_index)) continue; |
| 1394 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | 1393 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 1395 | | 1394 | |
| 1396 | if (!empty) try writer.writeByte(','); | 1395 | if (!empty) try writer.writeByte(','); |
| 1397 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { | 1396 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { |
| 1398 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | 1397 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ |
| 1399 | .ty = field_ty.toIntern(), | 1398 | .ty = field_ty.toIntern(), |
| 1400 | .storage = .{ .u64 = bytes[field_i] }, | 1399 | .storage = .{ .u64 = bytes[field_index] }, |
| 1401 | } }), | 1400 | } }), |
| 1402 | .elems => |elems| elems[field_i], | 1401 | .elems => |elems| elems[field_index], |
| 1403 | .repeated_elem => |elem| elem, | 1402 | .repeated_elem => |elem| elem, |
| 1404 | }; | 1403 | }; |
| 1405 | try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); | 1404 | try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); |
| ... | @@ -1413,13 +1412,13 @@ pub const DeclGen = struct { | ... | @@ -1413,13 +1412,13 @@ pub const DeclGen = struct { |
| 1413 | | 1412 | |
| 1414 | const bits = Type.smallestUnsignedBits(int_info.bits - 1); | 1413 | const bits = Type.smallestUnsignedBits(int_info.bits - 1); |
| 1415 | const bit_offset_ty = try mod.intType(.unsigned, bits); | 1414 | const bit_offset_ty = try mod.intType(.unsigned, bits); |
| 1416 | const field_types = struct_type.field_types.get(ip); | | |
| 1417 | | 1415 | |
| 1418 | var bit_offset: u64 = 0; | 1416 | var bit_offset: u64 = 0; |
| 1419 | var eff_num_fields: usize = 0; | 1417 | var eff_num_fields: usize = 0; |
| 1420 | | 1418 | |
| 1421 | for (field_types) |field_ty| { | 1419 | for (0..struct_type.field_types.len) |field_index| { |
| 1422 | if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; | 1420 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); |
| | 1421 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 1423 | eff_num_fields += 1; | 1422 | eff_num_fields += 1; |
| 1424 | } | 1423 | } |
| 1425 | | 1424 | |
| ... | @@ -1438,15 +1437,16 @@ pub const DeclGen = struct { | ... | @@ -1438,15 +1437,16 @@ pub const DeclGen = struct { |
| 1438 | | 1437 | |
| 1439 | var eff_index: usize = 0; | 1438 | var eff_index: usize = 0; |
| 1440 | var needs_closing_paren = false; | 1439 | var needs_closing_paren = false; |
| 1441 | for (field_types, 0..) |field_ty, field_i| { | 1440 | for (0..struct_type.field_types.len) |field_index| { |
| 1442 | if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; | 1441 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); |
| | 1442 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 1443 | | 1443 | |
| 1444 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { | 1444 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { |
| 1445 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | 1445 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ |
| 1446 | .ty = field_ty, | 1446 | .ty = field_ty.toIntern(), |
| 1447 | .storage = .{ .u64 = bytes[field_i] }, | 1447 | .storage = .{ .u64 = bytes[field_index] }, |
| 1448 | } }), | 1448 | } }), |
| 1449 | .elems => |elems| elems[field_i], | 1449 | .elems => |elems| elems[field_index], |
| 1450 | .repeated_elem => |elem| elem, | 1450 | .repeated_elem => |elem| elem, |
| 1451 | }; | 1451 | }; |
| 1452 | const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; | 1452 | const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; |
| ... | @@ -1454,19 +1454,19 @@ pub const DeclGen = struct { | ... | @@ -1454,19 +1454,19 @@ pub const DeclGen = struct { |
| 1454 | try writer.writeAll("zig_shl_"); | 1454 | try writer.writeAll("zig_shl_"); |
| 1455 | try dg.renderTypeForBuiltinFnName(writer, ty); | 1455 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 1456 | try writer.writeByte('('); | 1456 | try writer.writeByte('('); |
| 1457 | try dg.renderIntCast(writer, ty, cast_context, Type.fromInterned(field_ty), .FunctionArgument); | 1457 | try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); |
| 1458 | try writer.writeAll(", "); | 1458 | try writer.writeAll(", "); |
| 1459 | const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); | 1459 | const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); |
| 1460 | try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); | 1460 | try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); |
| 1461 | try writer.writeByte(')'); | 1461 | try writer.writeByte(')'); |
| 1462 | } else { | 1462 | } else { |
| 1463 | try dg.renderIntCast(writer, ty, cast_context, Type.fromInterned(field_ty), .FunctionArgument); | 1463 | try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); |
| 1464 | } | 1464 | } |
| 1465 | | 1465 | |
| 1466 | if (needs_closing_paren) try writer.writeByte(')'); | 1466 | if (needs_closing_paren) try writer.writeByte(')'); |
| 1467 | if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); | 1467 | if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); |
| 1468 | | 1468 | |
| 1469 | bit_offset += Type.fromInterned(field_ty).bitSize(mod); | 1469 | bit_offset += field_ty.bitSize(mod); |
| 1470 | needs_closing_paren = true; | 1470 | needs_closing_paren = true; |
| 1471 | eff_index += 1; | 1471 | eff_index += 1; |
| 1472 | } | 1472 | } |
| ... | @@ -1474,8 +1474,9 @@ pub const DeclGen = struct { | ... | @@ -1474,8 +1474,9 @@ pub const DeclGen = struct { |
| 1474 | try writer.writeByte('('); | 1474 | try writer.writeByte('('); |
| 1475 | // a << a_off | b << b_off | c << c_off | 1475 | // a << a_off | b << b_off | c << c_off |
| 1476 | var empty = true; | 1476 | var empty = true; |
| 1477 | for (field_types, 0..) |field_ty, field_i| { | 1477 | for (0..struct_type.field_types.len) |field_index| { |
| 1478 | if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; | 1478 | const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); |
| | 1479 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 1479 | | 1480 | |
| 1480 | if (!empty) try writer.writeAll(" | "); | 1481 | if (!empty) try writer.writeAll(" | "); |
| 1481 | try writer.writeByte('('); | 1482 | try writer.writeByte('('); |
| ... | @@ -1484,23 +1485,23 @@ pub const DeclGen = struct { | ... | @@ -1484,23 +1485,23 @@ pub const DeclGen = struct { |
| 1484 | | 1485 | |
| 1485 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { | 1486 | const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { |
| 1486 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | 1487 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ |
| 1487 | .ty = field_ty, | 1488 | .ty = field_ty.toIntern(), |
| 1488 | .storage = .{ .u64 = bytes[field_i] }, | 1489 | .storage = .{ .u64 = bytes[field_index] }, |
| 1489 | } }), | 1490 | } }), |
| 1490 | .elems => |elems| elems[field_i], | 1491 | .elems => |elems| elems[field_index], |
| 1491 | .repeated_elem => |elem| elem, | 1492 | .repeated_elem => |elem| elem, |
| 1492 | }; | 1493 | }; |
| 1493 | | 1494 | |
| 1494 | if (bit_offset != 0) { | 1495 | if (bit_offset != 0) { |
| 1495 | try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), .Other); | 1496 | try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); |
| 1496 | try writer.writeAll(" << "); | 1497 | try writer.writeAll(" << "); |
| 1497 | const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); | 1498 | const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); |
| 1498 | try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); | 1499 | try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); |
| 1499 | } else { | 1500 | } else { |
| 1500 | try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), .Other); | 1501 | try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); |
| 1501 | } | 1502 | } |
| 1502 | | 1503 | |
| 1503 | bit_offset += Type.fromInterned(field_ty).bitSize(mod); | 1504 | bit_offset += field_ty.bitSize(mod); |
| 1504 | empty = false; | 1505 | empty = false; |
| 1505 | } | 1506 | } |
| 1506 | try writer.writeByte(')'); | 1507 | try writer.writeByte(')'); |
| ... | @@ -1545,9 +1546,9 @@ pub const DeclGen = struct { | ... | @@ -1545,9 +1546,9 @@ pub const DeclGen = struct { |
| 1545 | try writer.writeByte(')'); | 1546 | try writer.writeByte(')'); |
| 1546 | } | 1547 | } |
| 1547 | | 1548 | |
| 1548 | const field_i = mod.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; | 1549 | const field_index = mod.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; |
| 1549 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_i]); | 1550 | const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 1550 | const field_name = union_obj.field_names.get(ip)[field_i]; | 1551 | const field_name = union_obj.field_names.get(ip)[field_index]; |
| 1551 | if (union_obj.getLayout(ip) == .Packed) { | 1552 | if (union_obj.getLayout(ip) == .Packed) { |
| 1552 | if (field_ty.hasRuntimeBits(mod)) { | 1553 | if (field_ty.hasRuntimeBits(mod)) { |
| 1553 | if (field_ty.isPtrAtRuntime(mod)) { | 1554 | if (field_ty.isPtrAtRuntime(mod)) { |
| ... | @@ -1581,9 +1582,10 @@ pub const DeclGen = struct { | ... | @@ -1581,9 +1582,10 @@ pub const DeclGen = struct { |
| 1581 | try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))}); | 1582 | try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))}); |
| 1582 | try dg.renderValue(writer, field_ty, Value.fromInterned(un.val), initializer_type); | 1583 | try dg.renderValue(writer, field_ty, Value.fromInterned(un.val), initializer_type); |
| 1583 | try writer.writeByte(' '); | 1584 | try writer.writeByte(' '); |
| 1584 | } else for (union_obj.field_types.get(ip)) |this_field_ty| { | 1585 | } else for (0..union_obj.field_types.len) |this_field_index| { |
| 1585 | if (!Type.fromInterned(this_field_ty).hasRuntimeBits(mod)) continue; | 1586 | const this_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[this_field_index]); |
| 1586 | try dg.renderValue(writer, Type.fromInterned(this_field_ty), Value.undef, initializer_type); | 1587 | if (!this_field_ty.hasRuntimeBits(mod)) continue; |
| | 1588 | try dg.renderValue(writer, this_field_ty, Value.undef, initializer_type); |
| 1587 | break; | 1589 | break; |
| 1588 | } | 1590 | } |
| 1589 | if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); | 1591 | if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); |
| ... | @@ -7090,17 +7092,16 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -7090,17 +7092,16 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7090 | } | 7092 | } |
| 7091 | }, | 7093 | }, |
| 7092 | .Struct => switch (inst_ty.containerLayout(mod)) { | 7094 | .Struct => switch (inst_ty.containerLayout(mod)) { |
| 7093 | .Auto, .Extern => for (resolved_elements, 0..) |element, field_i_usize| { | 7095 | .Auto, .Extern => for (resolved_elements, 0..) |element, field_index| { |
| 7094 | const field_i: u32 = @intCast(field_i_usize); | 7096 | if (inst_ty.structFieldIsComptime(field_index, mod)) continue; |
| 7095 | if (inst_ty.structFieldIsComptime(field_i, mod)) continue; | 7097 | const field_ty = inst_ty.structFieldType(field_index, mod); |
| 7096 | const field_ty = inst_ty.structFieldType(field_i, mod); | | |
| 7097 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | 7098 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 7098 | | 7099 | |
| 7099 | const a = try Assignment.start(f, writer, field_ty); | 7100 | const a = try Assignment.start(f, writer, field_ty); |
| 7100 | try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod)) | 7101 | try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod)) |
| 7101 | .{ .field = field_i } | 7102 | .{ .field = field_index } |
| 7102 | else | 7103 | else |
| 7103 | .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(field_i, mod)) }); | 7104 | .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(@intCast(field_index), mod)) }); |
| 7104 | try a.assign(f, writer); | 7105 | try a.assign(f, writer); |
| 7105 | try f.writeCValue(writer, element, .Other); | 7106 | try f.writeCValue(writer, element, .Other); |
| 7106 | try a.end(f, writer); | 7107 | try a.end(f, writer); |
| ... | @@ -7115,9 +7116,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -7115,9 +7116,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7115 | var bit_offset: u64 = 0; | 7116 | var bit_offset: u64 = 0; |
| 7116 | | 7117 | |
| 7117 | var empty = true; | 7118 | var empty = true; |
| 7118 | for (0..elements.len) |field_i| { | 7119 | for (0..elements.len) |field_index| { |
| 7119 | if (inst_ty.structFieldIsComptime(field_i, mod)) continue; | 7120 | if (inst_ty.structFieldIsComptime(field_index, mod)) continue; |
| 7120 | const field_ty = inst_ty.structFieldType(field_i, mod); | 7121 | const field_ty = inst_ty.structFieldType(field_index, mod); |
| 7121 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | 7122 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 7122 | | 7123 | |
| 7123 | if (!empty) { | 7124 | if (!empty) { |
| ... | @@ -7128,9 +7129,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -7128,9 +7129,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7128 | empty = false; | 7129 | empty = false; |
| 7129 | } | 7130 | } |
| 7130 | empty = true; | 7131 | empty = true; |
| 7131 | for (resolved_elements, 0..) |element, field_i| { | 7132 | for (resolved_elements, 0..) |element, field_index| { |
| 7132 | if (inst_ty.structFieldIsComptime(field_i, mod)) continue; | 7133 | if (inst_ty.structFieldIsComptime(field_index, mod)) continue; |
| 7133 | const field_ty = inst_ty.structFieldType(field_i, mod); | 7134 | const field_ty = inst_ty.structFieldType(field_index, mod); |
| 7134 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | 7135 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 7135 | | 7136 | |
| 7136 | if (!empty) try writer.writeAll(", "); | 7137 | if (!empty) try writer.writeAll(", "); |