Skip to content

Commit 13853c1

Browse files
committed
First part of multi-payload enum support
This handles multi-payload enums with fixed layouts that don't use spare payload bits. It includes XI calculations that allow us to handle single-payload enums where the payload ultimately includes a multi-payload enum (For example, on 32-bit platforms, String uses a multi-payload enum, so this now supports single-payload enums carrying Strings.)
1 parent 6fd0247 commit 13853c1

File tree

5 files changed

+221
-17
lines changed

5 files changed

+221
-17
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,50 @@ class ReflectionContext
801801
}
802802

803803
case RecordKind::MultiPayloadEnum: {
804-
// TODO: Support multipayload enums
804+
// Collect basic statistics about the enum
805+
unsigned long PayloadCaseCount = 0;
806+
unsigned long NonPayloadCaseCount = 0;
807+
unsigned long PayloadSize = 0;
808+
for (auto Field : Fields) {
809+
if (Field.TR != 0) {
810+
PayloadCaseCount += 1;
811+
if (Field.TI.getSize() > PayloadSize) {
812+
PayloadSize = Field.TI.getSize();
813+
}
814+
} else {
815+
NonPayloadCaseCount += 1;
816+
}
817+
}
818+
if (EnumSize > PayloadSize) {
819+
// If the compiler laid this out with a separate tag, use that.
820+
unsigned tag = 0;
821+
auto TagSize = EnumSize - PayloadSize;
822+
auto TagAddress = remote::RemoteAddress(EnumAddress.getAddressData() + PayloadSize);
823+
if (!getReader().readInteger(TagAddress, TagSize, &tag)
824+
|| tag >= Fields.size()) {
825+
return false;
826+
}
827+
if (tag < PayloadCaseCount) {
828+
*CaseIndex = tag;
829+
return true;
830+
}
831+
auto PayloadTagSize = std::min(PayloadSize, static_cast<size_t>(4));
832+
// Treat the tag as a page selector; payload carries the offset within the page
833+
auto Page = tag - PayloadCaseCount;
834+
// Zero for 32-bit because we'll never have more than one page
835+
auto PageSize = PayloadTagSize >= 4 ? 0 : 1 << (PayloadSize * 8U);
836+
auto PageStart = Page * PageSize;
837+
unsigned PayloadTag;
838+
if (!getReader().readInteger(EnumAddress, PayloadTagSize, &PayloadTag)) {
839+
return false;
840+
}
841+
*CaseIndex = PageStart + PayloadTag + PayloadCaseCount;
842+
return true;
843+
} else {
844+
// XXX TODO: If the payloads have common spare bits (e.g., all pointers)
845+
// then use those to decode the case.
846+
return false;
847+
}
805848
break;
806849
}
807850

stdlib/public/Reflection/TypeLowering.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,53 @@ bool RecordTypeInfo::readExtraInhabitantIndex(remote::MemoryReader &reader,
363363
return false;
364364
}
365365

366-
case RecordKind::MultiPayloadEnum:// XXX TODO
367-
return false;
368-
366+
case RecordKind::MultiPayloadEnum: {
367+
// Multi payload enums can export XIs from the tag, if any.
368+
// They never export XIs from their payloads.
369+
auto Fields = getFields();
370+
unsigned long PayloadCaseCount = 0;
371+
unsigned long NonPayloadCaseCount = 0;
372+
unsigned long PayloadSize = 0;
373+
for (auto Field : Fields) {
374+
if (Field.TR != 0) {
375+
PayloadCaseCount += 1;
376+
if (Field.TI.getSize() > PayloadSize) {
377+
PayloadSize = Field.TI.getSize();
378+
}
379+
} else {
380+
NonPayloadCaseCount += 1;
381+
}
382+
}
383+
if (getSize() > PayloadSize) {
384+
// Multipayload enums that do use a separate tag
385+
// export XIs from that tag.
386+
unsigned tag = 0;
387+
auto TagSize = getSize() - PayloadSize;
388+
auto TagAddress = remote::RemoteAddress(address.getAddressData() + PayloadSize);
389+
if (!reader.readInteger(TagAddress, TagSize, &tag))
390+
return false;
391+
if (tag < Fields.size()) {
392+
*extraInhabitantIndex = -1; // Valid payload, not an XI
393+
} else if (TagSize >= 4) {
394+
// This is really just the 32-bit 2s-complement negation of `tag`, but
395+
// coded so as to ensure we cannot overflow or underflow.
396+
*extraInhabitantIndex = static_cast<int>(
397+
std::numeric_limits<uint32_t>::max()
398+
- (tag & std::numeric_limits<uint32_t>::max())
399+
+ 1);
400+
} else {
401+
// XIs are coded starting from the highest value that fits
402+
// E.g., for 1-byte tag, tag 255 == XI #0, tag 254 == XI #1, etc.
403+
unsigned maxTag = (1U << (TagSize * 8U)) - 1;
404+
*extraInhabitantIndex = maxTag - tag;
405+
}
406+
return true;
407+
} else {
408+
// Multipayload enums that don't use a separate tag never
409+
// export XIs.
410+
return false;
411+
}
412+
}
369413
}
370414
return false;
371415
}
@@ -1260,7 +1304,7 @@ class EnumTypeInfoBuilder {
12601304
Size += tagCounts.numTagBytes;
12611305
// Dynamic multi-payload enums use the tag representations not assigned
12621306
// to cases for extra inhabitants.
1263-
if (tagCounts.numTagBytes >= 32) {
1307+
if (tagCounts.numTagBytes >= 4) {
12641308
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
12651309
} else {
12661310
NumExtraInhabitants =

validation-test/Reflection/reflect_Enum_TwoCaseTwoPayloads.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,30 @@ reflect(object: ClassWithTwoCaseTwoPayloadsEnum())
269269
// CHECK-32: (case name=none index=1)))
270270
// CHECK-32: (case name=none index=1))))
271271

272+
reflect(enumValue: TwoCaseTwoPayloadsEnum.valid(Marker()))
273+
274+
// CHECK-64: Reflecting an enum value.
275+
// CHECK-64-NEXT: Type reference:
276+
// CHECK-64-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
277+
// CHECK-64-NEXT: Value: .valid(_)
278+
279+
// CHECK-32: Reflecting an enum value.
280+
// CHECK-32-NEXT: Type reference:
281+
// CHECK-32-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
282+
// CHECK-32-NEXT: Value: .valid(_)
283+
284+
reflect(enumValue: TwoCaseTwoPayloadsEnum.invalid(7))
285+
286+
// CHECK-64: Reflecting an enum value.
287+
// CHECK-64-NEXT: Type reference:
288+
// CHECK-64-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
289+
// CHECK-64-NEXT: Value: .invalid(_)
290+
291+
// CHECK-32: Reflecting an enum value.
292+
// CHECK-32-NEXT: Type reference:
293+
// CHECK-32-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
294+
// CHECK-32-NEXT: Value: .invalid(_)
295+
272296
doneReflecting()
273297

274298
// CHECK-64: Done.

validation-test/Reflection/reflect_Enum_value.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,107 @@ reflect(enumValue: OneIndirectPayload.leafF)
378378
// CHECK-NEXT: (enum reflect_Enum_value.OneIndirectPayload)
379379
// CHECK-NEXT: Value: .leafF
380380

381+
enum MultiPayload {
382+
case stampA
383+
case envelopeA(Int64)
384+
case stampB
385+
case envelopeB(Double)
386+
case stampC
387+
case envelopeC((Int32, Int32))
388+
case stampD
389+
case stampE
390+
}
391+
392+
enum SinglePayloadEnumWithMultiPayloadEnumPayload {
393+
case payloadA(MultiPayload)
394+
case alsoA
395+
case alsoB
396+
case alsoC
397+
case alsoD
398+
}
399+
400+
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.payloadA(.stampB))
401+
402+
// CHECK: Reflecting an enum value.
403+
// CHECK-NEXT: Type reference:
404+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload)
405+
// CHECK-NEXT: Value: .payloadA(.stampB)
406+
407+
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.payloadA(.envelopeC((1,2))))
408+
409+
// CHECK: Reflecting an enum value.
410+
// CHECK-NEXT: Type reference:
411+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload)
412+
// CHECK-NEXT: Value: .payloadA(.envelopeC(_))
413+
414+
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.alsoC)
415+
416+
// CHECK: Reflecting an enum value.
417+
// CHECK-NEXT: Type reference:
418+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload)
419+
// CHECK-NEXT: Value: .alsoC
420+
421+
reflect(enumValue: Optional<Optional<SinglePayloadEnumWithMultiPayloadEnumPayload>>.some(.none))
422+
423+
// CHECK: Reflecting an enum value.
424+
// CHECK-NEXT: Type reference:
425+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
426+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
427+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload)))
428+
// CHECK-NEXT: Value: .some(.none)
429+
430+
enum SmallMultiPayload {
431+
case stampA
432+
case envelopeA(Int8)
433+
case stampB
434+
case envelopeB(Int16)
435+
case stampC
436+
case envelopeC((UInt8, Int8))
437+
case stampD
438+
case stampE
439+
}
440+
441+
enum SinglePayloadEnumWithSmallMultiPayloadEnumPayload {
442+
case payloadA(SmallMultiPayload)
443+
case alsoA
444+
case alsoB
445+
case alsoC
446+
case alsoD
447+
}
448+
449+
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.payloadA(.stampB))
450+
451+
// CHECK: Reflecting an enum value.
452+
// CHECK-NEXT: Type reference:
453+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload)
454+
// CHECK-NEXT: Value: .payloadA(.stampB)
455+
456+
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.payloadA(.envelopeC((1,2))))
457+
458+
// CHECK: Reflecting an enum value.
459+
// CHECK-NEXT: Type reference:
460+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload)
461+
// CHECK-NEXT: Value: .payloadA(.envelopeC(_))
462+
463+
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.alsoC)
464+
465+
// CHECK: Reflecting an enum value.
466+
// CHECK-NEXT: Type reference:
467+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload)
468+
// CHECK-NEXT: Value: .alsoC
469+
470+
reflect(enumValue: Optional<Optional<SinglePayloadEnumWithSmallMultiPayloadEnumPayload>>.some(.none))
471+
472+
// CHECK: Reflecting an enum value.
473+
// CHECK-NEXT: Type reference:
474+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
475+
// CHECK-NEXT: (bound_generic_enum Swift.Optional
476+
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload)))
477+
// CHECK-NEXT: Value: .some(.none)
478+
479+
480+
// XXX TODO: Multipayload enums with pointer payloads
481+
381482

382483
// XXX TODO: test enum with thin function payload XXX
383484

validation-test/Reflection/reflect_Enum_values.swift

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,12 +1575,9 @@ reflect(enum: ManyCasesOneStringPayload.payload("hello, world"))
15751575
// CHECK32-NEXT: (case name=otherC index=3))
15761576

15771577
// CHECKALL: Enum value:
1578-
// CHECK64-NEXT: (enum_value name=payload index=0
1579-
// CHECK64-NEXT: (struct Swift.String)
1580-
// CHECK64-NEXT: )
1581-
1582-
// XXX Note: 32-bit String contains a multi_payload_enum which projectEnumValue cannot yet handle.
1583-
// CHECK32-NEXT: swift_reflection_projectEnumValue failed.
1578+
// CHECKALL-NEXT: (enum_value name=payload index=0
1579+
// CHECKALL-NEXT: (struct Swift.String)
1580+
// CHECKALL-NEXT: )
15841581

15851582
reflect(enum: ManyCasesOneStringPayload.otherB)
15861583

@@ -1646,12 +1643,7 @@ reflect(enum: ManyCasesOneStringPayload.otherB)
16461643

16471644

16481645
// CHECKALL: Enum value:
1649-
// CHECK64-NEXT: (enum_value name=otherB index=2)
1650-
1651-
// XXX Note: 32-bit String contains a multi_payload_enum which projectEnumValue cannot yet handle.
1652-
// CHECK32-NEXT: swift_reflection_projectEnumValue failed.
1653-
1654-
//reflect(enum: ManyCasesManyPayloads.a("hi, world"))
1646+
// CHECKALL-NEXT: (enum_value name=otherB index=2)
16551647

16561648
doneReflecting()
16571649

0 commit comments

Comments
 (0)