Skip to content

Commit de53a64

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 b4a65a4 commit de53a64

File tree

4 files changed

+118
-1
lines changed

4 files changed

+118
-1
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,15 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
578578
VD->getOriginalWrappedProperty(PropertyWrapperSynthesizedPropertyKind::Backing))
579579
return true;
580580

581-
if (auto *nominalDecl = VD->getDeclContext()->getSelfClassDecl()) {
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+
589+
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'.
584592
auto *PBD = VD->getParentPatternBinding();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 init() {}
21+
22+
public var projectedValue: PropertyWrapper {
23+
get {
24+
return self
25+
}
26+
set {
27+
self = newValue
28+
}
29+
}
30+
31+
public var wrappedValue: Bool {
32+
return false
33+
}
34+
}
35+
36+
// CHECK-LABEL: sil [ossa] @$s23property_wrappers_final17useStorageWrapperyyAA7MyClassCF : $@convention(thin) (@guaranteed MyClass) -> () {
37+
public func useStorageWrapper(_ c: MyClass) {
38+
// CHECK: function_ref @$s23property_wrappers_final7MyClassC15$staticPropertyAA0G7WrapperVvgZ
39+
_ = MyClass.$staticProperty
40+
41+
// CHECK: function_ref @$s23property_wrappers_final7MyClassC15$staticPropertyAA0G7WrapperVvsZ
42+
MyClass.$staticProperty = PropertyWrapper()
43+
44+
// CHECK: $s23property_wrappers_final7MyClassC17$instancePropertyAA0G7WrapperVvg
45+
_ = c.$instanceProperty
46+
47+
// CHECK: $s23property_wrappers_final7MyClassC17$instancePropertyAA0G7WrapperVvs
48+
c.$instanceProperty = PropertyWrapper()
49+
50+
// CHECK: return
51+
}
52+
53+
// CHECK-LABEL: sil_vtable [serialized] MyClass {
54+
// CHECK-NEXT: #MyClass.init!allocator.1: (MyClass.Type) -> () -> MyClass : @$s23property_wrappers_final7MyClassCACycfC
55+
// CHECK-NEXT: #MyClass.deinit!deallocator.1: @$s23property_wrappers_final7MyClassCfD
56+
// 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)