Skip to content

Commit bbfcb55

Browse files
committed
[Property Wrappers] Inject the opaque value placeholder for a property
wrapper original wrapped value expression inside of CSApply. This prevents type checking the synthesized backing storage initializer twice - once with the original expression and again with the placeholder.
1 parent c301176 commit bbfcb55

File tree

9 files changed

+119
-123
lines changed

9 files changed

+119
-123
lines changed

include/swift/AST/PropertyWrappers.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ void simple_display(
191191
const PropertyWrapperBackingPropertyInfo &backingInfo);
192192

193193
/// Given the initializer for the given property with an attached property
194-
/// wrapper, dig out the original initialization expression.
194+
/// wrapper, dig out the placeholder for the original initialization expression.
195195
///
196196
/// Cannot just dig out the getOriginalInit() value because this function checks
197197
/// types, etc. Erroneous code won't return a result from here.
198-
Expr *findOriginalPropertyWrapperInitialValue(VarDecl *var, Expr *init);
198+
OpaqueValueExpr *findWrappedValuePlaceholder(VarDecl *var, Expr *init);
199199

200200
} // end namespace swift
201201

lib/AST/Decl.cpp

Lines changed: 13 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6349,8 +6349,10 @@ void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) {
63496349
}
63506350

63516351
/// Return nullptr if there is no property wrapper
6352-
Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
6353-
Expr *init) {
6352+
OpaqueValueExpr *swift::findWrappedValuePlaceholder(VarDecl *var, Expr *init) {
6353+
if (!var->hasAttachedPropertyWrapper())
6354+
return nullptr;
6355+
63546356
auto *PBD = var->getParentPatternBinding();
63556357
if (!PBD)
63566358
return nullptr;
@@ -6359,94 +6361,27 @@ Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
63596361
if (PBD->getEqualLoc(0).isInvalid() && !PBD->isDefaultInitializable())
63606362
return nullptr;
63616363

6362-
ASTContext &ctx = var->getASTContext();
6363-
auto dc = var->getInnermostDeclContext();
6364-
const auto wrapperAttrs = var->getAttachedPropertyWrappers();
6365-
if (wrapperAttrs.empty())
6366-
return nullptr;
6367-
auto innermostAttr = wrapperAttrs.back();
6368-
auto innermostNominal = evaluateOrDefault(
6369-
ctx.evaluator, CustomAttrNominalRequest{innermostAttr, dc}, nullptr);
6370-
if (!innermostNominal)
6371-
return nullptr;
6372-
6373-
// Walker
63746364
class Walker : public ASTWalker {
63756365
public:
6376-
NominalTypeDecl *innermostNominal;
6377-
Expr *initArg = nullptr;
6378-
6379-
Walker(NominalTypeDecl *innermostNominal)
6380-
: innermostNominal(innermostNominal) { }
6366+
OpaqueValueExpr *placeholder = nullptr;
63816367

63826368
virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
6383-
if (initArg)
6369+
if (placeholder)
63846370
return { false, E };
63856371

6386-
if (auto call = dyn_cast<CallExpr>(E)) {
6387-
ASTContext &ctx = innermostNominal->getASTContext();
6388-
6389-
// We're looking for an implicit call.
6390-
if (!call->isImplicit())
6391-
return { true, E };
6392-
6393-
// ... which may call the constructor of another property
6394-
// wrapper if there are multiple wrappers attached to the
6395-
// property.
6396-
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
6397-
if (tuple->getNumElements() > 0) {
6398-
for (unsigned i : range(tuple->getNumElements())) {
6399-
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
6400-
tuple->getElementName(i) == ctx.Id_initialValue) {
6401-
auto elem = tuple->getElement(i)->getSemanticsProvidingExpr();
6402-
6403-
// Look through autoclosures.
6404-
if (auto autoclosure = dyn_cast<AutoClosureExpr>(elem))
6405-
elem = autoclosure->getSingleExpressionBody();
6406-
6407-
if (elem->isImplicit() && isa<CallExpr>(elem)) {
6408-
return { true, E };
6409-
}
6410-
}
6411-
}
6412-
}
6413-
}
6414-
6415-
// ... producing a value of the same nominal type as the
6416-
// innermost property wrapper.
6417-
if (!call->getType() ||
6418-
call->getType()->getAnyNominal() != innermostNominal)
6419-
return { false, E };
6420-
6421-
// Find the implicit initialValue/wrappedValue argument.
6422-
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
6423-
for (unsigned i : range(tuple->getNumElements())) {
6424-
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
6425-
tuple->getElementName(i) == ctx.Id_initialValue) {
6426-
initArg = tuple->getElement(i);
6427-
return { false, E };
6428-
}
6429-
}
6372+
if (auto *opaqueValue = dyn_cast<OpaqueValueExpr>(E)) {
6373+
if (opaqueValue->getUnderlyingValue()) {
6374+
placeholder = opaqueValue;
6375+
return { false, opaqueValue };
64306376
}
64316377
}
64326378

64336379
return { true, E };
64346380
}
6435-
} walker(innermostNominal);
6381+
} walker;
64366382
init->walk(walker);
64376383

6438-
Expr *initArg = walker.initArg;
6439-
if (initArg) {
6440-
initArg = initArg->getSemanticsProvidingExpr();
6441-
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
6442-
if (!var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) {
6443-
// Remove the autoclosure part only for non-escaping autoclosures
6444-
initArg =
6445-
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6446-
}
6447-
}
6448-
}
6449-
return initArg;
6384+
return walker.placeholder;
64506385
}
64516386

64526387
/// Writes a tuple expression where each element is either `nil` or another such
@@ -6545,7 +6480,7 @@ ParamDecl::getDefaultValueStringRepresentation(
65456480
}
65466481

65476482
auto init =
6548-
findOriginalPropertyWrapperInitialValue(original, parentInit);
6483+
findWrappedValuePlaceholder(original, parentInit)->getUnderlyingValue();
65496484
return extractInlinableText(getASTContext().SourceMgr, init, scratch);
65506485
}
65516486
}

lib/Sema/CSApply.cpp

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ namespace {
298298
ConstraintSystem &cs;
299299
DeclContext *dc;
300300
Solution &solution;
301+
Optional<SolutionApplicationTarget> target;
301302
bool SuppressDiagnostics;
302303

303304
/// Coerce the given tuple to another tuple type.
@@ -2195,8 +2196,9 @@ namespace {
21952196

21962197
public:
21972198
ExprRewriter(ConstraintSystem &cs, Solution &solution,
2199+
Optional<SolutionApplicationTarget> target,
21982200
bool suppressDiagnostics)
2199-
: cs(cs), dc(cs.DC), solution(solution),
2201+
: cs(cs), dc(cs.DC), solution(solution), target(target),
22002202
SuppressDiagnostics(suppressDiagnostics) {}
22012203

22022204
ConstraintSystem &getConstraintSystem() const { return cs; }
@@ -5539,8 +5541,25 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType,
55395541
SmallVector<AnyFunctionType::Param, 8> args;
55405542
AnyFunctionType::decomposeInput(cs.getType(arg), args);
55415543

5544+
// If this application is an init(wrappedValue:) call that needs an injected
5545+
// wrapped value placeholder, the first non-defaulted argument must be
5546+
// wrapped in an OpaqueValueExpr.
5547+
bool shouldInjectWrappedValuePlaceholder =
5548+
target->shouldInjectWrappedValuePlaceholder(apply);
5549+
5550+
auto injectWrappedValuePlaceholder = [&](Expr *arg) -> Expr * {
5551+
auto *placeholder = new (ctx) OpaqueValueExpr(arg->getSourceRange(),
5552+
cs.getType(arg),
5553+
/*isPlaceholder=*/true,
5554+
arg);
5555+
cs.cacheType(placeholder);
5556+
shouldInjectWrappedValuePlaceholder = false;
5557+
return placeholder;
5558+
};
5559+
55425560
// Quickly test if any further fix-ups for the argument types are necessary.
5543-
if (AnyFunctionType::equalParams(args, params))
5561+
if (AnyFunctionType::equalParams(args, params) &&
5562+
!shouldInjectWrappedValuePlaceholder)
55445563
return arg;
55455564

55465565
// Apply labels to arguments.
@@ -5691,7 +5710,7 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType,
56915710

56925711
// If the types exactly match, this is easy.
56935712
auto paramType = param.getOldType();
5694-
if (argType->isEqual(paramType)) {
5713+
if (argType->isEqual(paramType) && !shouldInjectWrappedValuePlaceholder) {
56955714
newArgs.push_back(arg);
56965715
newParams.push_back(param);
56975716
continue;
@@ -5730,13 +5749,33 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType,
57305749
arg, closureType->getResult(),
57315750
locator.withPathElement(ConstraintLocator::AutoclosureResult));
57325751

5752+
if (shouldInjectWrappedValuePlaceholder) {
5753+
// If init(wrappedValue:) takes an escaping autoclosure, then we want
5754+
// the effect of autoclosure forwarding of the placeholder
5755+
// autoclosure. The only way to do this is to call the placeholder
5756+
// autoclosure when passing it to the init.
5757+
if (!closureType->isNoEscape()) {
5758+
auto *placeholder = injectWrappedValuePlaceholder(
5759+
cs.buildAutoClosureExpr(arg, closureType));
5760+
arg = CallExpr::createImplicit(ctx, placeholder, {}, {});
5761+
arg->setType(closureType->getResult());
5762+
cs.cacheType(arg);
5763+
} else {
5764+
arg = injectWrappedValuePlaceholder(arg);
5765+
}
5766+
}
5767+
57335768
convertedArg = cs.buildAutoClosureExpr(arg, closureType);
57345769
} else {
57355770
convertedArg = coerceToType(
57365771
arg, paramType,
57375772
getArgLocator(argIdx, paramIdx, param.getParameterFlags()));
57385773
}
57395774

5775+
// Perform the wrapped value placeholder injection
5776+
if (shouldInjectWrappedValuePlaceholder)
5777+
convertedArg = injectWrappedValuePlaceholder(convertedArg);
5778+
57405779
if (!convertedArg)
57415780
return nullptr;
57425781

@@ -8172,7 +8211,7 @@ Optional<SolutionApplicationTarget> ConstraintSystem::applySolution(
81728211
}
81738212
}
81748213

8175-
ExprRewriter rewriter(*this, solution, shouldSuppressDiagnostics());
8214+
ExprRewriter rewriter(*this, solution, target, shouldSuppressDiagnostics());
81768215
ExprWalker walker(rewriter);
81778216
auto resultTarget = walker.rewriteTarget(target);
81788217
if (!resultTarget)
@@ -8204,7 +8243,7 @@ Expr *Solution::coerceToType(Expr *expr, Type toType,
82048243
ConstraintLocator *locator,
82058244
Optional<Pattern*> typeFromPattern) {
82068245
auto &cs = getConstraintSystem();
8207-
ExprRewriter rewriter(cs, *this, /*suppressDiagnostics=*/false);
8246+
ExprRewriter rewriter(cs, *this, None, /*suppressDiagnostics=*/false);
82088247
Expr *result = rewriter.coerceToType(expr, toType, locator, typeFromPattern);
82098248
if (!result)
82108249
return nullptr;

lib/Sema/ConstraintSystem.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4182,9 +4182,11 @@ void SolutionApplicationTarget::maybeApplyPropertyWrapper() {
41824182
Expr *backingInitializer;
41834183
if (Expr *initializer = expression.expression) {
41844184
// Form init(wrappedValue:) call(s).
4185-
Expr *wrappedInitializer =
4186-
buildPropertyWrapperWrappedValueCall(
4187-
singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false);
4185+
Expr *wrappedInitializer = buildPropertyWrapperWrappedValueCall(
4186+
singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false,
4187+
[&](ApplyExpr *innermostInit) {
4188+
expression.innermostWrappedValueInit = innermostInit;
4189+
});
41884190
if (!wrappedInitializer)
41894191
return;
41904192

lib/Sema/TypeCheckPropertyWrapper.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -681,12 +681,13 @@ static bool isOpaquePlaceholderClosure(const Expr *value) {
681681
}
682682

683683
Expr *swift::buildPropertyWrapperWrappedValueCall(
684-
VarDecl *var, Type backingStorageType, Expr *value,
685-
bool ignoreAttributeArgs) {
684+
VarDecl *var, Type backingStorageType, Expr *value, bool ignoreAttributeArgs,
685+
llvm::function_ref<void(ApplyExpr *)> innermostInitCallback) {
686686
// From the innermost wrapper type out, form init(wrapperValue:) calls.
687687
ASTContext &ctx = var->getASTContext();
688688
auto wrapperAttrs = var->getAttachedPropertyWrappers();
689689
Expr *initializer = value;
690+
ApplyExpr *innermostInit = nullptr;
690691
if (var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure() &&
691692
isOpaquePlaceholderClosure(value)) {
692693
// We can't pass the opaque closure directly as an autoclosure arg.
@@ -727,10 +728,14 @@ Expr *swift::buildPropertyWrapperWrappedValueCall(
727728
if (endLoc.isInvalid() && startLoc.isValid())
728729
endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End;
729730

730-
initializer = CallExpr::create(
731+
auto *init = CallExpr::create(
731732
ctx, typeExpr, startLoc, {initializer}, {argName},
732733
{initializer->getStartLoc()}, endLoc,
733734
nullptr, /*implicit=*/true);
735+
initializer = init;
736+
737+
if (!innermostInit)
738+
innermostInit = init;
734739
continue;
735740
}
736741

@@ -759,10 +764,17 @@ Expr *swift::buildPropertyWrapperWrappedValueCall(
759764
if (endLoc.isInvalid() && startLoc.isValid())
760765
endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End;
761766

762-
initializer = CallExpr::create(
767+
auto *init = CallExpr::create(
763768
ctx, typeExpr, startLoc, elements, elementNames, elementLocs,
764769
endLoc, nullptr, /*implicit=*/true);
770+
initializer = init;
771+
772+
if (!innermostInit)
773+
innermostInit = init;
765774
}
766-
775+
776+
// Invoke the callback, passing in the innermost init(wrappedValue:) call
777+
innermostInitCallback(innermostInit);
778+
767779
return initializer;
768780
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,10 +2438,16 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
24382438
}
24392439

24402440
Expr *originalInitialValue = nullptr;
2441-
if (Expr *init = parentPBD->getInit(patternNumber)) {
2442-
pbd->setInit(0, init);
2441+
Expr *initializer = nullptr;
2442+
OpaqueValueExpr *opaqueValue = nullptr;
2443+
2444+
if ((initializer = parentPBD->getInit(patternNumber))) {
2445+
pbd->setInit(0, initializer);
24432446
pbd->setInitializerChecked(0);
2444-
originalInitialValue = findOriginalPropertyWrapperInitialValue(var, init);
2447+
opaqueValue = findWrappedValuePlaceholder(var, initializer);
2448+
if (opaqueValue) {
2449+
originalInitialValue = opaqueValue->getUnderlyingValue();
2450+
}
24452451
} else if (!parentPBD->isInitialized(patternNumber) &&
24462452
wrapperInfo.defaultInit) {
24472453
// FIXME: Record this expression somewhere so that DI can perform the
@@ -2461,30 +2467,31 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
24612467
storageVar = synthesizePropertyWrapperStorageWrapperProperty(
24622468
ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar);
24632469
}
2464-
2465-
// Get the property wrapper information.
2466-
if (!var->allAttachedPropertyWrappersHaveWrappedValueInit() &&
2467-
!originalInitialValue) {
2470+
2471+
// If this property can't be initialized out-of-line, we're done.
2472+
if ((!var->allAttachedPropertyWrappersHaveWrappedValueInit() &&
2473+
!originalInitialValue) || var->isStatic()) {
24682474
return PropertyWrapperBackingPropertyInfo(
24692475
backingVar, storageVar, nullptr, nullptr, nullptr);
24702476
}
24712477

24722478
// Form the initialization of the backing property from a value of the
24732479
// original property's type.
2474-
Type origValueInterfaceType = var->getPropertyWrapperInitValueInterfaceType();
2475-
Type origValueType =
2476-
var->getDeclContext()->mapTypeIntoContext(origValueInterfaceType);
2477-
OpaqueValueExpr *origValue =
2478-
new (ctx) OpaqueValueExpr(var->getSourceRange(), origValueType,
2479-
/*isPlaceholder=*/true);
2480-
Expr *initializer = buildPropertyWrapperWrappedValueCall(
2481-
var, storageType, origValue,
2482-
/*ignoreAttributeArgs=*/!originalInitialValue);
2483-
typeCheckSynthesizedWrapperInitializer(
2484-
pbd, backingVar, parentPBD, initializer);
2485-
2480+
if (!initializer) {
2481+
Type origValueInterfaceType = var->getPropertyWrapperInitValueInterfaceType();
2482+
Type origValueType =
2483+
var->getDeclContext()->mapTypeIntoContext(origValueInterfaceType);
2484+
opaqueValue =
2485+
new (ctx) OpaqueValueExpr(var->getSourceRange(), origValueType,
2486+
/*isPlaceholder=*/true);
2487+
initializer = buildPropertyWrapperWrappedValueCall(
2488+
var, storageType, opaqueValue, /*ignoreAttributeArgs=*/true);
2489+
typeCheckSynthesizedWrapperInitializer(
2490+
pbd, backingVar, parentPBD, initializer);
2491+
}
2492+
24862493
return PropertyWrapperBackingPropertyInfo(
2487-
backingVar, storageVar, originalInitialValue, initializer, origValue);
2494+
backingVar, storageVar, originalInitialValue, initializer, opaqueValue);
24882495
}
24892496

24902497
/// Given a storage declaration in a protocol, set it up with the right

0 commit comments

Comments
 (0)