Skip to content

Commit 0c20230

Browse files
authored
Merge pull request #25457 from DougGregor/property-wrapper-composition-fixes
2 parents 0ddd668 + 8cc7f09 commit 0c20230

File tree

4 files changed

+145
-42
lines changed

4 files changed

+145
-42
lines changed

lib/AST/Decl.cpp

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5489,12 +5489,13 @@ VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
54895489
return getPropertyWrapperBackingPropertyInfo().backingVar;
54905490
}
54915491

5492-
bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
5493-
auto customAttrs = getAttachedPropertyWrappers();
5492+
static bool propertyWrapperInitializedViaInitialValue(
5493+
const VarDecl *var, bool checkDefaultInit) {
5494+
auto customAttrs = var->getAttachedPropertyWrappers();
54945495
if (customAttrs.empty())
54955496
return false;
54965497

5497-
auto *PBD = getParentPatternBinding();
5498+
auto *PBD = var->getParentPatternBinding();
54985499
if (!PBD)
54995500
return false;
55005501

@@ -5509,36 +5510,23 @@ bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
55095510
return false;
55105511

55115512
// Default initialization does not use a value.
5512-
if (getAttachedPropertyWrapperTypeInfo(0).defaultInit)
5513+
if (checkDefaultInit &&
5514+
var->getAttachedPropertyWrapperTypeInfo(0).defaultInit)
55135515
return false;
55145516

55155517
// If all property wrappers have an initialValue initializer, the property
55165518
// wrapper will be initialized that way.
5517-
return allAttachedPropertyWrappersHaveInitialValueInit();
5519+
return var->allAttachedPropertyWrappersHaveInitialValueInit();
55185520
}
55195521

5520-
bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
5521-
auto customAttrs = getAttachedPropertyWrappers();
5522-
if (customAttrs.empty())
5523-
return false;
5524-
5525-
auto *PBD = getParentPatternBinding();
5526-
if (!PBD)
5527-
return false;
5528-
5529-
// If there was an initializer on the original property, initialize
5530-
// via the initial value.
5531-
if (PBD->getPatternList()[0].getEqualLoc().isValid())
5532-
return true;
5533-
5534-
// If there was an initializer on the outermost wrapper, initialize
5535-
// via the full wrapper.
5536-
if (customAttrs[0]->getArg() != nullptr)
5537-
return false;
5522+
bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
5523+
return propertyWrapperInitializedViaInitialValue(
5524+
this, /*checkDefaultInit=*/true);
5525+
}
55385526

5539-
// If all property wrappers have an initialValue initializer, the property
5540-
// wrapper will be initialized that way.
5541-
return allAttachedPropertyWrappersHaveInitialValueInit();
5527+
bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
5528+
return propertyWrapperInitializedViaInitialValue(
5529+
this, /*checkDefaultInit=*/false);
55425530
}
55435531

55445532
Identifier VarDecl::getObjCPropertyName() const {
@@ -5806,25 +5794,71 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) {
58065794

58075795
Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
58085796
Expr *init) {
5809-
auto attr = var->getAttachedPropertyWrappers().front();
5797+
auto *PBD = var->getParentPatternBinding();
5798+
if (!PBD)
5799+
return nullptr;
58105800

5811-
// Direct initialization implies no original initial value.
5812-
if (attr->getArg())
5801+
// If there is no '=' on the pattern, there was no initial value.
5802+
if (PBD->getPatternList()[0].getEqualLoc().isInvalid())
58135803
return nullptr;
58145804

5815-
// Look through any expressions wrapping the initializer.
5816-
init = init->getSemanticsProvidingExpr();
5817-
auto initCall = dyn_cast<CallExpr>(init);
5818-
if (!initCall)
5805+
ASTContext &ctx = var->getASTContext();
5806+
auto dc = var->getInnermostDeclContext();
5807+
auto innermostAttr = var->getAttachedPropertyWrappers().back();
5808+
auto innermostNominal = evaluateOrDefault(
5809+
ctx.evaluator, CustomAttrNominalRequest{innermostAttr, dc}, nullptr);
5810+
if (!innermostNominal)
58195811
return nullptr;
58205812

5821-
auto initArg = cast<TupleExpr>(initCall->getArg())->getElement(0);
5822-
initArg = initArg->getSemanticsProvidingExpr();
5823-
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
5824-
initArg =
5825-
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
5826-
}
5813+
// Walker
5814+
class Walker : public ASTWalker {
5815+
public:
5816+
NominalTypeDecl *innermostNominal;
5817+
Expr *initArg = nullptr;
5818+
5819+
Walker(NominalTypeDecl *innermostNominal)
5820+
: innermostNominal(innermostNominal) { }
5821+
5822+
virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
5823+
if (initArg)
5824+
return { false, E };
5825+
5826+
if (auto call = dyn_cast<CallExpr>(E)) {
5827+
// We're looking for an implicit call.
5828+
if (!call->isImplicit())
5829+
return { true, E };
5830+
5831+
// ... producing a value of the same nominal type as the innermost
5832+
// property wrapper.
5833+
if (call->getType()->getAnyNominal() != innermostNominal)
5834+
return { true, E };
5835+
5836+
// Find the implicit initialValue argument.
5837+
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
5838+
ASTContext &ctx = innermostNominal->getASTContext();
5839+
for (unsigned i : range(tuple->getNumElements())) {
5840+
if (tuple->getElementName(i) == ctx.Id_initialValue &&
5841+
tuple->getElementNameLoc(i).isInvalid()) {
5842+
initArg = tuple->getElement(i);
5843+
return { false, E };
5844+
}
5845+
}
5846+
}
5847+
}
58275848

5849+
return { true, E };
5850+
}
5851+
} walker(innermostNominal);
5852+
init->walk(walker);
5853+
5854+
Expr *initArg = walker.initArg;
5855+
if (initArg) {
5856+
initArg = initArg->getSemanticsProvidingExpr();
5857+
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
5858+
initArg =
5859+
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
5860+
}
5861+
}
58285862
return initArg;
58295863
}
58305864

lib/Sema/CSSimplify.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4662,7 +4662,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
46624662

46634663
// If the base type is optional because we haven't chosen to force an
46644664
// implicit optional, don't try to fix it. The IUO will be forced instead.
4665-
if (auto dotExpr = dyn_cast<UnresolvedDotExpr>(locator->getAnchor())) {
4665+
if (auto dotExpr =
4666+
dyn_cast_or_null<UnresolvedDotExpr>(locator->getAnchor())) {
46664667
auto baseExpr = dotExpr->getBase();
46674668
auto resolvedOverload = getResolvedOverloadSets();
46684669
while (resolvedOverload) {
@@ -4682,8 +4683,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
46824683
auto innerTV = createTypeVariable(locator,
46834684
TVO_CanBindToLValue |
46844685
TVO_CanBindToNoEscape);
4685-
Type optTy = getTypeChecker().getOptionalType(
4686-
locator->getAnchor()->getSourceRange().Start, innerTV);
4686+
Type optTy = getTypeChecker().getOptionalType(SourceLoc(), innerTV);
46874687
SmallVector<Constraint *, 2> optionalities;
46884688
auto nonoptionalResult = Constraint::createFixed(
46894689
*this, ConstraintKind::Bind,

test/SILGen/property_wrappers.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,60 @@ class UseWrapperWithDefaultInit {
296296
// CHECK: function_ref @$s17property_wrappers22WrapperWithDefaultInitVACyxGycfC
297297
// CHECK: return {{%.*}} : $WrapperWithDefaultInit<String>
298298

299+
// Property wrapper composition.
300+
@propertyWrapper
301+
struct WrapperA<Value> {
302+
var value: Value
303+
304+
init(initialValue: Value) {
305+
value = initialValue
306+
}
307+
}
308+
309+
@propertyWrapper
310+
struct WrapperB<Value> {
311+
var value: Value
312+
313+
init(initialValue: Value) {
314+
value = initialValue
315+
}
316+
}
317+
318+
@propertyWrapper
319+
struct WrapperC<Value> {
320+
var value: Value?
321+
322+
init(initialValue: Value?) {
323+
value = initialValue
324+
}
325+
}
326+
327+
struct CompositionMembers {
328+
// CompositionMembers.p1.getter
329+
// CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers18CompositionMembersV2p1SiSgvg : $@convention(method) (@guaranteed CompositionMembers) -> Optional<Int>
330+
// CHECK: bb0([[SELF:%.*]] : @guaranteed $CompositionMembers):
331+
// CHECK: [[P1:%.*]] = struct_extract [[SELF]] : $CompositionMembers, #CompositionMembers.$p1
332+
// CHECK: [[P1_VALUE:%.*]] = struct_extract [[P1]] : $WrapperA<WrapperB<WrapperC<Int>>>, #WrapperA.value
333+
// CHECK: [[P1_VALUE2:%.*]] = struct_extract [[P1_VALUE]] : $WrapperB<WrapperC<Int>>, #WrapperB.value
334+
// CHECK: [[P1_VALUE3:%.*]] = struct_extract [[P1_VALUE2]] : $WrapperC<Int>, #WrapperC.value
335+
// CHECK: return [[P1_VALUE3]] : $Optional<Int>
336+
@WrapperA @WrapperB @WrapperC var p1: Int?
337+
@WrapperA @WrapperB @WrapperC var p2 = "Hello"
338+
339+
// variable initialization expression of CompositionMembers.$p2
340+
// CHECK-LABEL: sil hidden [transparent] [ossa] @$s17property_wrappers18CompositionMembersV3$p233_{{.*}}8WrapperAVyAA0N1BVyAA0N1CVySSGGGvpfi : $@convention(thin) () -> @owned Optional<String> {
341+
// CHECK: %0 = string_literal utf8 "Hello"
342+
343+
// CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers18CompositionMembersV2p12p2ACSiSg_SSSgtcfC : $@convention(method) (Optional<Int>, @owned Optional<String>, @thin CompositionMembers.Type) -> @owned CompositionMembers
344+
// CHECK: function_ref @$s17property_wrappers8WrapperCV12initialValueACyxGxSg_tcfC
345+
// CHECK: function_ref @$s17property_wrappers8WrapperBV12initialValueACyxGx_tcfC
346+
// CHECK: function_ref @$s17property_wrappers8WrapperAV12initialValueACyxGx_tcfC
347+
}
348+
349+
func testComposition() {
350+
_ = CompositionMembers(p1: nil)
351+
}
352+
299353

300354
// CHECK-LABEL: sil_vtable ClassUsingWrapper {
301355
// CHECK-NEXT: #ClassUsingWrapper.x!getter.1: (ClassUsingWrapper) -> () -> Int : @$s17property_wrappers17ClassUsingWrapperC1xSivg // ClassUsingWrapper.x.getter

test/decl/var/property_wrappers.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,15 +799,30 @@ struct WrapperC<Value> {
799799
}
800800
}
801801

802+
@propertyWrapper
803+
struct WrapperD<Value, X, Y> { // expected-note{{property wrapper type 'WrapperD' declared here}}
804+
var value: Value
805+
}
806+
807+
@propertyWrapper
808+
struct WrapperE<Value> {
809+
var value: Value
810+
}
811+
802812
struct TestComposition {
803813
@WrapperA @WrapperB @WrapperC var p1: Int?
804814
@WrapperA @WrapperB @WrapperC var p2 = "Hello"
815+
@WrapperD<WrapperE, Int, String> @WrapperE var p3: Int?
816+
@WrapperD<WrapperC, Int, String> @WrapperC var p4: Int?
817+
@WrapperD<WrapperC, Int, String> @WrapperE var p5: Int // expected-error{{property type 'Int' does not match that of the 'value' property of its wrapper type 'WrapperD<WrapperC, Int, String>'}}
805818

806819
func triggerErrors(d: Double) {
807820
p1 = d // expected-error{{cannot assign value of type 'Double' to type 'Int?'}}
808821
p2 = d // expected-error{{cannot assign value of type 'Double' to type 'String?'}}
822+
p3 = d // expected-error{{cannot assign value of type 'Double' to type 'Int?'}}
809823

810824
$p1 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperA<WrapperB<WrapperC<Int>>>'}}
811825
$p2 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperA<WrapperB<WrapperC<String>>>'}}
826+
$p3 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperD<WrapperE<Int?>, Int, String>'}}
812827
}
813828
}

0 commit comments

Comments
 (0)