Skip to content

Commit ad3568e

Browse files
authored
Merge pull request #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

lib/Sema/TypeCheckAccess.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,29 @@ class AccessControlChecker : public AccessControlCheckerBase,
920920
const TypeRepr *complainRepr = nullptr;
921921
auto downgradeToWarning = DowngradeToWarning::No;
922922

923+
bool hasInaccessibleParameterWrapper = false;
923924
for (auto *P : *fn->getParameters()) {
925+
// Check for inaccessible API property wrappers attached to the parameter.
926+
if (P->hasExternalPropertyWrapper()) {
927+
auto wrapperAttrs = P->getAttachedPropertyWrappers();
928+
for (auto index : indices(wrapperAttrs)) {
929+
auto wrapperType = P->getAttachedPropertyWrapperType(index);
930+
auto wrapperTypeRepr = wrapperAttrs[index]->getTypeRepr();
931+
checkTypeAccess(wrapperType, wrapperTypeRepr, fn, /*mayBeInferred*/ false,
932+
[&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr,
933+
DowngradeToWarning downgradeDiag) {
934+
if (typeAccessScope.isChildOf(minAccessScope) ||
935+
(!complainRepr &&
936+
typeAccessScope.hasEqualDeclContextWith(minAccessScope))) {
937+
minAccessScope = typeAccessScope;
938+
complainRepr = thisComplainRepr;
939+
downgradeToWarning = downgradeDiag;
940+
hasInaccessibleParameterWrapper = true;
941+
}
942+
});
943+
}
944+
}
945+
924946
checkTypeAccess(
925947
P->getInterfaceType(), P->getTypeRepr(), fn, /*mayBeInferred*/ false,
926948
[&](AccessScope typeAccessScope, const TypeRepr *thisComplainRepr,
@@ -970,7 +992,8 @@ class AccessControlChecker : public AccessControlCheckerBase,
970992
diagID = diag::function_type_access_warn;
971993
auto diag = fn->diagnose(diagID, isExplicit, fnAccess,
972994
isa<FileUnit>(fn->getDeclContext()), minAccess,
973-
functionKind, problemIsResult);
995+
functionKind, problemIsResult,
996+
hasInaccessibleParameterWrapper);
974997
highlightOffendingType(diag, complainRepr);
975998
}
976999
}
@@ -1394,6 +1417,26 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
13941417
: isTypeContext ? FK_Method : FK_Function;
13951418

13961419
for (auto *P : *fn->getParameters()) {
1420+
// Check for inaccessible API property wrappers attached to the parameter.
1421+
if (P->hasExternalPropertyWrapper()) {
1422+
auto wrapperAttrs = P->getAttachedPropertyWrappers();
1423+
for (auto index : indices(wrapperAttrs)) {
1424+
auto wrapperType = P->getAttachedPropertyWrapperType(index);
1425+
auto wrapperTypeRepr = wrapperAttrs[index]->getTypeRepr();
1426+
checkTypeAccess(wrapperType, wrapperTypeRepr, fn, /*mayBeInferred*/ false,
1427+
[&](AccessScope typeAccessScope, const TypeRepr *complainRepr,
1428+
DowngradeToWarning downgradeDiag) {
1429+
auto diagID = diag::function_type_usable_from_inline;
1430+
if (!fn->getASTContext().isSwiftVersionAtLeast(5))
1431+
diagID = diag::function_type_usable_from_inline_warn;
1432+
auto diag = fn->diagnose(diagID, functionKind,
1433+
/*problemIsResult=*/false,
1434+
/*inaccessibleWrapper=*/true);
1435+
highlightOffendingType(diag, complainRepr);
1436+
});
1437+
}
1438+
}
1439+
13971440
checkTypeAccess(
13981441
P->getInterfaceType(), P->getTypeRepr(), fn, /*mayBeInferred*/ false,
13991442
[&](AccessScope typeAccessScope, const TypeRepr *complainRepr,
@@ -1402,7 +1445,8 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
14021445
if (!fn->getASTContext().isSwiftVersionAtLeast(5))
14031446
diagID = diag::function_type_usable_from_inline_warn;
14041447
auto diag = fn->diagnose(diagID, functionKind,
1405-
/*problemIsResult=*/false);
1448+
/*problemIsResult=*/false,
1449+
/*inaccessibleWrapper=*/false);
14061450
highlightOffendingType(diag, complainRepr);
14071451
});
14081452
}
@@ -1417,7 +1461,8 @@ class UsableFromInlineChecker : public AccessControlCheckerBase,
14171461
if (!fn->getASTContext().isSwiftVersionAtLeast(5))
14181462
diagID = diag::function_type_usable_from_inline_warn;
14191463
auto diag = fn->diagnose(diagID, functionKind,
1420-
/*problemIsResult=*/true);
1464+
/*problemIsResult=*/true,
1465+
/*inaccessibleWrapper=*/false);
14211466
highlightOffendingType(diag, complainRepr);
14221467
});
14231468
}
@@ -1718,8 +1763,15 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
17181763
void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) {
17191764
checkGenericParams(fn, fn);
17201765

1721-
for (auto *P : *fn->getParameters())
1766+
for (auto *P : *fn->getParameters()) {
1767+
auto wrapperAttrs = P->getAttachedPropertyWrappers();
1768+
for (auto index : indices(wrapperAttrs)) {
1769+
auto wrapperType = P->getAttachedPropertyWrapperType(index);
1770+
checkType(wrapperType, wrapperAttrs[index]->getTypeRepr(), fn);
1771+
}
1772+
17221773
checkType(P->getInterfaceType(), P->getTypeRepr(), fn);
1774+
}
17231775
}
17241776

17251777
void visitFuncDecl(FuncDecl *FD) {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,6 +3022,9 @@ void TypeChecker::checkParameterList(ParameterList *params,
30223022
}
30233023
}
30243024

3025+
if (param->hasAttachedPropertyWrapper())
3026+
(void) param->getPropertyWrapperInitializerInfo();
3027+
30253028
auto *SF = param->getDeclContext()->getParentSourceFile();
30263029
param->visitAuxiliaryDecls([&](VarDecl *auxiliaryDecl) {
30273030
if (!isa<ParamDecl>(auxiliaryDecl))

0 commit comments

Comments
 (0)