Skip to content

Commit d197571

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 ad412cb commit d197571

24 files changed

+416
-62
lines changed

include/swift/AST/Decl.h

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

1631+
/// Whether there was a ~ indicating suppression.
1632+
///
1633+
/// This is true in cases like ~Copyable but not (P & ~Copyable).
1634+
bool IsSuppressed : 1;
1635+
16311636
public:
16321637
InheritedEntry(const TypeLoc &typeLoc);
16331638

16341639
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1635-
bool isPreconcurrency)
1640+
bool isPreconcurrency, bool isSuppressed = false)
16361641
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
1637-
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency) {}
1642+
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
1643+
IsSuppressed(isSuppressed) {}
16381644

16391645
bool isUnchecked() const { return IsUnchecked; }
16401646
bool isRetroactive() const { return IsRetroactive; }
16411647
bool isPreconcurrency() const { return IsPreconcurrency; }
1648+
bool isSuppressed() const { return IsSuppressed; }
1649+
1650+
void setSuppressed() {
1651+
assert(!IsSuppressed && "setting suppressed again!?");
1652+
IsSuppressed = true;
1653+
}
16421654
};
16431655

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

4451+
bool suppressesConformance(KnownProtocolKind kp) const;
4452+
44394453
// Implement isa/cast/dyncast/etc.
44404454
static bool classof(const Decl *D) {
44414455
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
@@ -7699,6 +7699,12 @@ NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
76997699
ERROR(bitwise_copyable_outside_module,none,
77007700
"conformance to 'BitwiseCopyable' must occur in the same module as %kind0",
77017701
(const ValueDecl *))
7702+
ERROR(suppress_inferrable_protocol_extension,none,
7703+
"conformance to inferrable protocol %0 cannot be suppressed in an extension", (const ProtocolDecl *))
7704+
ERROR(suppress_nonsuppressable_protocol,none,
7705+
"conformance to %0 cannot be suppressed", (const ProtocolDecl *))
7706+
WARNING(suppress_already_suppressed_protocol,none,
7707+
"already suppressed conformance to %0", (const ProtocolDecl *))
77027708

77037709
// -- older ones below --
77047710
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.
@@ -4885,6 +4951,23 @@ class LifetimeDependenceInfoRequest
48854951
bool isCached() const { return true; }
48864952
};
48874953

4954+
class SuppressesConformanceRequest
4955+
: public SimpleRequest<SuppressesConformanceRequest,
4956+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
4957+
RequestFlags::Cached> {
4958+
public:
4959+
using SimpleRequest::SimpleRequest;
4960+
4961+
private:
4962+
friend SimpleRequest;
4963+
4964+
bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
4965+
KnownProtocolKind kp) const;
4966+
4967+
public:
4968+
bool isCached() const { return true; }
4969+
};
4970+
48884971
#define SWIFT_TYPEID_ZONE TypeChecker
48894972
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
48904973
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,6 @@ SWIFT_REQUEST(TypeChecker, ImportDeclRequest,
568568
Cached, NoLocationInfo)
569569
SWIFT_REQUEST(TypeChecker, LifetimeDependenceInfoRequest,
570570
LifetimeDependenceInfo(AbstractFunctionDecl *), Cached, NoLocationInfo)
571+
SWIFT_REQUEST(TypeChecker, SuppressesConformanceRequest,
572+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
573+
SeparatelyCached, NoLocationInfo)

include/swift/Basic/Features.def

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

344+
/// Enable the suppression of inferred, non-invertible, protocols via ~.
345+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ConformanceSuppression, true)
346+
344347
/// Enables the FixedArray data type.
345348
EXPERIMENTAL_FEATURE(FixedArrays, true)
346349

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,
@@ -5365,6 +5366,13 @@ bool NominalTypeDecl::isMainActor() const {
53655366
getParentModule()->getName() == getASTContext().Id_Concurrency;
53665367
}
53675368

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

lib/AST/FeatureSet.cpp

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

672672
UNINTERESTING_FEATURE(ClosureIsolation)
673673

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

0 commit comments

Comments
 (0)