Skip to content

Commit 364366b

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 b1c71c1 commit 364366b

24 files changed

+409
-63
lines changed

include/swift/AST/Decl.h

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

1641+
/// Whether there was a ~ indicating suppression but not inversion.
1642+
bool IsSuppressed : 1;
1643+
16411644
public:
16421645
InheritedEntry(const TypeLoc &typeLoc);
16431646

16441647
InheritedEntry(const TypeLoc &typeLoc, bool isUnchecked, bool isRetroactive,
1645-
bool isPreconcurrency)
1648+
bool isPreconcurrency, bool isSuppressed = false)
16461649
: TypeLoc(typeLoc), IsUnchecked(isUnchecked),
1647-
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency) {}
1650+
IsRetroactive(isRetroactive), IsPreconcurrency(isPreconcurrency),
1651+
IsSuppressed(isSuppressed) {}
16481652

16491653
bool isUnchecked() const { return IsUnchecked; }
16501654
bool isRetroactive() const { return IsRetroactive; }
16511655
bool isPreconcurrency() const { return IsPreconcurrency; }
1656+
bool isSuppressed() const { return IsSuppressed; }
1657+
1658+
void setSuppressed() {
1659+
assert(!IsSuppressed && "setting suppressed again!?");
1660+
IsSuppressed = true;
1661+
}
16521662
};
16531663

16541664
/// A wrapper for the collection of inherited types for either a `TypeDecl` or
@@ -4471,6 +4481,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
44714481
/// Type if it `isEscapable` instead of using this.
44724482
CanBeInvertible::Result canBeEscapable() const;
44734483

4484+
bool suppressesConformance(KnownProtocolKind kp) const;
4485+
44744486
// Implement isa/cast/dyncast/etc.
44754487
static bool classof(const Decl *D) {
44764488
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
@@ -7710,6 +7710,12 @@ NOTE(add_generic_parameter_non_bitwise_copyable_conformance,none,
77107710
ERROR(bitwise_copyable_outside_module,none,
77117711
"conformance to 'BitwiseCopyable' must occur in the same module as %kind0",
77127712
(const ValueDecl *))
7713+
ERROR(suppress_inferrable_protocol_extension,none,
7714+
"conformance to inferrable protocol %0 cannot be suppressed in an extension", (const ProtocolDecl *))
7715+
ERROR(suppress_nonsuppressable_protocol,none,
7716+
"conformance to %0 cannot be suppressed", (const ProtocolDecl *))
7717+
WARNING(suppress_already_suppressed_protocol,none,
7718+
"already suppressed conformance to %0", (const ProtocolDecl *))
77137719

77147720
// -- older ones below --
77157721
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.
@@ -4891,6 +4957,23 @@ class InheritanceClauseRequest
48914957
void cacheResult(ArrayRef<InheritedEntry>) const;
48924958
};
48934959

4960+
class SuppressesConformanceRequest
4961+
: public SimpleRequest<SuppressesConformanceRequest,
4962+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
4963+
RequestFlags::Cached> {
4964+
public:
4965+
using SimpleRequest::SimpleRequest;
4966+
4967+
private:
4968+
friend SimpleRequest;
4969+
4970+
bool evaluate(Evaluator &evaluator, NominalTypeDecl *decl,
4971+
KnownProtocolKind kp) const;
4972+
4973+
public:
4974+
bool isCached() const { return true; }
4975+
};
4976+
48944977
#define SWIFT_TYPEID_ZONE TypeChecker
48954978
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
48964979
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,6 @@ SWIFT_REQUEST(TypeChecker, ImportDeclRequest,
569569
std::optional<AttributedImport<ImportedModule>>(
570570
const SourceFile *sf, const ModuleDecl *mod),
571571
Cached, NoLocationInfo)
572+
SWIFT_REQUEST(TypeChecker, SuppressesConformanceRequest,
573+
bool(NominalTypeDecl *decl, KnownProtocolKind kp),
574+
SeparatelyCached, NoLocationInfo)

include/swift/Basic/Features.def

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

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

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(getASTContext());
16601661
}
16611662

16621663
ExtensionDecl::ExtensionDecl(SourceLoc extensionLoc,
@@ -5354,6 +5355,13 @@ bool NominalTypeDecl::isMainActor() const {
53545355
getParentModule()->getName() == getASTContext().Id_Concurrency;
53555356
}
53565357

5358+
bool NominalTypeDecl::suppressesConformance(KnownProtocolKind kp) const {
5359+
auto mutableThis = const_cast<NominalTypeDecl *>(this);
5360+
return evaluateOrDefault(getASTContext().evaluator,
5361+
SuppressesConformanceRequest{mutableThis, kp},
5362+
false);
5363+
}
5364+
53575365
GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC,
53585366
Identifier name, SourceLoc nameLoc,
53595367
ArrayRef<InheritedEntry> inherited,

lib/AST/FeatureSet.cpp

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

675675
UNINTERESTING_FEATURE(ClosureIsolation)
676676

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

0 commit comments

Comments
 (0)