Skip to content

Commit 579a965

Browse files
committed
Metadata and runtime support for suppressible protocol requirements
Introduce metadata and runtime support for describing conformances to "suppressible" protocols such as `Copyable`. The metadata changes occur in several different places: * Context descriptors gain a flag bit to indicate when the type itself has suppressed one or more suppressible protocols (e.g., it is `~Copyable`). When the bit is set, the context will have a trailing `SuppressibleProtocolSet`, a 16-bit bitfield that records one bit for each suppressed protocol. Types with no suppressed conformances will leave the bit unset (so the metadata is unchanged), and older runtimes don't look at the bit, so they will ignore the extra data. * Generic context descriptors gain a flag bit to indicate when the type has conditional conformances to suppressible protocols. When set, there will be trailing metadata containing another `SuppressibleProtocolSet` (a subset of the one in the main context descriptor) indicating which suppressible protocols have conditional conformances, followed by the actual lists of generic requirements for each of the conditional conformances. Again, if there are no conditional conformances to suppressible protocols, the bit won't be set. Old runtimes ignore the bit and any trailing metadata. * Generic requirements get a new "kind", which provides an ignored protocol set (another `SuppressibleProtocolSet`) stating which suppressible protocols should *not* be checked for the subject type of the generic requirement. For example, this encodes a requirement like `T: ~Copyable`. These generic requirements can occur anywhere that there is a generic requirement list, e.g., conditional conformances and extended existentials. Older runtimes handle unknown generic requirement kinds by stating that the requirement isn't satisfied. Extend the runtime to perform checking of the suppressible conformances on generic arguments as part of checking generic requirements. This checking follows the defaults of the language, which is that every generic argument must conform to each of the suppressible protocols unless there is an explicit generic requirement that states which suppressible protocols to ignore. Thus, a generic parameter list `<T, Y where T: ~Escapable>` will check that `T` is `Copyable` but not that it is `Escapable`, and check that `U` is both `Copyable` and `Escapable`. To implement this, we collect the ignored protocol sets from these suppressed requirements while processing the generic requirements, then check all of the generic arguments against any conformances not suppressed. Answering the actual question "does `X` conform to `Copyable`?" (for any suppressible protocol) looks at the context descriptor metadata to answer the question, e.g., 1. If there is no "suppressed protocol set", then the type conforms. This covers types that haven't suppressed any conformances, including all types that predate noncopyable generics. 2. If the suppressed protocol set doesn't contain `Copyable`, then the type conforms. 3. If the type is generic and has a conditional conformance to `Copyable`, evaluate the generic requirements for that conditional conformance to answer whether it conforms. The procedure above handles the bits of a `SuppressibleProtocolSet` opaquely, with no mapping down to specific protocols. Therefore, the same implementation will work even with future suppressible protocols, including back deployment. The end result of this is that we can dynamically evaluate conditional conformances to protocols that depend on conformances to suppressible protocols. Implements rdar://123466649.
1 parent 223645c commit 579a965

File tree

19 files changed

+1091
-87
lines changed

19 files changed

+1091
-87
lines changed

include/swift/ABI/GenericContext.h

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/ABI/TargetLayout.h"
2222
#include "swift/ABI/MetadataValues.h"
2323
#include "swift/ABI/MetadataRef.h"
24+
#include "swift/ABI/SuppressibleProtocols.h"
2425
#include "swift/ABI/TrailingObjects.h"
2526
#include "swift/Demangling/Demangle.h"
2627

@@ -103,6 +104,10 @@ struct TargetGenericContextDescriptorHeader {
103104
bool hasArguments() const {
104105
return getNumArguments() > 0;
105106
}
107+
108+
bool hasConditionalSuppressedProtocols() const {
109+
return Flags.hasConditionalSuppressedProtocols();
110+
}
106111
};
107112
using GenericContextDescriptorHeader =
108113
TargetGenericContextDescriptorHeader<InProcess>;
@@ -137,6 +142,20 @@ class TargetGenericRequirementDescriptor {
137142
///
138143
/// Only valid if the requirement has Layout kind.
139144
GenericRequirementLayoutKind Layout;
145+
146+
/// The set of suppressible protocols whose check is suppressed, along
147+
/// with the index of the generic parameter being suppressed.
148+
///
149+
/// The index is technically redundant with the subject type, but its
150+
/// storage is effectively free because this union is 32 bits anyway. The
151+
/// index 0xFFFF is reserved for "not a generic parameter", in case we
152+
/// need to use that in the future.
153+
///
154+
/// Only valid if the requirement has SuppressedProtocols kind.
155+
struct {
156+
uint16_t GenericParamIndex;
157+
SuppressibleProtocolSet Protocols;
158+
} SuppressedProtocols;
140159
};
141160

142161
constexpr GenericRequirementFlags getFlags() const {
@@ -204,6 +223,18 @@ class TargetGenericRequirementDescriptor {
204223
return Layout;
205224
}
206225

226+
/// Retrieve the set of suppressed protocols.
227+
SuppressibleProtocolSet getSuppressedProtocols() const {
228+
assert(getKind() == GenericRequirementKind::SuppressedProtocols);
229+
return SuppressedProtocols.Protocols;
230+
}
231+
232+
/// Retrieve the suppressible protocol kind.
233+
uint16_t getSuppressedProtocolsGenericParamIndex() const {
234+
assert(getKind() == GenericRequirementKind::SuppressedProtocols);
235+
return SuppressedProtocols.GenericParamIndex;
236+
}
237+
207238
/// Determine whether this generic requirement has a known kind.
208239
///
209240
/// \returns \c false for any future generic requirement kinds.
@@ -215,6 +246,7 @@ class TargetGenericRequirementDescriptor {
215246
case GenericRequirementKind::SameConformance:
216247
case GenericRequirementKind::SameType:
217248
case GenericRequirementKind::SameShape:
249+
case GenericRequirementKind::SuppressedProtocols:
218250
return true;
219251
}
220252

@@ -266,6 +298,20 @@ struct GenericPackShapeDescriptor {
266298
uint16_t Unused;
267299
};
268300

301+
/// A count for the number of requirements for the number of requirements
302+
/// for a given conditional conformance to a suppressible protocols.
303+
struct ConditionalSuppressibleProtocolsRequirementCount {
304+
uint16_t count;
305+
};
306+
307+
/// A generic requirement for describing a conditional conformance to a
308+
/// suppressible protocol.
309+
///
310+
/// This type is equivalent to a `TargetGenericRequirementDescriptor`, and
311+
/// differs only because it needs to occur alongside
312+
template<typename Runtime>
313+
struct TargetConditionalSuppressibleProtocolRequirement: TargetGenericRequirementDescriptor<Runtime> { };
314+
269315
/// An array of generic parameter descriptors, all
270316
/// GenericParamDescriptor::implicit(), which is by far
271317
/// the most common case. Some generic context storage can
@@ -306,7 +352,8 @@ class RuntimeGenericSignature {
306352

307353
public:
308354
RuntimeGenericSignature()
309-
: Header{0, 0, 0, 0}, Params(nullptr), Requirements(nullptr),
355+
: Header{0, 0, 0, GenericContextDescriptorFlags(false, false)},
356+
Params(nullptr), Requirements(nullptr),
310357
PackShapeHeader{0, 0}, PackShapeDescriptors(nullptr) {}
311358

312359
RuntimeGenericSignature(const TargetGenericContextDescriptorHeader<Runtime> &header,
@@ -425,20 +472,27 @@ class TrailingGenericContextObjects<TargetSelf<Runtime>,
425472
TargetGenericRequirementDescriptor<Runtime>,
426473
GenericPackShapeHeader,
427474
GenericPackShapeDescriptor,
475+
SuppressibleProtocolSet,
476+
ConditionalSuppressibleProtocolsRequirementCount,
477+
TargetConditionalSuppressibleProtocolRequirement<Runtime>,
428478
FollowingTrailingObjects...>
429479
{
430480
protected:
431481
using Self = TargetSelf<Runtime>;
432482
using GenericContextHeaderType = TargetGenericContextHeaderType<Runtime>;
433483
using GenericRequirementDescriptor =
434484
TargetGenericRequirementDescriptor<Runtime>;
435-
485+
using GenericConditionalSuppressibleProtocolRequirement =
486+
TargetConditionalSuppressibleProtocolRequirement<Runtime>;
436487
using TrailingObjects = swift::ABI::TrailingObjects<Self,
437488
GenericContextHeaderType,
438489
GenericParamDescriptor,
439490
GenericRequirementDescriptor,
440491
GenericPackShapeHeader,
441492
GenericPackShapeDescriptor,
493+
SuppressibleProtocolSet,
494+
ConditionalSuppressibleProtocolsRequirementCount,
495+
GenericConditionalSuppressibleProtocolRequirement,
442496
FollowingTrailingObjects...>;
443497
friend TrailingObjects;
444498

@@ -467,7 +521,83 @@ class TrailingGenericContextObjects<TargetSelf<Runtime>,
467521
/// HeaderType ought to be convertible to GenericContextDescriptorHeader.
468522
return getFullGenericContextHeader();
469523
}
470-
524+
525+
bool hasConditionalSuppressedProtocols() const {
526+
if (!asSelf()->isGeneric())
527+
return false;
528+
529+
return getGenericContextHeader().hasConditionalSuppressedProtocols();
530+
}
531+
532+
const SuppressibleProtocolSet &
533+
getConditionalSuppressedProtocols() const {
534+
assert(hasConditionalSuppressedProtocols());
535+
return *this->template getTrailingObjects<SuppressibleProtocolSet>();
536+
}
537+
538+
/// Retrieve the counts for # of conditional suppressible protocols for each
539+
/// conditional conformance to a suppressible protocol.
540+
///
541+
/// The counts are cumulative, so the first entry in the array is the
542+
/// number of requirements for the first conditional conformance. The
543+
/// second entry in the array is the number of requirements in the first
544+
/// and second conditional conformances. The last entry is, therefore, the
545+
/// total count of requirements in the structure.
546+
llvm::ArrayRef<ConditionalSuppressibleProtocolsRequirementCount>
547+
getConditionalSuppressibleProtocolRequirementCounts() const {
548+
if (!asSelf()->hasConditionalSuppressedProtocols())
549+
return {};
550+
551+
return {
552+
this->template
553+
getTrailingObjects<ConditionalSuppressibleProtocolsRequirementCount>(),
554+
getNumConditionalSuppressibleProtocolsRequirementCounts()
555+
};
556+
}
557+
558+
/// Retrieve the array of requirements for conditional conformances to
559+
/// the ith conditional conformance to a suppressible protocol.
560+
llvm::ArrayRef<GenericConditionalSuppressibleProtocolRequirement>
561+
getConditionalSuppressibleProtocolRequirementsAt(unsigned i) const {
562+
auto counts = getConditionalSuppressibleProtocolRequirementCounts();
563+
assert(i < counts.size());
564+
565+
unsigned startIndex = (i == 0) ? 0 : counts[i-1].count;
566+
unsigned endIndex = counts[i].count;
567+
568+
auto basePtr =
569+
this->template
570+
getTrailingObjects<GenericConditionalSuppressibleProtocolRequirement>();
571+
return { basePtr + startIndex, basePtr + endIndex };
572+
}
573+
574+
/// Retrieve the array of requirements for conditional conformances to
575+
/// the ith conditional conformance to a suppressible protocol.
576+
llvm::ArrayRef<GenericConditionalSuppressibleProtocolRequirement>
577+
getConditionalSuppressibleProtocolRequirementsFor(
578+
SuppressibleProtocolKind kind
579+
) const {
580+
if (!asSelf()->hasConditionalSuppressedProtocols())
581+
return { };
582+
583+
auto conditionallySuppressed = getConditionalSuppressedProtocols();
584+
if (!conditionallySuppressed.contains(kind))
585+
return { };
586+
587+
// Count the number of "set" bits up to (but not including) the
588+
// bit we're looking at.
589+
unsigned targetBit = static_cast<uint8_t>(kind);
590+
auto suppressedBits = conditionallySuppressed.rawBits();
591+
unsigned priorBits = 0;
592+
for (unsigned i = 0; i != targetBit; ++i) {
593+
if (suppressedBits & 0x01)
594+
++priorBits;
595+
suppressedBits = suppressedBits >> 1;
596+
}
597+
598+
return getConditionalSuppressibleProtocolRequirementsAt(priorBits);
599+
}
600+
471601
const TargetGenericContext<Runtime> *getGenericContext() const {
472602
if (!asSelf()->isGeneric())
473603
return nullptr;
@@ -549,6 +679,30 @@ class TrailingGenericContextObjects<TargetSelf<Runtime>,
549679
return getGenericPackShapeHeader().NumPacks;
550680
}
551681

682+
size_t numTrailingObjects(OverloadToken<SuppressibleProtocolSet>) const {
683+
return asSelf()->hasConditionalSuppressedProtocols() ? 1 : 0;
684+
}
685+
686+
unsigned getNumConditionalSuppressibleProtocolsRequirementCounts() const {
687+
if (!asSelf()->hasConditionalSuppressedProtocols())
688+
return 0;
689+
690+
return countBitsUsed(getConditionalSuppressedProtocols().rawBits());
691+
}
692+
693+
size_t numTrailingObjects(
694+
OverloadToken<ConditionalSuppressibleProtocolsRequirementCount>
695+
) const {
696+
return getNumConditionalSuppressibleProtocolsRequirementCounts();
697+
}
698+
699+
size_t numTrailingObjects(
700+
OverloadToken<GenericConditionalSuppressibleProtocolRequirement>
701+
) const {
702+
auto counts = getConditionalSuppressibleProtocolRequirementCounts();
703+
return counts.empty() ? 0 : counts.back().count;
704+
}
705+
552706
#if defined(_MSC_VER) && _MSC_VER < 1920
553707
#undef OverloadToken
554708
#endif

0 commit comments

Comments
 (0)