mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Introduce metadata and runtime support for describing conformances to "suppressible" protocols such as `Copyable`. The metadata changes occur in several different places: * Context descriptors gain a flag bit to indicate when the type itself has suppressed one or more suppressible protocols (e.g., it is `~Copyable`). When the bit is set, the context will have a trailing `SuppressibleProtocolSet`, a 16-bit bitfield that records one bit for each suppressed protocol. Types with no suppressed conformances will leave the bit unset (so the metadata is unchanged), and older runtimes don't look at the bit, so they will ignore the extra data. * Generic context descriptors gain a flag bit to indicate when the type has conditional conformances to suppressible protocols. When set, there will be trailing metadata containing another `SuppressibleProtocolSet` (a subset of the one in the main context descriptor) indicating which suppressible protocols have conditional conformances, followed by the actual lists of generic requirements for each of the conditional conformances. Again, if there are no conditional conformances to suppressible protocols, the bit won't be set. Old runtimes ignore the bit and any trailing metadata. * Generic requirements get a new "kind", which provides an ignored protocol set (another `SuppressibleProtocolSet`) stating which suppressible protocols should *not* be checked for the subject type of the generic requirement. For example, this encodes a requirement like `T: ~Copyable`. These generic requirements can occur anywhere that there is a generic requirement list, e.g., conditional conformances and extended existentials. Older runtimes handle unknown generic requirement kinds by stating that the requirement isn't satisfied. Extend the runtime to perform checking of the suppressible conformances on generic arguments as part of checking generic requirements. This checking follows the defaults of the language, which is that every generic argument must conform to each of the suppressible protocols unless there is an explicit generic requirement that states which suppressible protocols to ignore. Thus, a generic parameter list `<T, Y where T: ~Escapable>` will check that `T` is `Copyable` but not that it is `Escapable`, and check that `U` is both `Copyable` and `Escapable`. To implement this, we collect the ignored protocol sets from these suppressed requirements while processing the generic requirements, then check all of the generic arguments against any conformances not suppressed. Answering the actual question "does `X` conform to `Copyable`?" (for any suppressible protocol) looks at the context descriptor metadata to answer the question, e.g., 1. If there is no "suppressed protocol set", then the type conforms. This covers types that haven't suppressed any conformances, including all types that predate noncopyable generics. 2. If the suppressed protocol set doesn't contain `Copyable`, then the type conforms. 3. If the type is generic and has a conditional conformance to `Copyable`, evaluate the generic requirements for that conditional conformance to answer whether it conforms. The procedure above handles the bits of a `SuppressibleProtocolSet` opaquely, with no mapping down to specific protocols. Therefore, the same implementation will work even with future suppressible protocols, including back deployment. The end result of this is that we can dynamically evaluate conditional conformances to protocols that depend on conformances to suppressible protocols. Implements rdar://123466649.
82 lines
2.6 KiB
C++
82 lines
2.6 KiB
C++
//===--- KnownProtocols.h - Working with compiler protocols -----*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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"
|
|
#include "swift/Config.h"
|
|
|
|
namespace llvm {
|
|
class StringRef;
|
|
}
|
|
|
|
namespace swift {
|
|
|
|
/// The set of known protocols.
|
|
enum class KnownProtocolKind : uint8_t {
|
|
#define PROTOCOL_WITH_NAME(Id, Name) Id,
|
|
#include "swift/AST/KnownProtocols.def"
|
|
};
|
|
|
|
enum : uint8_t {
|
|
// This uses a preprocessor trick to count all the protocols. The enum value
|
|
// expression below expands to "+1+1+1...". (Note that the first plus
|
|
// is parsed as a unary operator.)
|
|
#define PROTOCOL_WITH_NAME(Id, Name) +1
|
|
/// The number of known protocols.
|
|
NumKnownProtocols =
|
|
#include "swift/AST/KnownProtocols.def"
|
|
};
|
|
|
|
enum : unsigned { NumKnownProtocolKindBits =
|
|
countBitsUsed(static_cast<unsigned>(NumKnownProtocols - 1)) };
|
|
|
|
/// Retrieve the name of the given known protocol.
|
|
llvm::StringRef getProtocolName(KnownProtocolKind kind);
|
|
|
|
/// MARK: Invertible protocols
|
|
///
|
|
/// The invertible protocols are a subset of the known protocols.
|
|
|
|
enum : uint8_t {
|
|
// Use preprocessor trick to count all the invertible protocols.
|
|
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) +1
|
|
/// The number of invertible protocols.
|
|
NumInvertibleProtocols =
|
|
#include "swift/AST/KnownProtocols.def"
|
|
};
|
|
|
|
using InvertibleProtocolSet = FixedBitSet<NumInvertibleProtocols,
|
|
InvertibleProtocolKind>;
|
|
|
|
/// Maps a KnownProtocol to the set of InvertibleProtocols, if a mapping exists.
|
|
/// \returns None if the known protocol is not invertible.
|
|
std::optional<InvertibleProtocolKind>
|
|
getInvertibleProtocolKind(KnownProtocolKind kp);
|
|
|
|
/// Returns the KnownProtocolKind corresponding to an InvertibleProtocolKind.
|
|
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
|