Skip to content

Commit 52c11c7

Browse files
committed
Sema: Property wrapper storage wrappers ($foo) inherit 'final' bit from original property
Otherwise, we would generate inconsistent vtable layouts for classes with static properties that have attached wrappers. The reason is that we normally force synthesis of the backing storage and storage wrapper for each instance property wrapper as part of computing the lowered stored properties. However, there was no such forcing for static properties. But since a static stored property (with an attached wrapper or otherwise) must be 'final', the real fix is to just ensure that the 'final' bit propagates to the storage wrapper as well. The backing storage property was already always final, so the issue did not arise there. Fixes <rdar://problem/59522703>, <https://bugs.swift.org/browse/SR-12429>.
1 parent 026c3c9 commit 52c11c7

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,14 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
578578
VD->getOriginalWrappedProperty(PropertyWrapperSynthesizedPropertyKind::Backing))
579579
return true;
580580

581+
// Property wrapper storage wrappers are final if the original property
582+
// is final.
583+
if (auto *original = VD->getOriginalWrappedProperty(
584+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper)) {
585+
if (original->isFinal())
586+
return true;
587+
}
588+
581589
if (VD->getDeclContext()->getSelfClassDecl()) {
582590
// If this variable is a class member, mark it final if the
583591
// class is final, or if it was declared with 'let'.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
3+
4+
// Test that the storage wrapper for a final property is itself final, and that
5+
// its accessors do not appear in the vtable.
6+
7+
public class MyClass {
8+
public init() { }
9+
10+
@PropertyWrapper()
11+
public static var staticProperty: Bool
12+
13+
@PropertyWrapper()
14+
public final var instanceProperty: Bool
15+
16+
}
17+
18+
@propertyWrapper
19+
public struct PropertyWrapper {
20+
public var projectedValue: PropertyWrapper {
21+
get {
22+
return self
23+
}
24+
set {
25+
self = newValue
26+
}
27+
}
28+
29+
public var wrappedValue: Bool {
30+
return false
31+
}
32+
}
33+
34+
// CHECK-LABEL: sil [ossa] @$s23property_wrappers_final17useStorageWrapperyyAA7MyClassCF : $@convention(thin) (@guaranteed MyClass) -> () {
35+
public func useStorageWrapper(_ c: MyClass) {
36+
// CHECK: function_ref @$s23property_wrappers_final7MyClassC15$staticPropertyAA0G7WrapperVvgZ
37+
_ = MyClass.$staticProperty
38+
39+
// CHECK: function_ref @$s23property_wrappers_final7MyClassC15$staticPropertyAA0G7WrapperVvsZ
40+
MyClass.$staticProperty = PropertyWrapper()
41+
42+
// CHECK: $s23property_wrappers_final7MyClassC17$instancePropertyAA0G7WrapperVvg
43+
_ = c.$instanceProperty
44+
45+
// CHECK: $s23property_wrappers_final7MyClassC17$instancePropertyAA0G7WrapperVvs
46+
c.$instanceProperty = PropertyWrapper()
47+
48+
// CHECK: return
49+
}
50+
51+
// CHECK-LABEL: sil_vtable [serialized] MyClass {
52+
// CHECK-NEXT: #MyClass.init!allocator: (MyClass.Type) -> () -> MyClass : @$s23property_wrappers_final7MyClassCACycfC
53+
// CHECK-NEXT: #MyClass.deinit!deallocator: @$s23property_wrappers_final7MyClassCfD
54+
// CHECK-NEXT: }

test/multifile/Inputs/sr12429.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
public class MyClass {
2+
public init() { }
3+
4+
@PropertyWrapper(key: "key", defaultValue: false)
5+
public static var wrappedProperty: Bool
6+
7+
public var otherProperty: String? {
8+
didSet {
9+
fatalError("Set this other property with value: \(String(describing: otherProperty)), even though we called `myClass.property = `")
10+
}
11+
}
12+
13+
public var property: String? {
14+
didSet {
15+
print("Set expected property: \(String(describing: property))")
16+
}
17+
}
18+
}
19+
20+
@propertyWrapper
21+
public struct PropertyWrapper<Value> {
22+
public let key: String
23+
public let defaultValue: Value
24+
25+
public var projectedValue: PropertyWrapper<Value> {
26+
get {
27+
return self
28+
}
29+
// Having this setter is what causes the mis-compilation
30+
set {
31+
self = newValue
32+
}
33+
}
34+
35+
public var wrappedValue: Value {
36+
return defaultValue
37+
}
38+
39+
public init(key: String, defaultValue: Value) {
40+
self.key = key
41+
self.defaultValue = defaultValue
42+
}
43+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: cp %s %t/main.swift
3+
// RUN: %target-build-swift -o %t/main %t/main.swift %S/Inputs/sr12429.swift
4+
// RUN: %target-codesign %t/main
5+
// RUN: %target-run %t/main
6+
7+
// REQUIRES: executable_test
8+
9+
let object = MyClass()
10+
object.property = "value"

0 commit comments

Comments
 (0)