Skip to content

Commit f748c84

Browse files
author
Josh Learn
committed
[Sema][MiscDiag] Fix constantness diag to handle result builder patterns
We currently have a problem with how constantness diagnostics traverse the AST to look for function calls to diagnose. We special case closure bodies and don't check them (unless they're single expression closures) because closure bodies are type- checked separately and will be covered later. This poses a problem in certain AST structures, such as what we see with result builders, because the call expressions are rooted in declarations, which aren't checked in the closure body type-checking covered by MiscDiag. This patch fixes the problem by manually checking all closure bodies and stopping misc diagnostics from checking the bodies separately. rdar://85737300
1 parent cfce85b commit f748c84

File tree

2 files changed

+50
-18
lines changed

2 files changed

+50
-18
lines changed

lib/Sema/ConstantnessSemaDiagnostics.cpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,10 @@ void swift::diagnoseConstantArgumentRequirement(
334334
const Expr *expr, const DeclContext *declContext) {
335335
class ConstantReqCallWalker : public ASTWalker {
336336
DeclContext *DC;
337+
bool insideClosure;
337338

338339
public:
339-
ConstantReqCallWalker(DeclContext *DC) : DC(DC) {}
340+
ConstantReqCallWalker(DeclContext *DC) : DC(DC), insideClosure(false) {}
340341

341342
// Descend until we find a call expressions. Note that the input expression
342343
// could be an assign expression or another expression that contains the
@@ -347,10 +348,15 @@ void swift::diagnoseConstantArgumentRequirement(
347348
if (auto *closureExpr = dyn_cast<ClosureExpr>(expr)) {
348349
return walkToClosureExprPre(closureExpr);
349350
}
351+
350352
// Interpolated expressions' bodies will be type checked
351353
// separately so exit early to avoid duplicate diagnostics.
354+
// The caveat is that they won't be checked inside closure
355+
// bodies because we manually check all closures to avoid
356+
// duplicate diagnostics. Therefore we must still descend into
357+
// interpolated expressions if we are inside of a closure.
352358
if (!expr || isa<ErrorExpr>(expr) || !expr->getType() ||
353-
isa<InterpolatedStringLiteralExpr>(expr))
359+
(isa<InterpolatedStringLiteralExpr>(expr) && !insideClosure))
354360
return {false, expr};
355361
if (auto *callExpr = dyn_cast<CallExpr>(expr)) {
356362
diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext());
@@ -359,33 +365,30 @@ void swift::diagnoseConstantArgumentRequirement(
359365
}
360366

361367
std::pair<bool, Expr *> walkToClosureExprPre(ClosureExpr *closure) {
362-
auto &ctx = DC->getASTContext();
363-
364-
if (closure->hasSingleExpressionBody() ||
365-
ctx.TypeCheckerOpts.EnableMultiStatementClosureInference) {
366-
// Closure bodies are not visited directly by the ASTVisitor,
367-
// so we must descend into the body manuall and set the
368-
// DeclContext to that of the closure.
369-
DC = closure;
370-
return {true, closure};
371-
}
372-
return {false, closure};
368+
DC = closure;
369+
insideClosure = true;
370+
return {true, closure};
373371
}
374372

375373
Expr *walkToExprPost(Expr *expr) override {
376374
if (auto *closureExpr = dyn_cast<ClosureExpr>(expr)) {
377375
// Reset the DeclContext to the outer scope if we descended
378-
// into a closure expr.
376+
// into a closure expr and check whether or not we are still
377+
// within a closure context.
379378
DC = closureExpr->getParent();
379+
insideClosure = isa<ClosureExpr>(DC);
380380
}
381381
return expr;
382382
}
383-
384-
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
385-
return {true, stmt};
386-
}
387383
};
388384

385+
// We manually check closure bodies from their outer contexts,
386+
// so bail early if we are being called directly on expressions
387+
// inside of a closure body.
388+
if (isa<ClosureExpr>(declContext)) {
389+
return;
390+
}
391+
389392
ConstantReqCallWalker walker(const_cast<DeclContext *>(declContext));
390393
const_cast<Expr *>(expr)->walk(walker);
391394
}

test/Sema/diag_constantness_check.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,32 @@ func testCallsWithinClosures(s: String, x: Int) {
424424
constantArgumentFunction("string with a single interpolation \(x)")
425425
}
426426
}
427+
428+
@resultBuilder
429+
struct MyArrayBuilder {
430+
typealias Component = [Int]
431+
typealias Expression = Int
432+
static func buildExpression(_ element: Expression) -> Component {
433+
return [element]
434+
}
435+
static func buildBlock(_ components: Component...) -> Component {
436+
return Array(components.joined())
437+
}
438+
}
439+
440+
struct MyArray {
441+
public init(@MyArrayBuilder arr: () -> [Int]) {}
442+
}
443+
444+
func testResultBuilder(x: Int, y: Int) -> MyArray {
445+
let _: MyArray = MyArray {
446+
constantArgumentFunctionReturningInt(x)
447+
// expected-error@-1 {{argument must be an integer literal}}
448+
constantArgumentFunctionReturningInt(y)
449+
// expected-error@-1 {{argument must be an integer literal}}
450+
}
451+
let _: MyArray = MyArray {
452+
constantArgumentFunctionReturningInt(x)
453+
// expected-error@-1 {{argument must be an integer literal}}
454+
}
455+
}

0 commit comments

Comments
 (0)