-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathTypeCheckConcurrency.h
333 lines (286 loc) · 11.7 KB
/
TypeCheckConcurrency.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
//===--- TypeCheckConcurrency.h - Concurrency -------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides type checking support for Swift's concurrency model.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_TYPECHECKCONCURRENCY_H
#define SWIFT_SEMA_TYPECHECKCONCURRENCY_H
#include "swift/AST/ASTContext.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/Module.h"
#include "swift/AST/Type.h"
#include <cassert>
namespace swift {
class AbstractFunctionDecl;
class ConstructorDecl;
class ActorIsolation;
class AnyFunctionType;
class ASTContext;
class ClassDecl;
class ClosureActorIsolation;
class ClosureExpr;
class ConcreteDeclRef;
class CustomAttr;
class Decl;
class DeclContext;
class EnumElementDecl;
class Expr;
class FuncDecl;
class Initializer;
class PatternBindingDecl;
class ProtocolConformance;
class TopLevelCodeDecl;
class TypeBase;
class ValueDecl;
class VarDecl;
/// Add notes suggesting the addition of 'async', as appropriate,
/// to a diagnostic for a function that isn't an async context.
void addAsyncNotes(AbstractFunctionDecl const* func);
/// Check actor isolation rules.
void checkTopLevelActorIsolation(TopLevelCodeDecl *decl);
void checkFunctionActorIsolation(AbstractFunctionDecl *decl);
void checkInitializerActorIsolation(Initializer *init, Expr *expr);
void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr);
/// Determine the isolation of a particular closure.
///
/// This forwards to \c ActorIsolationChecker::determineClosureActorIsolation
/// and thus assumes that enclosing closures have already had their isolation
/// checked.
///
/// This does not set the closure's actor isolation
ClosureActorIsolation
determineClosureActorIsolation(AbstractClosureExpr *closure);
/// Describes the kind of operation that introduced the concurrent refernece.
enum class ConcurrentReferenceKind {
/// A synchronous operation that was "promoted" to an asynchronous call
/// because it was out of the actor's domain.
SynchronousAsAsyncCall,
/// A cross-actor reference.
CrossActor,
/// A local capture referenced from concurrent code.
LocalCapture,
/// Concurrent function
ConcurrentFunction,
/// Nonisolated declaration.
Nonisolated,
};
/// The isolation restriction in effect for a given declaration that is
/// referenced from source.
class ActorIsolationRestriction {
public:
enum Kind {
/// There is no restriction on references to the given declaration.
Unrestricted,
/// Access to the declaration is unsafe in any concurrent context.
Unsafe,
/// References to this entity are allowed from anywhere, but doing so
/// may cross an actor boundary if it is not from within the same actor's
/// isolation domain.
CrossActorSelf,
/// References to this member of an actor are only permitted from within
/// the actor's isolation domain.
ActorSelf,
/// References to a declaration that is part of a global actor are
/// permitted from other declarations with that same global actor or
/// are permitted from elsewhere as a cross-actor reference.
GlobalActor,
/// References to a declaration that is part of a global actor are
/// permitted from other declarations with that same global actor or
/// are permitted from elsewhere as a cross-actor reference, but
/// contexts with unspecified isolation won't diagnose anything.
GlobalActorUnsafe,
};
private:
union {
/// The local context that an entity is tied to.
DeclContext *localContext;
/// The actor that the entity is declared in.
NominalTypeDecl *actorType;
/// The global actor type.
TypeBase *globalActor;
} data;
explicit ActorIsolationRestriction(Kind kind, bool isCrossActor)
: kind(kind), isCrossActor(isCrossActor) { }
public:
/// The kind of restriction.
const Kind kind;
/// Whether referencing this from another actor constitutes a cross-acter
/// reference.
const bool isCrossActor;
Kind getKind() const { return kind; }
/// Retrieve the actor type that the declaration is within.
NominalTypeDecl *getActorType() const {
assert(kind == ActorSelf ||
kind == CrossActorSelf);
return data.actorType;
}
/// Retrieve the actor that the declaration is within.
Type getGlobalActor() const {
assert(kind == GlobalActor || kind == GlobalActorUnsafe);
return Type(data.globalActor);
}
/// There are no restrictions on the use of the entity.
static ActorIsolationRestriction forUnrestricted() {
return ActorIsolationRestriction(Unrestricted, /*isCrossActor=*/false);
}
/// Accesses to the given declaration are unsafe.
static ActorIsolationRestriction forUnsafe() {
return ActorIsolationRestriction(Unsafe, /*isCrossActor=*/false);
}
/// Accesses to the given declaration can only be made via the 'self' of
/// the current actor or is a cross-actor access.
static ActorIsolationRestriction forActorSelf(
NominalTypeDecl *actor, bool isCrossActor) {
ActorIsolationRestriction result(isCrossActor? CrossActorSelf : ActorSelf,
isCrossActor);
result.data.actorType = actor;
return result;
}
/// Accesses to the given declaration can only be made via the 'self' of
/// the current actor.
static ActorIsolationRestriction forDistributedActorSelf(
NominalTypeDecl *actor, bool isCrossActor) {
ActorIsolationRestriction result(isCrossActor ? CrossActorSelf : ActorSelf,
isCrossActor);
result.data.actorType = actor;
return result;
}
/// Accesses to the given declaration can only be made via this particular
/// global actor or is a cross-actor access.
static ActorIsolationRestriction forGlobalActor(
Type globalActor, bool isCrossActor, bool isUnsafe) {
ActorIsolationRestriction result(
isUnsafe ? GlobalActorUnsafe : GlobalActor, isCrossActor);
result.data.globalActor = globalActor.getPointer();
return result;
}
/// Determine the isolation rules for a given declaration.
///
/// \param fromExpression Indicates that the reference is coming from an
/// expression.
static ActorIsolationRestriction forDeclaration(
ConcreteDeclRef declRef, const DeclContext *fromDC,
bool fromExpression = true);
operator Kind() const { return kind; };
};
/// Check that the actor isolation of an override matches that of its
/// overridden declaration.
void checkOverrideActorIsolation(ValueDecl *value);
/// Determine whether the given context uses concurrency features, such
/// as async functions or actors.
bool contextUsesConcurrencyFeatures(const DeclContext *dc);
/// Diagnose the presence of any non-sendable types when referencing a
/// given declaration from a particular declaration context.
///
/// This function should be invoked any time that the given declaration
/// reference is will move values of the declaration's types across a
/// concurrency domain, whether in/out of an actor or in/or of a concurrent
/// function or closure.
///
/// \param declRef The declaration being referenced from another concurrency
/// domain, including the substitutions so that (e.g.) we can consider the
/// specific types at the use site.
///
/// \param module The module from which the reference occurs. This is
/// used to perform lookup of conformances to the \c Sendable protocol.
///
/// \param loc The location at which the reference occurs, which will be
/// used when emitting diagnostics.
///
/// \param refKind Describes what kind of reference is being made, which is
/// used to tailor the diagnostic.
///
/// \returns true if an problem was detected, false otherwise.
bool diagnoseNonSendableTypesInReference(
ConcreteDeclRef declRef, ModuleDecl *module, SourceLoc loc,
ConcurrentReferenceKind refKind);
/// Produce a diagnostic for a missing conformance to Sendable.
void diagnoseMissingSendableConformance(
SourceLoc loc, Type type, ModuleDecl *module);
/// If the given nominal type is public and does not explicitly
/// state whether it conforms to Sendable, provide a diagnostic.
void diagnoseMissingExplicitSendable(NominalTypeDecl *nominal);
/// Diagnose any non-Sendable types that occur within the given type, using
/// the given diagnostic.
///
/// \param diagnose Emit a diagnostic indicating that the current type
/// is non-Sendable, with the suggested behavior limitation. Returns \c true
/// if an error was produced.
///
/// \returns \c true if any diagnostics were produced, \c false otherwise.
bool diagnoseNonSendableTypes(
Type type, ModuleDecl *module, SourceLoc loc,
llvm::function_ref<
std::pair<DiagnosticBehavior, bool>(Type, DiagnosticBehavior)> diagnose);
namespace detail {
template<typename T>
struct Identity {
typedef T type;
};
}
/// Diagnose any non-Sendable types that occur within the given type, using
/// the given diagnostic.
///
/// \returns \c true if any errors were produced, \c false otherwise.
template<typename ...DiagArgs>
bool diagnoseNonSendableTypes(
Type type, ModuleDecl *module, SourceLoc loc, Diag<Type, DiagArgs...> diag,
typename detail::Identity<DiagArgs>::type ...diagArgs) {
ASTContext &ctx = module->getASTContext();
return diagnoseNonSendableTypes(
type, module, loc, [&](Type specificType, DiagnosticBehavior behavior) {
ctx.Diags.diagnose(loc, diag, type, diagArgs...)
.limitBehavior(behavior);
return std::pair<DiagnosticBehavior, bool>(
behavior, behavior == DiagnosticBehavior::Unspecified);
});
}
/// How the concurrent value check should be performed.
enum class SendableCheck {
/// Sendable conformance was explicitly stated and should be
/// fully checked.
Explicit,
/// Sendable conformance was implied by one of the standard library
/// protocols that added Sendable after-the-fact.
ImpliedByStandardProtocol,
/// Implicit conformance to Sendable.
Implicit,
};
/// Given a set of custom attributes, pick out the global actor attributes
/// and perform any necessary resolution and diagnostics, returning the
/// global actor attribute and type it refers to (or \c None).
Optional<std::pair<CustomAttr *, NominalTypeDecl *>>
checkGlobalActorAttributes(
SourceLoc loc, DeclContext *dc, ArrayRef<CustomAttr *> attrs);
/// Get the explicit global actor specified for a closure.
Type getExplicitGlobalActor(ClosureExpr *closure);
/// Adjust the given function type to account for concurrency-specific
/// attributes whose affect on the type might differ based on context.
/// This includes adjustments for unsafe parameter attributes like
/// `@_unsafeSendable` and `@_unsafeMainActor` as well as a global actor
/// on the declaration itself.
AnyFunctionType *adjustFunctionTypeForConcurrency(
AnyFunctionType *fnType, ValueDecl *funcOrEnum, DeclContext *dc,
unsigned numApplies, bool isMainDispatchQueue);
/// Determine whether the given name is that of a DispatchQueue operation that
/// takes a closure to be executed on the queue.
bool isDispatchQueueOperationName(StringRef name);
/// Check the correctness of the given Sendable conformance.
///
/// \returns true if an error occurred.
bool checkSendableConformance(
ProtocolConformance *conformance, SendableCheck check);
} // end namespace swift
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */