@@ -2065,6 +2065,13 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
2065
2065
CalleeCandidateInfo &calleeInfo,
2066
2066
SourceLoc applyLoc);
2067
2067
2068
+ // / Produce diagnostic for failures related to attributes associated with
2069
+ // / candidate functions/methods e.g. mutability.
2070
+ bool diagnoseMethodAttributeFailures (ApplyExpr *expr,
2071
+ ArrayRef<Identifier> argLabels,
2072
+ bool hasTrailingClosure,
2073
+ CalleeCandidateInfo &candidates);
2074
+
2068
2075
bool visitExpr (Expr *E);
2069
2076
bool visitIdentityExpr (IdentityExpr *E);
2070
2077
bool visitTryExpr (TryExpr *E);
@@ -5272,6 +5279,87 @@ bool FailureDiagnosis::diagnoseNilLiteralComparison(
5272
5279
return true ;
5273
5280
}
5274
5281
5282
+ bool FailureDiagnosis::diagnoseMethodAttributeFailures (
5283
+ swift::ApplyExpr *callExpr, ArrayRef<Identifier> argLabels,
5284
+ bool hasTrailingClosure, CalleeCandidateInfo &candidates) {
5285
+ auto UDE = dyn_cast<UnresolvedDotExpr>(callExpr->getFn ());
5286
+ if (!UDE)
5287
+ return false ;
5288
+
5289
+ auto argExpr = callExpr->getArg ();
5290
+ auto argType = argExpr->getType ();
5291
+
5292
+ // If type of the argument hasn't been established yet, we can't diagnose.
5293
+ if (!argType || isUnresolvedOrTypeVarType (argType))
5294
+ return false ;
5295
+
5296
+ // Let's filter our candidate list based on that type.
5297
+ candidates.filterList (argType, argLabels);
5298
+
5299
+ if (candidates.closeness == CC_ExactMatch)
5300
+ return false ;
5301
+
5302
+ // And if filtering didn't give an exact match, such means that problem
5303
+ // might be related to function attributes which is best diagnosed by
5304
+ // unviable member candidates, if any.
5305
+ auto base = UDE->getBase ();
5306
+ auto baseType = base->getType ();
5307
+
5308
+ // This handles following situation:
5309
+ // struct S {
5310
+ // mutating func f(_ i: Int) {}
5311
+ // func f(_ f: Float) {}
5312
+ // }
5313
+ //
5314
+ // Given struct has an overloaded method "f" with a single argument of
5315
+ // multiple different types, one of the overloads is marked as
5316
+ // "mutating", which means it can only be applied on LValue base type.
5317
+ // So when struct is used like this:
5318
+ //
5319
+ // let answer: Int = 42
5320
+ // S().f(answer)
5321
+ //
5322
+ // Constraint system generator is going to pick `f(_ f: Float)` as
5323
+ // only possible overload candidate because "base" of the call is immutable
5324
+ // and contextual information about argument type is not available yet.
5325
+ // Such leads to incorrect contextual conversion failure diagnostic because
5326
+ // type of the argument is going to resolved as (Int) no matter what.
5327
+ // To workaround that fact and improve diagnostic of such cases we are going
5328
+ // to try and collect all unviable candidates for a given call and check if
5329
+ // at least one of them matches established argument type before even trying
5330
+ // to re-check argument expression.
5331
+ auto results = CS->performMemberLookup (
5332
+ ConstraintKind::ValueMember, UDE->getName (), baseType,
5333
+ UDE->getFunctionRefKind (), CS->getConstraintLocator (UDE),
5334
+ /* includeInaccessibleMembers=*/ false );
5335
+
5336
+ if (results.UnviableCandidates .empty ())
5337
+ return false ;
5338
+
5339
+ SmallVector<OverloadChoice, 2 > choices;
5340
+ for (auto &unviable : results.UnviableCandidates )
5341
+ choices.push_back (OverloadChoice (baseType, unviable.first ,
5342
+ /* isSpecialized=*/ false ,
5343
+ UDE->getFunctionRefKind ()));
5344
+
5345
+ CalleeCandidateInfo unviableCandidates (baseType, choices, hasTrailingClosure,
5346
+ CS);
5347
+
5348
+ // Filter list of the unviable candidates based on the
5349
+ // already established type of the argument expression.
5350
+ unviableCandidates.filterList (argType, argLabels);
5351
+
5352
+ // If one of the unviable candidates matches arguments exactly,
5353
+ // that means that actual problem is related to function attributes.
5354
+ if (unviableCandidates.closeness == CC_ExactMatch) {
5355
+ diagnoseUnviableLookupResults (results, baseType, base, UDE->getName (),
5356
+ UDE->getNameLoc (), UDE->getLoc ());
5357
+ return true ;
5358
+ }
5359
+
5360
+ return false ;
5361
+ }
5362
+
5275
5363
// / When initializing Unsafe[Mutable]Pointer<T> from Unsafe[Mutable]RawPointer,
5276
5364
// / issue a diagnostic that refers to the API for binding memory to a type.
5277
5365
static bool isCastToTypedPointer (ASTContext &Ctx, const Expr *Fn,
@@ -5445,7 +5533,15 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
5445
5533
if (diagnoseParameterErrors (calleeInfo, callExpr->getFn (),
5446
5534
callExpr->getArg (), argLabels))
5447
5535
return true ;
5448
-
5536
+
5537
+ // There might be a candidate with correct argument types but it's not
5538
+ // used by constraint solver because it doesn't have correct attributes,
5539
+ // let's try to diagnose such situation there right before type checking
5540
+ // argument expression, because that would overwrite original argument types.
5541
+ if (diagnoseMethodAttributeFailures (callExpr, argLabels, hasTrailingClosure,
5542
+ calleeInfo))
5543
+ return true ;
5544
+
5449
5545
Type argType; // Type of the argument list, if knowable.
5450
5546
if (auto FTy = fnType->getAs <AnyFunctionType>())
5451
5547
argType = FTy->getInput ();
0 commit comments