[CS] Fix DeclContext for multi-statement closure captures

Make sure we set the correct DeclContext for CSGen
of multi-statement closure captures, since
otherwise the DeclContext is set to the closure
itself, which is wrong.
This commit is contained in:
Hamish Knight
2025-02-18 11:30:22 +00:00
parent 963889d557
commit 11a4415ce7
2 changed files with 70 additions and 16 deletions

View File

@@ -436,7 +436,8 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join,
struct SyntacticElementContext struct SyntacticElementContext
: public llvm::PointerUnion<AbstractFunctionDecl *, AbstractClosureExpr *, : public llvm::PointerUnion<AbstractFunctionDecl *, AbstractClosureExpr *,
SingleValueStmtExpr *, ExprPattern *, TapExpr *> { SingleValueStmtExpr *, ExprPattern *, TapExpr *,
CaptureListExpr *> {
// Inherit the constructors from PointerUnion. // Inherit the constructors from PointerUnion.
using PointerUnion::PointerUnion; using PointerUnion::PointerUnion;
@@ -461,6 +462,10 @@ struct SyntacticElementContext
return {func}; return {func};
} }
static SyntacticElementContext forCaptureList(CaptureListExpr *CLE) {
return {CLE};
}
static SyntacticElementContext static SyntacticElementContext
forSingleValueStmtExpr(SingleValueStmtExpr *SVE, forSingleValueStmtExpr(SingleValueStmtExpr *SVE,
TypeJoinExpr *Join = nullptr) { TypeJoinExpr *Join = nullptr) {
@@ -484,6 +489,9 @@ struct SyntacticElementContext
return EP->getDeclContext(); return EP->getDeclContext();
} else if (auto *tap = this->dyn_cast<TapExpr *>()) { } else if (auto *tap = this->dyn_cast<TapExpr *>()) {
return tap->getVar()->getDeclContext(); return tap->getVar()->getDeclContext();
} else if (auto *CLE = this->dyn_cast<CaptureListExpr *>()) {
// The capture list is part of the closure's parent context.
return CLE->getClosureBody()->getParent();
} else { } else {
llvm_unreachable("unsupported kind"); llvm_unreachable("unsupported kind");
} }
@@ -552,6 +560,8 @@ class SyntacticElementConstraintGenerator
SyntacticElementContext context; SyntacticElementContext context;
ConstraintLocator *locator; ConstraintLocator *locator;
std::optional<llvm::SaveAndRestore<DeclContext *>> DCScope;
/// Whether a conjunction was generated. /// Whether a conjunction was generated.
bool generatedConjunction = false; bool generatedConjunction = false;
@@ -562,7 +572,17 @@ public:
SyntacticElementConstraintGenerator(ConstraintSystem &cs, SyntacticElementConstraintGenerator(ConstraintSystem &cs,
SyntacticElementContext context, SyntacticElementContext context,
ConstraintLocator *locator) ConstraintLocator *locator)
: cs(cs), context(context), locator(locator) {} : cs(cs), context(context), locator(locator) {
// Capture list bindings in multi-statement closures get solved as part of
// the closure's conjunction, which has the DeclContext set to the closure.
// This is wrong for captures though, which are semantically bound outside
// of the closure body. So we need to re-adjust their DeclContext here for
// constraint generation. The constraint system's DeclContext will be wrong
// for solving, but CSGen should ensure that constraints carry the correct
// DeclContext.
if (context.is<CaptureListExpr *>())
DCScope.emplace(cs.DC, context.getAsDeclContext());
}
void createConjunction(ArrayRef<ElementInfo> elements, void createConjunction(ArrayRef<ElementInfo> elements,
ConstraintLocator *locator, bool isIsolated = false, ConstraintLocator *locator, bool isIsolated = false,
@@ -1616,26 +1636,40 @@ bool isConditionOfStmt(ConstraintLocatorBuilder locator) {
return false; return false;
} }
static std::optional<SyntacticElementContext>
getSyntacticElementContext(ASTNode element, ConstraintLocatorBuilder locator) {
/// Capture list bindings are part of the capture list, which is semantically
/// outside the closure it's part of. As such, it needs its own context.
if (auto *PBD = getAsDecl<PatternBindingDecl>(element)) {
if (auto *VD = PBD->getSingleVar()) {
if (auto *CLE = VD->getParentCaptureList())
return SyntacticElementContext::forCaptureList(CLE);
}
}
auto anchor = locator.getAnchor();
if (auto *closure = getAsExpr<ClosureExpr>(anchor))
return SyntacticElementContext::forClosure(closure);
if (auto *fn = getAsDecl<AbstractFunctionDecl>(anchor))
return SyntacticElementContext::forFunction(fn);
if (auto *SVE = getAsExpr<SingleValueStmtExpr>(anchor))
return SyntacticElementContext::forSingleValueStmtExpr(SVE);
if (auto *EP = getAsPattern<ExprPattern>(anchor))
return SyntacticElementContext::forExprPattern(EP);
if (auto *tap = getAsExpr<TapExpr>(anchor))
return SyntacticElementContext::forTapExpr(tap);
return std::nullopt;
}
ConstraintSystem::SolutionKind ConstraintSystem::SolutionKind
ConstraintSystem::simplifySyntacticElementConstraint( ConstraintSystem::simplifySyntacticElementConstraint(
ASTNode element, ContextualTypeInfo contextInfo, bool isDiscarded, ASTNode element, ContextualTypeInfo contextInfo, bool isDiscarded,
TypeMatchOptions flags, ConstraintLocatorBuilder locator) { TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
auto anchor = locator.getAnchor();
std::optional<SyntacticElementContext> context; auto context = getSyntacticElementContext(element, locator);
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) { if (!context)
context = SyntacticElementContext::forClosure(closure);
} else if (auto *fn = getAsDecl<AbstractFunctionDecl>(anchor)) {
context = SyntacticElementContext::forFunction(fn);
} else if (auto *SVE = getAsExpr<SingleValueStmtExpr>(anchor)) {
context = SyntacticElementContext::forSingleValueStmtExpr(SVE);
} else if (auto *EP = getAsPattern<ExprPattern>(anchor)) {
context = SyntacticElementContext::forExprPattern(EP);
} else if (auto *tap = getAsExpr<TapExpr>(anchor)) {
context = SyntacticElementContext::forTapExpr(tap);
} else {
return SolutionKind::Error; return SolutionKind::Error;
}
SyntacticElementConstraintGenerator generator(*this, *context, SyntacticElementConstraintGenerator generator(*this, *context,
getConstraintLocator(locator)); getConstraintLocator(locator));

View File

@@ -0,0 +1,20 @@
// RUN: %target-typecheck-verify-swift
// https://github.com/swiftlang/swift/issues/79444
class C {
func foo() {
_ = { [x = "\(self)"] in } // expected-warning {{capture 'x' was never used}}
_ = { [x = "\(self)"] in x }
_ = { [x = "\(self)"] in
let y = x
return y
}
_ = { [x = "\(self)"] in
let fn = { [y = "\(x)"] in
let z = y
return z
}
return fn()
}
}
}