Skip to content

Commit 378b1be

Browse files
authored
[5.5][Diagnostics] Handle ambiguities related to use of nil literal (#37291)
* [ConstraintSystem] Extract logic that identifies application based on its argument node (cherry picked from commit 2f44f5b) * [Diagnostics] Add a tailored note for passing `nil` to incompatible argument position New note mentions both expected argument type and its position and anchors to the affected overload choice. (cherry picked from commit af3dcf0) * [Diagnostics] Handle ambiguities related to use of `nil` literal When `nil` is passed as an argument to call with multiple overloads it's possible that this would result in ambiguity where matched expected argument type doesn't conform to `ExpressibleByNilLiteral`. To handle situations like this locator for contextual mismatch has to be adjusted to point to the call where `nil` is used, so `diagnoseAmbiguityWithFixes` can identify multiple overloads and produce a correct ambiguity diagnostic. Resolves: rdar://75514153 (cherry picked from commit 29cb7dd)
1 parent cb26838 commit 378b1be

File tree

7 files changed

+128
-43
lines changed

7 files changed

+128
-43
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,9 @@ ERROR(cannot_convert_argument_value_anyobject,none,
425425
(Type, Type))
426426
ERROR(cannot_convert_argument_value_nil,none,
427427
"'nil' is not compatible with expected argument type %0", (Type))
428+
NOTE(note_incompatible_argument_value_nil_at_pos,none,
429+
"'nil' is not compatible with expected argument type %0 at position #%1",
430+
(Type, unsigned))
428431

429432
ERROR(cannot_convert_condition_value,none,
430433
"cannot convert value of type %0 to expected condition type %1",

include/swift/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4996,6 +4996,13 @@ class ConstraintSystem {
49964996
/// diagnostic.
49974997
void maybeProduceFallbackDiagnostic(SolutionApplicationTarget target) const;
49984998

4999+
/// Check whether given AST node represents an argument of an application
5000+
/// of some sort (call, operator invocation, subscript etc.)
5001+
/// and return AST node representing and argument index. E.g. for regular
5002+
/// calls `test(42)` passing `42` should return node representing
5003+
/// entire call and index `0`.
5004+
Optional<std::pair<Expr *, unsigned>> isArgumentExpr(Expr *expr);
5005+
49995006
SWIFT_DEBUG_DUMP;
50005007
SWIFT_DEBUG_DUMPER(dump(Expr *));
50015008

lib/Sema/CSDiagnostics.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2370,11 +2370,26 @@ bool ContextualFailure::diagnoseAsError() {
23702370
}
23712371

23722372
bool ContextualFailure::diagnoseAsNote() {
2373-
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
2373+
auto *locator = getLocator();
2374+
2375+
auto overload = getCalleeOverloadChoiceIfAvailable(locator);
23742376
if (!(overload && overload->choice.isDecl()))
23752377
return false;
23762378

23772379
auto *decl = overload->choice.getDecl();
2380+
2381+
if (auto *anchor = getAsExpr(getAnchor())) {
2382+
anchor = anchor->getSemanticsProvidingExpr();
2383+
2384+
if (isa<NilLiteralExpr>(anchor)) {
2385+
auto argLoc =
2386+
locator->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
2387+
emitDiagnosticAt(decl, diag::note_incompatible_argument_value_nil_at_pos,
2388+
getToType(), argLoc.getArgIdx() + 1);
2389+
return true;
2390+
}
2391+
}
2392+
23782393
emitDiagnosticAt(decl, diag::found_candidate_type, getFromType());
23792394
return true;
23802395
}

lib/Sema/CSSimplify.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6156,6 +6156,26 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
61566156
getContextualTypePurpose(Nil)))
61576157
: locator);
61586158

6159+
// Only requirement placed directly on `nil` literal is
6160+
// `ExpressibleByNilLiteral`, so if `nil` is an argument
6161+
// to an application, let's update locator accordingly to
6162+
// diagnose possible ambiguities with multiple mismatched
6163+
// overload choices.
6164+
if (fixLocator->directlyAt<NilLiteralExpr>()) {
6165+
if (auto argInfo =
6166+
isArgumentExpr(castToExpr(fixLocator->getAnchor()))) {
6167+
Expr *application;
6168+
unsigned argIdx;
6169+
6170+
std::tie(application, argIdx) = *argInfo;
6171+
6172+
fixLocator = getConstraintLocator(
6173+
application,
6174+
{LocatorPathElt::ApplyArgument(),
6175+
LocatorPathElt::ApplyArgToParam(argIdx, argIdx, /*flags=*/{})});
6176+
}
6177+
}
6178+
61596179
// Here the roles are reversed - `nil` is something we are trying to
61606180
// convert to `type` by making sure that it conforms to a specific
61616181
// protocol.

lib/Sema/ConstraintSystem.cpp

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3779,12 +3779,14 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes(
37793779
const auto &solution = *entry.first;
37803780
const auto *fix = entry.second;
37813781

3782-
if (fix->getLocator()->isForContextualType()) {
3782+
auto *locator = fix->getLocator();
3783+
3784+
if (locator->isForContextualType()) {
37833785
contextualFixes.push_back({&solution, fix});
37843786
continue;
37853787
}
37863788

3787-
auto *calleeLocator = solution.getCalleeLocator(fix->getLocator());
3789+
auto *calleeLocator = solution.getCalleeLocator(locator);
37883790
fixesByCallee[calleeLocator].push_back({&solution, fix});
37893791
}
37903792

@@ -4542,46 +4544,13 @@ Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
45424544
// It's only valid to use `&` in argument positions, but we need
45434545
// to figure out exactly where it was used.
45444546
if (auto *argExpr = getAsExpr<InOutExpr>(locator->getAnchor())) {
4545-
auto *argList = cs.getParentExpr(argExpr);
4546-
assert(argList);
4547-
4548-
// `inout` expression might be wrapped in a number of
4549-
// parens e.g. `test(((&x)))`.
4550-
if (isa<ParenExpr>(argList)) {
4551-
for (;;) {
4552-
auto nextParent = cs.getParentExpr(argList);
4553-
assert(nextParent && "Incorrect use of `inout` expression");
4554-
4555-
// e.g. `test((&x), x: ...)`
4556-
if (isa<TupleExpr>(nextParent)) {
4557-
argList = nextParent;
4558-
break;
4559-
}
4560-
4561-
// e.g. `test(((&x)))`
4562-
if (isa<ParenExpr>(nextParent)) {
4563-
argList = nextParent;
4564-
continue;
4565-
}
4566-
4567-
break;
4568-
}
4569-
}
4547+
auto argInfo = cs.isArgumentExpr(argExpr);
4548+
assert(argInfo && "Incorrect use of `inout` expression");
45704549

4571-
unsigned argIdx = 0;
4572-
if (auto *tuple = dyn_cast<TupleExpr>(argList)) {
4573-
auto arguments = tuple->getElements();
4550+
Expr *call;
4551+
unsigned argIdx;
45744552

4575-
for (auto idx : indices(arguments)) {
4576-
if (arguments[idx]->getSemanticsProvidingExpr() == argExpr) {
4577-
argIdx = idx;
4578-
break;
4579-
}
4580-
}
4581-
}
4582-
4583-
auto *call = cs.getParentExpr(argList);
4584-
assert(call);
4553+
std::tie(call, argIdx) = *argInfo;
45854554

45864555
ParameterTypeFlags flags;
45874556
locator = cs.getConstraintLocator(
@@ -4761,6 +4730,58 @@ bool constraints::isStandardComparisonOperator(ASTNode node) {
47614730
return false;
47624731
}
47634732

4733+
Optional<std::pair<Expr *, unsigned>>
4734+
ConstraintSystem::isArgumentExpr(Expr *expr) {
4735+
auto *argList = getParentExpr(expr);
4736+
4737+
if (isa<ParenExpr>(argList)) {
4738+
for (;;) {
4739+
auto *parent = getParentExpr(argList);
4740+
if (!parent)
4741+
return None;
4742+
4743+
if (isa<TupleExpr>(parent)) {
4744+
argList = parent;
4745+
break;
4746+
}
4747+
4748+
// Drop all of the semantically insignificant parens
4749+
// that might be wrapping an argument e.g. `test(((42)))`
4750+
if (isa<ParenExpr>(parent)) {
4751+
argList = parent;
4752+
continue;
4753+
}
4754+
4755+
break;
4756+
}
4757+
}
4758+
4759+
if (!(isa<ParenExpr>(argList) || isa<TupleExpr>(argList)))
4760+
return None;
4761+
4762+
auto *application = getParentExpr(argList);
4763+
if (!application)
4764+
return None;
4765+
4766+
if (!(isa<ApplyExpr>(application) || isa<SubscriptExpr>(application) ||
4767+
isa<ObjectLiteralExpr>(application)))
4768+
return None;
4769+
4770+
unsigned argIdx = 0;
4771+
if (auto *tuple = dyn_cast<TupleExpr>(argList)) {
4772+
auto arguments = tuple->getElements();
4773+
4774+
for (auto idx : indices(arguments)) {
4775+
if (arguments[idx]->getSemanticsProvidingExpr() == expr) {
4776+
argIdx = idx;
4777+
break;
4778+
}
4779+
}
4780+
}
4781+
4782+
return std::make_pair(application, argIdx);
4783+
}
4784+
47644785
bool constraints::isOperatorArgument(ConstraintLocator *locator,
47654786
StringRef expectedOperator) {
47664787
if (!locator->findLast<LocatorPathElt::ApplyArgToParam>())

test/ClangImporter/objc_parse.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,12 @@ func keyedSubscripting(_ b: B, idx: A, a: A) {
184184
dict[NSString()] = a
185185
let value = dict[NSString()]
186186

187-
dict[nil] = a // expected-error {{'nil' is not compatible with expected argument type 'NSCopying'}}
188-
let q = dict[nil] // expected-error {{'nil' is not compatible with expected argument type 'NSCopying'}}
187+
// notes attached to the partially matching declarations for both following subscripts:
188+
// - override subscript(_: Any) -> Any? -> 'nil' is not compatible with expected argument type 'Any' at position #1
189+
// - open subscript(key: NSCopying) -> Any? { get set } -> 'nil' is not compatible with expected argument type 'NSCopying' at position #1
190+
191+
dict[nil] = a // expected-error {{no exact matches in call to subscript}}
192+
let q = dict[nil] // expected-error {{no exact matches in call to subscript}}
189193
_ = q
190194
}
191195

test/Constraints/optional.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,18 @@ func rdar75146811() {
484484
// expected-error@-1 {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
485485
test_named(x: &(arr)) // expected-error {{cannot convert value of type '[Double]?' to expected argument type 'Double'}}
486486
}
487+
488+
// rdar://75514153 - Unable to produce a diagnostic for ambiguities related to use of `nil`
489+
func rdar75514153() {
490+
func test_no_label(_: Int) {} // expected-note 2 {{'nil' is not compatible with expected argument type 'Int' at position #1}}
491+
func test_no_label(_: String) {} // expected-note 2 {{'nil' is not compatible with expected argument type 'String' at position #1}}
492+
493+
test_no_label(nil) // expected-error {{no exact matches in call to local function 'test_no_label'}}
494+
test_no_label((nil)) // expected-error {{no exact matches in call to local function 'test_no_label'}}
495+
496+
func test_labeled(_: Int, x: Int) {} // expected-note 2 {{'nil' is not compatible with expected argument type 'Int' at position #2}}
497+
func test_labeled(_: Int, x: String) {} // expected-note 2 {{'nil' is not compatible with expected argument type 'String' at position #2}}
498+
499+
test_labeled(42, x: nil) // expected-error {{no exact matches in call to local function 'test_labeled'}}
500+
test_labeled(42, x: (nil)) // expected-error {{no exact matches in call to local function 'test_labeled'}}
501+
}

0 commit comments

Comments
 (0)