Skip to content

Commit bf6c673

Browse files
authored
Merge pull request #36959 from ktoso/wip-task-locals-revisions
2 parents 8672bac + 2fc1ed8 commit bf6c673

25 files changed

+865
-431
lines changed

include/swift/ABI/Task.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class alignas(2 * alignof(void*)) Job :
5050
NextWaitingTaskIndex = 0,
5151

5252
// The Dispatch object header is one pointer and two ints, which is
53-
// equvialent to three pointers on 32-bit and two pointers 64-bit. Set the
53+
// equivalent to three pointers on 32-bit and two pointers 64-bit. Set the
5454
// indexes accordingly so that DispatchLinkageIndex points to where Dispatch
5555
// expects.
5656
DispatchHasLongObjectHeader = sizeof(void *) == sizeof(int),
@@ -243,14 +243,13 @@ class AsyncTask : public Job {
243243

244244
// ==== Task Local Values ----------------------------------------------------
245245

246-
void localValuePush(const Metadata *keyType,
246+
void localValuePush(const HeapObject *key,
247247
/* +1 */ OpaqueValue *value, const Metadata *valueType) {
248-
Local.pushValue(this, keyType, value, valueType);
248+
Local.pushValue(this, key, value, valueType);
249249
}
250250

251-
OpaqueValue* localValueGet(const Metadata *keyType,
252-
TaskLocal::TaskLocalInheritance inherit) {
253-
return Local.getValue(this, keyType, inherit);
251+
OpaqueValue* localValueGet(const HeapObject *key) {
252+
return Local.getValue(this, key);
254253
}
255254

256255
void localValuePop() {

include/swift/ABI/TaskLocal.h

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,73 +34,50 @@ class TaskLocal {
3434
public:
3535
/// Type of the pointed at `next` task local item.
3636
enum class NextLinkType : uintptr_t {
37-
/// This task is known to be a "terminal" node in the lookup of task locals.
38-
/// In other words, even if it had a parent, the parent (and its parents)
39-
/// are known to not contain any any more task locals, and thus any further
40-
/// search beyond this task.
41-
IsTerminal = 0b00,
4237
/// The storage pointer points at the next TaskLocal::Item in this task.
43-
IsNext = 0b01,
38+
IsNext = 0b00,
4439
/// The storage pointer points at a item stored by another AsyncTask.
4540
///
4641
/// Note that this may not necessarily be the same as the task's parent
4742
/// task -- we may point to a super-parent if we know / that the parent
4843
/// does not "contribute" any task local values. This is to speed up
4944
/// lookups by skipping empty parent tasks during get(), and explained
5045
/// in depth in `createParentLink`.
51-
IsParent = 0b11
52-
};
53-
54-
/// Values must match `TaskLocalInheritance` declared in `TaskLocal.swift`.
55-
enum class TaskLocalInheritance : uint8_t {
56-
/// Default task local value behavior
57-
///
58-
/// Values declared with a default-inherited key are accessible from:
59-
/// - the current task that has bound the value,
60-
/// - any child task of the current task (e.g. created by async let or groups)
61-
///
62-
/// Such values are *not* carried through detached tasks.
63-
Default = 0,
64-
65-
/// Special semantics which confine a task's local value to *only* the current
66-
/// task. In other words, they ave never inherited by any child task created
67-
/// by the current task.
68-
///
69-
/// Values declared with a never-inherited key only accessible:
70-
/// - specifically from the current task itself
71-
///
72-
/// Such values are *not* accessible from child tasks or detached tasks.
73-
Never = 1
46+
IsParent = 0b01,
7447
};
7548

7649
class Item {
7750
private:
7851
/// Mask used for the low status bits in a task local chain item.
7952
static const uintptr_t statusMask = 0x03;
8053

81-
/// Pointer to the next task local item; be it in this task or in a parent.
82-
/// Low bits encode `NextLinkType`.
83-
/// Item *next = nullptr;
54+
/// Pointer to one of the following:
55+
/// - next task local item as OpaqueValue* if it is task-local allocated
56+
/// - next task local item as HeapObject* if it is heap allocated "heavy"
57+
/// - the parent task's TaskLocal::Storage
58+
///
59+
/// Low bits encode `NextLinkType`, based on which the type of the pointer
60+
/// is determined.
8461
uintptr_t next;
8562

8663
public:
8764
/// The type of the key with which this value is associated.
88-
const Metadata *keyType;
65+
const HeapObject *key;
8966
/// The type of the value stored by this item.
9067
const Metadata *valueType;
9168

9269
// Trailing storage for the value itself. The storage will be
9370
// uninitialized or contain an instance of \c valueType.
9471

95-
private:
72+
protected:
9673
explicit Item()
9774
: next(0),
98-
keyType(nullptr),
75+
key(nullptr),
9976
valueType(nullptr) {}
10077

101-
explicit Item(const Metadata *keyType, const Metadata *valueType)
78+
explicit Item(const HeapObject *key, const Metadata *valueType)
10279
: next(0),
103-
keyType(keyType),
80+
key(key),
10481
valueType(valueType) {}
10582

10683
public:
@@ -116,7 +93,7 @@ class TaskLocal {
11693
static Item *createParentLink(AsyncTask *task, AsyncTask *parent);
11794

11895
static Item *createLink(AsyncTask *task,
119-
const Metadata *keyType,
96+
const HeapObject *key,
12097
const Metadata *valueType);
12198

12299
void destroy(AsyncTask *task);
@@ -125,13 +102,13 @@ class TaskLocal {
125102
return reinterpret_cast<Item *>(next & ~statusMask);
126103
}
127104

128-
NextLinkType getNextLinkType() {
105+
NextLinkType getNextLinkType() const {
129106
return static_cast<NextLinkType>(next & statusMask);
130107
}
131108

132109
/// Item does not contain any actual value, and is only used to point at
133110
/// a specific parent item.
134-
bool isEmpty() {
111+
bool isEmpty() const {
135112
return !valueType;
136113
}
137114

@@ -144,6 +121,7 @@ class TaskLocal {
144121
/// Compute the offset of the storage from the base of the item.
145122
static size_t storageOffset(const Metadata *valueType) {
146123
size_t offset = sizeof(Item);
124+
147125
if (valueType) {
148126
size_t alignment = valueType->vw_alignment();
149127
return (offset + alignment - 1) & ~(alignment - 1);
@@ -162,7 +140,6 @@ class TaskLocal {
162140
}
163141
};
164142

165-
166143
class Storage {
167144
friend class TaskLocal::Item;
168145
private:
@@ -202,12 +179,10 @@ class TaskLocal {
202179
void initializeLinkParent(AsyncTask *task, AsyncTask *parent);
203180

204181
void pushValue(AsyncTask *task,
205-
const Metadata *keyType,
182+
const HeapObject *key,
206183
/* +1 */ OpaqueValue *value, const Metadata *valueType);
207184

208-
OpaqueValue* getValue(AsyncTask *task,
209-
const Metadata *keyType,
210-
TaskLocalInheritance inheritance);
185+
OpaqueValue* getValue(AsyncTask *task, const HeapObject *key);
211186

212187
void popValue(AsyncTask *task);
213188

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5606,6 +5606,11 @@ ERROR(property_wrapper_dynamic_self_type, none,
56065606
"property wrapper %select{wrapped value|projected value}0 cannot have "
56075607
"dynamic Self type", (bool))
56085608

5609+
ERROR(property_wrapper_var_must_be_static, none,
5610+
"property %0, must be static because property wrapper %1 can only "
5611+
"be applied to static properties",
5612+
(Identifier, Type))
5613+
56095614
ERROR(property_wrapper_attribute_not_on_property, none,
56105615
"property wrapper attribute %0 can only be applied to a property",
56115616
(Identifier))

include/swift/AST/KnownSDKTypes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
4242

43+
KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1)
44+
4345
#undef KNOWN_SDK_TYPE_DECL

include/swift/AST/PropertyWrappers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ struct PropertyWrapperTypeInfo {
8888
/// ability to reason about the enclosing "self".
8989
SubscriptDecl *enclosingInstanceProjectedSubscript = nullptr;
9090

91+
/// Forces that the property wrapper must be declared on a static, or
92+
/// global–once supported–property.
93+
bool requireNoEnclosingInstance = false;
94+
9195
///
9296
/// Whether this is a valid property wrapper.
9397
bool isValid() const {

include/swift/Runtime/Concurrency.h

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,19 @@ void swift_asyncLet_wait_throwing(OpaqueValue *,
333333
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
334334
void swift_asyncLet_end(AsyncLet *alet);
335335

336+
/// Returns true if the currently executing AsyncTask has a
337+
/// 'TaskGroupTaskStatusRecord' present.
338+
///
339+
/// This can be called from any thread.
340+
///
341+
/// Its Swift signature is
342+
///
343+
/// \code
344+
/// func swift_taskGroup_hasTaskGroupRecord()
345+
/// \endcode
346+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
347+
bool swift_taskGroup_hasTaskGroupRecord();
348+
336349
/// Add a status record to a task. The record should not be
337350
/// modified while it is registered with a task.
338351
///
@@ -362,11 +375,20 @@ bool swift_task_tryAddStatusRecord(TaskStatusRecord *record);
362375
///
363376
/// The given record need not be the last record added to
364377
/// the task, but the operation may be less efficient if not.
365-
///s
378+
///
366379
/// Returns false if the task has been cancelled.
367380
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
368381
bool swift_task_removeStatusRecord(TaskStatusRecord *record);
369382

383+
/// Signifies whether the current task is in the middle of executing the
384+
/// operation block of a `with(Throwing)TaskGroup(...) { <operation> }`.
385+
///
386+
/// Task local values must use un-structured allocation for values bound in this
387+
/// scope, as they may be referred to by `group.spawn`-ed tasks and therefore
388+
/// out-life the scope of a task-local value binding.
389+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
390+
bool swift_task_hasTaskGroupStatusRecord();
391+
370392
/// Attach a child task to its parent task and return the newly created
371393
/// `ChildTaskStatusRecord`.
372394
///
@@ -397,20 +419,23 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
397419
void swift_task_removeCancellationHandler(
398420
CancellationNotificationStatusRecord *record);
399421

422+
/// Report error about attempting to bind a task-local value from an illegal context.
423+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
424+
void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroup(
425+
const unsigned char *file, uintptr_t fileLength,
426+
bool fileIsASCII, uintptr_t line);
427+
400428
/// Get a task local value from the passed in task. Its Swift signature is
401429
///
402430
/// \code
403431
/// func _taskLocalValueGet<Key>(
404432
/// _ task: Builtin.NativeObject,
405-
/// keyType: Any.Type /*Key.Type*/,
406-
/// inheritance: UInt8/*TaskLocalInheritance*/
433+
/// keyType: Any.Type /*Key.Type*/
407434
/// ) -> UnsafeMutableRawPointer? where Key: TaskLocalKey
408435
/// \endcode
409436
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
410437
OpaqueValue*
411-
swift_task_localValueGet(AsyncTask* task,
412-
const Metadata *keyType,
413-
TaskLocal::TaskLocalInheritance inheritance);
438+
swift_task_localValueGet(AsyncTask* task, const HeapObject *key);
414439

415440
/// Add a task local value to the passed in task.
416441
///
@@ -427,7 +452,7 @@ swift_task_localValueGet(AsyncTask* task,
427452
/// \endcode
428453
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
429454
void swift_task_localValuePush(AsyncTask* task,
430-
const Metadata *keyType,
455+
const HeapObject *key,
431456
/* +1 */ OpaqueValue *value,
432457
const Metadata *valueType);
433458

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,30 @@ findSuitableWrapperInit(ASTContext &ctx, NominalTypeDecl *nominal,
244244
return viableInitializers.empty() ? nullptr : viableInitializers.front();
245245
}
246246

247+
/// Returns true if the enclosingInstance parameter is `Never`,
248+
/// implying that there should be NO enclosing instance.
249+
static bool enclosingInstanceTypeIsNever(ASTContext &ctx, SubscriptDecl *subscript) {
250+
if (!subscript)
251+
return false;
252+
253+
ParameterList *indices = subscript->getIndices();
254+
assert(indices && indices->size() > 0);
255+
256+
ParamDecl *param = indices->get(0);
257+
if (param->getArgumentName() != ctx.Id_enclosingInstance)
258+
return false;
259+
260+
auto paramTy = param->getType();
261+
auto neverTy = ctx.getNeverType();
262+
paramTy->dump();
263+
paramTy->getCanonicalType()->dump();
264+
neverTy->dump();
265+
neverTy->getCanonicalType()->dump();
266+
param->dump();
267+
param->getType()->dump();
268+
return neverTy->isEqual(paramTy);
269+
}
270+
247271
/// Determine whether we have a suitable static subscript to which we
248272
/// can pass along the enclosing self + key-paths.
249273
static SubscriptDecl *findEnclosingSelfSubscript(ASTContext &ctx,
@@ -385,6 +409,16 @@ PropertyWrapperTypeInfoRequest::evaluate(
385409
}
386410
}
387411

412+
result.requireNoEnclosingInstance =
413+
enclosingInstanceTypeIsNever(ctx, result.enclosingInstanceWrappedSubscript);
414+
// if (requireNoEnclosingInstance) { // && !valueVar->isStatic()) {
415+
// // this means that the property wrapper must be declared on a static property
416+
// valueVar->diagnose(
417+
// diag::property_wrapper_var_must_be_static, valueVar->getName());
418+
// return PropertyWrapperTypeInfo();
419+
// result
420+
// }
421+
388422
bool hasInvalidDynamicSelf = false;
389423
if (result.projectedValueVar &&
390424
result.projectedValueVar->getValueInterfaceType()->hasDynamicSelfType()) {
@@ -439,6 +473,18 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator,
439473
continue;
440474
}
441475

476+
// // If the property wrapper requested an `_enclosingInstance: Never`
477+
// // it must be declared as static. TODO: or global once we allow wrappers on top-level code
478+
// auto wrappedInfo = var->getPropertyWrapperTypeInfo();
479+
// if (wrappedInfo.requireNoEnclosingInstance) {
480+
// if (!var->isStatic()) {
481+
// ctx.Diags.diagnose(var->getLocation(),
482+
// diag::property_wrapper_var_must_be_static,
483+
// var->getName());
484+
// continue;
485+
// }
486+
// }
487+
442488
// Check that the variable is part of a single-variable pattern.
443489
auto binding = var->getParentPatternBinding();
444490
if (binding && binding->getSingleVar() != var) {
@@ -508,7 +554,7 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator,
508554
continue;
509555
}
510556
}
511-
557+
512558
result.push_back(mutableAttr);
513559
}
514560

@@ -601,6 +647,20 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate(
601647
wrappedValue->setInterfaceType(computeWrappedValueType(var, type));
602648
}
603649

650+
{
651+
auto *nominal = type->getDesugaredType()->getAnyNominal();
652+
if (auto wrappedInfo = nominal->getPropertyWrapperTypeInfo()) {
653+
if (wrappedInfo.requireNoEnclosingInstance &&
654+
!var->isStatic()) {
655+
ctx.Diags.diagnose(var->getNameLoc(),
656+
diag::property_wrapper_var_must_be_static,
657+
var->getName(), type);
658+
// TODO: fixit insert static?
659+
return Type();
660+
}
661+
}
662+
}
663+
604664
return type;
605665
}
606666

0 commit comments

Comments
 (0)