Skip to content

Commit da8555e

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.
1 parent aa86d86 commit da8555e

File tree

2 files changed

+65
-27
lines changed

2 files changed

+65
-27
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4914,59 +4914,64 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
49144914
// Aggregate all requirement fixes that belong to the same callee
49154915
// and attempt to diagnose possible ambiguities.
49164916
{
4917-
auto isResultBuilderRef = [&](ASTNode node) {
4917+
auto isResultBuilderMethodRef = [&](ASTNode node) {
49184918
auto *UDE = getAsExpr<UnresolvedDotExpr>(node);
4919-
if (!UDE)
4919+
if (!(UDE && UDE->isImplicit()))
49204920
return false;
49214921

49224922
auto &ctx = getASTContext();
4923-
return UDE->isImplicit() &&
4924-
UDE->getName().compare(DeclNameRef(ctx.Id_buildBlock)) == 0;
4923+
SmallVector<Identifier, 4> builderMethods(
4924+
{ctx.Id_buildBlock, ctx.Id_buildExpression, ctx.Id_buildPartialBlock,
4925+
ctx.Id_buildFinalResult});
4926+
4927+
return llvm::any_of(builderMethods, [&](const Identifier &methodId) {
4928+
return UDE->getName().compare(DeclNameRef(methodId)) == 0;
4929+
});
49254930
};
49264931

4927-
llvm::MapVector<SourceLoc,
4928-
SmallVector<FixInContext, 4>>
4929-
builderFixes;
4932+
// Aggregates fixes fixes attached to `buildExpression` and `buildBlock`
4933+
// methods at the particular source location.
4934+
llvm::MapVector<SourceLoc, SmallVector<FixInContext, 4>>
4935+
builderMethodRequirementFixes;
49304936

49314937
llvm::MapVector<ConstraintLocator *, SmallVector<FixInContext, 4>>
4932-
requirementFixes;
4938+
perCalleeRequirementFixes;
49334939

4934-
// TODO(diagnostics): This approach doesn't work for synthesized code
4935-
// because i.e. result builders would inject a new `buildBlock` or
4936-
// `buildExpression` for every kind of builder. We need to come up
4937-
// with the strategy to unify locators in such cases.
49384940
for (const auto &entry : fixes) {
49394941
auto *fix = entry.second;
49404942
if (!fix->getLocator()->isLastElement<LocatorPathElt::AnyRequirement>())
49414943
continue;
49424944

49434945
auto *calleeLoc = entry.first->getCalleeLocator(fix->getLocator());
49444946

4945-
if (isResultBuilderRef(calleeLoc->getAnchor())) {
4947+
if (isResultBuilderMethodRef(calleeLoc->getAnchor())) {
49464948
auto *anchor = castToExpr<Expr>(calleeLoc->getAnchor());
4947-
builderFixes[anchor->getLoc()].push_back(entry);
4949+
builderMethodRequirementFixes[anchor->getLoc()].push_back(entry);
49484950
} else {
4949-
requirementFixes[calleeLoc].push_back(entry);
4951+
perCalleeRequirementFixes[calleeLoc].push_back(entry);
49504952
}
49514953
}
49524954

4953-
SmallVector<FixInContext, 4> diagnosedFixes;
4955+
SmallVector<SmallVector<FixInContext, 4>, 4> viableGroups;
4956+
{
4957+
auto takeAggregateIfViable =
4958+
[&](const SmallVector<FixInContext, 4> &aggregate) {
4959+
// Ambiguity only if all of the solutions have a requirement
4960+
// fix at the given location.
4961+
if (aggregate.size() == solutions.size())
4962+
viableGroups.push_back(std::move(aggregate));
4963+
};
49544964

4955-
for (auto &entry : builderFixes) {
4956-
auto &aggregate = entry.second;
4965+
for (const auto &entry : builderMethodRequirementFixes.takeVector())
4966+
takeAggregateIfViable(entry.second);
49574967

4958-
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate))
4959-
diagnosedFixes.append(aggregate.begin(), aggregate.end());
4968+
for (const auto &entry : perCalleeRequirementFixes.takeVector())
4969+
takeAggregateIfViable(entry.second);
49604970
}
49614971

4962-
for (auto &entry : requirementFixes) {
4963-
auto &aggregate = entry.second;
4964-
4965-
// Ambiguity only if all of the solutions have a requirement
4966-
// fix at the given location.
4967-
if (aggregate.size() != solutions.size())
4968-
continue;
4972+
SmallVector<FixInContext, 4> diagnosedFixes;
49694973

4974+
for (auto &aggregate : viableGroups) {
49704975
if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate))
49714976
diagnosedFixes.append(aggregate.begin(), aggregate.end());
49724977
}

test/Constraints/result_builder_diags.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,3 +938,36 @@ func test_dependent_member_with_unresolved_base_type() {
938938
}
939939
}
940940
}
941+
942+
protocol Q {}
943+
944+
func test_requirement_failure_in_buildBlock() {
945+
struct A : P { typealias T = Int }
946+
struct B : Q { typealias T = String }
947+
948+
struct Result<T> : P { typealias T = Int }
949+
950+
@resultBuilder
951+
struct BuilderA {
952+
static func buildBlock<T0: P, T1: P>(_: T0, _: T1) -> Result<(T0, T1)> { fatalError() }
953+
// expected-note@-1 {{candidate requires that 'B' conform to 'P' (requirement specified as 'T1' : 'P')}}
954+
}
955+
956+
@resultBuilder
957+
struct BuilderB {
958+
static func buildBlock<U0: Q, U1: Q>(_ v: U0, _: U1) -> some Q { v }
959+
// expected-note@-1 {{candidate requires that 'A' conform to 'Q' (requirement specified as 'U0' : 'Q')}}
960+
}
961+
962+
struct Test {
963+
func fn<T: P>(@BuilderA _: () -> T) {}
964+
func fn<T: Q>(@BuilderB _: () -> T) {}
965+
966+
func test() {
967+
fn { // expected-error {{no exact matches in reference to static method 'buildBlock'}}
968+
A()
969+
B()
970+
}
971+
}
972+
}
973+
}

0 commit comments

Comments
 (0)