Skip to content

Commit b167eec

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 b167eec

20 files changed

+1107
-88
lines changed

include/swift/ABI/GenericContext.h

Lines changed: 166 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,26 @@ 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 suppressible protocol set used for the conditional conformances in a
308+
/// generic context.
309+
struct ConditionalSuppressibleProtocolSet: SuppressibleProtocolSet {
310+
using SuppressibleProtocolSet::SuppressibleProtocolSet;
311+
};
312+
313+
/// A generic requirement for describing a conditional conformance to a
314+
/// suppressible protocol.
315+
///
316+
/// This type is equivalent to a `TargetGenericRequirementDescriptor`, and
317+
/// differs only because it needs to occur alongside
318+
template<typename Runtime>
319+
struct TargetConditionalSuppressibleProtocolRequirement: TargetGenericRequirementDescriptor<Runtime> { };
320+
269321
/// An array of generic parameter descriptors, all
270322
/// GenericParamDescriptor::implicit(), which is by far
271323
/// the most common case. Some generic context storage can
@@ -306,7 +358,8 @@ class RuntimeGenericSignature {
306358

307359
public:
308360
RuntimeGenericSignature()
309-
: Header{0, 0, 0, 0}, Params(nullptr), Requirements(nullptr),
361+
: Header{0, 0, 0, GenericContextDescriptorFlags(false, false)},
362+
Params(nullptr), Requirements(nullptr),
310363
PackShapeHeader{0, 0}, PackShapeDescriptors(nullptr) {}
311364

312365
RuntimeGenericSignature(const TargetGenericContextDescriptorHeader<Runtime> &header,
@@ -425,20 +478,27 @@ class TrailingGenericContextObjects<TargetSelf<Runtime>,
425478
TargetGenericRequirementDescriptor<Runtime>,
426479
GenericPackShapeHeader,
427480
GenericPackShapeDescriptor,
481+
ConditionalSuppressibleProtocolSet,
482+
ConditionalSuppressibleProtocolsRequirementCount,
483+
TargetConditionalSuppressibleProtocolRequirement<Runtime>,
428484
FollowingTrailingObjects...>
429485
{
430486
protected:
431487
using Self = TargetSelf<Runtime>;
432488
using GenericContextHeaderType = TargetGenericContextHeaderType<Runtime>;
433489
using GenericRequirementDescriptor =
434490
TargetGenericRequirementDescriptor<Runtime>;
435-
491+
using GenericConditionalSuppressibleProtocolRequirement =
492+
TargetConditionalSuppressibleProtocolRequirement<Runtime>;
436493
using TrailingObjects = swift::ABI::TrailingObjects<Self,
437494
GenericContextHeaderType,
438495
GenericParamDescriptor,
439496
GenericRequirementDescriptor,
440497
GenericPackShapeHeader,
441498
GenericPackShapeDescriptor,
499+
ConditionalSuppressibleProtocolSet,
500+
ConditionalSuppressibleProtocolsRequirementCount,
501+
GenericConditionalSuppressibleProtocolRequirement,
442502
FollowingTrailingObjects...>;
443503
friend TrailingObjects;
444504

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

689+
size_t numTrailingObjects(
690+
OverloadToken<ConditionalSuppressibleProtocolSet>
691+
) const {
692+
return asSelf()->hasConditionalSuppressedProtocols() ? 1 : 0;
693+
}
694+
695+
unsigned getNumConditionalSuppressibleProtocolsRequirementCounts() const {
696+
if (!asSelf()->hasConditionalSuppressedProtocols())
697+
return 0;
698+
699+
return countBitsUsed(getConditionalSuppressedProtocols().rawBits());
700+
}
701+
702+
size_t numTrailingObjects(
703+
OverloadToken<ConditionalSuppressibleProtocolsRequirementCount>
704+
) const {
705+
return getNumConditionalSuppressibleProtocolsRequirementCounts();
706+
}
707+
708+
size_t numTrailingObjects(
709+
OverloadToken<GenericConditionalSuppressibleProtocolRequirement>
710+
) const {
711+
auto counts = getConditionalSuppressibleProtocolRequirementCounts();
712+
return counts.empty() ? 0 : counts.back().count;
713+
}
714+
552715
#if defined(_MSC_VER) && _MSC_VER < 1920
553716
#undef OverloadToken
554717
#endif

0 commit comments

Comments
 (0)