Skip to content

Commit 187a43f

Browse files
authored
Merge pull request #37178 from xedin/rdar-75514153
[Diagnostics] Handle ambiguities related to use of `nil` literal
2 parents 44a2bb3 + 29cb7dd commit 187a43f

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
@@ -4994,6 +4994,13 @@ class ConstraintSystem {
49944994
/// diagnostic.
49954995
void maybeProduceFallbackDiagnostic(SolutionApplicationTarget target) const;
49964996

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

lib/Sema/CSDiagnostics.cpp

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

23772377
bool ContextualFailure::diagnoseAsNote() {
2378-
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
2378+
auto *locator = getLocator();
2379+
2380+
auto overload = getCalleeOverloadChoiceIfAvailable(locator);
23792381
if (!(overload && overload->choice.isDecl()))
23802382
return false;
23812383

23822384
auto *decl = overload->choice.getDecl();
2385+
2386+
if (auto *anchor = getAsExpr(getAnchor())) {
2387+
anchor = anchor->getSemanticsProvidingExpr();
2388+
2389+
if (isa<NilLiteralExpr>(anchor)) {
2390+
auto argLoc =
2391+
locator->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
2392+
emitDiagnosticAt(decl, diag::note_incompatible_argument_value_nil_at_pos,
2393+
getToType(), argLoc.getArgIdx() + 1);
2394+
return true;
2395+
}
2396+
}
2397+
23832398
emitDiagnosticAt(decl, diag::found_candidate_type, getFromType());
23842399
return true;
23852400
}

lib/Sema/CSSimplify.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6151,6 +6151,26 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
61516151
getContextualTypePurpose(Nil)))
61526152
: locator);
61536153

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

lib/Sema/ConstraintSystem.cpp

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

3773-
if (fix->getLocator()->isForContextualType()) {
3773+
auto *locator = fix->getLocator();
3774+
3775+
if (locator->isForContextualType()) {
37743776
contextualFixes.push_back({&solution, fix});
37753777
continue;
37763778
}
37773779

3778-
auto *calleeLocator = solution.getCalleeLocator(fix->getLocator());
3780+
auto *calleeLocator = solution.getCalleeLocator(locator);
37793781
fixesByCallee[calleeLocator].push_back({&solution, fix});
37803782
}
37813783

@@ -4533,46 +4535,13 @@ Solution::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
45334535
// It's only valid to use `&` in argument positions, but we need
45344536
// to figure out exactly where it was used.
45354537
if (auto *argExpr = getAsExpr<InOutExpr>(locator->getAnchor())) {
4536-
auto *argList = cs.getParentExpr(argExpr);
4537-
assert(argList);
4538-
4539-
// `inout` expression might be wrapped in a number of
4540-
// parens e.g. `test(((&x)))`.
4541-
if (isa<ParenExpr>(argList)) {
4542-
for (;;) {
4543-
auto nextParent = cs.getParentExpr(argList);
4544-
assert(nextParent && "Incorrect use of `inout` expression");
4545-
4546-
// e.g. `test((&x), x: ...)`
4547-
if (isa<TupleExpr>(nextParent)) {
4548-
argList = nextParent;
4549-
break;
4550-
}
4551-
4552-
// e.g. `test(((&x)))`
4553-
if (isa<ParenExpr>(nextParent)) {
4554-
argList = nextParent;
4555-
continue;
4556-
}
4557-
4558-
break;
4559-
}
4560-
}
4538+
auto argInfo = cs.isArgumentExpr(argExpr);
4539+
assert(argInfo && "Incorrect use of `inout` expression");
45614540

4562-
unsigned argIdx = 0;
4563-
if (auto *tuple = dyn_cast<TupleExpr>(argList)) {
4564-
auto arguments = tuple->getElements();
4541+
Expr *call;
4542+
unsigned argIdx;
45654543

4566-
for (auto idx : indices(arguments)) {
4567-
if (arguments[idx]->getSemanticsProvidingExpr() == argExpr) {
4568-
argIdx = idx;
4569-
break;
4570-
}
4571-
}
4572-
}
4573-
4574-
auto *call = cs.getParentExpr(argList);
4575-
assert(call);
4544+
std::tie(call, argIdx) = *argInfo;
45764545

45774546
ParameterTypeFlags flags;
45784547
locator = cs.getConstraintLocator(
@@ -4746,6 +4715,58 @@ bool constraints::isStandardComparisonOperator(ASTNode node) {
47464715
return false;
47474716
}
47484717

4718+
Optional<std::pair<Expr *, unsigned>>
4719+
ConstraintSystem::isArgumentExpr(Expr *expr) {
4720+
auto *argList = getParentExpr(expr);
4721+
4722+
if (isa<ParenExpr>(argList)) {
4723+
for (;;) {
4724+
auto *parent = getParentExpr(argList);
4725+
if (!parent)
4726+
return None;
4727+
4728+
if (isa<TupleExpr>(parent)) {
4729+
argList = parent;
4730+
break;
4731+
}
4732+
4733+
// Drop all of the semantically insignificant parens
4734+
// that might be wrapping an argument e.g. `test(((42)))`
4735+
if (isa<ParenExpr>(parent)) {
4736+
argList = parent;
4737+
continue;
4738+
}
4739+
4740+
break;
4741+
}
4742+
}
4743+
4744+
if (!(isa<ParenExpr>(argList) || isa<TupleExpr>(argList)))
4745+
return None;
4746+
4747+
auto *application = getParentExpr(argList);
4748+
if (!application)
4749+
return None;
4750+
4751+
if (!(isa<ApplyExpr>(application) || isa<SubscriptExpr>(application) ||
4752+
isa<ObjectLiteralExpr>(application)))
4753+
return None;
4754+
4755+
unsigned argIdx = 0;
4756+
if (auto *tuple = dyn_cast<TupleExpr>(argList)) {
4757+
auto arguments = tuple->getElements();
4758+
4759+
for (auto idx : indices(arguments)) {
4760+
if (arguments[idx]->getSemanticsProvidingExpr() == expr) {
4761+
argIdx = idx;
4762+
break;
4763+
}
4764+
}
4765+
}
4766+
4767+
return std::make_pair(application, argIdx);
4768+
}
4769+
47494770
bool constraints::isOperatorArgument(ConstraintLocator *locator,
47504771
StringRef expectedOperator) {
47514772
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)