mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #76734 from jckarter/atomic-layout-runtime
Runtime: Set 'is not bitwise borrowable' bit for raw layout types.
This commit is contained in:
@@ -2275,6 +2275,24 @@ public:
|
|||||||
if (!hasGeneralizationSignature()) return 0;
|
if (!hasGeneralizationSignature()) return 0;
|
||||||
return getGenSigHeader()->getArgumentLayoutSizeInWords();
|
return getGenSigHeader()->getArgumentLayoutSizeInWords();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isCopyable() const {
|
||||||
|
if (!hasGeneralizationSignature()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto *reqts = getGenSigRequirements();
|
||||||
|
for (unsigned i = 0, e = getNumGenSigRequirements(); i < e; ++i) {
|
||||||
|
auto &reqt = reqts[i];
|
||||||
|
if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (reqt.getInvertedProtocols()
|
||||||
|
.contains(InvertibleProtocolKind::Copyable)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
using ExtendedExistentialTypeShape
|
using ExtendedExistentialTypeShape
|
||||||
= TargetExtendedExistentialTypeShape<InProcess>;
|
= TargetExtendedExistentialTypeShape<InProcess>;
|
||||||
|
|||||||
@@ -1558,6 +1558,12 @@ enum class RawLayoutFlags : uintptr_t {
|
|||||||
|
|
||||||
/// Whether or not this raw layout type was declared 'movesAsLike'.
|
/// Whether or not this raw layout type was declared 'movesAsLike'.
|
||||||
MovesAsLike = 0x2,
|
MovesAsLike = 0x2,
|
||||||
|
|
||||||
|
/// Whether this raw layout type is bitwise borrowable.
|
||||||
|
///
|
||||||
|
/// No raw layout types are yet, but should we change our mind about that in the future,
|
||||||
|
/// this flag is here.
|
||||||
|
BitwiseBorrowable = 0x4,
|
||||||
};
|
};
|
||||||
static inline RawLayoutFlags operator|(RawLayoutFlags lhs,
|
static inline RawLayoutFlags operator|(RawLayoutFlags lhs,
|
||||||
RawLayoutFlags rhs) {
|
RawLayoutFlags rhs) {
|
||||||
@@ -1573,6 +1579,9 @@ static inline bool isRawLayoutArray(RawLayoutFlags flags) {
|
|||||||
static inline bool shouldRawLayoutMoveAsLike(RawLayoutFlags flags) {
|
static inline bool shouldRawLayoutMoveAsLike(RawLayoutFlags flags) {
|
||||||
return uintptr_t(flags) & uintptr_t(RawLayoutFlags::MovesAsLike);
|
return uintptr_t(flags) & uintptr_t(RawLayoutFlags::MovesAsLike);
|
||||||
}
|
}
|
||||||
|
static inline bool isRawLayoutBitwiseBorrowable(RawLayoutFlags flags) {
|
||||||
|
return uintptr_t(flags) & uintptr_t(RawLayoutFlags::BitwiseBorrowable);
|
||||||
|
}
|
||||||
|
|
||||||
namespace SpecialPointerAuthDiscriminators {
|
namespace SpecialPointerAuthDiscriminators {
|
||||||
// All of these values are the stable string hash of the corresponding
|
// All of these values are the stable string hash of the corresponding
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
|
|||||||
const TypeLayout * const *payloadLayouts) {
|
const TypeLayout * const *payloadLayouts) {
|
||||||
// Accumulate the layout requirements of the payloads.
|
// Accumulate the layout requirements of the payloads.
|
||||||
size_t payloadSize = 0, alignMask = 0;
|
size_t payloadSize = 0, alignMask = 0;
|
||||||
bool isPOD = true, isBT = true;
|
bool isPOD = true, isBT = true, isBB = true;
|
||||||
for (unsigned i = 0; i < numPayloads; ++i) {
|
for (unsigned i = 0; i < numPayloads; ++i) {
|
||||||
const TypeLayout *payloadLayout = payloadLayouts[i];
|
const TypeLayout *payloadLayout = payloadLayouts[i];
|
||||||
payloadSize
|
payloadSize
|
||||||
@@ -390,6 +390,7 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
|
|||||||
alignMask |= payloadLayout->flags.getAlignmentMask();
|
alignMask |= payloadLayout->flags.getAlignmentMask();
|
||||||
isPOD &= payloadLayout->flags.isPOD();
|
isPOD &= payloadLayout->flags.isPOD();
|
||||||
isBT &= payloadLayout->flags.isBitwiseTakable();
|
isBT &= payloadLayout->flags.isBitwiseTakable();
|
||||||
|
isBB &= payloadLayout->flags.isBitwiseBorrowable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the max payload size in the metadata.
|
// Store the max payload size in the metadata.
|
||||||
@@ -418,6 +419,7 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
|
|||||||
.withAlignmentMask(alignMask)
|
.withAlignmentMask(alignMask)
|
||||||
.withPOD(isPOD)
|
.withPOD(isPOD)
|
||||||
.withBitwiseTakable(isBT)
|
.withBitwiseTakable(isBT)
|
||||||
|
.withBitwiseBorrowable(isBB)
|
||||||
.withEnumWitnesses(true)
|
.withEnumWitnesses(true)
|
||||||
.withInlineStorage(ValueWitnessTable::isValueInline(
|
.withInlineStorage(ValueWitnessTable::isValueInline(
|
||||||
isBT, totalSize, alignMask + 1)),
|
isBT, totalSize, alignMask + 1)),
|
||||||
|
|||||||
@@ -2142,6 +2142,7 @@ static void performBasicLayout(TypeLayout &layout,
|
|||||||
size_t alignMask = layout.flags.getAlignmentMask();
|
size_t alignMask = layout.flags.getAlignmentMask();
|
||||||
bool isPOD = layout.flags.isPOD();
|
bool isPOD = layout.flags.isPOD();
|
||||||
bool isBitwiseTakable = layout.flags.isBitwiseTakable();
|
bool isBitwiseTakable = layout.flags.isBitwiseTakable();
|
||||||
|
bool isBitwiseBorrowable = layout.flags.isBitwiseBorrowable();
|
||||||
for (unsigned i = 0; i != numElements; ++i) {
|
for (unsigned i = 0; i != numElements; ++i) {
|
||||||
auto &elt = elements[i];
|
auto &elt = elements[i];
|
||||||
|
|
||||||
@@ -2157,6 +2158,7 @@ static void performBasicLayout(TypeLayout &layout,
|
|||||||
alignMask = std::max(alignMask, eltLayout->flags.getAlignmentMask());
|
alignMask = std::max(alignMask, eltLayout->flags.getAlignmentMask());
|
||||||
if (!eltLayout->flags.isPOD()) isPOD = false;
|
if (!eltLayout->flags.isPOD()) isPOD = false;
|
||||||
if (!eltLayout->flags.isBitwiseTakable()) isBitwiseTakable = false;
|
if (!eltLayout->flags.isBitwiseTakable()) isBitwiseTakable = false;
|
||||||
|
if (!eltLayout->flags.isBitwiseBorrowable()) isBitwiseBorrowable = false;
|
||||||
}
|
}
|
||||||
bool isInline =
|
bool isInline =
|
||||||
ValueWitnessTable::isValueInline(isBitwiseTakable, size, alignMask + 1);
|
ValueWitnessTable::isValueInline(isBitwiseTakable, size, alignMask + 1);
|
||||||
@@ -2166,6 +2168,7 @@ static void performBasicLayout(TypeLayout &layout,
|
|||||||
.withAlignmentMask(alignMask)
|
.withAlignmentMask(alignMask)
|
||||||
.withPOD(isPOD)
|
.withPOD(isPOD)
|
||||||
.withBitwiseTakable(isBitwiseTakable)
|
.withBitwiseTakable(isBitwiseTakable)
|
||||||
|
.withBitwiseBorrowable(isBitwiseBorrowable)
|
||||||
.withInlineStorage(isInline);
|
.withInlineStorage(isInline);
|
||||||
layout.extraInhabitantCount = 0;
|
layout.extraInhabitantCount = 0;
|
||||||
layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask));
|
layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask));
|
||||||
@@ -3050,7 +3053,9 @@ void swift::swift_initRawStructMetadata(StructMetadata *structType,
|
|||||||
vwtable->stride = stride;
|
vwtable->stride = stride;
|
||||||
vwtable->flags = ValueWitnessFlags()
|
vwtable->flags = ValueWitnessFlags()
|
||||||
.withAlignmentMask(alignMask)
|
.withAlignmentMask(alignMask)
|
||||||
.withCopyable(false);
|
.withCopyable(false)
|
||||||
|
.withBitwiseTakable(true)
|
||||||
|
.withBitwiseBorrowable(false);
|
||||||
vwtable->extraInhabitantCount = extraInhabitantCount;
|
vwtable->extraInhabitantCount = extraInhabitantCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3090,6 +3095,9 @@ void swift::swift_initRawStructMetadata2(StructMetadata *structType,
|
|||||||
vwtable->flags = vwtable->flags
|
vwtable->flags = vwtable->flags
|
||||||
.withBitwiseTakable(likeTypeLayout->flags.isBitwiseTakable());
|
.withBitwiseTakable(likeTypeLayout->flags.isBitwiseTakable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vwtable->flags = vwtable->flags
|
||||||
|
.withBitwiseBorrowable(isRawLayoutBitwiseBorrowable(rawLayoutFlags));
|
||||||
|
|
||||||
// If the calculated size of this raw layout type is available to be put in
|
// If the calculated size of this raw layout type is available to be put in
|
||||||
// value buffers, then set the inline storage bit if our like type is also
|
// value buffers, then set the inline storage bit if our like type is also
|
||||||
@@ -4454,27 +4462,44 @@ public:
|
|||||||
|
|
||||||
class OpaqueExistentialValueWitnessTableCacheEntry {
|
class OpaqueExistentialValueWitnessTableCacheEntry {
|
||||||
public:
|
public:
|
||||||
|
struct Key {
|
||||||
|
unsigned numWitnessTables : 31;
|
||||||
|
unsigned copyable : 1;
|
||||||
|
|
||||||
|
bool operator==(struct Key k) {
|
||||||
|
return k.numWitnessTables == numWitnessTables
|
||||||
|
&& k.copyable == copyable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ValueWitnessTable Data;
|
ValueWitnessTable Data;
|
||||||
|
|
||||||
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numTables);
|
OpaqueExistentialValueWitnessTableCacheEntry(Key key);
|
||||||
|
|
||||||
unsigned getNumWitnessTables() const {
|
unsigned getNumWitnessTables() const {
|
||||||
return (Data.size - sizeof(OpaqueExistentialContainer))
|
return (Data.size - sizeof(OpaqueExistentialContainer))
|
||||||
/ sizeof(const WitnessTable *);
|
/ sizeof(const WitnessTable *);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isCopyable() const {
|
||||||
|
return Data.flags.isCopyable();
|
||||||
|
}
|
||||||
|
|
||||||
intptr_t getKeyIntValueForDump() {
|
intptr_t getKeyIntValueForDump() {
|
||||||
return getNumWitnessTables();
|
return getNumWitnessTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); }
|
bool matchesKey(Key key) const {
|
||||||
|
return key == Key{getNumWitnessTables(), isCopyable()};
|
||||||
|
}
|
||||||
|
|
||||||
friend llvm::hash_code
|
friend llvm::hash_code
|
||||||
hash_value(const OpaqueExistentialValueWitnessTableCacheEntry &value) {
|
hash_value(const OpaqueExistentialValueWitnessTableCacheEntry &value) {
|
||||||
return llvm::hash_value(value.getNumWitnessTables());
|
return llvm::hash_value(
|
||||||
|
std::make_pair(value.getNumWitnessTables(), value.isCopyable()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t getExtraAllocationSize(unsigned numTables) {
|
static size_t getExtraAllocationSize(Key key) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t getExtraAllocationSize() const {
|
size_t getExtraAllocationSize() const {
|
||||||
@@ -4562,19 +4587,24 @@ OpaqueExistentialValueWitnessTables;
|
|||||||
/// Instantiate a value witness table for an opaque existential container with
|
/// Instantiate a value witness table for an opaque existential container with
|
||||||
/// the given number of witness table pointers.
|
/// the given number of witness table pointers.
|
||||||
static const ValueWitnessTable *
|
static const ValueWitnessTable *
|
||||||
getOpaqueExistentialValueWitnesses(unsigned numWitnessTables) {
|
getOpaqueExistentialValueWitnesses(unsigned numWitnessTables,
|
||||||
|
bool copyable) {
|
||||||
// We pre-allocate a couple of important cases.
|
// We pre-allocate a couple of important cases.
|
||||||
if (numWitnessTables == 0)
|
if (copyable) {
|
||||||
return &OpaqueExistentialValueWitnesses_0;
|
if (numWitnessTables == 0)
|
||||||
if (numWitnessTables == 1)
|
return &OpaqueExistentialValueWitnesses_0;
|
||||||
return &OpaqueExistentialValueWitnesses_1;
|
if (numWitnessTables == 1)
|
||||||
|
return &OpaqueExistentialValueWitnesses_1;
|
||||||
|
}
|
||||||
|
|
||||||
return &OpaqueExistentialValueWitnessTables.getOrInsert(numWitnessTables)
|
return &OpaqueExistentialValueWitnessTables
|
||||||
.first->Data;
|
.getOrInsert(OpaqueExistentialValueWitnessTableCacheEntry::Key{
|
||||||
|
numWitnessTables, copyable})
|
||||||
|
.first->Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpaqueExistentialValueWitnessTableCacheEntry::
|
OpaqueExistentialValueWitnessTableCacheEntry::
|
||||||
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
|
OpaqueExistentialValueWitnessTableCacheEntry(Key key) {
|
||||||
using Box = NonFixedOpaqueExistentialBox;
|
using Box = NonFixedOpaqueExistentialBox;
|
||||||
using Witnesses = NonFixedValueWitnesses<Box, /*known allocated*/ true>;
|
using Witnesses = NonFixedValueWitnesses<Box, /*known allocated*/ true>;
|
||||||
|
|
||||||
@@ -4584,16 +4614,24 @@ OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
|
|||||||
#define DATA_VALUE_WITNESS(LOWER_ID, UPPER_ID, TYPE)
|
#define DATA_VALUE_WITNESS(LOWER_ID, UPPER_ID, TYPE)
|
||||||
#include "swift/ABI/ValueWitness.def"
|
#include "swift/ABI/ValueWitness.def"
|
||||||
|
|
||||||
Data.size = Box::Container::getSize(numWitnessTables);
|
Data.size = Box::Container::getSize(key.numWitnessTables);
|
||||||
Data.flags = ValueWitnessFlags()
|
Data.flags = ValueWitnessFlags()
|
||||||
.withAlignment(Box::Container::getAlignment(numWitnessTables))
|
.withAlignment(Box::Container::getAlignment(key.numWitnessTables))
|
||||||
.withPOD(false)
|
.withPOD(false)
|
||||||
.withBitwiseTakable(true)
|
.withBitwiseTakable(true)
|
||||||
.withInlineStorage(false);
|
.withInlineStorage(false)
|
||||||
|
.withCopyable(key.copyable)
|
||||||
|
// Non-bitwise-takable values are always stored out-of-line in existentials,
|
||||||
|
// so the existential representation itself is always bitwise-takable.
|
||||||
|
// Noncopyable values however can be bitwise-takable without being
|
||||||
|
// bitwise-borrowable, so noncopyable existentials are not bitwise-borrowable
|
||||||
|
// in the general case.
|
||||||
|
.withBitwiseBorrowable(key.copyable);
|
||||||
Data.extraInhabitantCount = Witnesses::numExtraInhabitants;
|
Data.extraInhabitantCount = Witnesses::numExtraInhabitants;
|
||||||
Data.stride = Box::Container::getStride(numWitnessTables);
|
Data.stride = Box::Container::getStride(key.numWitnessTables);
|
||||||
|
|
||||||
assert(getNumWitnessTables() == numWitnessTables);
|
assert(getNumWitnessTables() == key.numWitnessTables);
|
||||||
|
assert(isCopyable() == key.copyable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ValueWitnessTable ClassExistentialValueWitnesses_1 =
|
static const ValueWitnessTable ClassExistentialValueWitnesses_1 =
|
||||||
@@ -4662,7 +4700,8 @@ static const ValueWitnessTable *
|
|||||||
getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
|
getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
|
||||||
const Metadata *superclassConstraint,
|
const Metadata *superclassConstraint,
|
||||||
unsigned numWitnessTables,
|
unsigned numWitnessTables,
|
||||||
SpecialProtocol special) {
|
SpecialProtocol special,
|
||||||
|
bool copyable) {
|
||||||
// Use special representation for special protocols.
|
// Use special representation for special protocols.
|
||||||
switch (special) {
|
switch (special) {
|
||||||
case SpecialProtocol::Error:
|
case SpecialProtocol::Error:
|
||||||
@@ -4685,7 +4724,7 @@ getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
|
|||||||
numWitnessTables);
|
numWitnessTables);
|
||||||
case ProtocolClassConstraint::Any:
|
case ProtocolClassConstraint::Any:
|
||||||
assert(superclassConstraint == nullptr);
|
assert(superclassConstraint == nullptr);
|
||||||
return getOpaqueExistentialValueWitnesses(numWitnessTables);
|
return getOpaqueExistentialValueWitnesses(numWitnessTables, copyable);
|
||||||
}
|
}
|
||||||
|
|
||||||
swift_unreachable("Unhandled ProtocolClassConstraint in switch.");
|
swift_unreachable("Unhandled ProtocolClassConstraint in switch.");
|
||||||
@@ -4928,7 +4967,8 @@ ExistentialCacheEntry::ExistentialCacheEntry(Key key) {
|
|||||||
Data.ValueWitnesses = getExistentialValueWitnesses(key.ClassConstraint,
|
Data.ValueWitnesses = getExistentialValueWitnesses(key.ClassConstraint,
|
||||||
key.SuperclassConstraint,
|
key.SuperclassConstraint,
|
||||||
numWitnessTables,
|
numWitnessTables,
|
||||||
special);
|
special,
|
||||||
|
/*copyable*/ true);
|
||||||
Data.Flags = ExistentialTypeFlags()
|
Data.Flags = ExistentialTypeFlags()
|
||||||
.withNumWitnessTables(numWitnessTables)
|
.withNumWitnessTables(numWitnessTables)
|
||||||
.withClassConstraint(key.ClassConstraint)
|
.withClassConstraint(key.ClassConstraint)
|
||||||
@@ -5164,6 +5204,7 @@ public:
|
|||||||
const ValueWitnessTable *
|
const ValueWitnessTable *
|
||||||
ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
|
ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
|
||||||
auto shape = key.Shape;
|
auto shape = key.Shape;
|
||||||
|
bool copyable = shape->isCopyable();
|
||||||
|
|
||||||
if (auto witnesses = shape->getSuggestedValueWitnesses())
|
if (auto witnesses = shape->getSuggestedValueWitnesses())
|
||||||
return witnesses;
|
return witnesses;
|
||||||
@@ -5202,7 +5243,8 @@ ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
|
|||||||
return getExistentialValueWitnesses(ProtocolClassConstraint::Any,
|
return getExistentialValueWitnesses(ProtocolClassConstraint::Any,
|
||||||
/*superclass*/ nullptr,
|
/*superclass*/ nullptr,
|
||||||
wtableStorageSizeInWords,
|
wtableStorageSizeInWords,
|
||||||
SpecialProtocol::None);
|
SpecialProtocol::None,
|
||||||
|
copyable);
|
||||||
|
|
||||||
case SpecialKind::ExplicitLayout:
|
case SpecialKind::ExplicitLayout:
|
||||||
swift_unreachable("shape with explicit layout but no suggested VWT");
|
swift_unreachable("shape with explicit layout but no suggested VWT");
|
||||||
@@ -5214,7 +5256,8 @@ ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
|
|||||||
return getExistentialValueWitnesses(ProtocolClassConstraint::Class,
|
return getExistentialValueWitnesses(ProtocolClassConstraint::Class,
|
||||||
/*superclass*/ nullptr,
|
/*superclass*/ nullptr,
|
||||||
wtableStorageSizeInWords,
|
wtableStorageSizeInWords,
|
||||||
SpecialProtocol::None);
|
SpecialProtocol::None,
|
||||||
|
/*copyable*/ true);
|
||||||
|
|
||||||
case SpecialKind::Metatype:
|
case SpecialKind::Metatype:
|
||||||
// Existential metatypes don't store type metadata.
|
// Existential metatypes don't store type metadata.
|
||||||
|
|||||||
42
test/Runtime/bitwise-borrowable-generics.swift
Normal file
42
test/Runtime/bitwise-borrowable-generics.swift
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// RUN: %target-run-simple-swift(-enable-experimental-feature RawLayout)
|
||||||
|
// REQUIRES: executable_test
|
||||||
|
// UNSUPPORTED: use_os_stdlib
|
||||||
|
// UNSUPPORTED: back_deployment_runtime
|
||||||
|
|
||||||
|
import Synchronization
|
||||||
|
|
||||||
|
struct NC<T>: ~Copyable { var x: T }
|
||||||
|
|
||||||
|
@_rawLayout(like: T, movesAsLike)
|
||||||
|
struct Test<T>: ~Copyable {}
|
||||||
|
|
||||||
|
func isBitwiseBorrowable(_ type: any (~Copyable.Type)) -> Bool {
|
||||||
|
let metadataPtr = unsafeBitCast(type,
|
||||||
|
to: UnsafePointer<UnsafePointer<UInt>>.self)
|
||||||
|
let flags = metadataPtr[-1][10]
|
||||||
|
return flags & 0x0110_0000 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func test(_ type: any (~Copyable.Type)) {
|
||||||
|
print("\(isBitwiseBorrowable(type))")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol P: ~Copyable {}
|
||||||
|
|
||||||
|
// CHECK: begin
|
||||||
|
print("begin")
|
||||||
|
|
||||||
|
// CHECK-NEXT: true
|
||||||
|
test(Int.self)
|
||||||
|
// CHECK-NEXT: true
|
||||||
|
test(Any.self)
|
||||||
|
// CHECK-NEXT: true
|
||||||
|
test(P.self)
|
||||||
|
// CHECK-NEXT: true
|
||||||
|
test(NC<Int>.self)
|
||||||
|
// CHECK-NEXT: true
|
||||||
|
test(NC<Any>.self)
|
||||||
|
// CHECK-NEXT: false
|
||||||
|
test(Test<Int>.self)
|
||||||
|
// CHECK-NEXT: false
|
||||||
|
test(Test<Any>.self)
|
||||||
Reference in New Issue
Block a user