Skip to content

[BitwiseCopyable] Allow suppression via ~. #72646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1628,17 +1628,29 @@ struct InheritedEntry : public TypeLoc {
/// Whether there was an @preconcurrency attribute.
bool IsPreconcurrency : 1;

/// Whether there was a ~ indicating suppression.
///
/// This is true in cases like ~Copyable but not (P & ~Copyable).
bool IsSuppressed : 1;

public:
InheritedEntry(const TypeLoc &typeLoc);

InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
bool isPreconcurrency)
bool isPreconcurrency, bool isSuppressed = false)
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency) {}
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
IsSuppressed(isSuppressed) {}

bool isUnchecked() const { return IsUnchecked; }
bool isRetroactive() const { return IsRetroactive; }
bool isPreconcurrency() const { return IsPreconcurrency; }
bool isSuppressed() const { return IsSuppressed; }

void setSuppressed() {
assert(!IsSuppressed && "setting suppressed again!?");
IsSuppressed = true;
}
};

/// A wrapper for the collection of inherited types for either a `TypeDecl` or
Expand Down Expand Up @@ -4436,6 +4448,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
/// Type if it `isEscapable` instead of using this.
CanBeInvertible::Result canBeEscapable() const;

bool suppressesConformance(KnownProtocolKind kp) const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_NominalTypeDecl &&
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7696,6 +7696,8 @@ ERROR(non_bitwise_copyable_type_indirect_enum_element,none,
"enum with indirect case cannot conform to 'BitwiseCopyable'", ())
NOTE(note_non_bitwise_copyable_type_indirect_enum_element,none,
"indirect case is here", ())
ERROR(non_bitwise_copyable_type_suppressed,none,
"cannot both conform to and suppress conformance to 'BitwiseCopyable'", ())
ERROR(non_bitwise_copyable_type_cxx_nontrivial,none,
"non-trivial C++ type cannot conform to 'BitwiseCopyable'", ())
ERROR(non_bitwise_copyable_c_type_nontrivial,none,
Expand All @@ -7721,6 +7723,12 @@ NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
ERROR(bitwise_copyable_outside_module,none,
"conformance to 'BitwiseCopyable' must occur in the same module as %kind0",
(const ValueDecl *))
ERROR(suppress_inferrable_protocol_extension,none,
"conformance to inferrable protocol %0 cannot be suppressed in an extension", (const ProtocolDecl *))
ERROR(suppress_nonsuppressable_protocol,none,
"conformance to %0 cannot be suppressed", (const ProtocolDecl *))
WARNING(suppress_already_suppressed_protocol,none,
"already suppressed conformance to %0", (const ProtocolDecl *))

// -- older ones below --
ERROR(noncopyable_parameter_requires_ownership, none,
Expand Down
15 changes: 14 additions & 1 deletion include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
PROTOCOL_WITH_NAME(Id, Name)
#endif

/// \def REPRESSIBLE_PROTOCOL_WITH_NAME(id, name)
/// \param id the internal "enum name" of this protocol
/// \param name a string literal with the external name of this protocol
#ifndef REPRESSIBLE_PROTOCOL_WITH_NAME
#define REPRESSIBLE_PROTOCOL_WITH_NAME(Id, Name) \
PROTOCOL_WITH_NAME(Id, Name)
#endif

#define REPRESSIBLE_PROTOCOL(name) REPRESSIBLE_PROTOCOL_WITH_NAME(name, #name)
#define REPRESSIBLE_PROTOCOL_(name) REPRESSIBLE_PROTOCOL_WITH_NAME(name, "_" #name)

#define PROTOCOL(name) PROTOCOL_WITH_NAME(name, #name)
#define PROTOCOL_(name) PROTOCOL_WITH_NAME(name, "_" #name)
Expand Down Expand Up @@ -140,7 +150,7 @@ PROTOCOL(FloatingPoint)
INVERTIBLE_PROTOCOL_WITH_NAME(Name, #Name)
#include "swift/ABI/InvertibleProtocols.def"

PROTOCOL_(BitwiseCopyable)
REPRESSIBLE_PROTOCOL_(BitwiseCopyable)

EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
Expand Down Expand Up @@ -169,6 +179,9 @@ BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByBuiltinUnicodeScalarLitera
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
#undef BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_
#undef BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
#undef REPRESSIBLE_PROTOCOL_WITH_NAME
#undef REPRESSIBLE_PROTOCOL
#undef REPRESSIBLE_PROTOCOL_
#undef INVERTIBLE_PROTOCOL_WITH_NAME
#undef PROTOCOL
#undef PROTOCOL_
Expand Down
36 changes: 36 additions & 0 deletions include/swift/AST/KnownProtocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ enum class KnownProtocolKind : uint8_t {
#include "swift/AST/KnownProtocols.def"
};

enum class RepressibleProtocolKind : uint8_t {
#define REPRESSIBLE_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
Expand All @@ -44,6 +49,14 @@ enum : unsigned { NumKnownProtocolKindBits =
/// Retrieve the name of the given known protocol.
llvm::StringRef getProtocolName(KnownProtocolKind kind);

std::optional<RepressibleProtocolKind>
getRepressibleProtocolKind(KnownProtocolKind kp);

KnownProtocolKind getKnownProtocolKind(RepressibleProtocolKind ip);

void simple_display(llvm::raw_ostream &out,
const RepressibleProtocolKind &value);

/// MARK: Invertible protocols
///
/// The invertible protocols are a subset of the known protocols.
Expand Down Expand Up @@ -71,4 +84,27 @@ void simple_display(llvm::raw_ostream &out,

} // end namespace swift

namespace llvm {
template <typename T, typename Enable>
struct DenseMapInfo;
template <>
struct DenseMapInfo<swift::RepressibleProtocolKind> {
using RepressibleProtocolKind = swift::RepressibleProtocolKind;
using Impl = DenseMapInfo<uint8_t>;
static inline RepressibleProtocolKind getEmptyKey() {
return (RepressibleProtocolKind)Impl::getEmptyKey();
}
static inline RepressibleProtocolKind getTombstoneKey() {
return (RepressibleProtocolKind)Impl::getTombstoneKey();
}
static unsigned getHashValue(const RepressibleProtocolKind &Val) {
return Impl::getHashValue((uint8_t)Val);
}
static bool isEqual(const RepressibleProtocolKind &LHS,
const RepressibleProtocolKind &RHS) {
return LHS == RHS;
}
};
} // namespace llvm

#endif
8 changes: 6 additions & 2 deletions include/swift/AST/NameLookup.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,12 +589,16 @@ struct InheritedNominalEntry : Located<NominalTypeDecl *> {
/// The location of the "preconcurrency" attribute if present.
SourceLoc preconcurrencyLoc;

/// Whether this inherited entry was suppressed via "~".
bool isSuppressed;

InheritedNominalEntry() { }

InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc,
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc)
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc,
bool isSuppressed)
: Located(item, loc), uncheckedLoc(uncheckedLoc),
preconcurrencyLoc(preconcurrencyLoc) {}
preconcurrencyLoc(preconcurrencyLoc), isSuppressed(isSuppressed) {}
};

/// Retrieve the set of nominal type declarations that are directly
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ struct PrintOptions {
/// Suppress printing of `borrowing` and `consuming`.
bool SuppressNoncopyableOwnershipModifiers = false;

/// Suppress printing of '~Proto' for suppressible, non-invertible protocols.
bool SuppressConformanceSuppression = false;

/// List of attribute kinds that should not be printed.
std::vector<AnyAttrKind> ExcludeAttrList = {
DeclAttrKind::Transparent, DeclAttrKind::Effects,
Expand Down
103 changes: 93 additions & 10 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@
#ifndef SWIFT_TYPE_CHECK_REQUESTS_H
#define SWIFT_TYPE_CHECK_REQUESTS_H

#include "swift/AST/ActorIsolation.h"
#include "swift/AST/AnyFunctionRef.h"
#include "swift/AST/ASTNode.h"
#include "swift/AST/ASTTypeIDs.h"
#include "swift/AST/ActorIsolation.h"
#include "swift/AST/AnyFunctionRef.h"
#include "swift/AST/CatchNode.h"
#include "swift/AST/Effects.h"
#include "swift/AST/Evaluator.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Type.h"
#include "swift/AST/Evaluator.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PluginRegistry.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SimpleRequest.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeResolutionStage.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/TaggedUnion.h"
#include "swift/Basic/TypeID.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/TinyPtrVector.h"
Expand All @@ -51,6 +53,7 @@ class DoCatchStmt;
struct ExternalMacroDefinition;
class ClosureExpr;
class GenericParamList;
class InverseTypeRepr;
class LabeledStmt;
class MacroDefinition;
class PrecedenceGroupDecl;
Expand Down Expand Up @@ -78,13 +81,75 @@ void simple_display(

void simple_display(llvm::raw_ostream &out, ASTContext *ctx);

/// Emulates the following enum with associated values:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a lot of new code for just the new ~BitwiseCopyable syntax. Can you instead find a way to generalize the existing logic for ~Copyable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having a different representation for this case--the case of a protocol that isn't conformed to by all types by default (i.e. unlike Copyable) but is inferred automatically for some types--makes sense.

/// enum InheritedTypeResult {
/// case inherited(Type)
/// case suppressed(Type, InverseTypeRepr *)
/// case `default`
/// }
class InheritedTypeResult {
struct Inherited_ {
Type ty;
};
struct Suppressed_ {
// The type which is suppressed. Not the result of inverting a protocol.
Type ty;
InverseTypeRepr *repr;
};
struct Default_ {};
using Payload = TaggedUnion<Inherited_, Suppressed_, Default_>;
Payload payload;
InheritedTypeResult(Payload payload) : payload(payload) {}

public:
enum Kind { Inherited, Suppressed, Default };
static InheritedTypeResult forInherited(Type ty) { return {Inherited_{ty}}; }
static InheritedTypeResult forSuppressed(Type ty, InverseTypeRepr *repr) {
return {Suppressed_{ty, repr}};
}
static InheritedTypeResult forDefault() { return {Default_{}}; }
operator Kind() {
if (payload.isa<Inherited_>()) {
return Kind::Inherited;
} else if (payload.isa<Suppressed_>()) {
return Kind::Suppressed;
}
return Kind::Default;
}
explicit operator bool() { return !payload.isa<Default_>(); }
Type getInheritedTypeOrNull(ASTContext &ctx) {
switch (*this) {
case Inherited:
return getInheritedType();
case Suppressed: {
auto suppressed = getSuppressed();
auto kp = suppressed.first->getKnownProtocol();
if (!kp)
return Type();
auto ipk = getInvertibleProtocolKind(*kp);
if (!ipk)
return Type();
return ProtocolCompositionType::getInverseOf(ctx, *ipk);
}
case Default:
return Type();
}
}
Type getInheritedType() { return payload.get<Inherited_>().ty; }
std::pair<Type, InverseTypeRepr *> getSuppressed() {
auto &suppressed = payload.get<Suppressed_>();
return {suppressed.ty, suppressed.repr};
}
};

/// Request the type from the ith entry in the inheritance clause for the
/// given declaration.
class InheritedTypeRequest
: public SimpleRequest<
InheritedTypeRequest,
Type(llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
unsigned, TypeResolutionStage),
InheritedTypeResult(
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
unsigned, TypeResolutionStage),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -93,21 +158,22 @@ class InheritedTypeRequest
friend SimpleRequest;

// Evaluation.
Type
InheritedTypeResult
evaluate(Evaluator &evaluator,
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
unsigned index, TypeResolutionStage stage) const;

const TypeLoc &getTypeLoc() const;
const InheritedEntry &getInheritedEntry() const;
ASTContext &getASTContext() const;

public:
// Source location
SourceLoc getNearestLoc() const;

// Caching
bool isCached() const;
std::optional<Type> getCachedResult() const;
void cacheResult(Type value) const;
std::optional<InheritedTypeResult> getCachedResult() const;
void cacheResult(InheritedTypeResult value) const;
};

/// Request the superclass type for the given class.
Expand Down Expand Up @@ -4885,6 +4951,23 @@ class LifetimeDependenceInfoRequest
bool isCached() const { return true; }
};

class SuppressesConformanceRequest
: public SimpleRequest<SuppressesConformanceRequest,
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
KnownProtocolKind kp) const;

public:
bool isCached() const { return true; }
};

#define SWIFT_TYPEID_ZONE TypeChecker
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,6 @@ SWIFT_REQUEST(TypeChecker, ImportDeclRequest,
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, LifetimeDependenceInfoRequest,
LifetimeDependenceInfo(AbstractFunctionDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SuppressesConformanceRequest,
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
SeparatelyCached, NoLocationInfo)
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ EXPERIMENTAL_FEATURE(ExtractConstantsFromMembers, false)
/// Enable bitwise-copyable feature.
EXPERIMENTAL_FEATURE(BitwiseCopyable, true)

/// Enable the suppression of inferred, non-invertible, protocols via ~.
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ConformanceSuppression, true)

/// Enables the FixedArray data type.
EXPERIMENTAL_FEATURE(FixedArrays, true)

Expand Down
5 changes: 5 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ class Parser {
// to ensure there's no parsing performance regression.
bool EnabledNoncopyableGenerics;

bool canSuppressConformancesWithTilde() const {
return EnabledNoncopyableGenerics ||
Context.LangOpts.hasFeature(Feature::ConformanceSuppression);
}

/// Whether we should delay parsing nominal type, extension, and function
/// bodies.
bool isDelayedParsingEnabled() const;
Expand Down
Loading