Skip to content

Commit b6557e0

Browse files
committed
[NFC] Enable suppression of inferred conformances.
Add the machinery to support suppression of inference of conformance to protocols that would otherwise be derived automatically. This commit does not enable any conformances to be suppressed.
1 parent af47b21 commit b6557e0

23 files changed

+424
-51
lines changed

include/swift/AST/Decl.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,17 +1633,27 @@ struct InheritedEntry : public TypeLoc {
16331633
/// Whether there was an @preconcurrency attribute.
16341634
bool IsPreconcurrency : 1;
16351635

1636+
/// Whether there was a ~ indicating suppression but not inversion.
1637+
bool IsSuppressed : 1;
1638+
16361639
public:
16371640
InheritedEntry(const TypeLoc &typeLoc);
16381641

16391642
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1640-
bool isPreconcurrency)
1643+
bool isPreconcurrency, bool isSuppressed = false)
16411644
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
1642-
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency) {}
1645+
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
1646+
IsSuppressed(isSuppressed) {}
16431647

16441648
bool isUnchecked() const { return IsUnchecked; }
16451649
bool isRetroactive() const { return IsRetroactive; }
16461650
bool isPreconcurrency() const { return IsPreconcurrency; }
1651+
bool isSuppressed() const { return IsSuppressed; }
1652+
1653+
void setSuppressed() {
1654+
assert(!IsSuppressed && "setting suppressed again!?");
1655+
IsSuppressed = true;
1656+
}
16471657
};
16481658

16491659
/// A wrapper for the collection of inherited types for either a `TypeDecl` or
@@ -4466,6 +4476,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
44664476
/// Type if it `isEscapable` instead of using this.
44674477
CanBeInvertible::Result canBeEscapable() const;
44684478

4479+
bool suppressesConformance(KnownProtocolKind kp) const;
4480+
44694481
// Implement isa/cast/dyncast/etc.
44704482
static bool classof(const Decl *D) {
44714483
return D->getKind() >= DeclKind::First_NominalTypeDecl &&

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7698,6 +7698,12 @@ NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
76987698
ERROR(bitwise_copyable_outside_module,none,
76997699
"conformance to 'BitwiseCopyable' must occur in the same module as %kind0",
77007700
(const ValueDecl *))
7701+
ERROR(suppress_inferrable_protocol_extension,none,
7702+
"conformance to inferrable protocol %0 cannot be suppressed in an extension", (const ProtocolDecl *))
7703+
ERROR(suppress_nonsuppressable_protocol,none,
7704+
"conformance to %0 cannot be suppressed", (const ProtocolDecl *))
7705+
WARNING(suppress_already_suppressed_protocol,none,
7706+
"already suppressed conformance to %0", (const ProtocolDecl *))
77017707

77027708
// -- older ones below --
77037709
ERROR(noncopyable_parameter_requires_ownership, none,

include/swift/AST/NameLookup.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,16 @@ struct InheritedNominalEntry : Located<NominalTypeDecl *> {
589589
/// The location of the "preconcurrency" attribute if present.
590590
SourceLoc preconcurrencyLoc;
591591

592+
/// Whether this inherited entry was suppressed via "~".
593+
bool isSuppressed;
594+
592595
InheritedNominalEntry() { }
593596

594597
InheritedNominalEntry(NominalTypeDecl *item, SourceLoc loc,
595-
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc)
598+
SourceLoc uncheckedLoc, SourceLoc preconcurrencyLoc,
599+
bool isSuppressed)
596600
: Located(item, loc), uncheckedLoc(uncheckedLoc),
597-
preconcurrencyLoc(preconcurrencyLoc) {}
601+
preconcurrencyLoc(preconcurrencyLoc), isSuppressed(isSuppressed) {}
598602
};
599603

600604
/// Retrieve the set of nominal type declarations that are directly

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ struct PrintOptions {
388388
/// Suppress printing of `borrowing` and `consuming`.
389389
bool SuppressNoncopyableOwnershipModifiers = false;
390390

391+
/// Suppress printing of '~Proto' for suppressible, non-invertible protocols.
392+
bool SuppressConformanceSuppression = false;
393+
391394
/// List of attribute kinds that should not be printed.
392395
std::vector<AnyAttrKind> ExcludeAttrList = {
393396
DeclAttrKind::Transparent, DeclAttrKind::Effects,

include/swift/AST/TypeCheckRequests.h

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,25 @@
1616
#ifndef SWIFT_TYPE_CHECK_REQUESTS_H
1717
#define SWIFT_TYPE_CHECK_REQUESTS_H
1818

19-
#include "swift/AST/ActorIsolation.h"
20-
#include "swift/AST/AnyFunctionRef.h"
2119
#include "swift/AST/ASTNode.h"
2220
#include "swift/AST/ASTTypeIDs.h"
21+
#include "swift/AST/ActorIsolation.h"
22+
#include "swift/AST/AnyFunctionRef.h"
2323
#include "swift/AST/CatchNode.h"
2424
#include "swift/AST/Effects.h"
25+
#include "swift/AST/Evaluator.h"
2526
#include "swift/AST/GenericParamList.h"
2627
#include "swift/AST/GenericSignature.h"
27-
#include "swift/AST/Type.h"
28-
#include "swift/AST/Evaluator.h"
2928
#include "swift/AST/Pattern.h"
3029
#include "swift/AST/PluginRegistry.h"
3130
#include "swift/AST/ProtocolConformance.h"
3231
#include "swift/AST/SimpleRequest.h"
3332
#include "swift/AST/SourceFile.h"
33+
#include "swift/AST/Type.h"
3434
#include "swift/AST/TypeResolutionStage.h"
3535
#include "swift/Basic/Statistic.h"
36+
#include "swift/Basic/TaggedUnion.h"
37+
#include "swift/Basic/TypeID.h"
3638
#include "llvm/ADT/Hashing.h"
3739
#include "llvm/ADT/STLExtras.h"
3840
#include "llvm/ADT/TinyPtrVector.h"
@@ -78,13 +80,62 @@ void simple_display(
7880

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

83+
/// Emulates the following enum with associated values:
84+
/// enum InheritedTypeResult {
85+
/// case inherited(Type)
86+
/// case suppressed(KnownProtocolKind, TypeRepr *)
87+
/// case `default`
88+
/// }
89+
class InheritedTypeResult {
90+
struct Inherited_ {
91+
Type ty;
92+
};
93+
struct Suppressed_ {
94+
KnownProtocolKind kp;
95+
TypeRepr *repr;
96+
};
97+
struct Default_ {};
98+
using Payload = TaggedUnion<Inherited_, Suppressed_, Default_>;
99+
Payload payload;
100+
InheritedTypeResult(Payload payload) : payload(payload) {}
101+
102+
public:
103+
enum Kind { Inherited, Suppressed, Default };
104+
static InheritedTypeResult forInherited(Type ty) { return {Inherited_{ty}}; }
105+
static InheritedTypeResult forSuppressed(KnownProtocolKind kp,
106+
TypeRepr *repr) {
107+
return {Suppressed_{kp, repr}};
108+
}
109+
static InheritedTypeResult forDefault() { return {Default_{}}; }
110+
operator Kind() {
111+
if (payload.isa<Inherited_>()) {
112+
return Kind::Inherited;
113+
} else if (payload.isa<Suppressed_>()) {
114+
return Kind::Suppressed;
115+
}
116+
return Kind::Default;
117+
}
118+
explicit operator bool() { return !payload.isa<Default_>(); }
119+
Type getInheritedTypeOrNull() {
120+
if (auto *inherited = payload.dyn_cast<Inherited_>())
121+
return inherited->ty;
122+
return Type();
123+
}
124+
Type getInheritedType() { return payload.get<Inherited_>().ty; }
125+
std::pair<KnownProtocolKind, TypeRepr *> getSuppressed() {
126+
auto &suppressed = payload.get<Suppressed_>();
127+
return {suppressed.kp, suppressed.repr};
128+
}
129+
};
130+
81131
/// Request the type from the ith entry in the inheritance clause for the
82132
/// given declaration.
83133
class InheritedTypeRequest
84134
: public SimpleRequest<
85135
InheritedTypeRequest,
86-
Type(llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
87-
unsigned, TypeResolutionStage),
136+
InheritedTypeResult(
137+
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
138+
unsigned, TypeResolutionStage),
88139
RequestFlags::SeparatelyCached> {
89140
public:
90141
using SimpleRequest::SimpleRequest;
@@ -93,21 +144,22 @@ class InheritedTypeRequest
93144
friend SimpleRequest;
94145

95146
// Evaluation.
96-
Type
147+
InheritedTypeResult
97148
evaluate(Evaluator &evaluator,
98149
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
99150
unsigned index, TypeResolutionStage stage) const;
100151

101-
const TypeLoc &getTypeLoc() const;
152+
const InheritedEntry &getInheritedEntry() const;
153+
ASTContext &getASTContext() const;
102154

103155
public:
104156
// Source location
105157
SourceLoc getNearestLoc() const;
106158

107159
// Caching
108160
bool isCached() const;
109-
std::optional<Type> getCachedResult() const;
110-
void cacheResult(Type value) const;
161+
std::optional<InheritedTypeResult> getCachedResult() const;
162+
void cacheResult(InheritedTypeResult value) const;
111163
};
112164

113165
/// Request the superclass type for the given class.
@@ -4869,6 +4921,23 @@ class InheritanceClauseRequest
48694921
void cacheResult(ArrayRef<InheritedEntry>) const;
48704922
};
48714923

4924+
class SuppressesConformanceRequest
4925+
: public SimpleRequest<SuppressesConformanceRequest,
4926+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
4927+
RequestFlags::Cached> {
4928+
public:
4929+
using SimpleRequest::SimpleRequest;
4930+
4931+
private:
4932+
friend SimpleRequest;
4933+
4934+
bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
4935+
KnownProtocolKind kp) const;
4936+
4937+
public:
4938+
bool isCached() const { return true; }
4939+
};
4940+
48724941
#define SWIFT_TYPEID_ZONE TypeChecker
48734942
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
48744943
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,4 +565,7 @@ SWIFT_REQUEST(TypeChecker, LocalTypeDeclsRequest,
565565
SWIFT_REQUEST(TypeChecker, ObjCRequirementMapRequest,
566566
ObjCRequirementMap(const ProtocolDecl *proto),
567567
Cached, NoLocationInfo)
568+
SWIFT_REQUEST(TypeChecker, SuppressesConformanceRequest,
569+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
570+
SeparatelyCached, NoLocationInfo)
568571

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ EXPERIMENTAL_FEATURE(ExtractConstantsFromMembers, false)
339339
/// Enable bitwise-copyable feature.
340340
EXPERIMENTAL_FEATURE(BitwiseCopyable, true)
341341

342+
/// Enable the suppression of inferred, non-invertible, protocols via ~.
343+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ConformanceSuppression, true)
344+
342345
/// Enables the FixedArray data type.
343346
EXPERIMENTAL_FEATURE(FixedArrays, true)
344347

include/swift/Parse/Parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ class Parser {
163163
// to ensure there's no parsing performance regression.
164164
bool EnabledNoncopyableGenerics;
165165

166+
bool canSuppressConformancesWithTilde() const {
167+
return EnabledNoncopyableGenerics ||
168+
Context.LangOpts.hasFeature(Feature::ConformanceSuppression);
169+
}
170+
166171
/// Whether we should delay parsing nominal type, extension, and function
167172
/// bodies.
168173
bool isDelayedParsingEnabled() const;

lib/AST/ASTPrinter.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,6 +2796,8 @@ void PrintAST::printInherited(const Decl *decl) {
27962796
Printer << "@retroactive ";
27972797
if (inherited.isPreconcurrency())
27982798
Printer << "@preconcurrency ";
2799+
if (inherited.isSuppressed())
2800+
Printer << "~";
27992801
});
28002802
}, [&]() {
28012803
Printer << ", ";
@@ -3140,6 +3142,17 @@ static void suppressingFeatureNoncopyableGenerics(
31403142
options.ExcludeAttrList.resize(originalExcludeAttrCount);
31413143
}
31423144

3145+
static void
3146+
suppressingFeatureConformanceSuppression(PrintOptions &options,
3147+
llvm::function_ref<void()> action) {
3148+
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
3149+
options.ExcludeAttrList.push_back(DeclAttrKind::PreInverseGenerics);
3150+
llvm::SaveAndRestore<bool> scope(options.SuppressConformanceSuppression,
3151+
true);
3152+
action();
3153+
options.ExcludeAttrList.resize(originalExcludeAttrCount);
3154+
}
3155+
31433156
/// Suppress the printing of a particular feature.
31443157
static void suppressingFeature(PrintOptions &options, Feature feature,
31453158
llvm::function_ref<void()> action) {
@@ -7854,6 +7867,10 @@ swift::getInheritedForPrinting(
78547867
continue;
78557868
}
78567869
}
7870+
if (options.SuppressConformanceSuppression &&
7871+
inherited.getEntry(i).isSuppressed()) {
7872+
continue;
7873+
}
78577874

78587875
Results.push_back(inherited.getEntry(i));
78597876
}

lib/AST/ConformanceLookupTable.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,14 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
302302
bool anyObject = false;
303303
for (const auto &found :
304304
getDirectlyInheritedNominalTypeDecls(nominal, inverses, anyObject)) {
305-
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
305+
auto proto = dyn_cast<ProtocolDecl>(found.Item);
306+
if (!proto)
307+
continue;
308+
auto kp = proto->getKnownProtocolKind();
309+
assert(!found.isSuppressed ||
310+
kp.has_value() &&
311+
"suppressed conformance for non-known protocol!?");
312+
if (!found.isSuppressed) {
306313
addProtocol(proto, found.Loc,
307314
source.withUncheckedLoc(found.uncheckedLoc)
308315
.withPreconcurrencyLoc(found.preconcurrencyLoc));

lib/AST/Decl.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,8 @@ Type InheritedTypes::getResolvedType(unsigned i,
16561656
? Decl.get<const ExtensionDecl *>()->getASTContext()
16571657
: Decl.get<const TypeDecl *>()->getASTContext();
16581658
return evaluateOrDefault(ctx.evaluator, InheritedTypeRequest{Decl, i, stage},
1659-
Type());
1659+
InheritedTypeResult::forDefault())
1660+
.getInheritedTypeOrNull();
16601661
}
16611662

16621663
ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc,
@@ -5369,6 +5370,13 @@ bool NominalTypeDecl::isMainActor() const {
53695370
getParentModule()->getName() == getASTContext().Id_Concurrency;
53705371
}
53715372

5373+
bool NominalTypeDecl::suppressesConformance(KnownProtocolKind kp) const {
5374+
auto mutableThis = const_cast<NominalTypeDecl *>(this);
5375+
return evaluateOrDefault(getASTContext().evaluator,
5376+
SuppressesConformanceRequest{mutableThis, kp},
5377+
false);
5378+
}
5379+
53725380
GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC,
53735381
Identifier name, SourceLoc nameLoc,
53745382
ArrayRef<InheritedEntry> inherited,

lib/AST/FeatureSet.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,27 @@ UNINTERESTING_FEATURE(BorrowingSwitch)
673673

674674
UNINTERESTING_FEATURE(ClosureIsolation)
675675

676+
static bool usesFeatureConformanceSuppression(Decl *decl) {
677+
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
678+
if (!nominal)
679+
return false;
680+
681+
auto inherited = InheritedTypes(nominal);
682+
for (auto index : indices(inherited.getEntries())) {
683+
// Ensure that InheritedTypeRequest has set the isSuppressed bit if
684+
// appropriate.
685+
auto ty = inherited.getResolvedType(index);
686+
(void)ty;
687+
688+
auto entry = inherited.getEntry(index);
689+
if (entry.isSuppressed()) {
690+
return true;
691+
}
692+
}
693+
694+
return false;
695+
}
696+
676697
static bool usesFeatureIsolatedAny(Decl *decl) {
677698
return usesTypeMatching(decl, [](Type type) {
678699
if (auto fnType = type->getAs<AnyFunctionType>()) {

0 commit comments

Comments
 (0)