Skip to content

Commit 9dd7ccc

Browse files
committed
[ConstraintSystem] Handle requirement failures associated with injected result builder methods
Only way to aggregate build* methods is via source locations because each result builder kind would create a new build* expression node. (cherry picked from commit e99f0e2)
1 parent e01fa1c commit 9dd7ccc

File tree

2 files changed

+69
-34
lines changed

2 files changed

+69
-34
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4860,66 +4860,68 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
48604860
// Aggregate all requirement fixes that belong to the same callee
48614861
// and attempt to diagnose possible ambiguities.
48624862
{
4863-
auto isResultBuilderRef = [&](ASTNode node) {
4863+
auto isResultBuilderMethodRef = [&](ASTNode node) {
48644864
auto *UDE = getAsExpr<UnresolvedDotExpr>(node);
4865-
if (!UDE)
4865+
if (!(UDE && UDE->isImplicit()))
48664866
return false;
48674867

48684868
auto &ctx = getASTContext();
4869-
return UDE->isImplicit() &&
4870-
UDE->getName().compare(DeclNameRef(ctx.Id_buildBlock)) == 0;
4869+
SmallVector<Identifier, 4> builderMethods(
4870+
{ctx.Id_buildBlock, ctx.Id_buildExpression, ctx.Id_buildPartialBlock,
4871+
ctx.Id_buildFinalResult});
4872+
4873+
return llvm::any_of(builderMethods, [&](const Identifier &methodId) {
4874+
return UDE->getName().compare(DeclNameRef(methodId)) == 0;
4875+
});
48714876
};
48724877

4873-
llvm::MapVector<SourceLoc,
4874-
SmallVector<FixInContext, 4>>
4875-
builderFixes;
4878+
// Aggregates fixes fixes attached to `buildExpression` and `buildBlock`
4879+
// methods at the particular source location.
4880+
llvm::MapVector<SourceLoc, SmallVector<FixInContext, 4>>
4881+
builderMethodRequirementFixes;
48764882

48774883
llvm::MapVector<ConstraintLocator *, SmallVector<FixInContext, 4>>
4878-
requirementFixes;
4884+
perCalleeRequirementFixes;
48794885

4880-
// TODO(diagnostics): This approach doesn't work for synthesized code
4881-
// because i.e. result builders would inject a new `buildBlock` or
4882-
// `buildExpression` for every kind of builder. We need to come up
4883-
// with the strategy to unify locators in such cases.
48844886
for (const auto &entry : fixes) {
48854887
auto *fix = entry.second;
48864888
if (!fix->getLocator()->isLastElement<LocatorPathElt::AnyRequirement>())
48874889
continue;
48884890

48894891
auto *calleeLoc = entry.first->getCalleeLocator(fix->getLocator());
48904892

4891-
if (isResultBuilderRef(calleeLoc->getAnchor())) {
4893+
if (isResultBuilderMethodRef(calleeLoc->getAnchor())) {
48924894
auto *anchor = castToExpr<Expr>(calleeLoc->getAnchor());
4893-
builderFixes[anchor->getLoc()].push_back(entry);
4895+
builderMethodRequirementFixes[anchor->getLoc()].push_back(entry);
48944896
} else {
4895-
requirementFixes[calleeLoc].push_back(entry);
4897+
perCalleeRequirementFixes[calleeLoc].push_back(entry);
48964898
}
48974899
}
48984900

4899-
SmallVector<FixInContext, 4> diagnosedFixes;
4901+
SmallVector<SmallVector<FixInContext, 4>, 4> viableGroups;
4902+
{
4903+
auto takeAggregateIfViable =
4904+
[&](SmallVector<FixInContext, 4> &aggregate) {
4905+
// Ambiguity only if all of the solutions have a requirement
4906+
// fix at the given location.
4907+
if (aggregate.size() == solutions.size())
4908+
viableGroups.push_back(std::move(aggregate));
4909+
};
49004910

4901-
for (auto &entry : builderFixes) {
4902-
auto &aggregate = entry.second;
4911+
for (auto &entry : builderMethodRequirementFixes)
4912+
takeAggregateIfViable(entry.second);
49034913

4904-
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate))
4905-
diagnosedFixes.append(aggregate.begin(), aggregate.end());
4914+
for (auto &entry : perCalleeRequirementFixes)
4915+
takeAggregateIfViable(entry.second);
49064916
}
49074917

4908-
for (auto &entry : requirementFixes) {
4909-
auto &aggregate = entry.second;
4910-
4911-
// Ambiguity only if all of the solutions have a requirement
4912-
// fix at the given location.
4913-
if (aggregate.size() != solutions.size())
4914-
continue;
4915-
4916-
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate))
4917-
diagnosedFixes.append(aggregate.begin(), aggregate.end());
4918+
for (auto &aggregate : viableGroups) {
4919+
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate)) {
4920+
// Remove diagnosed fixes.
4921+
fixes.set_subtract(aggregate);
4922+
diagnosed = true;
4923+
}
49184924
}
4919-
4920-
diagnosed |= !diagnosedFixes.empty();
4921-
// Remove any diagnosed fixes.
4922-
fixes.set_subtract(diagnosedFixes);
49234925
}
49244926

49254927
llvm::MapVector<std::pair<FixKind, ConstraintLocator *>,

test/Constraints/result_builder_diags.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,3 +967,36 @@ func test_impact_of_control_flow_fix() {
967967
test("") // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}}
968968
}
969969
}
970+
971+
protocol Q {}
972+
973+
func test_requirement_failure_in_buildBlock() {
974+
struct A : P { typealias T = Int }
975+
struct B : Q { typealias T = String }
976+
977+
struct Result<T> : P { typealias T = Int }
978+
979+
@resultBuilder
980+
struct BuilderA {
981+
static func buildBlock<T0: P, T1: P>(_: T0, _: T1) -> Result<(T0, T1)> { fatalError() }
982+
// expected-note@-1 {{candidate requires that 'B' conform to 'P' (requirement specified as 'T1' : 'P')}}
983+
}
984+
985+
@resultBuilder
986+
struct BuilderB {
987+
static func buildBlock<U0: Q, U1: Q>(_ v: U0, _: U1) -> some Q { v }
988+
// expected-note@-1 {{candidate requires that 'A' conform to 'Q' (requirement specified as 'U0' : 'Q')}}
989+
}
990+
991+
struct Test {
992+
func fn<T: P>(@BuilderA _: () -> T) {}
993+
func fn<T: Q>(@BuilderB _: () -> T) {}
994+
995+
func test() {
996+
fn { // expected-error {{no exact matches in reference to static method 'buildBlock'}}
997+
A()
998+
B()
999+
}
1000+
}
1001+
}
1002+
}

0 commit comments

Comments
 (0)