Skip to content

Commit 9c2dca7

Browse files
roopDougGregor
authored andcommitted
[Property wrappers] Fix handling of @autoclosure in init(wrappedValue:)
If the 'wrappedValue:' parameter is an escaping autoclosure, and a struct property is marked with that property wrapper, the memberwise initializer of the struct is now synthesized with an escaping autoclosure for that property.
1 parent 7bc4148 commit 9c2dca7

10 files changed

+145
-14
lines changed

Diff for: include/swift/AST/Decl.h

+12
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
///

Diff for: include/swift/AST/PropertyWrappers.h

+4
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 {

Diff for: lib/AST/Decl.cpp

+24-2
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())
@@ -6368,8 +6387,11 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63686387
if (initArg) {
63696388
initArg = initArg->getSemanticsProvidingExpr();
63706389
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
6371-
initArg =
6372-
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6390+
if (!var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) {
6391+
// Remove the autoclosure part only for non-escaping autoclosures
6392+
initArg =
6393+
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6394+
}
63736395
}
63746396
}
63756397
return initArg;

Diff for: lib/SIL/TypeLowering.cpp

+5-4
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

Diff for: lib/SILGen/SILGenConstructor.cpp

+1-1
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

Diff for: lib/SILGen/SILGenExpr.cpp

+23
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

Diff for: lib/SILGen/SILGenFunction.cpp

+3-2
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

Diff for: lib/Sema/CodeSynthesis.cpp

+7-1
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();

Diff for: lib/Sema/TypeCheckPropertyWrapper.cpp

+62-3
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)

Diff for: lib/Sema/TypeCheckStorage.cpp

+4-1
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)