Skip to content

Commit 56d450b

Browse files
committed
[Property delegates] Contextualize direct initializers of custom attributes
When a custom attribute is given a direct initializer, save and re-use the initializer context we create so that it can be associated with the enclosing pattern binding. Fixes assertions involving explicit closures in property delegates.
1 parent fcd2fd9 commit 56d450b

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

lib/Parse/ParseDecl.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,18 @@ bool Parser::parseVersionTuple(llvm::VersionTuple &Version,
17111711
return false;
17121712
}
17131713

1714+
/// Check whether the attributes have already established an initializer
1715+
/// context within the given set of attributes.
1716+
static PatternBindingInitializer *findAttributeInitContent(
1717+
DeclAttributes &Attributes) {
1718+
for (auto custom : Attributes.getAttributes<CustomAttr>()) {
1719+
if (auto initContext = custom->getInitContext())
1720+
return initContext;
1721+
}
1722+
1723+
return nullptr;
1724+
}
1725+
17141726
/// \verbatim
17151727
/// attribute:
17161728
/// '_silgen_name' '(' identifier ')'
@@ -1853,7 +1865,10 @@ bool Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) {
18531865
// DeclContexts for any closures that may live inside of initializers.
18541866
Optional<ParseFunctionBody> initParser;
18551867
if (!CurDeclContext->isLocalContext()) {
1856-
initContext = new (Context) PatternBindingInitializer(CurDeclContext);
1868+
initContext = findAttributeInitContent(Attributes);
1869+
if (!initContext)
1870+
initContext = new (Context) PatternBindingInitializer(CurDeclContext);
1871+
18571872
initParser.emplace(*this, initContext);
18581873
}
18591874

@@ -5242,10 +5257,14 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
52425257
}
52435258
});
52445259

5260+
// Check whether we have already established an initializer context.
5261+
PatternBindingInitializer *initContext =
5262+
findAttributeInitContent(Attributes);
5263+
52455264
// Remember this pattern/init pair for our ultimate PatternBindingDecl. The
52465265
// Initializer will be added later when/if it is parsed.
52475266
PBDEntries.push_back({pattern, /*EqualLoc*/ SourceLoc(), /*Init*/ nullptr,
5248-
/*InitContext*/ nullptr});
5267+
initContext});
52495268

52505269
Expr *PatternInit = nullptr;
52515270

@@ -5255,7 +5274,6 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
52555274
// If we're not in a local context, we'll need a context to parse initializers
52565275
// into (should we have one). This happens for properties and global
52575276
// variables in libraries.
5258-
PatternBindingInitializer *initContext = nullptr;
52595277

52605278
// Record the variables that we're trying to initialize. This allows us
52615279
// to cleanly reject "var x = x" when "x" isn't bound to an enclosing

lib/Sema/TypeCheckStmt.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ using namespace swift;
4949

5050
#define DEBUG_TYPE "TypeCheckStmt"
5151

52+
#ifndef NDEBUG
53+
/// Determine whether the given context is for the backing property of a
54+
/// property delegate.
55+
static bool isPropertyDelegateBackingInitContext(DeclContext *dc) {
56+
auto initContext = dyn_cast<Initializer>(dc);
57+
if (!initContext) return false;
58+
59+
auto patternInitContext = dyn_cast<PatternBindingInitializer>(initContext);
60+
if (!patternInitContext) return false;
61+
62+
auto binding = patternInitContext->getBinding();
63+
if (!binding) return false;
64+
65+
auto singleVar = binding->getSingleVar();
66+
if (!singleVar) return false;
67+
68+
return singleVar->getOriginalDelegatedProperty() != nullptr;
69+
}
70+
#endif
71+
5272
namespace {
5373
class ContextualizeClosures : public ASTWalker {
5474
DeclContext *ParentDC;
@@ -116,8 +136,9 @@ namespace {
116136
ParentDC->getContextKind() != DeclContextKind::TopLevelCodeDecl) {
117137
// If a closure is nested within an auto closure, we'll need to update
118138
// its parent to the auto closure parent.
119-
assert(ParentDC->getContextKind() ==
120-
DeclContextKind::AbstractClosureExpr &&
139+
assert((ParentDC->getContextKind() ==
140+
DeclContextKind::AbstractClosureExpr ||
141+
isPropertyDelegateBackingInitContext(ParentDC)) &&
121142
"Incorrect parent decl context for closure");
122143
CE->setParent(ParentDC);
123144
}

test/decl/var/property_delegates.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ struct WrapperAcceptingAutoclosure<T> {
2828
init(initialValue fn: @autoclosure @escaping () -> T) {
2929
self.fn = fn
3030
}
31+
32+
init(body fn: @escaping () -> T) {
33+
self.fn = fn
34+
}
3135
}
3236

3337
@propertyDelegate
@@ -652,3 +656,14 @@ struct S {
652656
@BrokenLazy // expected-error{{struct 'BrokenLazy' cannot be used as an attribute}}
653657
var value: Int
654658
}
659+
660+
// ---------------------------------------------------------------------------
661+
// Closures in initializers
662+
// ---------------------------------------------------------------------------
663+
struct UsesExplicitClosures {
664+
@WrapperAcceptingAutoclosure(body: { 42 })
665+
var x: Int
666+
667+
@WrapperAcceptingAutoclosure(body: { return 42 })
668+
var y: Int
669+
}

0 commit comments

Comments
 (0)