Skip to content

Commit 38a43a4

Browse files
committed
[TaskLocals] Enforce @TaskLocal may only be used on static props
1 parent 070f9a7 commit 38a43a4

File tree

6 files changed

+93
-13
lines changed

6 files changed

+93
-13
lines changed

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/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 {

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 60 additions & 0 deletions
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) {
@@ -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

stdlib/private/StdlibUnittest/StdlibUnittest.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -723,13 +723,6 @@ public func expectCrash(withMessage message: String = "", executing: () -> Void)
723723
fatalError()
724724
}
725725

726-
public func expectCrashAsync(withMessage message: String = "", executing: () async -> Void) async -> Never {
727-
expectCrashLater(withMessage: message)
728-
await executing()
729-
expectUnreachable()
730-
fatalError()
731-
}
732-
733726
func _defaultTestSuiteFailedCallback() {
734727
abort()
735728
}

stdlib/public/Concurrency/TaskLocal.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,28 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
166166
self
167167
}
168168

169-
@available(*, unavailable, message: "use 'myTaskLocal.withValue(_:do:)' instead")
169+
@available(*, unavailable, message: "use '$myTaskLocal.withValue(_:do:)' instead")
170170
set {
171171
fatalError("Illegal attempt to set a \(Self.self) value, use `withValue(...) { ... }` instead.")
172172
}
173173
}
174174

175+
// This subscript is used to enforce that the property wrapper may only be used
176+
// on static (or rather, "without enclosing instance") properties.
177+
// This is done by marking the `_enclosingInstance` as `Never` which informs
178+
// the type-checker that this property-wrapper never wants to have an enclosing
179+
// instance (it is impossible to declare a property wrapper inside the `Never`
180+
// type).
181+
public static subscript(
182+
_enclosingInstance object: Never,
183+
wrapped wrappedKeyPath: ReferenceWritableKeyPath<Never, Value>,
184+
storage storageKeyPath: ReferenceWritableKeyPath<Never, TaskLocal<Value>>
185+
) -> Value {
186+
get {
187+
fatalError()
188+
}
189+
}
190+
175191
public var wrappedValue: Value {
176192
self.get()
177193
}

test/Concurrency/task_local.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
// REQUIRES: concurrency
33

44
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
5-
enum TL {
5+
struct TL {
66
@TaskLocal
77
static var number: Int = 0
88

99
@TaskLocal
1010
static var someNil: Int?
1111

12-
// TODO: ban non-static task-local definitions
13-
// @TaskLocal(default: 0)
14-
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
15-
// var notStatic
12+
@TaskLocal
13+
static var noValue: Int // expected-error{{'static var' declaration requires an initializer expression or an explicitly stated getter}}
14+
// expected-note@-1{{add an initializer to silence this error}}
15+
16+
@TaskLocal
17+
var notStatic: String? // expected-error{{property 'notStatic', must be static because property wrapper 'TaskLocal<String?>' can only be applied to static properties}}
1618
}
1719

1820
@TaskLocal // expected-error{{property wrappers are not yet supported in top-level code}}

0 commit comments

Comments
 (0)