Skip to content

Commit 96f4315

Browse files
committed
[Property Wrappers] Implement support for parameters with an implicit
property wrapper in the property wrapper requests.
1 parent 2c2d671 commit 96f4315

File tree

6 files changed

+139
-59
lines changed

6 files changed

+139
-59
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4917,7 +4917,10 @@ class VarDecl : public AbstractStorageDecl {
49174917

49184918
/// Whether this property has any attached property wrappers.
49194919
bool hasAttachedPropertyWrapper() const;
4920-
4920+
4921+
/// Whether this var has an implicit property wrapper attribute.
4922+
bool hasImplicitPropertyWrapper() const;
4923+
49214924
/// Whether all of the attached property wrappers have an init(wrappedValue:)
49224925
/// initializer.
49234926
bool allAttachedPropertyWrappersHaveWrappedValueInit() const;

lib/AST/Decl.cpp

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5861,7 +5861,17 @@ llvm::TinyPtrVector<CustomAttr *> VarDecl::getAttachedPropertyWrappers() const {
58615861

58625862
/// Whether this property has any attached property wrappers.
58635863
bool VarDecl::hasAttachedPropertyWrapper() const {
5864-
return !getAttachedPropertyWrappers().empty();
5864+
return !getAttachedPropertyWrappers().empty() || hasImplicitPropertyWrapper();
5865+
}
5866+
5867+
bool VarDecl::hasImplicitPropertyWrapper() const {
5868+
if (!getAttachedPropertyWrappers().empty())
5869+
return false;
5870+
5871+
auto *dc = getDeclContext();
5872+
bool isClosureParam = isa<ParamDecl>(this) &&
5873+
dc->getContextKind() == DeclContextKind::AbstractClosureExpr;
5874+
return !isImplicit() && getName().hasDollarPrefix() && isClosureParam;
58655875
}
58665876

58675877
/// Whether all of the attached property wrappers have an init(wrappedValue:)
@@ -5877,15 +5887,22 @@ bool VarDecl::allAttachedPropertyWrappersHaveWrappedValueInit() const {
58775887

58785888
PropertyWrapperTypeInfo
58795889
VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const {
5880-
auto attrs = getAttachedPropertyWrappers();
5881-
if (i >= attrs.size())
5882-
return PropertyWrapperTypeInfo();
5883-
5884-
auto attr = attrs[i];
5885-
auto dc = getDeclContext();
5886-
ASTContext &ctx = getASTContext();
5887-
auto nominal = evaluateOrDefault(
5888-
ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
5890+
NominalTypeDecl *nominal;
5891+
if (hasImplicitPropertyWrapper()) {
5892+
assert(i == 0);
5893+
nominal = getInterfaceType()->getAnyNominal();
5894+
} else {
5895+
auto attrs = getAttachedPropertyWrappers();
5896+
if (i >= attrs.size())
5897+
return PropertyWrapperTypeInfo();
5898+
5899+
auto attr = attrs[i];
5900+
auto dc = getDeclContext();
5901+
ASTContext &ctx = getASTContext();
5902+
nominal = evaluateOrDefault(
5903+
ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr);
5904+
}
5905+
58895906
if (!nominal)
58905907
return PropertyWrapperTypeInfo();
58915908

@@ -5966,7 +5983,7 @@ void VarDecl::visitAuxiliaryDecls(llvm::function_ref<void(VarDecl *)> visit) con
59665983
visit(backingVar);
59675984
}
59685985

5969-
if (getAttrs().hasAttribute<CustomAttr>()) {
5986+
if (getAttrs().hasAttribute<CustomAttr>() || hasImplicitPropertyWrapper()) {
59705987
if (auto *backingVar = getPropertyWrapperBackingProperty())
59715988
visit(backingVar);
59725989

@@ -6251,15 +6268,17 @@ Type ParamDecl::getVarargBaseTy(Type VarArgT) {
62516268
}
62526269

62536270
AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
6254-
if (!type)
6255-
type = getInterfaceType();
6271+
if (!type) {
6272+
if (hasAttachedPropertyWrapper() && !hasImplicitPropertyWrapper()) {
6273+
type = getPropertyWrapperBackingPropertyType();
6274+
} else {
6275+
type = getInterfaceType();
6276+
}
6277+
}
62566278

62576279
if (isVariadic())
62586280
type = ParamDecl::getVarargBaseTy(type);
62596281

6260-
if (auto wrapperType = getPropertyWrapperBackingPropertyType())
6261-
type = wrapperType;
6262-
62636282
auto label = getArgumentName();
62646283
auto flags = ParameterTypeFlags::fromParameterType(
62656284
type, isVariadic(), isAutoClosure(), isNonEphemeral(),

lib/AST/TypeCheckRequests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,22 +490,22 @@ bool PropertyWrapperBackingPropertyTypeRequest::isCached() const {
490490

491491
bool PropertyWrapperBackingPropertyInfoRequest::isCached() const {
492492
auto var = std::get<0>(getStorage());
493-
return !var->getAttrs().isEmpty();
493+
return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper();
494494
}
495495

496496
bool PropertyWrapperWrappedValueVarRequest::isCached() const {
497497
auto var = std::get<0>(getStorage());
498-
return !var->getAttrs().isEmpty();
498+
return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper();
499499
}
500500

501501
bool PropertyWrapperMutabilityRequest::isCached() const {
502502
auto var = std::get<0>(getStorage());
503-
return !var->getAttrs().isEmpty();
503+
return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper();
504504
}
505505

506506
bool PropertyWrapperLValuenessRequest::isCached() const {
507507
auto var = std::get<0>(getStorage());
508-
return !var->getAttrs().isEmpty();
508+
return !var->getAttrs().isEmpty() || var->hasImplicitPropertyWrapper();
509509
}
510510

511511
void swift::simple_display(

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ Type AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator,
516516
Type
517517
PropertyWrapperBackingPropertyTypeRequest::evaluate(
518518
Evaluator &evaluator, VarDecl *var) const {
519+
if (var->hasImplicitPropertyWrapper())
520+
return var->getInterfaceType();
521+
519522
Type rawType =
520523
evaluateOrDefault(evaluator,
521524
AttachedPropertyWrapperTypeRequest{var, 0}, Type());
@@ -558,15 +561,19 @@ PropertyWrapperBackingPropertyTypeRequest::evaluate(
558561
Type swift::computeWrappedValueType(const VarDecl *var, Type backingStorageType,
559562
Optional<unsigned> limit) {
560563
auto wrapperAttrs = var->getAttachedPropertyWrappers();
561-
unsigned realLimit = wrapperAttrs.size();
564+
unsigned realLimit = var->hasImplicitPropertyWrapper() ? 1 : wrapperAttrs.size();
562565
if (limit)
563566
realLimit = std::min(*limit, realLimit);
564567

565568
// Follow the chain of wrapped value properties.
566569
Type wrappedValueType = backingStorageType;
567570
DeclContext *dc = var->getDeclContext();
568-
for (unsigned i : range(realLimit)) {
569-
auto wrappedInfo = var->getAttachedPropertyWrapperTypeInfo(i);
571+
while (realLimit--) {
572+
auto *nominal = wrappedValueType->getDesugaredType()->getAnyNominal();
573+
if (!nominal)
574+
return Type();
575+
576+
auto wrappedInfo = nominal->getPropertyWrapperTypeInfo();
570577
if (!wrappedInfo)
571578
return wrappedValueType;
572579

@@ -581,6 +588,19 @@ Type swift::computeWrappedValueType(const VarDecl *var, Type backingStorageType,
581588
return wrappedValueType;
582589
}
583590

591+
Type swift::computeProjectedValueType(const VarDecl *var, Type backingStorageType) {
592+
if (!var->hasAttachedPropertyWrapper())
593+
return Type();
594+
595+
if (var->hasImplicitPropertyWrapper())
596+
return backingStorageType;
597+
598+
DeclContext *dc = var->getDeclContext();
599+
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0);
600+
return backingStorageType->getTypeOfMember(dc->getParentModule(),
601+
wrapperInfo.projectedValueVar);
602+
}
603+
584604
Expr *swift::buildPropertyWrapperInitCall(
585605
const VarDecl *var, Type backingStorageType, Expr *value,
586606
PropertyWrapperInitKind initKind,
@@ -591,6 +611,18 @@ Expr *swift::buildPropertyWrapperInitCall(
591611
Expr *initializer = value;
592612
ApplyExpr *innermostInit = nullptr;
593613

614+
// Projected-value initializers don't compose, so no need to iterate
615+
// over the wrapper attributes.
616+
if (initKind == PropertyWrapperInitKind::ProjectedValue) {
617+
auto typeExpr = TypeExpr::createImplicit(backingStorageType, ctx);
618+
auto argName = ctx.Id_projectedValue;
619+
auto *init =
620+
CallExpr::createImplicit(ctx, typeExpr, { initializer }, { argName });
621+
622+
innermostInitCallback(init);
623+
return init;
624+
}
625+
594626
for (unsigned i : llvm::reverse(indices(wrapperAttrs))) {
595627
Type wrapperType =
596628
backingStorageType ? computeWrappedValueType(var, backingStorageType, i)
@@ -613,20 +645,16 @@ Expr *swift::buildPropertyWrapperInitCall(
613645
auto attr = wrapperAttrs[i];
614646
if (!attr->getArg()) {
615647
Identifier argName;
616-
if (initKind == PropertyWrapperInitKind::ProjectedValue) {
617-
argName = ctx.Id_projectedValue;
618-
} else {
619-
assert(initKind == PropertyWrapperInitKind::WrappedValue);
620-
switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) {
621-
case PropertyWrapperTypeInfo::HasInitialValueInit:
622-
argName = ctx.Id_initialValue;
623-
break;
648+
assert(initKind == PropertyWrapperInitKind::WrappedValue);
649+
switch (var->getAttachedPropertyWrapperTypeInfo(i).wrappedValueInit) {
650+
case PropertyWrapperTypeInfo::HasInitialValueInit:
651+
argName = ctx.Id_initialValue;
652+
break;
624653

625-
case PropertyWrapperTypeInfo::HasWrappedValueInit:
626-
case PropertyWrapperTypeInfo::NoWrappedValueInit:
627-
argName = ctx.Id_wrappedValue;
628-
break;
629-
}
654+
case PropertyWrapperTypeInfo::HasWrappedValueInit:
655+
case PropertyWrapperTypeInfo::NoWrappedValueInit:
656+
argName = ctx.Id_wrappedValue;
657+
break;
630658
}
631659

632660
auto endLoc = initializer->getEndLoc();

lib/Sema/TypeCheckStorage.cpp

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,12 +2482,18 @@ static VarDecl *synthesizePropertyWrapperProjectionVar(
24822482
property->overwriteSetterAccess(var->getSetterFormalAccess());
24832483

24842484
// Add the accessors we need.
2485-
bool hasSetter = wrapperVar->isSettable(nullptr) &&
2486-
wrapperVar->isSetterAccessibleFrom(var->getInnermostDeclContext());
2487-
if (hasSetter)
2488-
property->setImplInfo(StorageImplInfo::getMutableComputed());
2489-
else
2485+
if (var->hasImplicitPropertyWrapper()) {
2486+
// FIXME: This can have a setter, but we need a resolved type first
2487+
// to figure it out.
24902488
property->setImplInfo(StorageImplInfo::getImmutableComputed());
2489+
} else {
2490+
bool hasSetter = wrapperVar->isSettable(nullptr) &&
2491+
wrapperVar->isSetterAccessibleFrom(var->getInnermostDeclContext());
2492+
if (hasSetter)
2493+
property->setImplInfo(StorageImplInfo::getMutableComputed());
2494+
else
2495+
property->setImplInfo(StorageImplInfo::getImmutableComputed());
2496+
}
24912497

24922498
if (!isa<ParamDecl>(var))
24932499
var->getAttrs().add(
@@ -2691,10 +2697,11 @@ PropertyWrapperLValuenessRequest::evaluate(Evaluator &,
26912697
PropertyWrapperBackingPropertyInfo
26922698
PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
26932699
VarDecl *var) const {
2694-
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0);
2695-
if (!wrapperInfo)
2700+
if (!var->hasAttachedPropertyWrapper())
26962701
return PropertyWrapperBackingPropertyInfo();
26972702

2703+
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0);
2704+
26982705
// Compute the name of the storage type.
26992706
ASTContext &ctx = var->getASTContext();
27002707
SmallString<64> nameBuf;
@@ -2723,14 +2730,18 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
27232730
}
27242731

27252732
VarDecl *projectionVar = nullptr;
2726-
if (wrapperInfo.projectedValueVar) {
2727-
projectionVar = synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType,
2728-
wrapperInfo.projectedValueVar);
2733+
if (wrapperInfo.projectedValueVar || var->getName().hasDollarPrefix()) {
2734+
projectionVar =
2735+
synthesizePropertyWrapperProjectionVar(ctx, var, wrapperType,
2736+
wrapperInfo.projectedValueVar);
27292737
}
27302738

27312739
return PropertyWrapperBackingPropertyInfo(backingVar, projectionVar, nullptr, nullptr);
27322740
}
27332741

2742+
if (!wrapperInfo)
2743+
return PropertyWrapperBackingPropertyInfo();
2744+
27342745
// Determine the type of the storage.
27352746
auto wrapperType = var->getPropertyWrapperBackingPropertyType();
27362747
if (!wrapperType || wrapperType->hasError())
@@ -2850,25 +2861,40 @@ PropertyWrapperWrappedValueVarRequest::evaluate(Evaluator &evaluator,
28502861

28512862
auto dc = var->getDeclContext();
28522863
auto &ctx = var->getASTContext();
2864+
2865+
SmallString<64> nameBuf;
2866+
if (var->getName().hasDollarPrefix()) {
2867+
nameBuf = var->getName().str().drop_front();
2868+
} else {
2869+
nameBuf = var->getName().str();
2870+
}
2871+
Identifier name = ctx.getIdentifier(nameBuf);
2872+
28532873
VarDecl *localVar = new (ctx) VarDecl(/*IsStatic=*/false,
28542874
VarDecl::Introducer::Var,
2855-
var->getLoc(),
2856-
var->getName(), dc);
2857-
localVar->setInterfaceType(var->getInterfaceType());
2875+
var->getLoc(), name, dc);
2876+
if (!var->hasImplicitPropertyWrapper())
2877+
localVar->setInterfaceType(var->getInterfaceType());
28582878
localVar->setImplicit();
28592879
localVar->getAttrs() = var->getAttrs();
28602880
localVar->overwriteAccess(var->getFormalAccess());
28612881

2862-
auto mutability = *var->getPropertyWrapperMutability();
2863-
if (mutability.Getter == PropertyWrapperMutability::Mutating) {
2864-
ctx.Diags.diagnose(var->getLoc(), diag::property_wrapper_param_mutating);
2865-
return nullptr;
2866-
}
2867-
2868-
if (mutability.Setter == PropertyWrapperMutability::Nonmutating) {
2869-
localVar->setImplInfo(StorageImplInfo::getMutableComputed());
2870-
} else {
2882+
if (var->hasImplicitPropertyWrapper()) {
2883+
// FIXME: This can have a setter, but we need a resolved wrapper type
2884+
// to figure it out.
28712885
localVar->setImplInfo(StorageImplInfo::getImmutableComputed());
2886+
} else {
2887+
auto mutability = *var->getPropertyWrapperMutability();
2888+
if (mutability.Getter == PropertyWrapperMutability::Mutating) {
2889+
ctx.Diags.diagnose(var->getLoc(), diag::property_wrapper_param_mutating);
2890+
return nullptr;
2891+
}
2892+
2893+
if (mutability.Setter == PropertyWrapperMutability::Nonmutating) {
2894+
localVar->setImplInfo(StorageImplInfo::getMutableComputed());
2895+
} else {
2896+
localVar->setImplInfo(StorageImplInfo::getImmutableComputed());
2897+
}
28722898
}
28732899

28742900
evaluator.cacheOutput(PropertyWrapperBackingPropertyInfoRequest{localVar},

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,10 @@ bool isValidKeyPathDynamicMemberLookup(SubscriptDecl *decl,
12481248
Type computeWrappedValueType(const VarDecl *var, Type backingStorageType,
12491249
Optional<unsigned> limit = None);
12501250

1251+
/// Compute the projected value type for the given property that has attached
1252+
/// property wrappers when the backing storage is known to have the given type.
1253+
Type computeProjectedValueType(const VarDecl *var, Type backingStorageType);
1254+
12511255
/// Build a call to the init(wrappedValue:) or init(projectedValue:)
12521256
/// initializer of the property wrapper, filling in the given \c value
12531257
/// as the wrapped or projected value argument.

0 commit comments

Comments
 (0)