Skip to content

Commit 4d6a18a

Browse files
authored
Merge pull request swiftlang#25518 from xedin/rdar-51167632
[Diagnostics] Make sure that fixes associated with function builders …
2 parents ef293a5 + d8debe7 commit 4d6a18a

File tree

2 files changed

+109
-30
lines changed

2 files changed

+109
-30
lines changed

lib/Sema/CSApply.cpp

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7413,44 +7413,78 @@ Expr *ConstraintSystem::coerceToRValue(Expr *expr) {
74137413
/// Emit the fixes computed as part of the solution, returning true if we were
74147414
/// able to emit an error message, or false if none of the fixits worked out.
74157415
bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
7416-
llvm::SmallDenseMap<Expr *, SmallVector<const ConstraintFix *, 4>>
7417-
fixesPerExpr;
7418-
7416+
// First transfer all of the deduced information back
7417+
// to the constraint system.
74197418
applySolution(solution);
74207419

7421-
for (auto *fix : solution.Fixes)
7422-
fixesPerExpr[fix->getAnchor()].push_back(fix);
7420+
class DiagnosticWalker : public ASTWalker {
7421+
Expr *root;
7422+
const Solution &solution;
7423+
llvm::SmallDenseMap<Expr *, SmallVector<ConstraintFix *, 4>> fixesPerExpr;
74237424

7424-
auto diagnoseExprFailures = [&](Expr *expr) -> bool {
7425-
auto fixes = fixesPerExpr.find(expr);
7426-
if (fixes == fixesPerExpr.end())
7427-
return false;
7425+
/// Determines whether any error have been diagnosed while
7426+
/// trying to apply fixes associated with a given solution.
7427+
bool DiagnosedAnyErrors = false;
7428+
7429+
public:
7430+
DiagnosticWalker(Expr *expr, const Solution &solution)
7431+
: root(expr), solution(solution) {
7432+
for (auto *fix : solution.Fixes)
7433+
fixesPerExpr[fix->getAnchor()].push_back(fix);
7434+
}
7435+
7436+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
7437+
// Diagnose root expression last.
7438+
if (E == root)
7439+
return {true, E};
7440+
7441+
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
7442+
auto result = solution.builderTransformedClosures.find(closure);
7443+
if (result != solution.builderTransformedClosures.end()) {
7444+
auto *transformedExpr = result->second.second;
7445+
// Since this closure has been transformed into something
7446+
// else let's look inside transformed expression instead.
7447+
return {true, transformedExpr};
7448+
}
7449+
}
74287450

7429-
bool diagnosedError = false;
7430-
for (const auto *fix : fixes->second) {
7431-
auto diagnosed = fix->diagnose(E);
7451+
diagnose(E);
7452+
return {true, E};
7453+
}
74327454

7433-
if (fix->isWarning()) {
7434-
assert(diagnosed && "warnings should always be diagnosed");
7435-
(void)diagnosed;
7436-
} else {
7437-
diagnosedError |= diagnosed;
7455+
Expr *walkToExprPost(Expr *E) override {
7456+
if (E == root)
7457+
diagnose(E);
7458+
return E;
7459+
}
7460+
7461+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
7462+
return {true, S};
7463+
}
7464+
7465+
bool hadErrors() const { return DiagnosedAnyErrors; }
7466+
7467+
private:
7468+
void diagnose(Expr *E) {
7469+
auto fixes = fixesPerExpr.find(E);
7470+
if (fixes == fixesPerExpr.end())
7471+
return;
7472+
7473+
for (const auto *fix : fixes->second) {
7474+
auto diagnosed = fix->diagnose(root);
7475+
if (fix->isWarning()) {
7476+
assert(diagnosed && "warnings should always be diagnosed");
7477+
(void)diagnosed;
7478+
} else {
7479+
DiagnosedAnyErrors |= diagnosed;
7480+
}
74387481
}
74397482
}
7440-
return diagnosedError;
74417483
};
74427484

7443-
bool diagnosedError = false;
7444-
E->forEachChildExpr([&](Expr *subExpr) -> Expr * {
7445-
// Diagnose root expression at the end to
7446-
// preserve ordering.
7447-
if (subExpr != E)
7448-
diagnosedError |= diagnoseExprFailures(subExpr);
7449-
return subExpr;
7450-
});
7451-
7452-
diagnosedError |= diagnoseExprFailures(E);
7453-
return diagnosedError;
7485+
DiagnosticWalker diagnostics(E, solution);
7486+
E->walk(diagnostics);
7487+
return diagnostics.hadErrors();
74547488
}
74557489

74567490
/// Apply a given solution to the expression, producing a fully

test/Constraints/function_builder_diags.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-opaque-result-types
22

33
@_functionBuilder
44
struct TupleBuilder { // expected-note 2{{struct 'TupleBuilder' declared here}}
@@ -126,3 +126,48 @@ func testOverloading(name: String) {
126126
}
127127
}
128128
}
129+
130+
protocol P {
131+
associatedtype T
132+
}
133+
134+
struct AnyP : P {
135+
typealias T = Any
136+
init<T>(_: T) where T : P {}
137+
}
138+
139+
struct TupleP<U> : P {
140+
typealias T = U
141+
init(_: U) {}
142+
}
143+
144+
@_functionBuilder
145+
struct Builder {
146+
static func buildBlock<S0, S1>(_ stmt1: S0, _ stmt2: S1) // expected-note {{where 'S1' = 'Label<Any>.Type'}}
147+
-> TupleP<(S0, S1)> where S0: P, S1: P {
148+
return TupleP((stmt1, stmt2))
149+
}
150+
}
151+
152+
struct G<C> : P where C : P {
153+
typealias T = C
154+
init(@Builder _: () -> C) {}
155+
}
156+
157+
struct Text : P {
158+
typealias T = String
159+
init(_: T) {}
160+
}
161+
162+
struct Label<L> : P where L : P { // expected-note {{'L' declared as parameter to type 'Label'}}
163+
typealias T = L
164+
init(@Builder _: () -> L) {}
165+
}
166+
167+
func test_51167632() -> some P {
168+
AnyP(G { // expected-error {{static method 'buildBlock' requires that 'Label<Any>.Type' conform to 'P'}}
169+
Text("hello")
170+
Label // expected-error {{generic parameter 'L' could not be inferred}}
171+
// expected-note@-1 {{explicitly specify the generic arguments to fix this issue}} {{10-10=<<#L: P#>>}}
172+
})
173+
}

0 commit comments

Comments
 (0)