Skip to content

Commit 66e8572

Browse files
authored
Merge pull request #30807 from hborla/property-wrapper-refactoring
[Property Wrappers] Refactor property wrappers so the synthesized backing init is only type checked once
2 parents 9b1c4c6 + 815ecad commit 66e8572

21 files changed

+368
-169
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5243,7 +5243,7 @@ class VarDecl : public AbstractStorageDecl {
52435243
///
52445244
/// \code
52455245
/// @Lazy var i = 17
5246-
/// \end
5246+
/// \endcode
52475247
///
52485248
/// Or when there is no initializer but each composed property wrapper has
52495249
/// a suitable `init(wrappedValue:)`.

include/swift/AST/Expr.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4041,7 +4041,61 @@ class OpaqueValueExpr : public Expr {
40414041
SourceRange getSourceRange() const { return Range; }
40424042

40434043
static bool classof(const Expr *E) {
4044-
return E->getKind() == ExprKind::OpaqueValue;
4044+
return E->getKind() == ExprKind::OpaqueValue;
4045+
}
4046+
};
4047+
4048+
/// A placeholder to substitute with a \c wrappedValue initialization expression
4049+
/// for a property with an attached property wrapper.
4050+
///
4051+
/// Wrapped value placeholder expressions are injected around the
4052+
/// \c wrappedValue argument in a synthesized \c init(wrappedValue:)
4053+
/// call. This injection happens for properties with attached property wrappers
4054+
/// that can be initialized out-of-line with a wrapped value expression, rather
4055+
/// than calling \c init(wrappedValue:) explicitly.
4056+
///
4057+
/// Wrapped value placeholders store the original initialization expression
4058+
/// if one exists, along with an opaque value placeholder that can be bound
4059+
/// to a different wrapped value expression.
4060+
class PropertyWrapperValuePlaceholderExpr : public Expr {
4061+
SourceRange Range;
4062+
OpaqueValueExpr *Placeholder;
4063+
Expr *WrappedValue;
4064+
4065+
PropertyWrapperValuePlaceholderExpr(SourceRange Range, Type Ty,
4066+
OpaqueValueExpr *placeholder,
4067+
Expr *wrappedValue)
4068+
: Expr(ExprKind::PropertyWrapperValuePlaceholder, /*Implicit=*/true, Ty),
4069+
Range(Range), Placeholder(placeholder), WrappedValue(wrappedValue) {}
4070+
4071+
public:
4072+
static PropertyWrapperValuePlaceholderExpr *
4073+
create(ASTContext &ctx, SourceRange range, Type ty, Expr *wrappedValue);
4074+
4075+
/// The original wrappedValue initialization expression provided via
4076+
/// \c = on a proprety with attached property wrappers.
4077+
Expr *getOriginalWrappedValue() const {
4078+
return WrappedValue;
4079+
}
4080+
4081+
void setOriginalWrappedValue(Expr *value) {
4082+
WrappedValue = value;
4083+
}
4084+
4085+
/// An opaque value placeholder that will be used to substitute in a
4086+
/// different wrapped value expression for out-of-line initialization.
4087+
OpaqueValueExpr *getOpaqueValuePlaceholder() const {
4088+
return Placeholder;
4089+
}
4090+
4091+
void setOpaqueValuePlaceholder(OpaqueValueExpr *placeholder) {
4092+
Placeholder = placeholder;
4093+
}
4094+
4095+
SourceRange getSourceRange() const { return Range; }
4096+
4097+
static bool classof(const Expr *E) {
4098+
return E->getKind() == ExprKind::PropertyWrapperValuePlaceholder;
40454099
}
40464100
};
40474101

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ EXPR(VarargExpansion, Expr)
126126
EXPR(DynamicType, Expr)
127127
EXPR(RebindSelfInConstructor, Expr)
128128
EXPR(OpaqueValue, Expr)
129+
EXPR(PropertyWrapperValuePlaceholder, Expr)
129130
EXPR(DefaultArgument, Expr)
130131
EXPR(BindOptional, Expr)
131132
EXPR(OptionalEvaluation, Expr)

include/swift/AST/PropertyWrappers.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ struct PropertyWrapperBackingPropertyInfo {
141141
///
142142
/// \code
143143
/// @Lazy var i = 17
144-
/// \end
144+
/// \endcode
145145
///
146146
/// This is the specified initial value (\c 17), which is suitable for
147147
/// embedding in the expression \c initializeFromOriginal.
@@ -190,12 +190,13 @@ void simple_display(
190190
llvm::raw_ostream &out,
191191
const PropertyWrapperBackingPropertyInfo &backingInfo);
192192

193-
/// Given the initializer for the given property with an attached property
194-
/// wrapper, dig out the original initialization expression.
193+
/// Given the initializer for a property with an attached property wrapper,
194+
/// dig out the wrapped value placeholder for the original initialization
195+
/// expression.
195196
///
196-
/// Cannot just dig out the getOriginalInit() value because this function checks
197-
/// types, etc. Erroneous code won't return a result from here.
198-
Expr *findOriginalPropertyWrapperInitialValue(VarDecl *var, Expr *init);
197+
/// \note The wrapped value placeholder is injected for properties that can
198+
/// be initialized out-of-line using an expression of the wrapped property type.
199+
PropertyWrapperValuePlaceholderExpr *findWrappedValuePlaceholder(Expr *init);
199200

200201
} // end namespace swift
201202

lib/AST/ASTDumper.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,6 +2511,18 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
25112511
PrintWithColorRAII(OS, ParenthesisColor) << ')';
25122512
}
25132513

2514+
void visitPropertyWrapperValuePlaceholderExpr(
2515+
PropertyWrapperValuePlaceholderExpr *E) {
2516+
printCommon(E, "property_wrapper_value_placeholder_expr");
2517+
OS << '\n';
2518+
printRec(E->getOpaqueValuePlaceholder());
2519+
if (auto *value = E->getOriginalWrappedValue()) {
2520+
OS << '\n';
2521+
printRec(value);
2522+
}
2523+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2524+
}
2525+
25142526
void visitDefaultArgumentExpr(DefaultArgumentExpr *E) {
25152527
printCommon(E, "default_argument_expr");
25162528
OS << " default_args_owner=";

lib/AST/ASTWalker.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,23 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
508508

509509
Expr *visitOpaqueValueExpr(OpaqueValueExpr *E) { return E; }
510510

511+
Expr *visitPropertyWrapperValuePlaceholderExpr(
512+
PropertyWrapperValuePlaceholderExpr *E) {
513+
if (auto *placeholder = doIt(E->getOpaqueValuePlaceholder()))
514+
E->setOpaqueValuePlaceholder(dyn_cast<OpaqueValueExpr>(placeholder));
515+
else
516+
return nullptr;
517+
518+
if (E->getOriginalWrappedValue()) {
519+
if (auto *newValue = doIt(E->getOriginalWrappedValue()))
520+
E->setOriginalWrappedValue(newValue);
521+
else
522+
return nullptr;
523+
}
524+
525+
return E;
526+
}
527+
511528
Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *E) { return E; }
512529

513530
Expr *visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E) {

lib/AST/Decl.cpp

Lines changed: 10 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -6356,105 +6356,27 @@ void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) {
63566356
DefaultValueAndFlags.getPointer()->Captures = captures;
63576357
}
63586358

6359-
/// Return nullptr if there is no property wrapper
6360-
Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
6361-
Expr *init) {
6362-
auto *PBD = var->getParentPatternBinding();
6363-
if (!PBD)
6364-
return nullptr;
6365-
6366-
// If there is no '=' on the pattern, there was no initial value.
6367-
if (PBD->getEqualLoc(0).isInvalid() && !PBD->isDefaultInitializable())
6368-
return nullptr;
6369-
6370-
ASTContext &ctx = var->getASTContext();
6371-
auto dc = var->getInnermostDeclContext();
6372-
const auto wrapperAttrs = var->getAttachedPropertyWrappers();
6373-
if (wrapperAttrs.empty())
6374-
return nullptr;
6375-
auto innermostAttr = wrapperAttrs.back();
6376-
auto innermostNominal = evaluateOrDefault(
6377-
ctx.evaluator, CustomAttrNominalRequest{innermostAttr, dc}, nullptr);
6378-
if (!innermostNominal)
6379-
return nullptr;
6380-
6381-
// Walker
6359+
PropertyWrapperValuePlaceholderExpr *
6360+
swift::findWrappedValuePlaceholder(Expr *init) {
63826361
class Walker : public ASTWalker {
63836362
public:
6384-
NominalTypeDecl *innermostNominal;
6385-
Expr *initArg = nullptr;
6386-
6387-
Walker(NominalTypeDecl *innermostNominal)
6388-
: innermostNominal(innermostNominal) { }
6363+
PropertyWrapperValuePlaceholderExpr *placeholder = nullptr;
63896364

63906365
virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
6391-
if (initArg)
6366+
if (placeholder)
63926367
return { false, E };
63936368

6394-
if (auto call = dyn_cast<CallExpr>(E)) {
6395-
ASTContext &ctx = innermostNominal->getASTContext();
6396-
6397-
// We're looking for an implicit call.
6398-
if (!call->isImplicit())
6399-
return { true, E };
6400-
6401-
// ... which may call the constructor of another property
6402-
// wrapper if there are multiple wrappers attached to the
6403-
// property.
6404-
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
6405-
if (tuple->getNumElements() > 0) {
6406-
for (unsigned i : range(tuple->getNumElements())) {
6407-
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
6408-
tuple->getElementName(i) == ctx.Id_initialValue) {
6409-
auto elem = tuple->getElement(i)->getSemanticsProvidingExpr();
6410-
6411-
// Look through autoclosures.
6412-
if (auto autoclosure = dyn_cast<AutoClosureExpr>(elem))
6413-
elem = autoclosure->getSingleExpressionBody();
6414-
6415-
if (elem->isImplicit() && isa<CallExpr>(elem)) {
6416-
return { true, E };
6417-
}
6418-
}
6419-
}
6420-
}
6421-
}
6422-
6423-
// ... producing a value of the same nominal type as the
6424-
// innermost property wrapper.
6425-
if (!call->getType() ||
6426-
call->getType()->getAnyNominal() != innermostNominal)
6427-
return { false, E };
6428-
6429-
// Find the implicit initialValue/wrappedValue argument.
6430-
if (auto tuple = dyn_cast<TupleExpr>(call->getArg())) {
6431-
for (unsigned i : range(tuple->getNumElements())) {
6432-
if (tuple->getElementName(i) == ctx.Id_wrappedValue ||
6433-
tuple->getElementName(i) == ctx.Id_initialValue) {
6434-
initArg = tuple->getElement(i);
6435-
return { false, E };
6436-
}
6437-
}
6438-
}
6369+
if (auto *value = dyn_cast<PropertyWrapperValuePlaceholderExpr>(E)) {
6370+
placeholder = value;
6371+
return { false, value };
64396372
}
64406373

64416374
return { true, E };
64426375
}
6443-
} walker(innermostNominal);
6376+
} walker;
64446377
init->walk(walker);
64456378

6446-
Expr *initArg = walker.initArg;
6447-
if (initArg) {
6448-
initArg = initArg->getSemanticsProvidingExpr();
6449-
if (auto autoclosure = dyn_cast<AutoClosureExpr>(initArg)) {
6450-
if (!var->isInnermostPropertyWrapperInitUsesEscapingAutoClosure()) {
6451-
// Remove the autoclosure part only for non-escaping autoclosures
6452-
initArg =
6453-
autoclosure->getSingleExpressionBody()->getSemanticsProvidingExpr();
6454-
}
6455-
}
6456-
}
6457-
return initArg;
6379+
return walker.placeholder;
64586380
}
64596381

64606382
/// Writes a tuple expression where each element is either `nil` or another such
@@ -6553,7 +6475,7 @@ ParamDecl::getDefaultValueStringRepresentation(
65536475
}
65546476

65556477
auto init =
6556-
findOriginalPropertyWrapperInitialValue(original, parentInit);
6478+
findWrappedValuePlaceholder(parentInit)->getOriginalWrappedValue();
65576479
return extractInlinableText(getASTContext().SourceMgr, init, scratch);
65586480
}
65596481
}

lib/AST/Expr.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
311311
PASS_THROUGH_REFERENCE(RebindSelfInConstructor, getSubExpr);
312312

313313
NO_REFERENCE(OpaqueValue);
314+
NO_REFERENCE(PropertyWrapperValuePlaceholder);
314315
NO_REFERENCE(DefaultArgument);
315316

316317
PASS_THROUGH_REFERENCE(BindOptional, getSubExpr);
@@ -624,6 +625,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
624625

625626
case ExprKind::RebindSelfInConstructor:
626627
case ExprKind::OpaqueValue:
628+
case ExprKind::PropertyWrapperValuePlaceholder:
627629
case ExprKind::DefaultArgument:
628630
case ExprKind::BindOptional:
629631
case ExprKind::OptionalEvaluation:
@@ -1403,6 +1405,17 @@ static ValueDecl *getCalledValue(Expr *E) {
14031405
return nullptr;
14041406
}
14051407

1408+
PropertyWrapperValuePlaceholderExpr *
1409+
PropertyWrapperValuePlaceholderExpr::create(ASTContext &ctx, SourceRange range,
1410+
Type ty, Expr *wrappedValue) {
1411+
auto *placeholder =
1412+
new (ctx) OpaqueValueExpr(range, ty, /*isPlaceholder=*/true);
1413+
1414+
return new (ctx) PropertyWrapperValuePlaceholderExpr(range, ty,
1415+
placeholder,
1416+
wrappedValue);
1417+
}
1418+
14061419
const ParamDecl *DefaultArgumentExpr::getParamDecl() const {
14071420
return getParameterAt(DefaultArgsOwner.getDecl(), ParamIndex);
14081421
}

lib/SILGen/SILGenConstructor.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,21 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
220220

221221
// Cleanup after this initialization.
222222
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
223+
224+
// If this is a property wrapper backing storage var that isn't
225+
// memberwise initialized and has an original wrapped value, apply
226+
// the property wrapper backing initializer.
227+
if (auto *wrappedVar = field->getOriginalWrappedProperty()) {
228+
auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo();
229+
if (wrappedInfo.originalInitialValue) {
230+
auto arg = SGF.emitRValue(wrappedInfo.originalInitialValue);
231+
maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs,
232+
std::move(arg))
233+
.forwardInto(SGF, Loc, init.get());
234+
continue;
235+
}
236+
}
237+
223238
SGF.emitExprInto(field->getParentInitializer(), init.get());
224239
}
225240
}
@@ -235,27 +250,37 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
235250
for (VarDecl *field : decl->getStoredProperties()) {
236251
auto fieldTy =
237252
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());
238-
SILValue v;
253+
RValue value;
239254

240255
// If it's memberwise initialized, do so now.
241256
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/false)) {
242-
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
243257
assert(elti != eltEnd && "number of args does not match number of fields");
244258
(void)eltEnd;
245-
v = maybeEmitPropertyWrapperInitFromValue(
246-
SGF, Loc, field, subs, std::move(*elti))
247-
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
259+
value = std::move(*elti);
248260
++elti;
249261
} else {
250262
// Otherwise, use its initializer.
251263
assert(field->isParentInitialized());
264+
Expr *init = field->getParentInitializer();
265+
266+
// If this is a property wrapper backing storage var that isn't
267+
// memberwise initialized, use the original wrapped value if it exists.
268+
if (auto *wrappedVar = field->getOriginalWrappedProperty()) {
269+
auto wrappedInfo = wrappedVar->getPropertyWrapperBackingPropertyInfo();
270+
if (wrappedInfo.originalInitialValue) {
271+
init = wrappedInfo.originalInitialValue;
272+
}
273+
}
252274

253-
// Cleanup after this initialization.
254-
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
255-
v = SGF.emitRValue(field->getParentInitializer())
256-
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
275+
value = SGF.emitRValue(init);
257276
}
258277

278+
// Cleanup after this initialization.
279+
FullExpr scope(SGF.Cleanups, field->getParentPatternBinding());
280+
SILValue v = maybeEmitPropertyWrapperInitFromValue(SGF, Loc, field, subs,
281+
std::move(value))
282+
.forwardAsSingleStorageValue(SGF, fieldTy, Loc);
283+
259284
eltValues.push_back(v);
260285
}
261286

0 commit comments

Comments
 (0)