From a9e5a8a07a798fc47fc5b04013cf3d03ac179fab Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 5 Jun 2025 22:26:19 -0700 Subject: [PATCH] [Diagnostics] Correctly diagnose situations when immutable value is passed to `inout` parameter Currently the note is going to point to the "callee" but that is incorrect when the failure is related to an argument of a call. Detect this situation in `RValueTreatedAsLValueFailure::diagnoseAsNote` and produce a correct note. Resolves: rdar://150689994 (cherry picked from commit 6bbc101a986e7df77c9fefbc9e431af4d2c7154c) --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/CSDiagnostics.cpp | 15 +++++++++++++- test/Constraints/overload.swift | 28 +++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 02889d855de..84b40e6d303 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5136,6 +5136,9 @@ ERROR(assignment_bang_has_immutable_subcomponent,none, NOTE(candidate_is_not_assignable,none, "candidate is not assignable: %kind0", (const ValueDecl *)) +NOTE(candidate_expects_inout_argument,none, + "candidate expects in-out value for parameter #%0 but argument is immutable", + (unsigned)) NOTE(change_to_mutating,none, "mark %select{method|%1}0 'mutating' to make 'self' mutable", diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index a8532d2be37..4c88ddca416 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2049,11 +2049,24 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { } bool RValueTreatedAsLValueFailure::diagnoseAsNote() { - auto overload = getCalleeOverloadChoiceIfAvailable(getLocator()); + auto *locator = getLocator(); + + auto overload = getCalleeOverloadChoiceIfAvailable(locator); if (!(overload && overload->choice.isDecl())) return false; auto *decl = overload->choice.getDecl(); + + if (locator->isLastElement()) { + auto argConv = locator->findLast(); + if (!argConv) + return false; + + emitDiagnosticAt(decl, diag::candidate_expects_inout_argument, + argConv->getParamIdx() + 1); + return true; + } + emitDiagnosticAt(decl, diag::candidate_is_not_assignable, decl); return true; } diff --git a/test/Constraints/overload.swift b/test/Constraints/overload.swift index 59f918dddc2..87c132e21c5 100644 --- a/test/Constraints/overload.swift +++ b/test/Constraints/overload.swift @@ -349,3 +349,31 @@ do { } } } + +do { + struct S { + let x: Int + var y: Int + } + + func overloaded(_: String) {} + // expected-note@-1 3 {{candidate expects value of type 'String' for parameter #1 (got 'Int')}} + func overloaded(_: inout Int) {} + // expected-note@-1 3 {{candidate expects in-out value for parameter #1 but argument is immutable}} + + func testImmutable(s: S) { + overloaded(s.x) // expected-error {{no exact matches in call to local function 'overloaded'}} + } + + func testMutable(s: inout S) { + overloaded(s.x) // expected-error {{no exact matches in call to local function 'overloaded'}} + } + + func testImmutableBase(s: S) { + overloaded(s.y) // expected-error {{no exact matches in call to local function 'overloaded'}} + } + + func testMissingAddressOf(s: inout S) { + overloaded(s.y) // expected-error {{passing value of type 'Int' to an inout parameter requires explicit '&'}} + } +}