Skip to content

Commit 71a5935

Browse files
committed
[Sema] factor out the StorageVisitor pattern
This visitor is generally useful for other kinds of marker protocols like `Copyable` that require structural checking over the storage of the struct or enum.
1 parent d5f2d54 commit 71a5935

File tree

6 files changed

+110
-82
lines changed

6 files changed

+110
-82
lines changed

lib/AST/Decl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4836,6 +4836,9 @@ GenericParameterReferenceInfo ValueDecl::findExistentialSelfReferences(
48364836
}
48374837

48384838
bool TypeDecl::isNoncopyable() const {
4839+
assert(!getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics)
4840+
&& "TypeDecl::isNoncopyable() is not compatible with NoncopyableGenerics");
4841+
48394842
return evaluateOrDefault(getASTContext().evaluator,
48404843
IsNoncopyableRequest{const_cast<TypeDecl *>(this)},
48414844
true); // default to true for safety

lib/AST/Type.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ bool TypeBase::isMarkerExistential() {
158158
}
159159

160160
bool TypeBase::isNoncopyable() {
161-
// FIXME: we need to support the full spectrum of TypeDecls here.
161+
assert(!getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics)
162+
&& "TypeBase::isNoncopyable() is not compatible with NoncopyableGenerics");
163+
162164
if (auto *nom = getAnyNominal())
163165
return nom->isNoncopyable();
164166

lib/Sema/MiscDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6248,6 +6248,10 @@ bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,
62486248

62496249
void swift::diagnoseCopyableTypeContainingMoveOnlyType(
62506250
NominalTypeDecl *copyableNominalType) {
6251+
auto &ctx = copyableNominalType->getASTContext();
6252+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics))
6253+
return; // taken care of in conformance checking
6254+
62516255
// If we already have a move only type, just bail, we have no further work to
62526256
// do.
62536257
if (copyableNominalType->isNoncopyable())

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 14 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616
#include "TypeCheckConcurrency.h"
1717
#include "TypeCheckDistributed.h"
18+
#include "TypeCheckInvertible.h"
1819
#include "TypeChecker.h"
1920
#include "TypeCheckType.h"
2021
#include "swift/Strings.h"
@@ -1113,10 +1114,6 @@ void swift::diagnoseMissingSendableConformance(
11131114
}
11141115

11151116
namespace {
1116-
template<typename Visitor>
1117-
bool visitInstanceStorage(
1118-
NominalTypeDecl *nominal, DeclContext *dc, Visitor &visitor);
1119-
11201117
/// Infer Sendable from the instance storage of the given nominal type.
11211118
/// \returns \c llvm::None if there is no way to make the type \c Sendable,
11221119
/// \c true if \c Sendable needs to be @unchecked, \c false if it can be
@@ -1131,20 +1128,22 @@ namespace {
11311128
}
11321129
}
11331130

1134-
struct Visitor {
1131+
class Visitor: public StorageVisitor {
1132+
public:
11351133
NominalTypeDecl *nominal;
11361134
SmallVectorImpl<Requirement> &requirements;
11371135
bool isUnchecked = false;
11381136
ProtocolDecl *sendableProto = nullptr;
11391137

11401138
Visitor(
11411139
NominalTypeDecl *nominal, SmallVectorImpl<Requirement> &requirements
1142-
) : nominal(nominal), requirements(requirements) {
1140+
) : StorageVisitor(),
1141+
nominal(nominal), requirements(requirements) {
11431142
ASTContext &ctx = nominal->getASTContext();
11441143
sendableProto = ctx.getProtocol(KnownProtocolKind::Sendable);
11451144
}
11461145

1147-
bool operator()(VarDecl *var, Type propertyType) {
1146+
bool operator()(VarDecl *var, Type propertyType) override {
11481147
// If we have a class with mutable state, only an @unchecked
11491148
// conformance will work.
11501149
if (isa<ClassDecl>(nominal) && var->supportsMutation())
@@ -1153,7 +1152,7 @@ namespace {
11531152
return checkType(propertyType);
11541153
}
11551154

1156-
bool operator()(EnumElementDecl *element, Type elementType) {
1155+
bool operator()(EnumElementDecl *element, Type elementType) override {
11571156
return checkType(elementType);
11581157
}
11591158

@@ -1195,7 +1194,7 @@ namespace {
11951194
}
11961195
} visitor(nominal, requirements);
11971196

1198-
return visitInstanceStorage(nominal, nominal, visitor);
1197+
return visitor.visit(nominal, nominal);
11991198
}
12001199
}
12011200

@@ -4823,53 +4822,6 @@ bool swift::contextRequiresStrictConcurrencyChecking(
48234822
return false;
48244823
}
48254824

4826-
namespace {
4827-
/// Visit the instance storage of the given nominal type as seen through
4828-
/// the given declaration context.
4829-
///
4830-
/// \param visitor Called with each (stored property, property type) pair
4831-
/// for classes/structs and with each (enum element, associated value type)
4832-
/// pair for enums.
4833-
///
4834-
/// \returns \c true if any call to the \c visitor returns \c true, and
4835-
/// \c false otherwise.
4836-
template<typename Visitor>
4837-
bool visitInstanceStorage(
4838-
NominalTypeDecl *nominal, DeclContext *dc, Visitor &visitor) {
4839-
// Walk the stored properties of classes and structs.
4840-
if (isa<StructDecl>(nominal) || isa<ClassDecl>(nominal)) {
4841-
for (auto property : nominal->getStoredProperties()) {
4842-
auto propertyType = dc->mapTypeIntoContext(property->getInterfaceType())
4843-
->getRValueType()->getReferenceStorageReferent();
4844-
if (visitor(property, propertyType))
4845-
return true;
4846-
}
4847-
4848-
return false;
4849-
}
4850-
4851-
// Walk the enum elements that have associated values.
4852-
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
4853-
for (auto caseDecl : enumDecl->getAllCases()) {
4854-
for (auto element : caseDecl->getElements()) {
4855-
if (!element->hasAssociatedValues())
4856-
continue;
4857-
4858-
// Check that the associated value type is Sendable.
4859-
auto elementType = dc->mapTypeIntoContext(
4860-
element->getArgumentInterfaceType());
4861-
if (visitor(element, elementType))
4862-
return true;
4863-
}
4864-
}
4865-
4866-
return false;
4867-
}
4868-
4869-
return false;
4870-
}
4871-
}
4872-
48734825
/// Check the instance storage of the given nominal type to verify whether
48744826
/// it is comprised only of Sendable instance storage.
48754827
static bool checkSendableInstanceStorage(
@@ -4891,19 +4843,20 @@ static bool checkSendableInstanceStorage(
48914843

48924844
// Stored properties of structs and classes must have
48934845
// Sendable-conforming types.
4894-
struct Visitor {
4846+
class Visitor: public StorageVisitor {
4847+
public:
48954848
bool invalid = false;
48964849
NominalTypeDecl *nominal;
48974850
DeclContext *dc;
48984851
SendableCheck check;
48994852
const LangOptions &langOpts;
49004853

49014854
Visitor(NominalTypeDecl *nominal, DeclContext *dc, SendableCheck check)
4902-
: nominal(nominal), dc(dc), check(check),
4855+
: StorageVisitor(), nominal(nominal), dc(dc), check(check),
49034856
langOpts(nominal->getASTContext().LangOpts) { }
49044857

49054858
/// Handle a stored property.
4906-
bool operator()(VarDecl *property, Type propertyType) {
4859+
bool operator()(VarDecl *property, Type propertyType) override {
49074860
// Classes with mutable properties are not Sendable.
49084861
if (property->supportsMutation() && isa<ClassDecl>(nominal)) {
49094862
if (isImplicitSendableCheck(check)) {
@@ -4958,7 +4911,7 @@ static bool checkSendableInstanceStorage(
49584911
}
49594912

49604913
/// Handle an enum associated value.
4961-
bool operator()(EnumElementDecl *element, Type elementType) {
4914+
bool operator()(EnumElementDecl *element, Type elementType) override {
49624915
diagnoseNonSendableTypes(
49634916
elementType, SendableCheckContext(dc, check), element->getLoc(),
49644917
[&](Type type, DiagnosticBehavior behavior) {
@@ -4993,7 +4946,7 @@ static bool checkSendableInstanceStorage(
49934946
}
49944947
} visitor(nominal, dc, check);
49954948

4996-
return visitInstanceStorage(nominal, dc, visitor) || visitor.invalid;
4949+
return visitor.visit(nominal, dc) || visitor.invalid;
49974950
}
49984951

49994952
bool swift::checkSendableConformance(

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,42 @@
2323

2424
namespace swift {
2525

26-
//static bool hasConformanceRequirement(ArrayRef<Requirement> reqs,
27-
// KnownProtocolKind query) {
28-
// for (auto req : reqs) {
29-
// if (req.getKind() == RequirementKind::Conformance)
30-
// if (auto kp = req.getSecondType()->getKnownProtocol())
31-
// if (*kp == query)
32-
// return true;
33-
// }
34-
// return false;
35-
//}
36-
37-
//static const GenericContext *findGenericContext(DeclContext *dc) {
38-
// do {
39-
// if (auto decl = dc->getAsDecl())
40-
// if (auto GC = decl->getAsGenericContext())
41-
// return GC;
42-
// } while ((dc = dc->getParent()));
43-
//
44-
// return nullptr;
45-
//}
26+
27+
/// Visit the instance storage of the given nominal type as seen through
28+
/// the given declaration context.
29+
bool StorageVisitor::visit(NominalTypeDecl *nominal, DeclContext *dc) {
30+
// Walk the stored properties of classes and structs.
31+
if (isa<StructDecl>(nominal) || isa<ClassDecl>(nominal)) {
32+
for (auto property : nominal->getStoredProperties()) {
33+
auto propertyType = dc->mapTypeIntoContext(property->getInterfaceType())
34+
->getRValueType()->getReferenceStorageReferent();
35+
if ((*this)(property, propertyType))
36+
return true;
37+
}
38+
39+
return false;
40+
}
41+
42+
// Walk the enum elements that have associated values.
43+
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
44+
for (auto caseDecl : enumDecl->getAllCases()) {
45+
for (auto element : caseDecl->getElements()) {
46+
if (!element->hasAssociatedValues())
47+
continue;
48+
49+
// Check that the associated value type is Sendable.
50+
auto elementType = dc->mapTypeIntoContext(
51+
element->getArgumentInterfaceType());
52+
if ((*this)(element, elementType))
53+
return true;
54+
}
55+
}
56+
57+
return false;
58+
}
59+
60+
return false;
61+
}
4662

4763
bool canBeNoncopyable(Type type) {
4864
auto &ctx = type->getASTContext();
@@ -75,6 +91,29 @@ bool isCopyable(Type type, DeclContext *dc) {
7591
module, false);
7692
}
7793

94+
/// \returns true iff the given nominal meets the requirements of Copyable>
95+
bool checkCopyableConformance(Evaluator &evaluator,
96+
NominalTypeDecl *nom) {
97+
// All classes can store noncopyable values.
98+
if (isa<ClassDecl>(nom))
99+
return true;
100+
101+
// Protocols do not directly define any storage.
102+
if (isa<ProtocolDecl>(nom))
103+
return true;
104+
105+
if (isa<BuiltinTupleDecl>(nom))
106+
llvm_unreachable("unexpected BuiltinTupleDecl");
107+
108+
if (auto strct = dyn_cast<StructDecl>(nom)) {
109+
for (auto stored : strct->getStoredProperties()) {
110+
// stored->getInterfaceType()
111+
// ImplicitKnownProtocolConformanceRequest
112+
}
113+
}
114+
115+
}
116+
78117
ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
79118
NominalTypeDecl *nominal,
80119
KnownProtocolKind kp) {
@@ -86,6 +125,10 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
86125
if (evaluateOrDefault(evaluator, IsNoncopyableRequest{nominal}, false))
87126
return nullptr; // it's not Copyable.
88127

128+
// Otherwise, verify Copyable conformance.
129+
130+
131+
89132
// form a full unconditional conformance to Copyable.
90133
auto conformance = ctx.getNormalConformance(
91134
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),

lib/Sema/TypeCheckInvertible.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@
66

77
namespace swift {
88

9+
class StorageVisitor {
10+
public:
11+
/// Visit the instance storage of the given nominal type as seen through
12+
/// the given declaration context.
13+
///
14+
/// The `this` instance is invoked with each (stored property, property type)
15+
/// pair for classes/structs and with each (enum elem, associated value type)
16+
/// pair for enums.
17+
///
18+
/// Thus, the requirements of your `VisitorImpl` must match up with this
19+
/// public interface.
20+
///
21+
/// \returns \c true if any call to the \c visitor returns \c true, and
22+
/// \c false otherwise.
23+
bool visit(NominalTypeDecl *nominal, DeclContext *dc);
24+
25+
/// Handle a stored property.
26+
virtual bool operator()(VarDecl *property, Type propertyType) = 0;
27+
28+
/// Handle an enum associated value.
29+
virtual bool operator()(EnumElementDecl *element, Type elementType) = 0;
30+
};
31+
932

1033
// Is this type copyable in the given decl context?
1134
bool isCopyable(Type type, DeclContext *dc);

0 commit comments

Comments
 (0)