diff --git a/include/swift/ABI/GenericContext.h b/include/swift/ABI/GenericContext.h index f7700bd5475f7..1231b72350d87 100644 --- a/include/swift/ABI/GenericContext.h +++ b/include/swift/ABI/GenericContext.h @@ -21,6 +21,7 @@ #include "swift/ABI/TargetLayout.h" #include "swift/ABI/MetadataValues.h" #include "swift/ABI/MetadataRef.h" +#include "swift/ABI/SuppressibleProtocols.h" #include "swift/ABI/TrailingObjects.h" #include "swift/Demangling/Demangle.h" @@ -103,6 +104,10 @@ struct TargetGenericContextDescriptorHeader { bool hasArguments() const { return getNumArguments() > 0; } + + bool hasConditionalSuppressedProtocols() const { + return Flags.hasConditionalSuppressedProtocols(); + } }; using GenericContextDescriptorHeader = TargetGenericContextDescriptorHeader; @@ -137,6 +142,20 @@ class TargetGenericRequirementDescriptor { /// /// Only valid if the requirement has Layout kind. GenericRequirementLayoutKind Layout; + + /// The set of suppressible protocols whose check is suppressed, along + /// with the index of the generic parameter being suppressed. + /// + /// The index is technically redundant with the subject type, but its + /// storage is effectively free because this union is 32 bits anyway. The + /// index 0xFFFF is reserved for "not a generic parameter", in case we + /// need to use that in the future. + /// + /// Only valid if the requirement has SuppressedProtocols kind. + struct { + uint16_t GenericParamIndex; + SuppressibleProtocolSet Protocols; + } SuppressedProtocols; }; constexpr GenericRequirementFlags getFlags() const { @@ -204,6 +223,18 @@ class TargetGenericRequirementDescriptor { return Layout; } + /// Retrieve the set of suppressed protocols. + SuppressibleProtocolSet getSuppressedProtocols() const { + assert(getKind() == GenericRequirementKind::SuppressedProtocols); + return SuppressedProtocols.Protocols; + } + + /// Retrieve the suppressible protocol kind. + uint16_t getSuppressedProtocolsGenericParamIndex() const { + assert(getKind() == GenericRequirementKind::SuppressedProtocols); + return SuppressedProtocols.GenericParamIndex; + } + /// Determine whether this generic requirement has a known kind. /// /// \returns \c false for any future generic requirement kinds. @@ -215,6 +246,7 @@ class TargetGenericRequirementDescriptor { case GenericRequirementKind::SameConformance: case GenericRequirementKind::SameType: case GenericRequirementKind::SameShape: + case GenericRequirementKind::SuppressedProtocols: return true; } @@ -266,6 +298,26 @@ struct GenericPackShapeDescriptor { uint16_t Unused; }; +/// A count for the number of requirements for the number of requirements +/// for a given conditional conformance to a suppressible protocols. +struct ConditionalSuppressibleProtocolsRequirementCount { + uint16_t count; +}; + +/// A suppressible protocol set used for the conditional conformances in a +/// generic context. +struct ConditionalSuppressibleProtocolSet: SuppressibleProtocolSet { + using SuppressibleProtocolSet::SuppressibleProtocolSet; +}; + +/// A generic requirement for describing a conditional conformance to a +/// suppressible protocol. +/// +/// This type is equivalent to a `TargetGenericRequirementDescriptor`, and +/// differs only because it needs to occur alongside +template +struct TargetConditionalSuppressibleProtocolRequirement: TargetGenericRequirementDescriptor { }; + /// An array of generic parameter descriptors, all /// GenericParamDescriptor::implicit(), which is by far /// the most common case. Some generic context storage can @@ -306,7 +358,8 @@ class RuntimeGenericSignature { public: RuntimeGenericSignature() - : Header{0, 0, 0, 0}, Params(nullptr), Requirements(nullptr), + : Header{0, 0, 0, GenericContextDescriptorFlags(false, false)}, + Params(nullptr), Requirements(nullptr), PackShapeHeader{0, 0}, PackShapeDescriptors(nullptr) {} RuntimeGenericSignature(const TargetGenericContextDescriptorHeader &header, @@ -425,6 +478,9 @@ class TrailingGenericContextObjects, TargetGenericRequirementDescriptor, GenericPackShapeHeader, GenericPackShapeDescriptor, + ConditionalSuppressibleProtocolSet, + ConditionalSuppressibleProtocolsRequirementCount, + TargetConditionalSuppressibleProtocolRequirement, FollowingTrailingObjects...> { protected: @@ -432,13 +488,17 @@ class TrailingGenericContextObjects, using GenericContextHeaderType = TargetGenericContextHeaderType; using GenericRequirementDescriptor = TargetGenericRequirementDescriptor; - + using GenericConditionalSuppressibleProtocolRequirement = + TargetConditionalSuppressibleProtocolRequirement; using TrailingObjects = swift::ABI::TrailingObjects; friend TrailingObjects; @@ -467,7 +527,84 @@ class TrailingGenericContextObjects, /// HeaderType ought to be convertible to GenericContextDescriptorHeader. return getFullGenericContextHeader(); } - + + bool hasConditionalSuppressedProtocols() const { + if (!asSelf()->isGeneric()) + return false; + + return getGenericContextHeader().hasConditionalSuppressedProtocols(); + } + + const SuppressibleProtocolSet & + getConditionalSuppressedProtocols() const { + assert(hasConditionalSuppressedProtocols()); + return *this->template + getTrailingObjects(); + } + + /// Retrieve the counts for # of conditional suppressible protocols for each + /// conditional conformance to a suppressible protocol. + /// + /// The counts are cumulative, so the first entry in the array is the + /// number of requirements for the first conditional conformance. The + /// second entry in the array is the number of requirements in the first + /// and second conditional conformances. The last entry is, therefore, the + /// total count of requirements in the structure. + llvm::ArrayRef + getConditionalSuppressibleProtocolRequirementCounts() const { + if (!asSelf()->hasConditionalSuppressedProtocols()) + return {}; + + return { + this->template + getTrailingObjects(), + getNumConditionalSuppressibleProtocolsRequirementCounts() + }; + } + + /// Retrieve the array of requirements for conditional conformances to + /// the ith conditional conformance to a suppressible protocol. + llvm::ArrayRef + getConditionalSuppressibleProtocolRequirementsAt(unsigned i) const { + auto counts = getConditionalSuppressibleProtocolRequirementCounts(); + assert(i < counts.size()); + + unsigned startIndex = (i == 0) ? 0 : counts[i-1].count; + unsigned endIndex = counts[i].count; + + auto basePtr = + this->template + getTrailingObjects(); + return { basePtr + startIndex, basePtr + endIndex }; + } + + /// Retrieve the array of requirements for conditional conformances to + /// the ith conditional conformance to a suppressible protocol. + llvm::ArrayRef + getConditionalSuppressibleProtocolRequirementsFor( + SuppressibleProtocolKind kind + ) const { + if (!asSelf()->hasConditionalSuppressedProtocols()) + return { }; + + auto conditionallySuppressed = getConditionalSuppressedProtocols(); + if (!conditionallySuppressed.contains(kind)) + return { }; + + // Count the number of "set" bits up to (but not including) the + // bit we're looking at. + unsigned targetBit = static_cast(kind); + auto suppressedBits = conditionallySuppressed.rawBits(); + unsigned priorBits = 0; + for (unsigned i = 0; i != targetBit; ++i) { + if (suppressedBits & 0x01) + ++priorBits; + suppressedBits = suppressedBits >> 1; + } + + return getConditionalSuppressibleProtocolRequirementsAt(priorBits); + } + const TargetGenericContext *getGenericContext() const { if (!asSelf()->isGeneric()) return nullptr; @@ -549,6 +686,32 @@ class TrailingGenericContextObjects, return getGenericPackShapeHeader().NumPacks; } + size_t numTrailingObjects( + OverloadToken + ) const { + return asSelf()->hasConditionalSuppressedProtocols() ? 1 : 0; + } + + unsigned getNumConditionalSuppressibleProtocolsRequirementCounts() const { + if (!asSelf()->hasConditionalSuppressedProtocols()) + return 0; + + return countBitsUsed(getConditionalSuppressedProtocols().rawBits()); + } + + size_t numTrailingObjects( + OverloadToken + ) const { + return getNumConditionalSuppressibleProtocolsRequirementCounts(); + } + + size_t numTrailingObjects( + OverloadToken + ) const { + auto counts = getConditionalSuppressibleProtocolRequirementCounts(); + return counts.empty() ? 0 : counts.back().count; + } + #if defined(_MSC_VER) && _MSC_VER < 1920 #undef OverloadToken #endif diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 067859621b6d8..ef2e390207b0b 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2928,6 +2928,9 @@ struct swift_ptrauth_struct_context_descriptor(ContextDescriptor) bool isGeneric() const { return Flags.isGeneric(); } bool isUnique() const { return Flags.isUnique(); } + bool hasSuppressibleProtocols() const { + return Flags.hasSuppressibleProtocols(); + } ContextDescriptorKind getKind() const { return Flags.getKind(); } /// Get the generic context information for this context, or null if the @@ -2937,6 +2940,15 @@ struct swift_ptrauth_struct_context_descriptor(ContextDescriptor) /// Get the module context for this context. const TargetModuleContextDescriptor *getModuleContext() const; + /// Retrieve the set of protocols that are suppressed by this type's + /// primary definition. + /// + /// This type might still conditionally conform to any of the protocols + /// that are suppressed here, but that information is recorded in the + /// conditional suppressed protocols of the corresponding `GenericContext`. + const SuppressibleProtocolSet * + getSuppressedProtocols() const; + /// Is this context part of a C-imported module? bool isCImportedContext() const; @@ -3237,13 +3249,15 @@ struct swift_ptrauth_struct_context_descriptor(OpaqueTypeDescriptor) : TargetContextDescriptor, TrailingGenericContextObjects, TargetGenericContextDescriptorHeader, - RelativeDirectPointer> + RelativeDirectPointer, + SuppressibleProtocolSet> { private: using TrailingGenericContextObjects = swift::TrailingGenericContextObjects, TargetGenericContextDescriptorHeader, - RelativeDirectPointer>; + RelativeDirectPointer, + SuppressibleProtocolSet>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; @@ -3281,6 +3295,23 @@ struct swift_ptrauth_struct_context_descriptor(OpaqueTypeDescriptor) return Demangle::makeSymbolicMangledNameStringRef(ptr); } + /// Retrieve the set of protocols that are suppressed by this type's + /// primary definition. + /// + /// This type might still conditionally conform to any of the protocols + /// that are suppressed here, but that information is recorded in the + /// conditional suppressed protocols of the corresponding `GenericContext`. + const SuppressibleProtocolSet & + getSuppressedProtocols() const { + assert(this->hasSuppressibleProtocols()); + return + *this->template getTrailingObjects(); + } + + size_t numTrailingObjects(OverloadToken) const { + return this->hasSuppressibleProtocols() ? 1 : 0; + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::OpaqueType; } @@ -4052,7 +4083,8 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) TargetCanonicalSpecializedMetadatasListCount, TargetCanonicalSpecializedMetadatasListEntry, TargetCanonicalSpecializedMetadataAccessorsListEntry, - TargetCanonicalSpecializedMetadatasCachingOnceToken> { + TargetCanonicalSpecializedMetadatasCachingOnceToken, + SuppressibleProtocolSet> { private: using TrailingGenericContextObjects = swift::TrailingGenericContextObjects, @@ -4068,7 +4100,8 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) TargetCanonicalSpecializedMetadatasListCount, TargetCanonicalSpecializedMetadatasListEntry, TargetCanonicalSpecializedMetadataAccessorsListEntry, - TargetCanonicalSpecializedMetadatasCachingOnceToken>; + TargetCanonicalSpecializedMetadatasCachingOnceToken, + SuppressibleProtocolSet>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4418,6 +4451,23 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor) return box->token.get(); } + /// Retrieve the set of protocols that are suppressed by this type's + /// primary definition. + /// + /// This type might still conditionally conform to any of the protocols + /// that are suppressed here, but that information is recorded in the + /// conditional suppressed protocols of the corresponding `GenericContext`. + const SuppressibleProtocolSet & + getSuppressedProtocols() const { + assert(this->hasSuppressibleProtocols()); + return + *this->template getTrailingObjects(); + } + + size_t numTrailingObjects(OverloadToken) const { + return this->hasSuppressibleProtocols() ? 1 : 0; + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Class; } @@ -4447,7 +4497,8 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor) TargetSingletonMetadataInitialization, TargetCanonicalSpecializedMetadatasListCount, TargetCanonicalSpecializedMetadatasListEntry, - TargetCanonicalSpecializedMetadatasCachingOnceToken> { + TargetCanonicalSpecializedMetadatasCachingOnceToken, + SuppressibleProtocolSet> { public: using ForeignMetadataInitialization = TargetForeignMetadataInitialization; @@ -4470,7 +4521,8 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor) SingletonMetadataInitialization, MetadataListCount, MetadataListEntry, - MetadataCachingOnceToken>; + MetadataCachingOnceToken, + SuppressibleProtocolSet>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4557,6 +4609,23 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor) return box->token.get(); } + /// Retrieve the set of protocols that are suppressed by this type's + /// primary definition. + /// + /// This type might still conditionally conform to any of the protocols + /// that are suppressed here, but that information is recorded in the + /// conditional suppressed protocols of the corresponding `GenericContext`. + const SuppressibleProtocolSet & + getSuppressedProtocols() const { + assert(this->hasSuppressibleProtocols()); + return + *this->template getTrailingObjects(); + } + + size_t numTrailingObjects(OverloadToken) const { + return this->hasSuppressibleProtocols() ? 1 : 0; + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Struct; } @@ -4575,7 +4644,8 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor) TargetSingletonMetadataInitialization, TargetCanonicalSpecializedMetadatasListCount, TargetCanonicalSpecializedMetadatasListEntry, - TargetCanonicalSpecializedMetadatasCachingOnceToken> { + TargetCanonicalSpecializedMetadatasCachingOnceToken, + SuppressibleProtocolSet> { public: using SingletonMetadataInitialization = TargetSingletonMetadataInitialization; @@ -4598,7 +4668,8 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor) SingletonMetadataInitialization, MetadataListCount, MetadataListEntry, - MetadataCachingOnceToken>; + MetadataCachingOnceToken, + SuppressibleProtocolSet>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; @@ -4699,6 +4770,23 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor) return box->token.get(); } + /// Retrieve the set of protocols that are suppressed by this type's + /// primary definition. + /// + /// This type might still conditionally conform to any of the protocols + /// that are suppressed here, but that information is recorded in the + /// conditional suppressed protocols of the corresponding `GenericContext`. + const SuppressibleProtocolSet & + getSuppressedProtocols() const { + assert(this->hasSuppressibleProtocols()); + return + *this->template getTrailingObjects(); + } + + size_t numTrailingObjects(OverloadToken) const { + return this->hasSuppressibleProtocols() ? 1 : 0; + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Enum; } @@ -4744,6 +4832,31 @@ TargetContextDescriptor::getGenericContext() const { } } +template +inline const SuppressibleProtocolSet * +TargetContextDescriptor::getSuppressedProtocols() const { + if (!this->hasSuppressibleProtocols()) + return nullptr; + + switch (getKind()) { + case ContextDescriptorKind::Class: + return &llvm::cast>(this) + ->getSuppressedProtocols(); + case ContextDescriptorKind::Enum: + return &llvm::cast>(this) + ->getSuppressedProtocols(); + case ContextDescriptorKind::Struct: + return &llvm::cast>(this) + ->getSuppressedProtocols(); + case ContextDescriptorKind::OpaqueType: + return &llvm::cast>(this) + ->getSuppressedProtocols(); + default: + // We don't know about this kind of descriptor. + return nullptr; + } +} + template int32_t TargetTypeContextDescriptor::getGenericArgumentOffset() const { switch (this->getKind()) { diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 57c2fd6a3791a..8aea96a05e32b 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -25,6 +25,7 @@ #include "swift/ABI/KeyPath.h" #include "swift/ABI/ProtocolDispatchStrategy.h" +#include "swift/ABI/SuppressibleProtocols.h" // FIXME: this include shouldn't be here, but removing it causes symbol // mangling mismatches on Windows for some reason? @@ -1198,6 +1199,10 @@ class TargetExtendedFunctionTypeFlags { // Values if we have a transferring result. HasTransferringResult = 0x00000010U, + + /// A SuppressibleProtocolSet in the high bits. + SuppressedProtocolShift = 16, + SuppressedProtocolMask = 0xFFFFU << SuppressedProtocolShift, }; int_type Data; @@ -1229,6 +1234,13 @@ class TargetExtendedFunctionTypeFlags { (newValue ? HasTransferringResult : 0)); } + const TargetExtendedFunctionTypeFlags + withSuppressedProtocols(SuppressibleProtocolSet suppressed) const { + return TargetExtendedFunctionTypeFlags( + (Data & ~SuppressedProtocolMask) | + (suppressed.rawBits() << SuppressedProtocolShift)); + } + bool isTypedThrows() const { return bool(Data & TypedThrowsMask); } bool isIsolatedAny() const { @@ -1243,6 +1255,10 @@ class TargetExtendedFunctionTypeFlags { return Data; } + SuppressibleProtocolSet getSuppressedProtocols() const { + return SuppressibleProtocolSet(Data >> SuppressedProtocolShift); + } + static TargetExtendedFunctionTypeFlags fromIntValue(int_type Data) { return TargetExtendedFunctionTypeFlags(Data); } @@ -1688,13 +1704,15 @@ struct ContextDescriptorFlags { constexpr ContextDescriptorFlags(ContextDescriptorKind kind, bool isGeneric, bool isUnique, - uint8_t version, + bool hasSuppressibleProtocols, uint16_t kindSpecificFlags) : ContextDescriptorFlags(ContextDescriptorFlags() .withKind(kind) .withGeneric(isGeneric) .withUnique(isUnique) - .withVersion(version) + .withSuppressibleProtocols( + hasSuppressibleProtocols + ) .withKindSpecificFlags(kindSpecificFlags)) {} @@ -1713,10 +1731,10 @@ struct ContextDescriptorFlags { return (Value & 0x40u) != 0; } - /// The format version of the descriptor. Higher version numbers may have - /// additional fields that aren't present in older versions. - constexpr uint8_t getVersion() const { - return (Value >> 8u) & 0xFFu; + /// Whether the context has information about suppressible protocols, which + /// will show up as a trailing field in the context descriptor. + constexpr bool hasSuppressibleProtocols() const { + return (Value & 0x20u) != 0; } /// The most significant two bytes of the flags word, which can have @@ -1740,8 +1758,11 @@ struct ContextDescriptorFlags { | (isUnique ? 0x40u : 0)); } - constexpr ContextDescriptorFlags withVersion(uint8_t version) const { - return ContextDescriptorFlags((Value & 0xFFFF00FFu) | (version << 8u)); + constexpr ContextDescriptorFlags withSuppressibleProtocols( + bool hasSuppressibleProtocols + ) const { + return ContextDescriptorFlags((Value & ~0x20u) + | (hasSuppressibleProtocols ? 0x20u : 0)); } constexpr ContextDescriptorFlags @@ -1981,10 +2002,13 @@ class GenericContextDescriptorFlags { explicit constexpr GenericContextDescriptorFlags(uint16_t value) : Value(value) {} - constexpr GenericContextDescriptorFlags(bool hasTypePacks) - : GenericContextDescriptorFlags( + constexpr GenericContextDescriptorFlags( + bool hasTypePacks, bool hasConditionalSuppressedProtocols + ) : GenericContextDescriptorFlags( GenericContextDescriptorFlags((uint16_t)0) - .withHasTypePacks(hasTypePacks)) {} + .withHasTypePacks(hasTypePacks) + .withConditionalSuppressedProtocols( + hasConditionalSuppressedProtocols)) {} /// Whether this generic context has at least one type parameter /// pack, in which case the generic context will have a trailing @@ -1993,12 +2017,25 @@ class GenericContextDescriptorFlags { return (Value & 0x1) != 0; } + /// Whether this generic context has any conditional conformances to + /// suppressed protocols, in which case the generic context will have a + /// trailing SuppressibleProtocolSet and conditional requirements. + constexpr bool hasConditionalSuppressedProtocols() const { + return (Value & 0x2) != 0; + } + constexpr GenericContextDescriptorFlags withHasTypePacks(bool hasTypePacks) const { return GenericContextDescriptorFlags((uint16_t)( (Value & ~0x1) | (hasTypePacks ? 0x1 : 0))); } + constexpr GenericContextDescriptorFlags + withConditionalSuppressedProtocols(bool value) const { + return GenericContextDescriptorFlags((uint16_t)( + (Value & ~0x2) | (value ? 0x2 : 0))); + } + constexpr uint16_t getIntValue() const { return Value; } @@ -2095,6 +2132,12 @@ enum class GenericRequirementKind : uint8_t { SameConformance = 3, /// A same-shape requirement between generic parameter packs. SameShape = 4, + /// A requirement stating which suppressible protocol checks are + /// suppressed. + /// + /// This is more of an "anti-requirement", specifing which checks don't need + /// to happen for a given type. + SuppressedProtocols = 5, /// A layout requirement. Layout = 0x1F, }; diff --git a/include/swift/ABI/SuppressibleProtocols.def b/include/swift/ABI/SuppressibleProtocols.def new file mode 100644 index 0000000000000..d3258d5a12d9a --- /dev/null +++ b/include/swift/ABI/SuppressibleProtocols.def @@ -0,0 +1,33 @@ +//===--- SuppressibleProtocols.def - Suppressible protocol meta -*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines macros used for macro-metaprogramming with ABI-defined +// suppressible protocols. +// +// The SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) macro is used to specify +// each suppressible protocol that's conceptually part of the ABI. The +// arguments are: +// Name: The name of the protocol, e.g., Copyable +// Bit: The bit in the set bitset of suppressible protocols that is used +// to indicate this. +// MangleChar: The character used for the name mangling to refer to this +// protocol. +//===----------------------------------------------------------------------===// + +#ifndef SUPPRESSIBLE_PROTOCOL +# error Must define SUPPRESSIBLE_PROTOCOL macro before including this file +#endif + +SUPPRESSIBLE_PROTOCOL(Copyable, 0, 'c') +SUPPRESSIBLE_PROTOCOL(Escapable, 1, 'e') + +#undef SUPPRESSIBLE_PROTOCOL diff --git a/include/swift/ABI/SuppressibleProtocols.h b/include/swift/ABI/SuppressibleProtocols.h new file mode 100644 index 0000000000000..8f3cacee1edfa --- /dev/null +++ b/include/swift/ABI/SuppressibleProtocols.h @@ -0,0 +1,225 @@ +//===--- SuppressibleProtocols.h - Suppressible protocol meta ---*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This header declares various types for suppressible protocols, such as +// Copyable. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ABI_SUPPRESSIBLEPROTOCOLS_H +#define SWIFT_ABI_SUPPRESSIBLEPROTOCOLS_H + +#include +#include + +namespace swift { + +/// Describes a "suppressible" protocol, such as Copyable, which is assumed to +/// hold for all types unless explicitly suppressed. +enum class SuppressibleProtocolKind : uint8_t { +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) Name = Bit, +#include "swift/ABI/SuppressibleProtocols.def" +}; + +/// A set of suppressible protocols, whose bits correspond to the cases of +/// SuppressibleProtocolKind. +class SuppressibleProtocolSet { + using StorageType = uint16_t; + + /// The stored bits. + StorageType bits; + + /// Retrieve the mask for this bit. + static StorageType getMask(SuppressibleProtocolKind kind) { + return 1 << static_cast(kind); + } + +public: + explicit constexpr SuppressibleProtocolSet(StorageType bits) : bits(bits) {} + constexpr SuppressibleProtocolSet() : bits(0) {} + + /// Retrieve the raw bits that describe this set. + StorageType rawBits() const { return bits; } + + /// Whether the set contains no protocols. + bool empty() const { return bits == 0; } + + /// Check whether the set contains the specific suppressible protocol. + bool contains(SuppressibleProtocolKind kind) const { + return bits & getMask(kind); + } + + /// Insert the suppressible protocol into the set. + void insert(SuppressibleProtocolKind kind) { + bits = bits | getMask(kind); + } + + /// Remove the given suppressible protocol from the set. + void remove(SuppressibleProtocolKind kind) { + uint16_t mask = getMask(kind); + bits = bits & ~mask; + } + + /// Clear out all of the protocols from the set. + void clear() { bits = 0; } + +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + bool contains##Name() const { \ + return contains(SuppressibleProtocolKind::Name); \ + } +#include "swift/ABI/SuppressibleProtocols.def" + + /// Produce a suppressible protocol set containing all known suppressible + /// protocols. + static SuppressibleProtocolSet allKnown() { + SuppressibleProtocolSet result; +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + result.insert(SuppressibleProtocolKind::Name); +#include "swift/ABI/SuppressibleProtocols.def" + return result; + } + + /// Determine whether this protocol set contains any unknown protocols. + /// + /// This can occur when an older Swift runtime is working with metadata + /// or mangled names generated by a newer compiler that has introduced + /// another kind of suppressible protocol. The Swift runtime will need to + /// step lightly around protocol sets with unknown protocols. + bool hasUnknownProtocols() const { + return !(*this - allKnown()).empty(); + } + + class iterator { + /// The bits remaining in the set, which will be 0 when we have hit the + /// end. + unsigned bitsRemaining; + + /// The current bit index + unsigned currentBitIndex; + + /// Advance to the next set bit. + void advance() { + // Already at the end. + if (bitsRemaining == 0) + return; + + do { + ++currentBitIndex; + unsigned mask = 1 << currentBitIndex; + if ((bitsRemaining & mask) != 0) + break; + } while (true); + } + + /// Clear out the current bit, then advance. + void clearAndAdvance() { + // Clear the current bit. + unsigned mask = 1 << currentBitIndex; + bitsRemaining = bitsRemaining & ~mask; + + // Advance to the next set bit, or stop if we're done. + advance(); + } + + public: + using difference_type = int; + using value_type = SuppressibleProtocolKind; + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterator() : bitsRemaining(0), currentBitIndex(0) { } + iterator(unsigned bitsRemaining) + : bitsRemaining(bitsRemaining), currentBitIndex(0) + { + if ((bitsRemaining & 0x01) == 0) + advance(); + } + + iterator &operator++() { + clearAndAdvance(); + return *this; + } + + iterator operator++(int) { + iterator tmp = *this; + ++(*this); + return tmp; + } + + SuppressibleProtocolKind operator*() const { + return static_cast(currentBitIndex); + } + + friend bool operator ==(iterator lhs, iterator rhs) { + if (lhs.bitsRemaining != rhs.bitsRemaining) + return false; + + if (lhs.bitsRemaining == 0) + return true; + + return lhs.currentBitIndex == rhs.currentBitIndex; + } + + friend bool operator !=(iterator lhs, iterator rhs) { + return !(lhs == rhs); + } + }; + + iterator begin() const { return iterator(rawBits()); } + iterator end() const { return iterator(0); } + + friend bool operator==( + SuppressibleProtocolSet lhs, SuppressibleProtocolSet rhs) { + return lhs.bits == rhs.bits; + } + + friend bool operator!=( + SuppressibleProtocolSet lhs, SuppressibleProtocolSet rhs) { + return lhs.bits != rhs.bits; + } + + friend SuppressibleProtocolSet operator-( + SuppressibleProtocolSet lhs, SuppressibleProtocolSet rhs) { + return SuppressibleProtocolSet(lhs.bits & ~rhs.bits); + } + + SuppressibleProtocolSet &operator-=(SuppressibleProtocolSet rhs) { + bits = bits & ~rhs.bits; + return *this; + } + + friend SuppressibleProtocolSet operator|( + SuppressibleProtocolSet lhs, SuppressibleProtocolSet rhs) { + return SuppressibleProtocolSet(lhs.bits | rhs.bits); + } + + SuppressibleProtocolSet &operator|=(SuppressibleProtocolSet rhs) { + bits |= rhs.bits; + return *this; + } +}; + +/// Retrieve the name for the given suppressible protocol. +static inline const char * +getSuppressibleProtocolKindName(SuppressibleProtocolKind kind) { + switch (kind) { +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + case SuppressibleProtocolKind::Name: return #Name; +#include "swift/ABI/SuppressibleProtocols.def" + } + + return ""; +} + +} // end namespace swift +#endif // SWIFT_ABI_SUPPRESSIBLEPROTOCOLS_H diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 5d5ca6ed7e3bf..2655903a23df2 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4370,6 +4370,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Returns null if the type is a class, or does not have a declared `deinit`. DestructorDecl *getValueTypeDestructor(); + /// Does a conformance for a given suppressible protocol exist for this + /// type declaration. + CanBeInvertible::Result canConformTo(InvertibleProtocolKind kind) const; + /// "Does a conformance for Copyable exist for this type declaration?" /// /// This doesn't mean that all instance of this type are Copyable, because diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index f9d779333ab52..2f77ee18f2c1e 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -136,8 +136,10 @@ PROTOCOL(AsyncIteratorProtocol) PROTOCOL(FloatingPoint) -INVERTIBLE_PROTOCOL_WITH_NAME(Copyable, "Copyable") -INVERTIBLE_PROTOCOL_WITH_NAME(Escapable, "Escapable") +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + INVERTIBLE_PROTOCOL_WITH_NAME(Name, #Name) +#include "swift/ABI/SuppressibleProtocols.def" + PROTOCOL_(BitwiseCopyable) EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false) diff --git a/include/swift/AST/KnownProtocols.h b/include/swift/AST/KnownProtocols.h index 467d50dbef8be..d91e2418969bd 100644 --- a/include/swift/AST/KnownProtocols.h +++ b/include/swift/AST/KnownProtocols.h @@ -13,6 +13,7 @@ #ifndef SWIFT_AST_KNOWNPROTOCOLS_H #define SWIFT_AST_KNOWNPROTOCOLS_H +#include "swift/ABI/SuppressibleProtocols.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/FixedBitSet.h" #include "swift/AST/InvertibleProtocolKind.h" @@ -72,6 +73,9 @@ KnownProtocolKind getKnownProtocolKind(InvertibleProtocolKind ip); void simple_display(llvm::raw_ostream &out, const InvertibleProtocolKind &value); +SuppressibleProtocolKind asSuppressible(InvertibleProtocolKind kind); +InvertibleProtocolKind asInvertible(SuppressibleProtocolKind kind); + } // end namespace swift #endif diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index ac3d34c9faa74..7aa763e5c9631 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -1354,6 +1354,9 @@ class MetadataReader { case GenericRequirementKind::SameShape: return TypeLookupError( "Unexpected same-shape requirement in runtime generic signature"); + case GenericRequirementKind::SuppressedProtocols: + return TypeLookupError( + "Unexpected suppressible protocol in runtime generic signature"); } } @@ -2883,6 +2886,8 @@ class MetadataReader { case GenericRequirementKind::SameShape: llvm_unreachable("Implement me"); + case GenericRequirementKind::SuppressedProtocols: + llvm_unreachable("Implement me"); } } diff --git a/include/swift/Runtime/Casting.h b/include/swift/Runtime/Casting.h index 8d31de9568c83..f2801d0e160dc 100644 --- a/include/swift/Runtime/Casting.h +++ b/include/swift/Runtime/Casting.h @@ -259,6 +259,7 @@ SWIFT_RUNTIME_EXPORT const WitnessTable * swift_conformsToProtocolCommon(const Metadata *type, const ProtocolDescriptor *protocol); + } // end namespace swift #endif // SWIFT_RUNTIME_CASTING_H diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 3e0e355c1ee4c..e91bc49875f5d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -130,6 +130,28 @@ void swift::simple_display(llvm::raw_ostream &out, out << getProtocolName(getKnownProtocolKind(value)); } +// Metadata stores a 16-bit field for suppressible protocols. Trigger a build +// error when we assign the 15th bit so we can think about what to do. +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + static_assert(Bit < 15); +#include "swift/ABI/SuppressibleProtocols.def" + +SuppressibleProtocolKind swift::asSuppressible(InvertibleProtocolKind kind) { + switch (kind) { +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + case InvertibleProtocolKind::Name: return SuppressibleProtocolKind::Name; +#include "swift/ABI/SuppressibleProtocols.def" + } +} + +InvertibleProtocolKind swift::asInvertible(SuppressibleProtocolKind kind) { + switch (kind) { +#define SUPPRESSIBLE_PROTOCOL(Name, Bit, MangleChar) \ + case SuppressibleProtocolKind::Name: return InvertibleProtocolKind::Name; +#include "swift/ABI/SuppressibleProtocols.def" + } +} + namespace { enum class SearchPathKind : uint8_t { Import = 1 << 0, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1ed011270c481..40139f5b58f50 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4916,22 +4916,22 @@ GenericParameterReferenceInfo ValueDecl::findExistentialSelfReferences( std::nullopt); } -static TypeDecl::CanBeInvertible::Result -conformanceExists(TypeDecl const *decl, InvertibleProtocolKind ip) { - auto *proto = decl->getASTContext().getProtocol(getKnownProtocolKind(ip)); +TypeDecl::CanBeInvertible::Result +NominalTypeDecl::canConformTo(InvertibleProtocolKind ip) const { + auto *proto = getASTContext().getProtocol(getKnownProtocolKind(ip)); assert(proto && "missing Copyable/Escapable from stdlib!"); // Handle protocols specially, without building a GenericSignature. - if (auto *protoDecl = dyn_cast(decl)) { + if (auto *protoDecl = dyn_cast(this)) { return protoDecl->inheritsFrom(proto) ? TypeDecl::CanBeInvertible::Always : TypeDecl::CanBeInvertible::Never; } - Type selfTy = decl->getDeclaredInterfaceType(); + Type selfTy = getDeclaredInterfaceType(); assert(selfTy); - auto conformance = decl->getModuleContext()->lookupConformance(selfTy, proto, + auto conformance = getModuleContext()->lookupConformance(selfTy, proto, /*allowMissing=*/false); if (conformance.isInvalid()) @@ -4944,11 +4944,11 @@ conformanceExists(TypeDecl const *decl, InvertibleProtocolKind ip) { } TypeDecl::CanBeInvertible::Result NominalTypeDecl::canBeCopyable() const { - return conformanceExists(this, InvertibleProtocolKind::Copyable); + return canConformTo(InvertibleProtocolKind::Copyable); } TypeDecl::CanBeInvertible::Result NominalTypeDecl::canBeEscapable() const { - return conformanceExists(this, InvertibleProtocolKind::Escapable); + return canConformTo(InvertibleProtocolKind::Escapable); } Type TypeDecl::getDeclaredInterfaceType() const { diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index eb7ce5849ab1d..0c540916e8b13 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -424,6 +424,7 @@ namespace { unsigned NumGenericKeyArguments = 0; SmallVector ShapeClasses; SmallVector GenericPackArguments; + SuppressibleProtocolSet ConditionalSuppressedProtocols; GenericSignatureHeaderBuilder(IRGenModule &IGM, ConstantStructBuilder &builder) @@ -464,7 +465,10 @@ namespace { NumGenericKeyArguments + ShapeClasses.size()); bool hasTypePacks = !GenericPackArguments.empty(); - GenericContextDescriptorFlags flags(hasTypePacks); + bool hasConditionalSuppressedProtocols = + !ConditionalSuppressedProtocols.empty(); + GenericContextDescriptorFlags flags( + hasTypePacks, hasConditionalSuppressedProtocols); b.fillPlaceholderWithInt(FlagsPP, IGM.Int16Ty, flags.getIntValue()); } @@ -497,7 +501,7 @@ namespace { ContextDescriptorFlags(asImpl().getContextKind(), !asImpl().getGenericSignature().isNull(), asImpl().isUniqueDescriptor(), - asImpl().getVersion(), + !asImpl().getSuppressedProtocols().empty(), asImpl().getKindSpecificFlags()) .getIntValue()); } @@ -519,6 +523,7 @@ namespace { asImpl().addGenericParameters(); asImpl().addGenericRequirements(); asImpl().addGenericPackShapeDescriptors(); + asImpl().addConditionalSuppressedProtocols(); asImpl().finishGenericParameters(); } @@ -545,12 +550,29 @@ namespace { void addGenericRequirements() { auto metadata = - irgen::addGenericRequirements(IGM, B, - asImpl().getGenericSignature(), - asImpl().getGenericSignature().getRequirements()); + irgen::addGenericRequirements(IGM, B, asImpl().getGenericSignature()); SignatureHeader->add(metadata); } + /// Adds the set of suppressed protocols, which must be explicitly called + /// by the concrete subclasses. + void addSuppressedProtocols() { + auto protocols = asImpl().getSuppressedProtocols(); + if (protocols.empty()) + return; + + B.addInt(IGM.Int16Ty, protocols.rawBits()); + } + + SuppressibleProtocolSet getConditionalSuppressedProtocols() { + return SuppressibleProtocolSet(); + } + + void addConditionalSuppressedProtocols() { + assert(asImpl().getConditionalSuppressedProtocols().empty() && + "Subclass must implement this operation"); + } + void finishGenericParameters() { SignatureHeader->finish(IGM, B); } @@ -577,10 +599,11 @@ namespace { irgen::addGenericPackShapeDescriptors(IGM, B, shapes, packArgs); } - uint8_t getVersion() { - return 0; + /// Retrieve the set of protocols that are suppressed in this context. + SuppressibleProtocolSet getSuppressedProtocols() { + return SuppressibleProtocolSet(); } - + uint16_t getKindSpecificFlags() { return 0; } @@ -852,10 +875,13 @@ namespace { } void addRequirementSignature() { - auto requirements = Proto->getRequirementSignature().getRequirements(); + SmallVector requirements; + SmallVector inverses; + Proto->getRequirementSignature().getRequirementsWithInverses( + Proto, requirements, inverses); auto metadata = irgen::addGenericRequirements(IGM, B, Proto->getGenericSignature(), - requirements); + requirements, inverses); B.fillPlaceholderWithInt(*NumRequirementsInSignature, IGM.Int32Ty, metadata.NumRequirements); @@ -1226,6 +1252,110 @@ namespace { asImpl().maybeAddMetadataInitialization(); } + /// Retrieve the set of protocols that are suppressed by this type. + SuppressibleProtocolSet getSuppressedProtocols() { + SuppressibleProtocolSet result; + auto nominal = dyn_cast(Type); + if (!nominal) + return result; + + auto checkProtocol = [&](SuppressibleProtocolKind kind) { + switch (nominal->canConformTo(asInvertible(kind))) { + case TypeDecl::CanBeInvertible::Never: + case TypeDecl::CanBeInvertible::Conditionally: + result.insert(kind); + break; + + case TypeDecl::CanBeInvertible::Always: + break; + } + }; + + for (auto kind : SuppressibleProtocolSet::allKnown()) + checkProtocol(kind); + + return result; + } + + /// Retrieve the set of suppressible protocols to which this type + /// conditionally conforms. + SuppressibleProtocolSet getConditionalSuppressedProtocols() { + SuppressibleProtocolSet result; + auto nominal = dyn_cast(Type); + if (!nominal) + return result; + + auto checkProtocol = [&](SuppressibleProtocolKind kind) { + switch (nominal->canConformTo(asInvertible(kind))) { + case TypeDecl::CanBeInvertible::Never: + case TypeDecl::CanBeInvertible::Always: + break; + + case TypeDecl::CanBeInvertible::Conditionally: + result.insert(kind); + break; + } + }; + + for (auto kind : SuppressibleProtocolSet::allKnown()) + checkProtocol(kind); + + return result; + } + + void addConditionalSuppressedProtocols() { + auto protocols = asImpl().getConditionalSuppressedProtocols(); + if (protocols.empty()) + return; + + // Note the conditional suppressed protocols. + this->SignatureHeader->ConditionalSuppressedProtocols = protocols; + + // The suppressed protocols with conditional conformances. + B.addInt(IGM.Int16Ty, protocols.rawBits()); + + // Create placeholders for the counts of the conditional requirements + // for each conditional conformance to a supressible protocol. + unsigned numProtocols = countBitsUsed(protocols.rawBits()); + using PlaceholderPosition = + ConstantAggregateBuilderBase::PlaceholderPosition; + SmallVector countPlaceholders; + for (unsigned i : range(0, numProtocols)) { + (void)i; + countPlaceholders.push_back( + B.addPlaceholderWithSize(IGM.Int16Ty)); + } + + // Emit the generic requirements for the conditional conformance + // to each suppressible protocol. + auto nominal = cast(Type); + auto genericSig = nominal->getGenericSignatureOfContext(); + ASTContext &ctx = nominal->getASTContext(); + unsigned index = 0; + unsigned totalNumRequirements = 0; + for (auto kind : protocols) { + auto proto = ctx.getProtocol(getKnownProtocolKind(asInvertible(kind))); + SmallVector conformances; + (void)nominal->lookupConformance(proto, conformances); + auto conformance = conformances.front(); + + SmallVector inverses; + if (auto conformanceSig = + conformance->getDeclContext()->getGenericSignatureOfContext()) { + SmallVector scratchReqs; + conformanceSig->getRequirementsWithInverses(scratchReqs, inverses); + } + + auto metadata = irgen::addGenericRequirements( + IGM, B, genericSig, conformance->getConditionalRequirements(), + inverses); + + totalNumRequirements += metadata.NumRequirements; + B.fillPlaceholderWithInt(countPlaceholders[index++], IGM.Int16Ty, + totalNumRequirements); + } + } + /// Fill out all the aspects of the type identity. void computeIdentity() { // Remember the user-facing name. @@ -1335,6 +1465,17 @@ namespace { return Type->getGenericSignature(); } + bool hasSuppressibleProtocols() { + auto genericSig = asImpl().getGenericSignature(); + if (!genericSig) + return false; + + SmallVector requirements; + SmallVector inverses; + genericSig->getRequirementsWithInverses(requirements, inverses); + return !inverses.empty(); + } + /// Fill in the fields of a TypeGenericContextDescriptorHeader. void addGenericParametersHeader() { asImpl().addMetadataInstantiationCache(); @@ -1555,6 +1696,7 @@ namespace { void layout() { super::layout(); maybeAddCanonicalMetadataPrespecializations(); + addSuppressedProtocols(); } ContextDescriptorKind getContextKind() { @@ -1628,6 +1770,7 @@ namespace { void layout() { super::layout(); maybeAddCanonicalMetadataPrespecializations(); + addSuppressedProtocols(); } ContextDescriptorKind getContextKind() { @@ -1760,6 +1903,7 @@ namespace { addOverrideTable(); addObjCResilientClassStubInfo(); maybeAddCanonicalMetadataPrespecializations(); + addSuppressedProtocols(); } void addIncompleteMetadataOrRelocationFunction() { @@ -6751,10 +6895,20 @@ static void addGenericRequirement(IRGenModule &IGM, ConstantStructBuilder &B, addReference(); } +GenericArgumentMetadata irgen::addGenericRequirements( + IRGenModule &IGM, ConstantStructBuilder &B, + GenericSignature sig) { + SmallVector reqs; + SmallVector inverses; + sig->getRequirementsWithInverses(reqs, inverses); + return addGenericRequirements(IGM, B, sig, reqs, inverses); +} + GenericArgumentMetadata irgen::addGenericRequirements( IRGenModule &IGM, ConstantStructBuilder &B, GenericSignature sig, - ArrayRef requirements) { + ArrayRef requirements, + ArrayRef inverses) { assert(sig); GenericArgumentMetadata metadata; @@ -6858,6 +7012,47 @@ GenericArgumentMetadata irgen::addGenericRequirements( } } + if (inverses.empty()) + return metadata; + + // Collect the inverse requirements on each of the generic parameters. + SmallVector + suppressed(sig.getGenericParams().size(), { }); + for (const auto &inverse : inverses) { + // Determine which generic parameter this constraint applies to. + auto genericParam = + sig.getReducedType(inverse.subject)->getAs(); + if (!genericParam) + continue; + + // Insert this suppression into the set for that generic parameter. + auto suppressibleKind = asSuppressible(inverse.getKind()); + unsigned index = sig->getGenericParamOrdinal(genericParam); + suppressed[index].insert(suppressibleKind); + } + + // Go through the generic parameters, emitting a requirement for each + // that suppresses checking of some protocols. + for (unsigned index : indices(suppressed)) { + if (suppressed[index].empty()) + continue; + + // Encode the suppressed protocols constraint. + auto genericParam = sig.getGenericParams()[index]; + auto flags = GenericRequirementFlags( + GenericRequirementKind::SuppressedProtocols, + /*key argument*/ false, + genericParam->isParameterPack()); + addGenericRequirement(IGM, B, metadata, sig, flags, + Type(genericParam), + [&]{ + B.addInt16(index); + B.addInt16(suppressed[index].rawBits()); + }); + + ++metadata.NumRequirements; + } + return metadata; } @@ -7152,16 +7347,19 @@ irgen::emitExtendedExistentialTypeShape(IRGenModule &IGM, addPaddingAfterGenericParamDescriptors(IGM, b, totalParamDescriptors); auto addRequirementDescriptors = [&](CanGenericSignature sig, - GenericSignatureHeaderBuilder &header) { - auto info = addGenericRequirements(IGM, b, sig, sig.getRequirements()); + GenericSignatureHeaderBuilder &header, + bool suppressInverses) { + auto info = suppressInverses + ? addGenericRequirements(IGM, b, sig, sig.getRequirements(), { }) + : addGenericRequirements(IGM, b, sig); header.add(info); header.finish(IGM, b); }; // GenericRequirementDescriptor GenericRequirements[*]; - addRequirementDescriptors(reqSig, reqHeader); + addRequirementDescriptors(reqSig, reqHeader, /*suppressInverses=*/false); if (genSig) { - addRequirementDescriptors(genSig, *genHeader); + addRequirementDescriptors(genSig, *genHeader, /*suppressInverses=*/true); } // The 'Self' parameter in an existential is not variadic diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 2b3880eca1331..4bc7c35a62ef5 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -236,17 +236,30 @@ namespace irgen { GenericSignature sig, bool implicit); + /// Add generic requirements to the given constant struct builder. + /// + /// This is a convenience implementation that passes along the generic + /// signature's requirements. + /// + /// \param sig The generic signature whose requirements should be added. + GenericArgumentMetadata addGenericRequirements( + IRGenModule &IGM, + ConstantStructBuilder &B, + GenericSignature sig); + /// Add generic requirements to the given constant struct builder. /// /// \param sig The generic signature against which the requirements are /// described. /// /// \param requirements The requirements to add. + /// \param inverses The inverse requirements. GenericArgumentMetadata addGenericRequirements( IRGenModule &IGM, ConstantStructBuilder &B, GenericSignature sig, - ArrayRef requirements); + ArrayRef requirements, + ArrayRef inverses); /// Add generic pack shape descriptors to the given constant struct builder. /// diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 8dea87612d0da..02d9f10ef2374 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2116,40 +2116,38 @@ namespace { if (!normal) return; - SmallVector condReqs; - { - // FIXME(kavon): probably need to emit the inverse requirements in the - // metadata so the runtime knows not to check for Copyable? For now - // filter them out. - auto origCondReqs = normal->getConditionalRequirements(); - for (auto req : origCondReqs) { - if (req.getKind() == RequirementKind::Conformance && - req.getProtocolDecl()->getInvertibleProtocolKind()) - continue; - condReqs.push_back(req); - } + // Compute the inverse requirements from the generic signature where the + // conformance occurs. + SmallVector scratchReqs; + SmallVector inverses; + if (auto genericSig = + normal->getDeclContext()->getGenericSignatureOfContext()) { + genericSig->getRequirementsWithInverses(scratchReqs, inverses); + scratchReqs.clear(); } + auto condReqs = normal->getConditionalRequirements(); if (condReqs.empty()) { // For a protocol P that conforms to another protocol, introduce a // conditional requirement for that P's Self: P. This aligns with // SILWitnessTable::enumerateWitnessTableConditionalConformances(). if (auto selfProto = normal->getDeclContext()->getSelfProtocolDecl()) { auto selfType = selfProto->getSelfInterfaceType()->getCanonicalType(); - condReqs.emplace_back(RequirementKind::Conformance, selfType, - selfProto->getDeclaredInterfaceType()); + scratchReqs.emplace_back(RequirementKind::Conformance, selfType, + selfProto->getDeclaredInterfaceType()); + condReqs = scratchReqs; } - if (condReqs.empty()) + if (condReqs.empty() && inverses.empty()) return; } - Flags = Flags.withNumConditionalRequirements(condReqs.size()); - auto nominal = normal->getDeclContext()->getSelfNominalTypeDecl(); auto sig = nominal->getGenericSignatureOfContext(); - auto metadata = irgen::addGenericRequirements(IGM, B, sig, condReqs); + auto metadata = irgen::addGenericRequirements( + IGM, B, sig, condReqs, inverses); + Flags = Flags.withNumConditionalRequirements(metadata.NumRequirements); Flags = Flags.withNumConditionalPackDescriptors( metadata.GenericPackArguments.size()); @@ -4356,8 +4354,7 @@ llvm::Constant *IRGenModule::getAddrOfGenericEnvironment( fields.addAlignmentPadding(Alignment(4)); // Generic requirements - irgen::addGenericRequirements(*this, fields, signature, - signature.getRequirements()); + irgen::addGenericRequirements(*this, fields, signature); return fields.finishAndCreateFuture(); }); } diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index e2dc7f6a15fa9..427dd2fcfceba 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1416,11 +1416,35 @@ getFunctionTypeFlags(CanFunctionType type) { break; } + // Compute the set of suppressed protocols. + SuppressibleProtocolSet suppressedProtocols; + for (auto suppressibleKind : SuppressibleProtocolSet::allKnown()) { + switch (suppressibleKind) { + case SuppressibleProtocolKind::Copyable: { + // If the function type is noncopyable, note that in the suppressed + // protocols. + auto proto = + type->getASTContext().getProtocol(KnownProtocolKind::Copyable); + if (proto && + proto->getParentModule()->lookupConformance(type, proto).isInvalid()) + suppressedProtocols.insert(suppressibleKind); + break; + } + + case SuppressibleProtocolKind::Escapable: + // We intentionally do not record the "escapable" bit here, because it's + // already in the normal function type flags. The runtime will + // introduce it as necessary. + break; + } + } + auto isolation = type->getIsolation(); auto extFlags = ExtendedFunctionTypeFlags() .withTypedThrows(!type->getThrownError().isNull()) - .withTransferringResult(type->hasTransferringResult()); + .withTransferringResult(type->hasTransferringResult()) + .withSuppressedProtocols(suppressedProtocols); if (isolation.isErased()) extFlags = extFlags.withIsolatedAny(); diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index 9e50842055f40..7f7b6128a779e 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -1843,29 +1843,29 @@ static DynamicCastResult tryCastToExtendedExistential( return DynamicCastResult::Failure; } - llvm::SmallVector allGenericArgsVec; - unsigned witnessesMark = 0; + llvm::SmallVector allGenericArgsVec; + llvm::SmallVector witnessTables; { // Line up the arguments to the requirement signature. auto genArgs = destExistentialType->getGeneralizationArguments(); allGenericArgsVec.append(genArgs, genArgs + shapeArgumentCount); // Tack on the `Self` argument. allGenericArgsVec.push_back((const void *)selfType); - // Mark the point where the generic arguments end. - // _checkGenericRequirements is going to fill in a set of witness tables - // after that. - witnessesMark = allGenericArgsVec.size(); SubstGenericParametersFromMetadata substitutions(destExistentialShape, allGenericArgsVec.data()); // Verify the requirements in the requirement signature against the // arguments from the source value. + auto requirementSig = destExistentialShape->getRequirementSignature(); auto error = swift::_checkGenericRequirements( - destExistentialShape->getRequirementSignature().getRequirements(), - allGenericArgsVec, + requirementSig.getParams(), + requirementSig.getRequirements(), + witnessTables, [&substitutions](unsigned depth, unsigned index) { - // FIXME: Variadic generics - return substitutions.getMetadata(depth, index).getMetadata(); + return substitutions.getMetadata(depth, index).Ptr; + }, + [&substitutions](unsigned ordinal) { + return substitutions.getMetadataOrdinal(ordinal).Ptr; }, [](const Metadata *type, unsigned index) -> const WitnessTable * { swift_unreachable("Resolution of witness tables is not supported"); @@ -1906,7 +1906,7 @@ static DynamicCastResult tryCastToExtendedExistential( } // Fill in the trailing set of witness tables. - const unsigned numWitnessTables = allGenericArgsVec.size() - witnessesMark; + const unsigned numWitnessTables = witnessTables.size(); assert(numWitnessTables == llvm::count_if(destExistentialShape->getRequirementSignature().getRequirements(), [](const auto &req) -> bool { @@ -1914,9 +1914,7 @@ static DynamicCastResult tryCastToExtendedExistential( GenericRequirementKind::Protocol; })); for (unsigned i = 0; i < numWitnessTables; ++i) { - const auto witness = i + witnessesMark; - destWitnesses[i] = - reinterpret_cast(allGenericArgsVec[witness]); + destWitnesses[i] = reinterpret_cast(witnessTables[i]); } if (takeOnSuccess) { diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index d171386d71727..9bc380c791748 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -1190,6 +1190,7 @@ class SubstGenericParametersFromWrittenArgs { genericParamCounts(genericParamCounts) {} MetadataOrPack getMetadata(unsigned depth, unsigned index) const; + MetadataOrPack getMetadataOrdinal(unsigned ordinal) const; const WitnessTable *getWitnessTable(const Metadata *type, unsigned index) const; }; @@ -1404,10 +1405,14 @@ _gatherGenericParameters(const ContextDescriptor *context, SubstGenericParametersFromWrittenArgs substitutions(allGenericArgs, genericParamCounts); auto error = _checkGenericRequirements( + generics->getGenericParams(), generics->getGenericRequirements(), allGenericArgsVec, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index).Ptr; }, + [&substitutions](unsigned ordinal) { + return substitutions.getMetadataOrdinal(ordinal).Ptr; + }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); @@ -1831,14 +1836,21 @@ class DecodedMetadataBuilder { // Collect any other generic arguments. auto error = _checkGenericRequirements( - genSig.getRequirements(), allArgsVec, + genSig.getParams(), genSig.getRequirements(), allArgsVec, [genArgs](unsigned depth, unsigned index) -> const Metadata * { if (depth != 0 || index >= genArgs.size()) - return nullptr; + return (const Metadata*)nullptr; // FIXME: variadic generics return genArgs[index].getMetadata(); }, + [genArgs](unsigned ordinal) { + if (ordinal >= genArgs.size()) + return (const Metadata*)nullptr; + + // FIXME: variadic generics + return genArgs[ordinal].getMetadata(); + }, [](const Metadata *type, unsigned index) -> const WitnessTable * { swift_unreachable("never called"); }); @@ -2790,10 +2802,14 @@ swift_distributed_getWitnessTables(GenericEnvironmentDescriptor *genericEnv, SubstGenericParametersFromMetadata substFn(genericEnv, genericArguments); auto error = _checkGenericRequirements( + genericEnv->getGenericParameters(), genericEnv->getGenericRequirements(), witnessTables, [&substFn](unsigned depth, unsigned index) { return substFn.getMetadata(depth, index).Ptr; }, + [&substFn](unsigned ordinal) { + return substFn.getMetadataOrdinal(ordinal).Ptr; + }, [&substFn](const Metadata *type, unsigned index) { return substFn.getWitnessTable(type, index); }); @@ -3220,6 +3236,18 @@ SubstGenericParametersFromMetadata::getMetadata( return MetadataOrPack(genericArgs[flatIndex]); } +MetadataOrPack +SubstGenericParametersFromMetadata::getMetadataOrdinal(unsigned ordinal) const { + // Don't attempt anything if we have no generic parameters. + if (genericArgs == nullptr) + return MetadataOrPack(); + + // On first access, compute the descriptor path. + setup(); + + return MetadataOrPack(genericArgs[numShapeClasses + ordinal]); +} + const WitnessTable * SubstGenericParametersFromMetadata::getWitnessTable(const Metadata *type, unsigned index) const { @@ -3246,6 +3274,15 @@ MetadataOrPack SubstGenericParametersFromWrittenArgs::getMetadata( return MetadataOrPack(); } +MetadataOrPack SubstGenericParametersFromWrittenArgs::getMetadataOrdinal( + unsigned ordinal) const { + if (ordinal < allGenericArgs.size()) { + return MetadataOrPack(allGenericArgs[ordinal]); + } + + return MetadataOrPack(); +} + const WitnessTable * SubstGenericParametersFromWrittenArgs::getWitnessTable(const Metadata *type, unsigned index) const { diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index ebb45b39b79e7..79a573f9db189 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -294,6 +294,13 @@ class TypeInfo { using SubstGenericParameterFn = std::function; + /// Callback used to provide the substitution of a generic parameter + /// (described by the ordinal, or "flat index") to its metadata. + /// + /// The return type here is a lie; it's actually a MetadataOrPack. + using SubstGenericParameterOrdinalFn = + std::function; + /// Callback used to provide the substitution of a witness table based on /// its index into the enclosing generic environment. using SubstDependentWitnessTableFn = @@ -453,6 +460,7 @@ class TypeInfo { const void * const *getGenericArgs() const { return genericArgs; } MetadataOrPack getMetadata(unsigned depth, unsigned index) const; + MetadataOrPack getMetadataOrdinal(unsigned ordinal) const; const WitnessTable *getWitnessTable(const Metadata *type, unsigned index) const; }; @@ -530,6 +538,9 @@ class TypeInfo { /// arguments, collecting the key arguments (e.g., witness tables) for /// the caller. /// + /// \param genericParams The generic parameters corresponding to the + /// arguments. + /// /// \param requirements The set of requirements to evaluate. /// /// \param extraArguments The extra arguments determined while checking @@ -538,9 +549,11 @@ class TypeInfo { /// /// \returns the error if an error occurred, None otherwise. std::optional _checkGenericRequirements( + llvm::ArrayRef genericParams, llvm::ArrayRef requirements, llvm::SmallVectorImpl &extraArguments, SubstGenericParameterFn substGenericParam, + SubstGenericParameterOrdinalFn substGenericParamOrdinal, SubstDependentWitnessTableFn substWitnessTable); /// A helper function which avoids performing a store if the destination diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 68741abc61d69..79a0d671a8764 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -328,13 +328,21 @@ const WitnessTable * ProtocolConformanceDescriptor::getWitnessTable(const Metadata *type) const { // If needed, check the conditional requirements. llvm::SmallVector conditionalArgs; - if (hasConditionalRequirements()) { + + llvm::ArrayRef genericParams; + if (auto typeDescriptor = type->getTypeContextDescriptor()) + genericParams = typeDescriptor->getGenericParams(); + + if (hasConditionalRequirements() || !genericParams.empty()) { SubstGenericParametersFromMetadata substitutions(type); auto error = _checkGenericRequirements( - getConditionalRequirements(), conditionalArgs, + genericParams, getConditionalRequirements(), conditionalArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index).Ptr; }, + [&substitutions](unsigned ordinal) { + return substitutions.getMetadataOrdinal(ordinal).Ptr; + }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); @@ -1342,10 +1350,12 @@ bool swift::_swift_class_isSubclass(const Metadata *subclass, } static std::optional -checkGenericRequirement(const GenericRequirementDescriptor &req, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +checkGenericRequirement( + const GenericRequirementDescriptor &req, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable, + llvm::SmallVectorImpl &suppressed) { assert(!req.getFlags().isPackRequirement()); // Make sure we understand the requirement we're dealing with. @@ -1433,6 +1443,20 @@ checkGenericRequirement(const GenericRequirementDescriptor &req, return TYPE_LOOKUP_ERROR_FMT("can't have same-shape requirement where " "subject type is not a pack"); } + case GenericRequirementKind::SuppressedProtocols: { + uint16_t index = req.getSuppressedProtocolsGenericParamIndex(); + if (index == 0xFFFF) + return TYPE_LOOKUP_ERROR_FMT("unable to suppress protocols"); + + // Expand the suppression set so we can record these protocols. + if (index >= suppressed.size()) { + suppressed.resize(index + 1, SuppressibleProtocolSet()); + } + + // Record these suppressed protocols for this generic parameter. + suppressed[index] |= req.getSuppressedProtocols(); + return std::nullopt; + } } // Unknown generic requirement kind. @@ -1441,10 +1465,12 @@ checkGenericRequirement(const GenericRequirementDescriptor &req, } static std::optional -checkGenericPackRequirement(const GenericRequirementDescriptor &req, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +checkGenericPackRequirement( + const GenericRequirementDescriptor &req, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable, + llvm::SmallVectorImpl &suppressed) { assert(req.getFlags().isPackRequirement()); // Make sure we understand the requirement we're dealing with. @@ -1584,6 +1610,21 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, return std::nullopt; } + + case GenericRequirementKind::SuppressedProtocols: { + uint16_t index = req.getSuppressedProtocolsGenericParamIndex(); + if (index == 0xFFFF) + return TYPE_LOOKUP_ERROR_FMT("unable to suppress protocols"); + + // Expand the suppression set so we can record these protocols. + if (index >= suppressed.size()) { + suppressed.resize(index + 1, SuppressibleProtocolSet()); + } + + // Record these suppressed protocols for this generic parameter. + suppressed[index] |= req.getSuppressedProtocols(); + return std::nullopt; + } } // Unknown generic requirement kind. @@ -1591,27 +1632,301 @@ checkGenericPackRequirement(const GenericRequirementDescriptor &req, (unsigned)req.getKind()); } +static std::optional +checkSuppressibleRequirements(const Metadata *type, + SuppressibleProtocolSet ignored); + +static std::optional +checkSuppressibleRequirementsStructural(const Metadata *type, + SuppressibleProtocolSet ignored) { + switch (type->getKind()) { + case MetadataKind::Class: + case MetadataKind::Struct: + case MetadataKind::Enum: + case MetadataKind::Optional: + case MetadataKind::ForeignClass: + case MetadataKind::ForeignReferenceType: + case MetadataKind::ObjCClassWrapper: + // All handled via context descriptor in the caller. + return std::nullopt; + + case MetadataKind::HeapLocalVariable: + case MetadataKind::Opaque: + case MetadataKind::HeapGenericLocalVariable: + case MetadataKind::ErrorObject: + case MetadataKind::Task: + case MetadataKind::Job: + // Not part of the user-visible type system; assumed to handle all + // suppressible requirements. + return std::nullopt; + + case MetadataKind::Tuple: { + // Check every element type in the tuple. + auto tupleMetadata = cast(type); + for (unsigned i = 0, n = tupleMetadata->NumElements; i != n; ++i) { + if (auto error = + checkSuppressibleRequirements(&*tupleMetadata->getElement(i).Type, + ignored)) + return error; + } + return std::nullopt; + } + + case MetadataKind::Function: { + auto functionMetadata = cast(type); + + // Determine the set of protocols that are suppressed by the function + // type. + SuppressibleProtocolSet suppressed; + if (functionMetadata->hasExtendedFlags()) { + suppressed = functionMetadata->getExtendedFlags() + .getSuppressedProtocols(); + } + + // Map the existing "noescape" bit as a suppressed protocol, when + // appropriate. + switch (functionMetadata->getConvention()) { + case FunctionMetadataConvention::Swift: + // Swift function types can be non-escaping, so honor the bit. + if (!functionMetadata->isEscaping()) + suppressed.insert(SuppressibleProtocolKind::Escapable); + break; + + case FunctionMetadataConvention::Block: + // Objective-C block types don't encode non-escaping-ness in metadata, + // so we assume that they are always escaping. + break; + + case FunctionMetadataConvention::Thin: + case FunctionMetadataConvention::CFunctionPointer: + // Thin and C function pointers have no captures, so whether they + // escape is irrelevant. + break; + } + + auto missing = suppressed - ignored; + if (!missing.empty()) { + return TYPE_LOOKUP_ERROR_FMT( + "function type missing suppressible protocols %x", missing.rawBits()); + } + + return std::nullopt; + } + + case MetadataKind::ExtendedExistential: { + auto existential = cast(type); + auto &shape = *existential->Shape; + llvm::ArrayRef reqs( + shape.getReqSigRequirements(), shape.getNumReqSigRequirements()); + // Look for any suppressed protocol requirements. If the existential + // has suppressed a protocol that is not ignored, then the existential + // does not meet the specified requirements. + for (const auto& req : reqs) { + if (req.getKind() != GenericRequirementKind::SuppressedProtocols) + continue; + + auto suppressed = req.getSuppressedProtocols(); + auto missing = suppressed - ignored; + if (!missing.empty()) { + return TYPE_LOOKUP_ERROR_FMT( + "existential type missing suppressible protocols %x", + missing.rawBits()); + } + } + + return std::nullopt; + } + + case MetadataKind::Metatype: + case MetadataKind::ExistentialMetatype: + // Metatypes themselves can't have suppressible protocols. + return std::nullopt; + + case MetadataKind::Existential: + // The existential representation has no room for specifying any + // suppressed requirements, so it always succeeds. + return std::nullopt; + + case MetadataKind::LastEnumerated: + break; + } + + // Just accept any unknown types. + return std::nullopt; +} + +/// Check that the given `type` meets all suppressible protocol requirements +/// that haven't been explicitly suppressed by `ignored`. +std::optional +checkSuppressibleRequirements(const Metadata *type, + SuppressibleProtocolSet ignored) { + auto contextDescriptor = type->getTypeContextDescriptor(); + if (!contextDescriptor) + return checkSuppressibleRequirementsStructural(type, ignored); + + // If no conformances are suppressed, then it conforms to everything. + if (!contextDescriptor->hasSuppressibleProtocols()) { + return std::nullopt; + } + + // If this type has suppressed conformances, but we can't find them... + // bail out. + auto suppressedProtocols = contextDescriptor->getSuppressedProtocols(); + if (!suppressedProtocols) { + return TYPE_LOOKUP_ERROR_FMT("unable to find suppressed protocols"); + } + + // Determine the set of suppressible conformances that the type has + // suppressed but aren't being ignored. These are missing conformances + // based on the primary definition of the type. + SuppressibleProtocolSet missingConformances = *suppressedProtocols - ignored; + if (missingConformances.empty()) + return std::nullopt; + + // If the context descriptor is not generic, there are no conditional + // conformances: fail. + if (!contextDescriptor->isGeneric()) { + return TYPE_LOOKUP_ERROR_FMT("type missing suppressible conformances %x", + missingConformances.rawBits()); + } + + auto genericContext = contextDescriptor->getGenericContext(); + if (!genericContext || + !genericContext->hasConditionalSuppressedProtocols()) { + return TYPE_LOOKUP_ERROR_FMT("type missing suppressible conformances %x", + missingConformances.rawBits()); + } + + // If there are missing conformances that do not have corresponding + // conditional conformances, then the nominal type does not satisfy these + // suppressed conformances. We're done. + auto conditionalSuppressed = + genericContext->getConditionalSuppressedProtocols(); + auto alwaysMissingConformances = missingConformances - conditionalSuppressed; + if (!alwaysMissingConformances.empty()) { + return TYPE_LOOKUP_ERROR_FMT("type missing suppressible conformances %x", + alwaysMissingConformances.rawBits()); + } + + // Now we need to check the conditional conformances for each of the + // missing conformances. + for (auto suppressibleKind : missingConformances) { + // Get the conditional requirements. + // Note: This will end up being quadratic in the number of suppressible + // protocols. That number is small (currently 2) and cannot be more than 16, + // but if it's a problem we can switch to a different strategy. + auto condReqs = + genericContext->getConditionalSuppressibleProtocolRequirementsFor( + suppressibleKind); + + // Check the conditional requirements. + llvm::ArrayRef requirements( + reinterpret_cast(condReqs.data()), + condReqs.size()); + SubstGenericParametersFromMetadata substFn(type); + llvm::SmallVector extraArguments; + auto error = _checkGenericRequirements( + genericContext->getGenericParams(), + requirements, extraArguments, + [&substFn](unsigned depth, unsigned index) { + return substFn.getMetadata(depth, index).Ptr; + }, + [&substFn](unsigned ordinal) { + return substFn.getMetadataOrdinal(ordinal).Ptr; + }, + [&substFn](const Metadata *type, unsigned index) { + return substFn.getWitnessTable(type, index); + }); + if (error) + return error; + } + + return std::nullopt; +} + std::optional swift::_checkGenericRequirements( + llvm::ArrayRef genericParams, llvm::ArrayRef requirements, llvm::SmallVectorImpl &extraArguments, SubstGenericParameterFn substGenericParam, + SubstGenericParameterOrdinalFn substGenericParamOrdinal, SubstDependentWitnessTableFn substWitnessTable) { + // The suppressed conformances for each generic parameter. + llvm::SmallVector allSuppressed; + for (const auto &req : requirements) { if (req.getFlags().isPackRequirement()) { auto error = checkGenericPackRequirement(req, extraArguments, substGenericParam, - substWitnessTable); + substWitnessTable, + allSuppressed); if (error) return error; } else { auto error = checkGenericRequirement(req, extraArguments, substGenericParam, - substWitnessTable); + substWitnessTable, + allSuppressed); if (error) return error; } } + // Now, check all of the generic arguments for suppressible protocols. + unsigned numGenericParams = genericParams.size(); + for (unsigned index = 0; index != numGenericParams; ++index) { + // Non-key arguments don't need to be checked, because they are + // aliased to another type. + if (!genericParams[index].hasKeyArgument()) + continue; + + SuppressibleProtocolSet suppressed; + if (index < allSuppressed.size()) + suppressed = allSuppressed[index]; + + MetadataOrPack metadataOrPack(substGenericParamOrdinal(index)); + switch (genericParams[index].getKind()) { + case GenericParamKind::Type: { + if (!metadataOrPack || metadataOrPack.isMetadataPack()) { + return TYPE_LOOKUP_ERROR_FMT( + "unexpected pack for generic parameter %u", index); + } + + auto metadata = metadataOrPack.getMetadata(); + if (auto error = checkSuppressibleRequirements(metadata, suppressed)) + return error; + + break; + } + + case GenericParamKind::TypePack: { + // NULL can be used to indicate an empty pack. + if (!metadataOrPack) + break; + + if (metadataOrPack.isMetadata()) { + return TYPE_LOOKUP_ERROR_FMT( + "unexpected metadata for generic pack parameter %u", index); + } + + auto pack = metadataOrPack.getMetadataPack(); + if (pack.getElements() != 0) { + llvm::ArrayRef elements( + pack.getElements(), pack.getNumElements()); + for (auto element : elements) { + if (auto error = checkSuppressibleRequirements(element, suppressed)) + return error; + } + } + break; + } + + default: + return TYPE_LOOKUP_ERROR_FMT("unknown generic parameter kind %u", + index); + } + } + // Success! return std::nullopt; } diff --git a/test/Interpreter/escapable_generics_casting.swift b/test/Interpreter/escapable_generics_casting.swift new file mode 100644 index 0000000000000..fe1d39d77de1d --- /dev/null +++ b/test/Interpreter/escapable_generics_casting.swift @@ -0,0 +1,50 @@ +// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes) | %FileCheck %s +// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes) | %FileCheck %s + +// REQUIRES: executable_test, asserts + +protocol P { + func speak() +} + +extension P { + func speak() { print("hello") } +} + +struct Nonescapable: ~Escapable {} +struct Ordinary {} + +struct Dog: Escapable {} +extension Dog: P where T: Escapable {} + +func attemptCall(_ a: Any) { + if let value = a as? P { + value.speak() + return + } + print("failed to cast (attemptCall)") +} + +defer { main({}) } +func main(_ noEscapeFunc: () -> Void) { + // CHECK: hello + attemptCall(Dog()) + + // CHECK: failed to cast (attemptCall) + attemptCall(Dog()) + + // CHECK: function types + print("function types") + + // CHECK: hello + attemptCall(Dog<() -> Void>()) + + // CHECK: failed to cast (attemptCall) + func doFuncCall(_: F.Type) { + attemptCall(Dog()) + } + // This is the mangled name for a non-escaping Swift function type. + let fnType = _typeByName("yyXE")! + _openExistential(fnType, do: doFuncCall) +} + diff --git a/test/Interpreter/moveonly_generics_casting.swift b/test/Interpreter/moveonly_generics_casting.swift index 44c662d8d5110..776d0357dd9e0 100644 --- a/test/Interpreter/moveonly_generics_casting.swift +++ b/test/Interpreter/moveonly_generics_casting.swift @@ -21,7 +21,16 @@ enum Cat: Copyable { case meows init() { self = .meows } } -extension Cat: P where Left: Copyable {} +extension Cat: P where Left: Copyable, Right: ~Copyable {} + +struct ConditionallyCopyable: ~Copyable { + var value: Int = 17 +} +extension ConditionallyCopyable: Copyable where T: Copyable { } + +// FIXME: Not yet supported +// struct VariadicCopyable: Copyable { } +// extension VariadicCopyable: P where repeat each T: Copyable { } func attemptCall(_ a: Any) { if let value = a as? P { @@ -31,22 +40,91 @@ func attemptCall(_ a: Any) { print("failed to cast (attemptCall)") } +@_silgen_name("swift_getExtendedFunctionTypeMetadata") +func _getExtendedFunctionTypeMetadata( + flags: UInt, differentiabilityKind: UInt, + parameterTypes: UnsafePointer?, + parameterFlags: UnsafePointer?, + resultType: Any.Type, globalActorType: Any.Type? = nil, + extendedFlags: UInt32, thrownErrorType: Any.Type? = nil) -> Any.Type + + defer { main() } func main() { // CHECK: hello attemptCall(Dog()) - // FIXME: this is NOT suppose to succeed! (rdar://123466649) - // CHECK: hello + // CHECK: failed to cast (attemptCall) attemptCall(Dog()) + // CHECK: failed to cast (attemptCall) + attemptCall(Dog>()) + // CHECK: hello attemptCall(Cat()) - // FIXME: this is NOT suppose to succeed! (rdar://123466649) - // CHECK: hello + // CHECK: failed to cast (attemptCall) attemptCall(Cat()) + // CHECK: failed to cast (attemptCall) + attemptCall(Cat>()) + + // CHECK-FIXME: hello + // attemptCall(VariadicCopyable>()) + + // CHECK-FIXME: failed to cast (attemptCall) + // attemptCall(VariadicCopyable>()) + + // CHECK: tuple types + print("tuple types") + + // CHECK: hello + attemptCall(Dog<(Ordinary, Ordinary)>()) + + // CHECK-FIXME: failed to cast (attemptCall) + // FIXME: Requires the ability to create such tuple types + // attemptCall(Dog<(Ordinary, Noncopyable)>()) + + // CHECK: metatype types + print("metatype types") + + // CHECK: hello + attemptCall(Dog()) + + // CHECK: hello + attemptCall(Dog()) + + // CHECK: function types + print("function types") + + attemptCall(Dog<(Ordinary) -> Noncopyable>()) + + // This is a nonmovable function type, which cannot currently be + // expressed in the language. + let noncopyableFnType = _getExtendedFunctionTypeMetadata( + flags: 0x04000000 | 0x80000000, + differentiabilityKind: 0, + parameterTypes: nil, + parameterFlags: nil, + resultType: Void.self, + extendedFlags: 0x1 << 16) + + // CHECK: failed to cast (attemptCall) + func doFuncCall(_: F.Type) { + attemptCall(Dog()) + } + _openExistential(noncopyableFnType, do: doFuncCall) + + // CHECK: existential types + print("existential types") + + // CHECK: hello + attemptCall(Dog()) + + // CHECK-FIXME: failed to cast (attemptCall) + typealias NoncopyableAny = ~Copyable + // FIXME crashes: attemptCall(Dog()) + // CHECK: cast succeeded test_radar124171788(.nothing) } diff --git a/unittests/runtime/MetadataObjectBuilder.h b/unittests/runtime/MetadataObjectBuilder.h index 8431b4dd23094..355b1febab9db 100644 --- a/unittests/runtime/MetadataObjectBuilder.h +++ b/unittests/runtime/MetadataObjectBuilder.h @@ -34,7 +34,7 @@ inline void addModuleContextDescriptor(AnyObjectBuilder &builder, auto contextFlags = ContextDescriptorFlags(ContextDescriptorKind::Module, /*generic*/ false, /*unique*/ true, - /*version*/ 0, + /*hasSuppressibleProtocols*/ false, /*kindSpecific*/ 0); builder.add32(contextFlags.getIntValue()); @@ -55,7 +55,7 @@ inline void addProtocolDescriptor(AnyObjectBuilder &builder, auto contextFlags = ContextDescriptorFlags(ContextDescriptorKind::Protocol, /*generic*/ false, /*unique*/ true, - /*version*/ 0, + /*hasSuppressibleProtocols*/ false, /*kindSpecific*/ 0); builder.add32(contextFlags.getIntValue()); @@ -158,4 +158,4 @@ buildGlobalProtocolDescriptor(const ModuleContextDescriptor *module, Fn &&fn) { } // end namespace swift -#endif \ No newline at end of file +#endif