Skip to content

Commit ad3568e

Browse files
authored
Merge pull request swiftlang#36344 from hborla/property-wrapper-parameter-revision
[SE-0293] Implement revision #3 of property wrapper parameters
2 parents d66382d + b85bf55 commit ad3568e

15 files changed

+268
-58
lines changed

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,36 @@ CHANGELOG
2929
Swift 5.5
3030
---------
3131

32+
* [SE-0293][]:
33+
34+
Property wrappers can now be applied to function and closure parameters:
35+
36+
```swift
37+
@propertyWrapper
38+
struct Wrapper<Value> {
39+
var wrappedValue: Value
40+
41+
var projectedValue: Self { return self }
42+
43+
init(wrappedValue: Value) { ... }
44+
45+
init(projectedValue: Self) { ... }
46+
}
47+
48+
func test(@Wrapper value: Int) {
49+
print(value)
50+
print($value)
51+
print(_value)
52+
}
53+
54+
test(value: 10)
55+
56+
let projection = Wrapper(wrappedValue: 10)
57+
test($value: projection)
58+
```
59+
60+
The call-site can pass a wrapped value or a projected value, and the property wrapper will be initialized using `init(wrappedValue:)` or `init(projectedValue:)`, respectively.
61+
3262
* [SE-0299][]:
3363

3464
It is now possible to use leading-dot syntax in generic contexts to access static members of protocol extensions where `Self` is constrained to a fully concrete type:

include/swift/AST/AnyFunctionRef.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ class AnyFunctionRef {
110110
return TheFunction.get<AbstractClosureExpr *>()->getParameters();
111111
}
112112

113-
bool hasPropertyWrapperParameters() const {
113+
bool hasExternalPropertyWrapperParameters() const {
114114
return llvm::any_of(*getParameters(), [](const ParamDecl *param) {
115-
return param->hasAttachedPropertyWrapper();
115+
return param->hasExternalPropertyWrapper();
116116
});
117117
}
118118

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5013,6 +5013,10 @@ class VarDecl : public AbstractStorageDecl {
50135013
/// Whether this var has an implicit property wrapper attribute.
50145014
bool hasImplicitPropertyWrapper() const;
50155015

5016+
/// Whether this var is a parameter with an attached property wrapper
5017+
/// that has an external effect on the function.
5018+
bool hasExternalPropertyWrapper() const;
5019+
50165020
/// Whether all of the attached property wrappers have an init(wrappedValue:)
50175021
/// initializer.
50185022
bool allAttachedPropertyWrappersHaveWrappedValueInit() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,8 +1788,9 @@ ERROR(function_type_access,none,
17881788
"|cannot be declared "
17891789
"%select{in this context|fileprivate|internal|public|open}1}0 "
17901790
"because its %select{parameter|result}5 uses "
1791-
"%select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}3 type",
1792-
(bool, AccessLevel, bool, AccessLevel, unsigned, bool))
1791+
"%select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}3"
1792+
"%select{| API wrapper}6 type",
1793+
(bool, AccessLevel, bool, AccessLevel, unsigned, bool, bool))
17931794
ERROR(function_type_spi,none,
17941795
"%select{function|method|initializer}0 "
17951796
"cannot be declared '@_spi' "
@@ -1802,18 +1803,19 @@ WARNING(function_type_access_warn,none,
18021803
"%select{should be declared %select{private|fileprivate|internal|%error|%error}1"
18031804
"|should not be declared %select{in this context|fileprivate|internal|public|open}1}0 "
18041805
"because its %select{parameter|result}5 uses "
1805-
"%select{a private|a fileprivate|an internal|%error|%error}3 type",
1806-
(bool, AccessLevel, bool, AccessLevel, unsigned, bool))
1806+
"%select{a private|a fileprivate|an internal|%error|%error}3 "
1807+
"%select{|API wrapper}6 type",
1808+
(bool, AccessLevel, bool, AccessLevel, unsigned, bool, bool))
18071809
ERROR(function_type_usable_from_inline,none,
1808-
"the %select{parameter|result}1 of a "
1810+
"the %select{parameter|result}1%select{| API wrapper}2 of a "
18091811
"'@usableFromInline' %select{function|method|initializer}0 "
18101812
"must be '@usableFromInline' or public",
1811-
(unsigned, bool))
1813+
(unsigned, bool, bool))
18121814
WARNING(function_type_usable_from_inline_warn,none,
1813-
"the %select{parameter|result}1 of a "
1815+
"the %select{parameter|result}1%select{| API wrapper}2 of a "
18141816
"'@usableFromInline' %select{function|method|initializer}0 "
18151817
"should be '@usableFromInline' or public",
1816-
(unsigned, bool))
1818+
(unsigned, bool, bool))
18171819

18181820
ERROR(spi_attribute_on_non_public,none,
18191821
"%select{private|fileprivate|internal|%error|%error}0 %1 "

lib/AST/Decl.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5903,6 +5903,27 @@ bool VarDecl::hasImplicitPropertyWrapper() const {
59035903
return !isImplicit() && getName().hasDollarPrefix() && isClosureParam;
59045904
}
59055905

5906+
bool VarDecl::hasExternalPropertyWrapper() const {
5907+
if (!hasAttachedPropertyWrapper() || !isa<ParamDecl>(this))
5908+
return false;
5909+
5910+
// This decision needs to be made before closures are type checked (and
5911+
// the wrapper types are potentially inferred) so closure parameters with
5912+
// property wrappers are always "external". This is fine, because the
5913+
// type checker will always inject a thunk with the wrapped or projected type
5914+
// around the closure, so the wrapper will never affect the caller's
5915+
// arguments directly anyway.
5916+
if (isa<AbstractClosureExpr>(getDeclContext()))
5917+
return true;
5918+
5919+
// Wrappers with attribute arguments are always implementation-detail.
5920+
if (getAttachedPropertyWrappers().front()->getArg())
5921+
return false;
5922+
5923+
auto wrapperInfo = getAttachedPropertyWrapperTypeInfo(0);
5924+
return wrapperInfo.projectedValueVar && wrapperInfo.hasProjectedValueInit;
5925+
}
5926+
59065927
/// Whether all of the attached property wrappers have an init(wrappedValue:)
59075928
/// initializer.
59085929
bool VarDecl::allAttachedPropertyWrappersHaveWrappedValueInit() const {
@@ -6303,7 +6324,7 @@ Type ParamDecl::getVarargBaseTy(Type VarArgT) {
63036324

63046325
AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
63056326
if (!type) {
6306-
if (hasAttachedPropertyWrapper() && !hasImplicitPropertyWrapper()) {
6327+
if (hasExternalPropertyWrapper()) {
63076328
type = getPropertyWrapperBackingPropertyType();
63086329
} else {
63096330
type = getInterfaceType();

lib/AST/Type.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ ParameterListInfo::ParameterListInfo(
10131013
acceptsUnlabeledTrailingClosures.set(i);
10141014
}
10151015

1016-
if (param->hasAttachedPropertyWrapper()) {
1016+
if (param->hasExternalPropertyWrapper()) {
10171017
propertyWrappers.set(i);
10181018
}
10191019

lib/SILGen/SILGenFunction.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -512,17 +512,29 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
512512
emitProlog(captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), fd,
513513
fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc());
514514
prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd));
515-
for (auto *param : *fd->getParameters()) {
516-
param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
517-
visit(auxiliaryVar);
518-
});
519-
}
520515

521516
if (fd->isAsyncHandler() &&
522517
// If F.isAsync() we are emitting the asyncHandler body and not the
523518
// original asyncHandler.
524519
!F.isAsync()) {
525520
emitAsyncHandler(fd);
521+
} else if (llvm::any_of(*fd->getParameters(),
522+
[](ParamDecl *p){ return p->hasAttachedPropertyWrapper(); })) {
523+
// If any parameters have property wrappers, emit the local auxiliary
524+
// variables before emitting the function body.
525+
LexicalScope BraceScope(*this, CleanupLocation(fd));
526+
for (auto *param : *fd->getParameters()) {
527+
param->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
528+
SILLocation WrapperLoc(auxiliaryVar);
529+
WrapperLoc.markAsPrologue();
530+
if (auto *patternBinding = auxiliaryVar->getParentPatternBinding())
531+
visitPatternBindingDecl(patternBinding);
532+
533+
visit(auxiliaryVar);
534+
});
535+
}
536+
537+
emitStmt(fd->getTypecheckedBody());
526538
} else {
527539
emitStmt(fd->getTypecheckedBody());
528540
}

lib/SILGen/SILGenProlog.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,13 @@ struct ArgumentInitHelper {
244244
}
245245

246246
void emitParam(ParamDecl *PD) {
247-
if (auto *backingVar = PD->getPropertyWrapperBackingProperty()) {
247+
if (PD->hasExternalPropertyWrapper()) {
248248
auto initInfo = PD->getPropertyWrapperInitializerInfo();
249249
if (initInfo.hasSynthesizedInitializers()) {
250250
SGF.SGM.emitPropertyWrapperBackingInitializer(PD);
251251
}
252252

253-
PD = cast<ParamDecl>(backingVar);
253+
PD = cast<ParamDecl>(PD->getPropertyWrapperBackingProperty());
254254
}
255255

256256
auto type = PD->getType();

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ namespace {
690690
Expr *result = forceUnwrapIfExpected(declRefExpr, choice, locator);
691691

692692
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
693-
if (AnyFunctionRef(fnDecl).hasPropertyWrapperParameters() &&
693+
if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
694694
(declRefExpr->getFunctionRefKind() == FunctionRefKind::Compound ||
695695
declRefExpr->getFunctionRefKind() == FunctionRefKind::Unapplied)) {
696696
auto &appliedWrappers = solution.appliedPropertyWrappers[locator.getAnchor()];
@@ -1034,8 +1034,7 @@ namespace {
10341034
ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ?
10351035
ValueKind::ProjectedValue : ValueKind::WrappedValue);
10361036

1037-
paramRef = AppliedPropertyWrapperExpr::create(context, ref, innerParam,
1038-
innerParam->getStartLoc(),
1037+
paramRef = AppliedPropertyWrapperExpr::create(context, ref, innerParam, SourceLoc(),
10391038
wrapperType, paramRef, valueKind);
10401039
cs.cacheExprTypes(paramRef);
10411040

@@ -8041,7 +8040,7 @@ namespace {
80418040
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
80428041
rewriteFunction(closure);
80438042

8044-
if (AnyFunctionRef(closure).hasPropertyWrapperParameters()) {
8043+
if (AnyFunctionRef(closure).hasExternalPropertyWrapperParameters()) {
80458044
return { false, rewriteClosure(closure) };
80468045
}
80478046

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,21 +4091,7 @@ ConstraintSystem::applyPropertyWrapperToParameter(
40914091
anchor = apply->getFn();
40924092
}
40934093

4094-
auto supportsProjectedValueInit = [&](ParamDecl *param) -> bool {
4095-
if (!param->hasAttachedPropertyWrapper())
4096-
return false;
4097-
4098-
if (param->hasImplicitPropertyWrapper())
4099-
return true;
4100-
4101-
if (param->getAttachedPropertyWrappers().front()->getArg())
4102-
return false;
4103-
4104-
auto wrapperInfo = param->getAttachedPropertyWrapperTypeInfo(0);
4105-
return wrapperInfo.projectedValueVar && wrapperInfo.hasProjectedValueInit;
4106-
};
4107-
4108-
if (argLabel.hasDollarPrefix() && (!param || !supportsProjectedValueInit(param))) {
4094+
if (argLabel.hasDollarPrefix() && (!param || !param->hasExternalPropertyWrapper())) {
41094095
if (!shouldAttemptFixes())
41104096
return getTypeMatchFailure(locator);
41114097

0 commit comments

Comments
 (0)