Skip to content

Commit 60f7aa2

Browse files
authored
Merge pull request #31460 from artemcm/5.3-NonmutatingPWSetterInit
[5.3] Allow initializing a wrapped property with a nonmutating setter
2 parents bf72a8e + 46775cc commit 60f7aa2

File tree

6 files changed

+148
-24
lines changed

6 files changed

+148
-24
lines changed

lib/SILGen/SILGenLValue.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,18 +1302,28 @@ namespace {
13021302
{
13031303
}
13041304

1305-
bool canRewriteSetAsPropertyWrapperInit() const {
1305+
bool canRewriteSetAsPropertyWrapperInit(SILGenFunction &SGF) const {
13061306
if (auto *VD = dyn_cast<VarDecl>(Storage)) {
13071307
// If this is not a wrapper property that can be initialized from
13081308
// a value of the wrapped type, we can't perform the initialization.
13091309
auto wrapperInfo = VD->getPropertyWrapperBackingPropertyInfo();
13101310
if (!wrapperInfo.initializeFromOriginal)
13111311
return false;
13121312

1313+
bool isAssignmentToSelfParamInInit =
1314+
IsOnSelfParameter &&
1315+
isa<ConstructorDecl>(SGF.FunctionDC->getAsDecl());
1316+
13131317
// If we have a nonmutating setter on a value type, the call
13141318
// captures all of 'self' and we cannot rewrite an assignment
13151319
// into an initialization.
1316-
if (!VD->isSetterMutating() &&
1320+
1321+
// Unless this is an assignment to a self parameter inside a
1322+
// constructor, in which case we would like to still emit a
1323+
// assign_by_wrapper because the setter will be deleted by lowering
1324+
// anyway.
1325+
if (!isAssignmentToSelfParamInInit &&
1326+
!VD->isSetterMutating() &&
13171327
VD->getDeclContext()->getSelfNominalTypeDecl() &&
13181328
VD->isInstanceMember() &&
13191329
!VD->getDeclContext()->getDeclaredInterfaceType()
@@ -1396,7 +1406,7 @@ namespace {
13961406
assert(getAccessorDecl()->isSetter());
13971407
SILDeclRef setter = Accessor;
13981408

1399-
if (IsOnSelfParameter && canRewriteSetAsPropertyWrapperInit() &&
1409+
if (IsOnSelfParameter && canRewriteSetAsPropertyWrapperInit(SGF) &&
14001410
!Storage->isStatic() &&
14011411
isBackingVarVisible(cast<VarDecl>(Storage),
14021412
SGF.FunctionDC)) {
@@ -1477,6 +1487,16 @@ namespace {
14771487
capturedBase = base.copy(SGF, loc).forward(SGF);
14781488
}
14791489

1490+
// If the base is a reference and the setter expects a value, emit a
1491+
// load. This pattern is emitted for property wrappers with a
1492+
// nonmutating setter, for example.
1493+
if (base.getType().isAddress() &&
1494+
base.getType().getObjectType() ==
1495+
setterConv.getSILArgumentType(argIdx)) {
1496+
capturedBase = SGF.B.createTrivialLoadOr(
1497+
loc, capturedBase, LoadOwnershipQualifier::Take);
1498+
}
1499+
14801500
PartialApplyInst *setterPAI =
14811501
SGF.B.createPartialApply(loc, setterFRef,
14821502
Substitutions, { capturedBase },
@@ -1515,6 +1535,7 @@ namespace {
15151535
SGF.B.createAssignByWrapper(loc, Mval.forward(SGF), proj.forward(SGF),
15161536
initFn.getValue(), setterFn.getValue(),
15171537
AssignOwnershipQualifier::Unknown);
1538+
15181539
return;
15191540
}
15201541

@@ -4300,7 +4321,7 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc,
43004321
}
43014322

43024323
auto &setterComponent = static_cast<GetterSetterComponent&>(component);
4303-
if (setterComponent.canRewriteSetAsPropertyWrapperInit())
4324+
if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF))
43044325
return false;
43054326
setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest),
43064327
std::move(src));

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/Expr.h"
1818
#include "swift/AST/Stmt.h"
1919
#include "swift/ClangImporter/ClangModule.h"
20+
#include "swift/SIL/SILValue.h"
2021
#include "swift/SIL/InstructionUtils.h"
2122
#include "swift/SIL/SILArgument.h"
2223
#include "swift/SIL/SILBuilder.h"
@@ -1802,6 +1803,20 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
18021803
TheMemory.isAnyInitSelf() && !TheMemory.isClassInitSelf()) {
18031804
if (!shouldEmitError(Inst)) return;
18041805

1806+
// Ignore loads used only for a set-by-value (nonmutating) setter
1807+
// since it will be deleted by lowering anyway.
1808+
auto load = cast<SingleValueInstruction>(Inst);
1809+
if (auto Op = load->getSingleUse()) {
1810+
if (auto PAI = dyn_cast<PartialApplyInst>(Op->getUser())) {
1811+
if (std::find_if(PAI->use_begin(), PAI->use_end(),
1812+
[](auto PAIUse) {
1813+
return isa<AssignByWrapperInst>(PAIUse->getUser());
1814+
}) != PAI->use_end()) {
1815+
return;
1816+
}
1817+
}
1818+
}
1819+
18051820
diagnose(Module, Inst->getLoc(), diag::use_of_self_before_fully_init);
18061821
noteUninitializedMembers(Use);
18071822
return;

lib/Sema/CSApply.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6925,9 +6925,37 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
69256925
llvm_unreachable("Unhandled coercion");
69266926
}
69276927

6928+
/// Detect if the expression is an assignment to a `self` wrapped property that
6929+
/// has a nonmutating setter, inside a constructor.
6930+
///
6931+
/// We use this to decide when to produce an inout_expr instead of a load_expr
6932+
/// for the sake of emitting a reference required by the assign_by_wrapper
6933+
/// instruction.
6934+
static bool isNonMutatingSetterPWAssignInsideInit(Expr *baseExpr,
6935+
ValueDecl *member,
6936+
DeclContext *UseDC) {
6937+
// Setter is mutating
6938+
if (cast<AbstractStorageDecl>(member)->isSetterMutating())
6939+
return false;
6940+
// Member is not a wrapped property
6941+
auto *VD = dyn_cast<VarDecl>(member);
6942+
if (!(VD && VD->hasAttachedPropertyWrapper()))
6943+
return false;
6944+
// This is not an expression inside a constructor
6945+
auto *CD = dyn_cast<ConstructorDecl>(UseDC);
6946+
if (!CD)
6947+
return false;
6948+
// This is not an assignment on self
6949+
if (!baseExpr->isSelfExprOf(CD))
6950+
return false;
6951+
6952+
return true;
6953+
}
6954+
69286955
/// Adjust the given type to become the self type when referring to
69296956
/// the given member.
6930-
static Type adjustSelfTypeForMember(Type baseTy, ValueDecl *member,
6957+
static Type adjustSelfTypeForMember(Expr *baseExpr,
6958+
Type baseTy, ValueDecl *member,
69316959
AccessSemantics semantics,
69326960
DeclContext *UseDC) {
69336961
auto baseObjectTy = baseTy->getWithoutSpecifierType();
@@ -6951,10 +6979,15 @@ static Type adjustSelfTypeForMember(Type baseTy, ValueDecl *member,
69516979
bool isSettableFromHere =
69526980
SD->isSettable(UseDC) && SD->isSetterAccessibleFrom(UseDC);
69536981

6954-
// If neither the property's getter nor its setter are mutating, the base
6955-
// can be an rvalue.
6956-
if (!SD->isGetterMutating()
6957-
&& (!isSettableFromHere || !SD->isSetterMutating()))
6982+
// If neither the property's getter nor its setter are mutating, and
6983+
// this is not a nonmutating property wrapper setter,
6984+
// the base can be an rvalue.
6985+
// With the exception of assignments to a wrapped property inside a
6986+
// constructor, where we need to produce a reference to be used on
6987+
// the assign_by_wrapper instruction.
6988+
if (!SD->isGetterMutating() &&
6989+
(!isSettableFromHere || !SD->isSetterMutating()) &&
6990+
!isNonMutatingSetterPWAssignInsideInit(baseExpr, member, UseDC))
69586991
return baseObjectTy;
69596992

69606993
// If we're calling an accessor, keep the base as an inout type, because the
@@ -6984,7 +7017,7 @@ ExprRewriter::coerceObjectArgumentToType(Expr *expr,
69847017
Type baseTy, ValueDecl *member,
69857018
AccessSemantics semantics,
69867019
ConstraintLocatorBuilder locator) {
6987-
Type toType = adjustSelfTypeForMember(baseTy, member, semantics, dc);
7020+
Type toType = adjustSelfTypeForMember(expr, baseTy, member, semantics, dc);
69887021

69897022
// If our expression already has the right type, we're done.
69907023
Type fromType = cs.getType(expr);

test/SILGen/property_wrappers.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,38 @@ struct ObservedObject<ObjectType : AnyObject > {
845845
}
846846
}
847847

848+
849+
850+
// rdar://problem/60600911
851+
// Ensure assign_by_wrapper is emitted for initialization
852+
// of a property wrapper with a nonmutating set. Even though such setters
853+
// take `self` by-value.
854+
@propertyWrapper
855+
struct NonMutatingSetterWrapper<Value> {
856+
var value: Value
857+
init(wrappedValue: Value) {
858+
value = wrappedValue
859+
}
860+
var wrappedValue: Value {
861+
get { value }
862+
nonmutating set {
863+
print(" .. nonmutatingSet \(newValue)")
864+
}
865+
}
866+
}
867+
868+
struct NonMutatingWrapperTestStruct {
869+
// CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers28NonMutatingWrapperTestStructV3valACSi_tcfC : $@convention(method) (Int, @thin NonMutatingWrapperTestStruct.Type) -> NonMutatingWrapperTestStruct {
870+
// CHECK: %[[LOAD:[0-9]+]] = load [trivial] %[[SRC:[0-9]+]] : $*NonMutatingWrapperTestStruct
871+
// CHECK-NEXT: %[[SET_PA:[0-9]+]] = partial_apply [callee_guaranteed] %[[PW_SETTER:[0-9]+]](%[[LOAD]]) : $@convention(method) (Int, NonMutatingWrapperTestStruct) -> ()
872+
// CHECK-NEXT: assign_by_wrapper %[[SETVAL:[0-9]+]] : $Int to %[[ADDR:[0-9]+]] : $*NonMutatingSetterWrapper<Int>, init %[[INIT_PA:[0-9]+]] : $@callee_guaranteed (Int) -> NonMutatingSetterWrapper<Int>, set %[[SET_PA]] : $@callee_guaranteed (Int) -> ()
873+
@NonMutatingSetterWrapper var SomeProp: Int
874+
init(val: Int) {
875+
SomeProp = val
876+
}
877+
}
878+
879+
848880
// SR-12443: Crash on property with wrapper override that adds observer.
849881
@propertyWrapper
850882
struct BasicIntWrapper {

test/SILOptimizer/di_property_wrappers.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ public final class Synchronized<Value> {
501501
}
502502
}
503503

504+
504505
struct SR_12341 {
505506
@Wrapper var wrapped: Int = 10
506507
var str: String
@@ -533,6 +534,34 @@ func testSR_12341() {
533534
_ = SR_12341(condition: true)
534535
}
535536

537+
@propertyWrapper
538+
struct NonMutatingSetterWrapper<Value> {
539+
var value: Value
540+
init(wrappedValue: Value) {
541+
value = wrappedValue
542+
}
543+
var wrappedValue: Value {
544+
get { value }
545+
nonmutating set {
546+
print(" .. nonmutatingSet \(newValue)")
547+
}
548+
}
549+
}
550+
551+
struct NonMutatingWrapperTestStruct {
552+
@NonMutatingSetterWrapper var SomeProp: Int
553+
init(val: Int) {
554+
SomeProp = val
555+
}
556+
}
557+
558+
func testNonMutatingSetterStruct() {
559+
// CHECK: ## NonMutatingSetterWrapper
560+
print("\n## NonMutatingSetterWrapper")
561+
let A = NonMutatingWrapperTestStruct(val: 11)
562+
// CHECK-NEXT: .. nonmutatingSet 11
563+
}
564+
536565
testIntStruct()
537566
testIntClass()
538567
testRefStruct()
@@ -543,3 +572,4 @@ testDefaultNilOptIntStruct()
543572
testComposed()
544573
testWrapperInitWithDefaultArg()
545574
testSR_12341()
575+
testNonMutatingSetterStruct()

test/SILOptimizer/di_property_wrappers_errors.swift

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,23 @@ struct IntStructWithClassWrapper {
2323
@ClassWrapper var wrapped: Int
2424

2525
init() {
26-
wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}}
27-
// expected-note@-1{{'self.wrapped' not initialized}}
28-
} // expected-error{{return from initializer without initializing all stored properties}}
29-
// expected-note@-1{{'self.wrapped' not initialized}}
26+
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
27+
}
3028

3129
init(conditional b: Bool) {
3230
if b {
3331
self._wrapped = ClassWrapper(wrappedValue: 32)
3432
} else {
35-
wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}}
36-
// expected-note@-1{{'self.wrapped' not initialized}}
33+
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
3734
}
38-
} // expected-error{{return from initializer without initializing all stored properties}}
39-
// expected-note@-1{{'self.wrapped' not initialized}}
35+
}
4036

4137
init(dynamic b: Bool) {
4238
if b {
43-
wrapped = 42 // expected-error{{'self' used before all stored properties are initialized}}
44-
// expected-note@-1{{'self.wrapped' not initialized}}
39+
wrapped = 42 // expected-error{{variable 'self.wrapped' used before being initialized}}
4540
}
46-
wrapped = 27 // expected-error{{'self' used before all stored properties are initialized}}
47-
// expected-note@-1{{'self.wrapped' not initialized}}
48-
} // expected-error{{return from initializer without initializing all stored properties}}
49-
// expected-note@-1{{'self.wrapped' not initialized}}
41+
wrapped = 27 // expected-error{{variable 'self.wrapped' used before being initialized}}
42+
}
5043
}
5144

5245
// SR_11477

0 commit comments

Comments
 (0)