Skip to content

Commit aacdd3a

Browse files
authored
Merge pull request #30537 from DougGregor/roop-sr10950_prop_wrap_autoclosure
[Property wrappers] Fix handling of @autoclosure in init(wrappedValue:)
2 parents 7e21d7b + 32be6f5 commit aacdd3a

13 files changed

+303
-28
lines changed

include/swift/AST/Decl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5191,6 +5191,18 @@ class VarDecl : public AbstractStorageDecl {
51915191
/// a suitable `init(initialValue:)`.
51925192
bool isPropertyMemberwiseInitializedWithWrappedType() const;
51935193

5194+
/// Whether the innermost property wrapper's initializer's 'wrappedValue' parameter
5195+
/// is marked with '@autoclosure' and '@escaping'.
5196+
bool isInnermostPropertyWrapperInitUsesEscapingAutoClosure() const;
5197+
5198+
/// Return the interface type of the value used for the 'wrappedValue:'
5199+
/// parameter when initializing a property wrapper.
5200+
///
5201+
/// If the property has an attached property wrapper and the 'wrappedValue:'
5202+
/// parameter is an autoclosure, return a function type returning the stored
5203+
/// value. Otherwise, return the interface type of the stored value.
5204+
Type getPropertyWrapperInitValueInterfaceType() const;
5205+
51945206
/// If this property is the backing storage for a property with an attached
51955207
/// property wrapper, return the original property.
51965208
///

include/swift/AST/PropertyWrappers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct PropertyWrapperTypeInfo {
4343
HasInitialValueInit
4444
} wrappedValueInit = NoWrappedValueInit;
4545

46+
/// Whether the init(wrappedValue:), if it exists, has the wrappedValue
47+
/// argument as an escaping autoclosure.
48+
bool isWrappedValueInitUsingEscapingAutoClosure = false;
49+
4650
/// The initializer that will be called to default-initialize a
4751
/// value with an attached property wrapper.
4852
enum {

lib/AST/Decl.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5930,6 +5930,25 @@ bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
59305930
return allAttachedPropertyWrappersHaveInitialValueInit();
59315931
}
59325932

5933+
bool VarDecl::isInnermostPropertyWrapperInitUsesEscapingAutoClosure() const {
5934+
auto customAttrs = getAttachedPropertyWrappers();
5935+
if (customAttrs.empty())
5936+
return false;
5937+
5938+
unsigned innermostWrapperIndex = customAttrs.size() - 1;
5939+
auto typeInfo = getAttachedPropertyWrapperTypeInfo(innermostWrapperIndex);
5940+
return typeInfo.isWrappedValueInitUsingEscapingAutoClosure;
5941+
}
5942+
5943+
Type VarDecl::getPropertyWrapperInitValueInterfaceType() const {
5944+
Type valueInterfaceTy = getValueInterfaceType();
5945+
5946+
if (isInnermostPropertyWrapperInitUsesEscapingAutoClosure())
5947+
return FunctionType::get({}, valueInterfaceTy);
5948+
5949+
return valueInterfaceTy;
5950+
}
5951+
59335952
Identifier VarDecl::getObjCPropertyName() const {
59345953
if (auto attr = getAttrs().getAttribute<ObjCAttr>()) {
59355954
if (auto name = attr->getName())
@@ -6324,6 +6343,8 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63246343
return { false, E };
63256344

63266345
if (auto call = dyn_cast<CallExpr>(E)) {
6346+
ASTContext &ctx = innermostNominal->getASTContext();
6347+
63276348
// We're looking for an implicit call.
63286349
if (!call->isImplicit())
63296350
return { true, E };
@@ -6333,9 +6354,19 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63336354
// property.
63346355
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
63356356
if (tuple->getNumElements() > 0) {
6336-
auto elem = tuple->getElement(0);
6337-
if (elem->isImplicit() && isa<CallExpr>(elem)) {
6338-
return { true, E };
6357+
for (unsigned i : range(tuple->getNumElements())) {
6358+
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
6359+
tuple->getElementName(i) == ctx.Id_initialValue) {
6360+
auto elem = tuple->getElement(i)->getSemanticsProvidingExpr();
6361+
6362+
// Look through autoclosures.
6363+
if (auto autoclosure = dyn_cast<AutoClosureExpr>(elem))
6364+
elem = autoclosure->getSingleExpressionBody();
6365+
6366+
if (elem->isImplicit() && isa<CallExpr>(elem)) {
6367+
return { true, E };
6368+
}
6369+
}
63396370
}
63406371
}
63416372
}
@@ -6348,7 +6379,6 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63486379

63496380
// Find the implicit initialValue/wrappedValue argument.
63506381
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
6351-
ASTContext &ctx = innermostNominal->getASTContext();
63526382
for (unsigned i : range(tuple->getNumElements())) {
63536383
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
63546384
tuple->getElementName(i) == ctx.Id_initialValue) {
@@ -6368,8 +6398,11 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63686398
if (initArg) {
63696399
initArg = initArg->getSemanticsProvidingExpr();
63706400
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
6371-
initArg =
6372-
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6401+
if (!var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) {
6402+
// Remove the autoclosure part only for non-escaping autoclosures
6403+
initArg =
6404+
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6405+
}
63736406
}
63746407
}
63756408
return initArg;

lib/SIL/TypeLowering.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,8 +1895,10 @@ static CanAnyFunctionType getStoredPropertyInitializerInterfaceType(
18951895
// wrapper that was initialized with '=', the stored property initializer
18961896
// will be in terms of the original property's type.
18971897
if (auto originalProperty = VD->getOriginalWrappedProperty()) {
1898-
if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType())
1899-
resultTy = originalProperty->getValueInterfaceType()->getCanonicalType();
1898+
if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) {
1899+
resultTy = originalProperty->getPropertyWrapperInitValueInterfaceType()
1900+
->getCanonicalType();
1901+
}
19001902
}
19011903

19021904
auto sig = DC->getGenericSignatureOfContext();
@@ -1915,8 +1917,7 @@ static CanAnyFunctionType getPropertyWrapperBackingInitializerInterfaceType(
19151917

19161918
auto *DC = VD->getInnermostDeclContext();
19171919
CanType inputType =
1918-
VD->getParentPattern()->getType()->mapTypeOutOfContext()
1919-
->getCanonicalType();
1920+
VD->getPropertyWrapperInitValueInterfaceType()->getCanonicalType();
19201921

19211922
auto sig = DC->getGenericSignatureOfContext();
19221923

lib/SILGen/SILGenConstructor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ static Type getInitializationTypeInContext(
930930
if (auto singleVar = pattern->getSingleVar()) {
931931
if (auto originalProperty = singleVar->getOriginalWrappedProperty()) {
932932
if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType())
933-
interfaceType = originalProperty->getValueInterfaceType();
933+
interfaceType = originalProperty->getPropertyWrapperInitValueInterfaceType();
934934
}
935935
}
936936

lib/SILGen/SILGenExpr.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,9 +2397,32 @@ RValue RValueEmitter::visitCaptureListExpr(CaptureListExpr *E, SGFContext C) {
23972397
return visit(E->getClosureBody(), C);
23982398
}
23992399

2400+
static OpaqueValueExpr *opaqueValueExprToSubstituteForAutoClosure(
2401+
const AbstractClosureExpr *e) {
2402+
// When we find an autoclosure that just calls an opaque closure,
2403+
// this is a case where we've created the opaque closure as a
2404+
// stand-in for the autoclosure itself. Such an opaque closure is
2405+
// created when we have a property wrapper's 'init(wrappedValue:)'
2406+
// taking an autoclosure argument.
2407+
if (auto ace = dyn_cast<AutoClosureExpr>(e)) {
2408+
if (auto ce = dyn_cast<CallExpr>(ace->getSingleExpressionBody())) {
2409+
if (auto ove = dyn_cast<OpaqueValueExpr>(ce->getFn())) {
2410+
if (!ace->isImplicit() || !ove->isImplicit() || !ove->isPlaceholder())
2411+
return nullptr;
2412+
2413+
if (ace->getType()->isEqual(ove->getType()))
2414+
return ove;
2415+
}
2416+
}
2417+
}
2418+
return nullptr;
2419+
}
24002420

24012421
RValue RValueEmitter::visitAbstractClosureExpr(AbstractClosureExpr *e,
24022422
SGFContext C) {
2423+
if (auto ove = opaqueValueExprToSubstituteForAutoClosure(e))
2424+
return visitOpaqueValueExpr(ove, C);
2425+
24032426
// Emit the closure body.
24042427
SGF.SGM.emitClosure(e);
24052428

lib/SILGen/SILGenFunction.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,8 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value,
750750
ctx.getIdentifier("$input_value"),
751751
dc);
752752
param->setSpecifier(ParamSpecifier::Owned);
753-
param->setInterfaceType(function.getDecl()->getInterfaceType());
753+
auto vd = cast<VarDecl>(function.getDecl());
754+
param->setInterfaceType(vd->getPropertyWrapperInitValueInterfaceType());
754755

755756
params = ParameterList::create(ctx, SourceLoc(), {param}, SourceLoc());
756757
}
@@ -800,7 +801,7 @@ void SILGenFunction::emitGeneratorFunction(SILDeclRef function, VarDecl *var) {
800801
// will be in terms of the original property's type.
801802
if (auto originalProperty = var->getOriginalWrappedProperty()) {
802803
if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) {
803-
interfaceType = originalProperty->getValueInterfaceType();
804+
interfaceType = originalProperty->getPropertyWrapperInitValueInterfaceType();
804805
}
805806
}
806807

lib/SILGen/SILGenLValue.cpp

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

1305-
bool hasPropertyWrapper() const {
1305+
bool canRewriteSetAsPropertyWrapperInit() 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.
@@ -1321,6 +1321,13 @@ namespace {
13211321
return false;
13221322
}
13231323

1324+
// If this property wrapper uses autoclosure in it's initializer,
1325+
// the argument types of the setter and initializer shall be
1326+
// different, so we don't rewrite an assignment into an
1327+
// initialization.
1328+
if (VD->isInnermostPropertyWrapperInitUsesEscapingAutoClosure())
1329+
return false;
1330+
13241331
return true;
13251332
}
13261333

@@ -1389,9 +1396,8 @@ namespace {
13891396
assert(getAccessorDecl()->isSetter());
13901397
SILDeclRef setter = Accessor;
13911398

1392-
if (IsOnSelfParameter &&
1399+
if (IsOnSelfParameter && canRewriteSetAsPropertyWrapperInit() &&
13931400
!Storage->isStatic() &&
1394-
hasPropertyWrapper() &&
13951401
isBackingVarVisible(cast<VarDecl>(Storage),
13961402
SGF.FunctionDC)) {
13971403
// This is wrapped property. Instead of emitting a setter, emit an
@@ -4294,7 +4300,7 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc,
42944300
}
42954301

42964302
auto &setterComponent = static_cast<GetterSetterComponent&>(component);
4297-
if (setterComponent.hasPropertyWrapper())
4303+
if (setterComponent.canRewriteSetAsPropertyWrapperInit())
42984304
return false;
42994305
setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest),
43004306
std::move(src));

lib/Sema/CodeSynthesis.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
206206
accessLevel = std::min(accessLevel, var->getFormalAccess());
207207

208208
auto varInterfaceType = var->getValueInterfaceType();
209+
bool isAutoClosure = false;
209210

210211
if (var->getAttrs().hasAttribute<LazyAttr>()) {
211212
// If var is a lazy property, its value is provided for the underlying
@@ -221,7 +222,11 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
221222
// accept a value of the original property type. Otherwise, the
222223
// memberwise initializer will be in terms of the backing storage
223224
// type.
224-
if (!var->isPropertyMemberwiseInitializedWithWrappedType()) {
225+
if (var->isPropertyMemberwiseInitializedWithWrappedType()) {
226+
varInterfaceType = var->getPropertyWrapperInitValueInterfaceType();
227+
isAutoClosure =
228+
var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure();
229+
} else {
225230
varInterfaceType = backingPropertyType;
226231
}
227232
}
@@ -233,6 +238,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
233238
arg->setSpecifier(ParamSpecifier::Default);
234239
arg->setInterfaceType(varInterfaceType);
235240
arg->setImplicit();
241+
arg->setAutoClosure(isAutoClosure);
236242

237243
// Don't allow the parameter to accept temporary pointer conversions.
238244
arg->setNonEphemeralIfPossible();

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,42 @@ static SubscriptDecl *findEnclosingSelfSubscript(ASTContext &ctx,
291291
return subscript;
292292
}
293293

294+
/// Whether the argument with label 'argumentLabel' in the initializer 'init'
295+
/// is an escaping autoclosure argument.
296+
static bool isEscapingAutoclosureArgument(const ConstructorDecl *init,
297+
Identifier argumentLabel) {
298+
if (!init)
299+
return false;
300+
301+
Optional<size_t> parameterIndex = None;
302+
auto params = init->getParameters();
303+
for (size_t i = 0; i < params->size(); i++) {
304+
if (params->get(i)->getArgumentName() == argumentLabel) {
305+
parameterIndex = i;
306+
break;
307+
}
308+
}
309+
310+
if (!parameterIndex.hasValue())
311+
return false;
312+
313+
size_t paramIndex = parameterIndex.getValue();
314+
if (!params->get(paramIndex)->isAutoClosure())
315+
return false;
316+
317+
if (auto initTy = init->getInterfaceType()->getAs<AnyFunctionType>()) {
318+
if (auto funcTy = initTy->getResult()->getAs<FunctionType>()) {
319+
if (funcTy->getNumParams() > paramIndex) {
320+
Type paramTy = funcTy->getParams()[paramIndex].getPlainType();
321+
if (auto paramFuncTy = paramTy->getAs<FunctionType>())
322+
return !paramFuncTy->isNoEscape();
323+
}
324+
}
325+
}
326+
327+
return false;
328+
}
329+
294330
llvm::Expected<PropertyWrapperTypeInfo>
295331
PropertyWrapperTypeInfoRequest::evaluate(
296332
Evaluator &eval, NominalTypeDecl *nominal) const {
@@ -313,12 +349,16 @@ PropertyWrapperTypeInfoRequest::evaluate(
313349

314350
PropertyWrapperTypeInfo result;
315351
result.valueVar = valueVar;
316-
if (findSuitableWrapperInit(ctx, nominal, valueVar,
317-
PropertyWrapperInitKind::WrappedValue))
352+
if (auto init = findSuitableWrapperInit(ctx, nominal, valueVar,
353+
PropertyWrapperInitKind::WrappedValue)) {
318354
result.wrappedValueInit = PropertyWrapperTypeInfo::HasWrappedValueInit;
319-
else if (auto init = findSuitableWrapperInit(
355+
result.isWrappedValueInitUsingEscapingAutoClosure =
356+
isEscapingAutoclosureArgument(init, ctx.Id_wrappedValue);
357+
} else if (auto init = findSuitableWrapperInit(
320358
ctx, nominal, valueVar, PropertyWrapperInitKind::InitialValue)) {
321359
result.wrappedValueInit = PropertyWrapperTypeInfo::HasInitialValueInit;
360+
result.isWrappedValueInitUsingEscapingAutoClosure =
361+
isEscapingAutoclosureArgument(init, ctx.Id_initialValue);
322362

323363
if (init->getLoc().isValid()) {
324364
auto diag = init->diagnose(diag::property_wrapper_init_initialValue);
@@ -632,13 +672,32 @@ Type swift::computeWrappedValueType(VarDecl *var, Type backingStorageType,
632672
return wrappedValueType;
633673
}
634674

675+
static bool isOpaquePlaceholderClosure(const Expr *value) {
676+
auto ove = dyn_cast<OpaqueValueExpr>(value);
677+
if (!ove || !ove->isPlaceholder())
678+
return false;
679+
680+
if (auto valueFnTy = ove->getType()->getAs<FunctionType>()) {
681+
return (valueFnTy->getNumParams() == 0);
682+
}
683+
684+
return false;
685+
}
686+
635687
Expr *swift::buildPropertyWrapperInitialValueCall(
636688
VarDecl *var, Type backingStorageType, Expr *value,
637689
bool ignoreAttributeArgs) {
638690
// From the innermost wrapper type out, form init(wrapperValue:) calls.
639691
ASTContext &ctx = var->getASTContext();
640692
auto wrapperAttrs = var->getAttachedPropertyWrappers();
641693
Expr *initializer = value;
694+
if (var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure() &&
695+
isOpaquePlaceholderClosure(value)) {
696+
// We can't pass the opaque closure directly as an autoclosure arg.
697+
// So we instead pass a CallExpr calling the opaque closure, which
698+
// the type checker shall wrap in an AutoClosureExpr.
699+
initializer = CallExpr::createImplicit(ctx, value, {}, {});
700+
}
642701
for (unsigned i : llvm::reverse(indices(wrapperAttrs))) {
643702
Type wrapperType =
644703
backingStorageType ? computeWrappedValueType(var, backingStorageType, i)

lib/Sema/TypeCheckStorage.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2485,8 +2485,11 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
24852485

24862486
// Form the initialization of the backing property from a value of the
24872487
// original property's type.
2488+
Type origValueInterfaceType = var->getPropertyWrapperInitValueInterfaceType();
2489+
Type origValueType =
2490+
var->getDeclContext()->mapTypeIntoContext(origValueInterfaceType);
24882491
OpaqueValueExpr *origValue =
2489-
new (ctx) OpaqueValueExpr(var->getSourceRange(), var->getType(),
2492+
new (ctx) OpaqueValueExpr(var->getSourceRange(), origValueType,
24902493
/*isPlaceholder=*/true);
24912494
Expr *initializer = buildPropertyWrapperInitialValueCall(
24922495
var, storageType, origValue,

0 commit comments

Comments
 (0)