-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathTypeCheckConcurrency.h
387 lines (328 loc) · 13.6 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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
//===--- 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 requires strict concurrency checking,
/// e.g., because it uses concurrency features directly or because it's in
/// code where strict checking has been enabled.
bool contextRequiresStrictConcurrencyChecking(
const DeclContext *dc,
llvm::function_ref<Type(const AbstractClosureExpr *)> getType);
/// 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 fromDC The context from which the reference occurs.
///
/// \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, const DeclContext *fromDC, SourceLoc loc,
ConcurrentReferenceKind refKind);
/// Produce a diagnostic for a missing conformance to Sendable.
void diagnoseMissingSendableConformance(
SourceLoc loc, Type type, const DeclContext *fromDC);
/// If the given nominal type is public and does not explicitly
/// state whether it conforms to Sendable, provide a diagnostic.
void diagnoseMissingExplicitSendable(NominalTypeDecl *nominal);
/// How the Sendable check should be performed.
enum class SendableCheck {
/// Sendable conformance was explicitly stated and should be
/// fully checked.
Explicit,
/// Sendable conformance was implied by a protocol that inherits from
/// Sendable and also predates concurrency.
ImpliedByStandardProtocol,
/// Implicit conformance to Sendable.
Implicit,
};
/// Describes the context in which a \c Sendable check occurs.
struct SendableCheckContext {
const DeclContext * const fromDC;
const Optional<SendableCheck> conformanceCheck;
SendableCheckContext(
const DeclContext * fromDC,
Optional<SendableCheck> conformanceCheck = None
) : fromDC(fromDC), conformanceCheck(conformanceCheck) { }
/// Determine the default diagnostic behavior for a missing/unavailable
/// Sendable conformance in this context.
DiagnosticBehavior defaultDiagnosticBehavior() const;
/// Determine the diagnostic behavior when referencing the given nominal
/// type in this context.
DiagnosticBehavior diagnosticBehavior(NominalTypeDecl *nominal) const;
/// Whether we are in an explicit conformance to Sendable.
bool isExplicitSendableConformance() const;
};
/// 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, SendableCheckContext fromContext, SourceLoc loc,
llvm::function_ref<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, SendableCheckContext fromContext, SourceLoc loc,
Diag<Type, DiagArgs...> diag,
typename detail::Identity<DiagArgs>::type ...diagArgs) {
ASTContext &ctx = fromContext.fromDC->getASTContext();
return diagnoseNonSendableTypes(
type, fromContext, loc, [&](Type specificType,
DiagnosticBehavior behavior) {
if (behavior != DiagnosticBehavior::Ignore) {
ctx.Diags.diagnose(loc, diag, type, diagArgs...)
.limitBehavior(behavior);
}
return false;
});
}
/// 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 type of the variable for concurrency.
Type adjustVarTypeForConcurrency(
Type type, VarDecl *var, DeclContext *dc,
llvm::function_ref<Type(const AbstractClosureExpr *)> getType);
/// 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 *decl, DeclContext *dc,
unsigned numApplies, bool isMainDispatchQueue,
llvm::function_ref<Type(const AbstractClosureExpr *)> getType);
/// 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);
/// Check whether we are in an actor's initializer or deinitializer.
/// \returns nullptr iff we are not in such a declaration. Otherwise,
/// returns a pointer to the declaration.
AbstractFunctionDecl const *isActorInitOrDeInitContext(
const DeclContext *dc,
llvm::function_ref<bool(const AbstractClosureExpr *)> isSendable);
/// Find the directly-referenced parameter or capture of a parameter for
/// for the given expression.
VarDecl *getReferencedParamOrCapture(
Expr *expr,
llvm::function_ref<Expr *(OpaqueValueExpr *)> getExistentialValue);
/// Check whether given variable references to a potentially
/// isolated actor.
bool isPotentiallyIsolatedActor(
VarDecl *var, llvm::function_ref<bool(ParamDecl *)> isIsolated =
[](ParamDecl *P) { return P->isIsolated(); });
} // end namespace swift
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */