Skip to content

Commit 0e241a4

Browse files
author
Josh Learn
authored
Merge pull request swiftlang#38138 from guitard0g/guitard0g/constantnessDiagSingleExprClosureFix-5.5
[5.5] Fix constantness diagnostics for single expression closures
2 parents 56dd670 + 285be56 commit 0e241a4

File tree

3 files changed

+141
-6
lines changed

3 files changed

+141
-6
lines changed

lib/Sema/ConstantnessSemaDiagnostics.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,25 +365,56 @@ static void diagnoseConstantArgumentRequirementOfCall(const CallExpr *callExpr,
365365
void swift::diagnoseConstantArgumentRequirement(
366366
const Expr *expr, const DeclContext *declContext) {
367367
class ConstantReqCallWalker : public ASTWalker {
368-
const ASTContext &astContext;
368+
DeclContext *DC;
369369

370370
public:
371-
ConstantReqCallWalker(ASTContext &ctx) : astContext(ctx) {}
371+
ConstantReqCallWalker(DeclContext *DC) : DC(DC) {}
372372

373373
// Descend until we find a call expressions. Note that the input expression
374374
// could be an assign expression or another expression that contains the
375375
// call.
376376
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
377-
if (!expr || isa<ErrorExpr>(expr) || !expr->getType())
377+
// Handle closure expressions separately as we may need to
378+
// manually descend into the body.
379+
if (auto *closureExpr = dyn_cast<ClosureExpr>(expr)) {
380+
return walkToClosureExprPre(closureExpr);
381+
}
382+
// Interpolated expressions' bodies will be type checked
383+
// separately so exit early to avoid duplicate diagnostics.
384+
if (!expr || isa<ErrorExpr>(expr) || !expr->getType() ||
385+
isa<InterpolatedStringLiteralExpr>(expr))
378386
return {false, expr};
379387
if (auto *callExpr = dyn_cast<CallExpr>(expr)) {
380-
diagnoseConstantArgumentRequirementOfCall(callExpr, astContext);
381-
return {false, expr};
388+
diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext());
382389
}
383390
return {true, expr};
384391
}
392+
393+
std::pair<bool, Expr *> walkToClosureExprPre(ClosureExpr *closure) {
394+
if (closure->hasSingleExpressionBody()) {
395+
// Single expression closure bodies are not visited directly
396+
// by the ASTVisitor, so we must descend into the body manually
397+
// and set the DeclContext to that of the closure.
398+
DC = closure;
399+
return {true, closure};
400+
}
401+
return {false, closure};
402+
}
403+
404+
Expr *walkToExprPost(Expr *expr) override {
405+
if (auto *closureExpr = dyn_cast<ClosureExpr>(expr)) {
406+
// Reset the DeclContext to the outer scope if we descended
407+
// into a closure expr.
408+
DC = closureExpr->getParent();
409+
}
410+
return expr;
411+
}
412+
413+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
414+
return {true, stmt};
415+
}
385416
};
386417

387-
ConstantReqCallWalker walker(declContext->getASTContext());
418+
ConstantReqCallWalker walker(const_cast<DeclContext *>(declContext));
388419
const_cast<Expr *>(expr)->walk(walker);
389420
}

test/Sema/diag_constantness_check.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,66 @@ func testConstructorAnnotation(x: Int) {
361361
let _ = ConstructorTest(x)
362362
// expected-error@-1 {{argument must be an integer literal}}
363363
}
364+
365+
// Test closure expressions
366+
367+
func funcAcceptingClosure<T>(_ x: () -> T) -> T {
368+
return x()
369+
}
370+
371+
func normalFunction() {}
372+
373+
@_semantics("oslog.requires_constant_arguments")
374+
func constantArgumentFunctionReturningIntCollection(_ constArg: Int) -> Array<Int> {
375+
return [constArg, constArg, constArg]
376+
}
377+
378+
@_semantics("oslog.requires_constant_arguments")
379+
func constantArgumentFunctionReturningInt(_ constArg: Int) -> Int {
380+
return constArg
381+
}
382+
383+
func testCallsWithinClosures(s: String, x: Int) {
384+
funcAcceptingClosure {
385+
constantArgumentFunction(s)
386+
// expected-error@-1 {{argument must be a string literal}}
387+
}
388+
funcAcceptingClosure {
389+
constantArgumentFunction(s)
390+
// expected-error@-1 {{argument must be a string literal}}
391+
constantArgumentFunction(s)
392+
// expected-error@-1 {{argument must be a string literal}}
393+
}
394+
funcAcceptingClosure {
395+
funcAcceptingClosure {
396+
constantArgumentFunction(s)
397+
// expected-error@-1 {{argument must be a string literal}}
398+
}
399+
}
400+
funcAcceptingClosure {
401+
normalFunction()
402+
funcAcceptingClosure {
403+
constantArgumentFunction(s)
404+
// expected-error@-1 {{argument must be a string literal}}
405+
}
406+
}
407+
let _ =
408+
funcAcceptingClosure {
409+
constantArgumentFunctionReturningIntCollection(x)
410+
// expected-error@-1 {{argument must be an integer literal}}
411+
}
412+
.filter { $0 > 0 }
413+
.map { $0 + 1 }
414+
let _ =
415+
funcAcceptingClosure {
416+
constantArgumentFunctionReturningInt(x)
417+
// expected-error@-1 {{argument must be an integer literal}}
418+
} + 10 * x
419+
let _ = { constantArgumentFunctionReturningIntCollection(x) }
420+
// expected-error@-1 {{argument must be an integer literal}}
421+
funcAcceptingClosure {
422+
constantArgumentFunction(1)
423+
constantArgumentFunction("string literal")
424+
constantArgumentFunction("string with a single interpolation \(x)")
425+
}
426+
}

test/Sema/diag_constantness_check_os_log.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,44 @@ func testLogMessageWrappingDiagnostics() {
185185
_osLogTestHelper(nonConstantFunction("key", fallback: "A literal message"))
186186
// expected-error@-1{{argument must be a string interpolation}}
187187
}
188+
189+
// Test closure expressions
190+
191+
func funcAcceptingClosure<T>(_ x: () -> T) -> T {
192+
return x()
193+
}
194+
195+
func normalFunction() {}
196+
197+
func testCallsWithinClosures(formatOpt: OSLogIntegerFormatting) {
198+
funcAcceptingClosure {
199+
_osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)")
200+
// expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}}
201+
}
202+
funcAcceptingClosure {
203+
_osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)")
204+
// expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}}
205+
_osLogTestHelper("Maximum integer value: \(Int.max, format: formatOpt)")
206+
// expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}}
207+
}
208+
funcAcceptingClosure {
209+
funcAcceptingClosure {
210+
_osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)")
211+
// expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}}
212+
}
213+
}
214+
funcAcceptingClosure {
215+
normalFunction()
216+
funcAcceptingClosure {
217+
_osLogTestHelper("Minimum integer value: \(Int.min, format: formatOpt)")
218+
// expected-error@-1 {{argument must be a static method or property of 'OSLogIntegerFormatting'}}
219+
}
220+
}
221+
funcAcceptingClosure {
222+
_osLogTestHelper("Minimum integer value: \(Int.min, format: .hex)")
223+
}
224+
funcAcceptingClosure {
225+
_osLogTestHelper("Minimum integer value: \(Int.min, format: .hex)")
226+
_osLogTestHelper("Maximum integer value: \(Int.max, privacy: .public)")
227+
}
228+
}

0 commit comments

Comments
 (0)