Skip to content

Commit cd63c18

Browse files
authored
Merge pull request #25559 from xedin/rdar-51167632-5.1
[5.1][Diagnostics] Make sure that fixes associated with function builders …
2 parents 4a01d54 + c8ac7fb commit cd63c18

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
@@ -7661,44 +7661,78 @@ Expr *ConstraintSystem::coerceToRValue(Expr *expr) {
76617661
/// Emit the fixes computed as part of the solution, returning true if we were
76627662
/// able to emit an error message, or false if none of the fixits worked out.
76637663
bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
7664-
llvm::SmallDenseMap<Expr *, SmallVector<const ConstraintFix *, 4>>
7665-
fixesPerExpr;
7666-
7664+
// First transfer all of the deduced information back
7665+
// to the constraint system.
76677666
applySolution(solution);
76687667

7669-
for (auto *fix : solution.Fixes)
7670-
fixesPerExpr[fix->getAnchor()].push_back(fix);
7668+
class DiagnosticWalker : public ASTWalker {
7669+
Expr *root;
7670+
const Solution &solution;
7671+
llvm::SmallDenseMap<Expr *, SmallVector<ConstraintFix *, 4>> fixesPerExpr;
76717672

7672-
auto diagnoseExprFailures = [&](Expr *expr) -> bool {
7673-
auto fixes = fixesPerExpr.find(expr);
7674-
if (fixes == fixesPerExpr.end())
7675-
return false;
7673+
/// Determines whether any error have been diagnosed while
7674+
/// trying to apply fixes associated with a given solution.
7675+
bool DiagnosedAnyErrors = false;
7676+
7677+
public:
7678+
DiagnosticWalker(Expr *expr, const Solution &solution)
7679+
: root(expr), solution(solution) {
7680+
for (auto *fix : solution.Fixes)
7681+
fixesPerExpr[fix->getAnchor()].push_back(fix);
7682+
}
7683+
7684+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
7685+
// Diagnose root expression last.
7686+
if (E == root)
7687+
return {true, E};
7688+
7689+
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
7690+
auto result = solution.builderTransformedClosures.find(closure);
7691+
if (result != solution.builderTransformedClosures.end()) {
7692+
auto *transformedExpr = result->second.second;
7693+
// Since this closure has been transformed into something
7694+
// else let's look inside transformed expression instead.
7695+
return {true, transformedExpr};
7696+
}
7697+
}
76767698

7677-
bool diagnosedError = false;
7678-
for (const auto *fix : fixes->second) {
7679-
auto diagnosed = fix->diagnose(E);
7699+
diagnose(E);
7700+
return {true, E};
7701+
}
76807702

7681-
if (fix->isWarning()) {
7682-
assert(diagnosed && "warnings should always be diagnosed");
7683-
(void)diagnosed;
7684-
} else {
7685-
diagnosedError |= diagnosed;
7703+
Expr *walkToExprPost(Expr *E) override {
7704+
if (E == root)
7705+
diagnose(E);
7706+
return E;
7707+
}
7708+
7709+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
7710+
return {true, S};
7711+
}
7712+
7713+
bool hadErrors() const { return DiagnosedAnyErrors; }
7714+
7715+
private:
7716+
void diagnose(Expr *E) {
7717+
auto fixes = fixesPerExpr.find(E);
7718+
if (fixes == fixesPerExpr.end())
7719+
return;
7720+
7721+
for (const auto *fix : fixes->second) {
7722+
auto diagnosed = fix->diagnose(root);
7723+
if (fix->isWarning()) {
7724+
assert(diagnosed && "warnings should always be diagnosed");
7725+
(void)diagnosed;
7726+
} else {
7727+
DiagnosedAnyErrors |= diagnosed;
7728+
}
76867729
}
76877730
}
7688-
return diagnosedError;
76897731
};
76907732

7691-
bool diagnosedError = false;
7692-
E->forEachChildExpr([&](Expr *subExpr) -> Expr * {
7693-
// Diagnose root expression at the end to
7694-
// preserve ordering.
7695-
if (subExpr != E)
7696-
diagnosedError |= diagnoseExprFailures(subExpr);
7697-
return subExpr;
7698-
});
7699-
7700-
diagnosedError |= diagnoseExprFailures(E);
7701-
return diagnosedError;
7733+
DiagnosticWalker diagnostics(E, solution);
7734+
E->walk(diagnostics);
7735+
return diagnostics.hadErrors();
77027736
}
77037737

77047738
/// 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)