Skip to content

Commit bf6cb69

Browse files
committed
[Sema] 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 fca6724 commit bf6cb69

24 files changed

+411
-63
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,17 +1640,29 @@ struct InheritedEntry : public TypeLoc {
16401640
/// Whether there was an @preconcurrency attribute.
16411641
bool IsPreconcurrency : 1;
16421642

1643+
/// Whether there was a ~ indicating suppression.
1644+
///
1645+
/// This is true in cases like ~Copyable but not (P & ~Copyable).
1646+
bool IsSuppressed : 1;
1647+
16431648
public:
16441649
InheritedEntry(const TypeLoc &typeLoc);
16451650

16461651
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1647-
bool isPreconcurrency)
1652+
bool isPreconcurrency, bool isSuppressed = false)
16481653
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
1649-
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency) {}
1654+
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
1655+
IsSuppressed(isSuppressed) {}
16501656

16511657
bool isUnchecked() const { return IsUnchecked; }
16521658
bool isRetroactive() const { return IsRetroactive; }
16531659
bool isPreconcurrency() const { return IsPreconcurrency; }
1660+
bool isSuppressed() const { return IsSuppressed; }
1661+
1662+
void setSuppressed() {
1663+
assert(!IsSuppressed && "setting suppressed again!?");
1664+
IsSuppressed = true;
1665+
}
16541666
};
16551667

16561668
/// A wrapper for the collection of inherited types for either a `TypeDecl` or
@@ -4473,6 +4485,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
44734485
/// Type if it `isEscapable` instead of using this.
44744486
CanBeInvertible::Result canBeEscapable() const;
44754487

4488+
bool suppressesConformance(KnownProtocolKind kp) const;
4489+
44764490
// Implement isa/cast/dyncast/etc.
44774491
static bool classof(const Decl *D) {
44784492
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
@@ -7717,6 +7717,12 @@ NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
77177717
ERROR(bitwise_copyable_outside_module,none,
77187718
"conformance to 'BitwiseCopyable' must occur in the same module as %kind0",
77197719
(const ValueDecl *))
7720+
ERROR(suppress_inferrable_protocol_extension,none,
7721+
"conformance to inferrable protocol %0 cannot be suppressed in an extension", (const ProtocolDecl *))
7722+
ERROR(suppress_nonsuppressable_protocol,none,
7723+
"conformance to %0 cannot be suppressed", (const ProtocolDecl *))
7724+
WARNING(suppress_already_suppressed_protocol,none,
7725+
"already suppressed conformance to %0", (const ProtocolDecl *))
77207726

77217727
// -- older ones below --
77227728
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: 93 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"
@@ -51,6 +53,7 @@ class DoCatchStmt;
5153
struct ExternalMacroDefinition;
5254
class ClosureExpr;
5355
class GenericParamList;
56+
class InverseTypeRepr;
5457
class LabeledStmt;
5558
class MacroDefinition;
5659
class PrecedenceGroupDecl;
@@ -78,13 +81,75 @@ void simple_display(
7881

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

84+
/// Emulates the following enum with associated values:
85+
/// enum InheritedTypeResult {
86+
/// case inherited(Type)
87+
/// case suppressed(Type, InverseTypeRepr *)
88+
/// case `default`
89+
/// }
90+
class InheritedTypeResult {
91+
struct Inherited_ {
92+
Type ty;
93+
};
94+
struct Suppressed_ {
95+
// The type which is suppressed. Not the result of inverting a protocol.
96+
Type ty;
97+
InverseTypeRepr *repr;
98+
};
99+
struct Default_ {};
100+
using Payload = TaggedUnion<Inherited_, Suppressed_, Default_>;
101+
Payload payload;
102+
InheritedTypeResult(Payload payload) : payload(payload) {}
103+
104+
public:
105+
enum Kind { Inherited, Suppressed, Default };
106+
static InheritedTypeResult forInherited(Type ty) { return {Inherited_{ty}}; }
107+
static InheritedTypeResult forSuppressed(Type ty, InverseTypeRepr *repr) {
108+
return {Suppressed_{ty, repr}};
109+
}
110+
static InheritedTypeResult forDefault() { return {Default_{}}; }
111+
operator Kind() {
112+
if (payload.isa<Inherited_>()) {
113+
return Kind::Inherited;
114+
} else if (payload.isa<Suppressed_>()) {
115+
return Kind::Suppressed;
116+
}
117+
return Kind::Default;
118+
}
119+
explicit operator bool() { return !payload.isa<Default_>(); }
120+
Type getInheritedTypeOrNull(ASTContext &ctx) {
121+
switch (*this) {
122+
case Inherited:
123+
return getInheritedType();
124+
case Suppressed: {
125+
auto suppressed = getSuppressed();
126+
auto kp = suppressed.first->getKnownProtocol();
127+
if (!kp)
128+
return Type();
129+
auto ipk = getInvertibleProtocolKind(*kp);
130+
if (!ipk)
131+
return Type();
132+
return ProtocolCompositionType::getInverseOf(ctx, *ipk);
133+
}
134+
case Default:
135+
return Type();
136+
}
137+
}
138+
Type getInheritedType() { return payload.get<Inherited_>().ty; }
139+
std::pair<Type, InverseTypeRepr *> getSuppressed() {
140+
auto &suppressed = payload.get<Suppressed_>();
141+
return {suppressed.ty, suppressed.repr};
142+
}
143+
};
144+
81145
/// Request the type from the ith entry in the inheritance clause for the
82146
/// given declaration.
83147
class InheritedTypeRequest
84148
: public SimpleRequest<
85149
InheritedTypeRequest,
86-
Type(llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
87-
unsigned, TypeResolutionStage),
150+
InheritedTypeResult(
151+
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *>,
152+
unsigned, TypeResolutionStage),
88153
RequestFlags::SeparatelyCached> {
89154
public:
90155
using SimpleRequest::SimpleRequest;
@@ -93,21 +158,22 @@ class InheritedTypeRequest
93158
friend SimpleRequest;
94159

95160
// Evaluation.
96-
Type
161+
InheritedTypeResult
97162
evaluate(Evaluator &evaluator,
98163
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
99164
unsigned index, TypeResolutionStage stage) const;
100165

101-
const TypeLoc &getTypeLoc() const;
166+
const InheritedEntry &getInheritedEntry() const;
167+
ASTContext &getASTContext() const;
102168

103169
public:
104170
// Source location
105171
SourceLoc getNearestLoc() const;
106172

107173
// Caching
108174
bool isCached() const;
109-
std::optional<Type> getCachedResult() const;
110-
void cacheResult(Type value) const;
175+
std::optional<InheritedTypeResult> getCachedResult() const;
176+
void cacheResult(InheritedTypeResult value) const;
111177
};
112178

113179
/// Request the superclass type for the given class.
@@ -4911,6 +4977,23 @@ class InheritanceClauseRequest
49114977
void cacheResult(ArrayRef<InheritedEntry>) const;
49124978
};
49134979

4980+
class SuppressesConformanceRequest
4981+
: public SimpleRequest<SuppressesConformanceRequest,
4982+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
4983+
RequestFlags::Cached> {
4984+
public:
4985+
using SimpleRequest::SimpleRequest;
4986+
4987+
private:
4988+
friend SimpleRequest;
4989+
4990+
bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
4991+
KnownProtocolKind kp) const;
4992+
4993+
public:
4994+
bool isCached() const { return true; }
4995+
};
4996+
49144997
#define SWIFT_TYPEID_ZONE TypeChecker
49154998
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
49164999
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,3 +571,6 @@ SWIFT_REQUEST(TypeChecker, ImportDeclRequest,
571571
Cached, NoLocationInfo)
572572
SWIFT_REQUEST(TypeChecker, LifetimeDependenceInfoRequest,
573573
LifetimeDependenceInfo(AbstractFunctionDecl *), Cached, NoLocationInfo)
574+
SWIFT_REQUEST(TypeChecker, SuppressesConformanceRequest,
575+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
576+
SeparatelyCached, NoLocationInfo)

include/swift/Basic/Features.def

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

348+
/// Enable the suppression of inferred, non-invertible, protocols via ~.
349+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ConformanceSuppression, true)
350+
348351
/// Enables the FixedArray data type.
349352
EXPERIMENTAL_FEATURE(FixedArrays, true)
350353

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
@@ -1669,7 +1669,8 @@ Type InheritedTypes::getResolvedType(unsigned i,
16691669
? Decl.get<const ExtensionDecl *>()->getASTContext()
16701670
: Decl.get<const TypeDecl *>()->getASTContext();
16711671
return evaluateOrDefault(ctx.evaluator, InheritedTypeRequest{Decl, i, stage},
1672-
Type());
1672+
InheritedTypeResult::forDefault())
1673+
.getInheritedTypeOrNull(getASTContext());
16731674
}
16741675

16751676
ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc,
@@ -5367,6 +5368,13 @@ bool NominalTypeDecl::isMainActor() const {
53675368
getParentModule()->getName() == getASTContext().Id_Concurrency;
53685369
}
53695370

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

lib/AST/FeatureSet.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,42 @@ UNINTERESTING_FEATURE(BorrowingSwitch)
675675

676676
UNINTERESTING_FEATURE(ClosureIsolation)
677677

678+
static bool usesFeatureConformanceSuppression(Decl *decl) {
679+
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
680+
if (!nominal)
681+
return false;
682+
683+
auto inherited = InheritedTypes(nominal);
684+
for (auto index : indices(inherited.getEntries())) {
685+
// Ensure that InheritedTypeRequest has set the isSuppressed bit if
686+
// appropriate.
687+
auto resolvedTy = inherited.getResolvedType(index);
688+
(void)resolvedTy;
689+
690+
auto entry = inherited.getEntry(index);
691+
692+
if (!entry.isSuppressed())
693+
continue;
694+
695+
auto ty = entry.getType();
696+
697+
if (!ty)
698+
continue;
699+
700+
auto kp = ty->getKnownProtocol();
701+
if (!kp)
702+
continue;
703+
704+
auto rpk = getRepressibleProtocolKind(*kp);
705+
if (!rpk)
706+
continue;
707+
708+
return true;
709+
}
710+
711+
return false;
712+
}
713+
678714
static bool usesFeatureIsolatedAny(Decl *decl) {
679715
return usesTypeMatching(decl, [](Type type) {
680716
if (auto fnType = type->getAs<AnyFunctionType>()) {

0 commit comments

Comments
 (0)