Skip to content

[Diagnostics] Don't filter overload set candidates based on labels in lookup #26302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,9 @@ ERROR(argument_out_of_order_unnamed_named,none,
ERROR(argument_out_of_order_unnamed_unnamed,none,
"unnamed argument #%0 must precede unnamed argument #%1",
(unsigned, unsigned))
NOTE(candidate_expected_different_labels,none,
"incorrect labels for candidate (have: '%0', expected: '%1')",
(StringRef, StringRef))

ERROR(member_shadows_global_function,none,
"use of %0 refers to %1 %2 rather than %3 %4 in %5 %6",
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,6 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
return;

switch (firstProblem) {
case MemberLookupResult::UR_LabelMismatch:
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
Expand Down Expand Up @@ -4192,7 +4191,7 @@ bool FailureDiagnosis::diagnoseTrailingClosureErrors(ApplyExpr *callExpr) {
};

SmallPtrSet<TypeBase *, 4> possibleTypes;
auto currentType = CS.getType(fnExpr);
auto currentType = CS.simplifyType(CS.getType(fnExpr));

// If current type has type variables or unresolved types
// let's try to re-typecheck it to see if we can get some
Expand Down
41 changes: 41 additions & 0 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include <string>

using namespace swift;
using namespace constraints;
Expand Down Expand Up @@ -691,6 +692,46 @@ bool LabelingFailure::diagnoseAsError() {
isa<SubscriptExpr>(anchor));
}

bool LabelingFailure::diagnoseAsNote() {
auto *anchor = getRawAnchor();

auto *argExpr = getArgumentExprFor(anchor);
if (!argExpr)
return false;

SmallVector<Identifier, 4> argLabels;
if (auto *paren = dyn_cast<ParenExpr>(argExpr)) {
argLabels.push_back(Identifier());
} else if (auto *tuple = dyn_cast<TupleExpr>(argExpr)) {
argLabels.append(tuple->getElementNames().begin(),
tuple->getElementNames().end());
} else {
return false;
}

auto stringifyLabels = [](ArrayRef<Identifier> labels) -> std::string {
std::string str;
for (auto label : labels) {
str += label.empty() ? "_" : label.str();
str += ':';
}
return "(" + str + ")";
};

auto selectedOverload = getChoiceFor(anchor);
if (!selectedOverload)
return false;

const auto &choice = selectedOverload->choice;
if (auto *decl = choice.getDeclOrNull()) {
emitDiagnostic(decl, diag::candidate_expected_different_labels,
stringifyLabels(argLabels), stringifyLabels(CorrectLabels));
return true;
}

return false;
}

bool NoEscapeFuncToTypeConversionFailure::diagnoseAsError() {
auto *anchor = getAnchor();

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ class LabelingFailure final : public FailureDiagnostic {
: FailureDiagnostic(root, cs, locator), CorrectLabels(labels) {}

bool diagnoseAsError() override;
bool diagnoseAsNote() override;
};

/// Diagnose errors related to converting function type which
Expand Down
145 changes: 70 additions & 75 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,31 @@ bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy,
// curried self.
if (decl->isInstanceMember()) {
assert(baseTy);
if (isa<AbstractFunctionDecl>(decl) && baseTy->is<AnyMetatypeType>())
if (isa<AbstractFunctionDecl>(decl) &&
baseTy->getRValueType()->is<AnyMetatypeType>())
return false;
}

// Otherwise the reference applies self.
return true;
}

bool constraints::areConservativelyCompatibleArgumentLabels(
OverloadChoice choice,
ArrayRef<Identifier> labels,
bool hasTrailingClosure) {
static bool
areConservativelyCompatibleArgumentLabels(OverloadChoice choice,
ArrayRef<FunctionType::Param> args,
bool hasTrailingClosure) {
ValueDecl *decl = nullptr;
Type baseType;
switch (choice.getKind()) {
case OverloadChoiceKind::Decl:
case OverloadChoiceKind::DeclViaBridge:
case OverloadChoiceKind::DeclViaDynamic:
case OverloadChoiceKind::DeclViaUnwrappedOptional:
decl = choice.getDecl();
baseType = choice.getBaseType();
if (baseType)
baseType = baseType->getRValueType();
break;

case OverloadChoiceKind::KeyPathApplication:
// Key path applications are written as if subscript[keyPath:].
return !hasTrailingClosure && labels.size() == 1 && labels[0].is("keyPath");

case OverloadChoiceKind::BaseType:
// KeyPath application is not filtered in `performMemberLookup`.
case OverloadChoiceKind::KeyPathApplication:
case OverloadChoiceKind::DynamicMemberLookup:
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
case OverloadChoiceKind::TupleIndex:
Expand All @@ -149,17 +144,13 @@ bool constraints::areConservativelyCompatibleArgumentLabels(
if (!decl->hasParameterList())
return true;

SmallVector<AnyFunctionType::Param, 8> argInfos;
for (auto argLabel : labels) {
argInfos.push_back(AnyFunctionType::Param(Type(), argLabel, {}));
}

// This is a member lookup, which generally means that the call arguments
// (if we have any) will apply to the second level of parameters, with
// the member lookup applying the curried self at the first level. But there
// are cases where we can get an unapplied declaration reference back.
auto hasAppliedSelf =
decl->hasCurriedSelf() && doesMemberRefApplyCurriedSelf(baseType, decl);
decl->hasCurriedSelf() &&
doesMemberRefApplyCurriedSelf(choice.getBaseType(), decl);

auto *fnType = decl->getInterfaceType()->castTo<AnyFunctionType>();
if (hasAppliedSelf) {
Expand All @@ -173,10 +164,9 @@ bool constraints::areConservativelyCompatibleArgumentLabels(
MatchCallArgumentListener listener;
SmallVector<ParamBinding, 8> unusedParamBindings;

return !matchCallArguments(argInfos, params, paramInfo,
hasTrailingClosure,
/*allow fixes*/ false,
listener, unusedParamBindings);
return !matchCallArguments(args, params, paramInfo, hasTrailingClosure,
/*allow fixes*/ false, listener,
unusedParamBindings);
}

Expr *constraints::getArgumentLabelTargetExpr(Expr *fn) {
Expand Down Expand Up @@ -873,14 +863,19 @@ getCalleeDeclAndArgs(ConstraintSystem &cs,

class ArgumentFailureTracker : public MatchCallArgumentListener {
ConstraintSystem &CS;
ArrayRef<AnyFunctionType::Param> Arguments;
ArrayRef<AnyFunctionType::Param> Parameters;
SmallVectorImpl<ParamBinding> &Bindings;
ConstraintLocatorBuilder Locator;

public:
ArgumentFailureTracker(ConstraintSystem &cs,
ArrayRef<AnyFunctionType::Param> args,
ArrayRef<AnyFunctionType::Param> params,
SmallVectorImpl<ParamBinding> &bindings,
ConstraintLocatorBuilder locator)
: CS(cs), Bindings(bindings), Locator(locator) {}
: CS(cs), Arguments(args), Parameters(params), Bindings(bindings),
Locator(locator) {}

bool missingLabel(unsigned paramIndex) override {
return !CS.shouldAttemptFixes();
Expand Down Expand Up @@ -912,9 +907,27 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
if (!anchor)
return true;

unsigned numExtraneous = 0;
for (unsigned paramIdx = 0, n = Bindings.size(); paramIdx != n;
++paramIdx) {
if (Bindings[paramIdx].empty())
continue;

const auto paramLabel = Parameters[paramIdx].getLabel();
for (auto argIdx : Bindings[paramIdx]) {
auto argLabel = Arguments[argIdx].getLabel();
if (paramLabel.empty() && !argLabel.empty())
++numExtraneous;
}
}

auto *locator = CS.getConstraintLocator(anchor);
auto *fix = RelabelArguments::create(CS, newLabels, locator);
CS.recordFix(fix);
// Re-labeling fixes with extraneous labels should take
// lower priority vs. other fixes on same/different
// overload(s) where labels did line up correctly.
CS.increaseScore(ScoreKind::SK_Fix, numExtraneous);
return false;
}
};
Expand Down Expand Up @@ -944,7 +957,8 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(

// Match up the call arguments to the parameters.
SmallVector<ParamBinding, 4> parameterBindings;
ArgumentFailureTracker listener(cs, parameterBindings, locator);
ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings,
locator);
if (constraints::matchCallArguments(argsWithLabels, params,
paramInfo,
hasTrailingClosure,
Expand Down Expand Up @@ -4175,19 +4189,15 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,

// If we have a simple name, determine whether there are argument
// labels we can use to restrict the set of lookup results.
Optional<ArgumentLabelState> argumentLabels;
if (memberName.isSimpleName()) {
argumentLabels = getArgumentLabels(*this,
ConstraintLocatorBuilder(memberLocator));

if (baseObjTy->isAnyObject() && memberName.isSimpleName()) {
// If we're referencing AnyObject and we have argument labels, put
// the argument labels into the name: we don't want to look for
// anything else, because the cost of the general search is so
// high.
if (baseObjTy->isAnyObject() && argumentLabels) {
if (auto argumentLabels =
getArgumentLabels(*this, ConstraintLocatorBuilder(memberLocator))) {
memberName = DeclName(TC.Context, memberName.getBaseName(),
argumentLabels->Labels);
argumentLabels.reset();
}
}

Expand Down Expand Up @@ -4226,7 +4236,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
bridgedType = classType;
}
}
bool labelMismatch = false;

// Local function that adds the given declaration if it is a
// reasonable choice.
Expand Down Expand Up @@ -4291,20 +4300,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
hasInstanceMethods = true;
}

// If the argument labels for this result are incompatible with
// the call site, skip it.
// FIXME: The subscript check here forces the use of the
// function-application simplification logic to handle labels.
if (argumentLabels &&
(!candidate.isDecl() || !isa<SubscriptDecl>(candidate.getDecl())) &&
!areConservativelyCompatibleArgumentLabels(
candidate, argumentLabels->Labels,
argumentLabels->HasTrailingClosure)) {
labelMismatch = true;
result.addUnviable(candidate, MemberLookupResult::UR_LabelMismatch);
return;
}

// If our base is an existential type, we can't make use of any
// member whose signature involves associated types.
if (instanceTy->isExistentialType()) {
Expand Down Expand Up @@ -4494,8 +4489,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
};

// Add all results from this lookup.
retry_after_fail:
labelMismatch = false;
for (auto result : lookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*isBridged=*/false,
Expand Down Expand Up @@ -4603,14 +4596,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
}
}

// If we rejected some possibilities due to an argument-label
// mismatch and ended up with nothing, try again ignoring the
// labels. This allows us to perform typo correction on the labels.
if (result.ViableCandidates.empty() && labelMismatch && shouldAttemptFixes()){
argumentLabels.reset();
goto retry_after_fail;
}

// If we have no viable or unviable candidates, and we're generating,
// diagnostics, rerun the query with inaccessible members included, so we can
// include them in the unviable candidates list.
Expand Down Expand Up @@ -4855,7 +4840,6 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
: nullptr;
}

case MemberLookupResult::UR_LabelMismatch:
// TODO(diagnostics): Add a new fix that is suggests to
// add `subscript(dynamicMember: {Writable}KeyPath<T, U>)`
// overload here, that would help if such subscript has
Expand Down Expand Up @@ -5882,7 +5866,6 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint(
Type ConstraintSystem::simplifyAppliedOverloads(
TypeVariableType *fnTypeVar,
const FunctionType *argFnType,
Optional<ArgumentLabelState> argumentLabels,
ConstraintLocatorBuilder locator) {
Type fnType(fnTypeVar);

Expand Down Expand Up @@ -5923,6 +5906,8 @@ Type ConstraintSystem::simplifyAppliedOverloads(
return markFailure();
};

auto argumentInfo = getArgumentLabels(*this, locator);

// Consider each of the constraints in the disjunction.
retry_after_fail:
bool hasUnhandledConstraints = false;
Expand All @@ -5936,12 +5921,18 @@ Type ConstraintSystem::simplifyAppliedOverloads(

// Determine whether the argument labels we have conflict with those of
// this overload choice.
if (argumentLabels &&
!areConservativelyCompatibleArgumentLabels(
choice, argumentLabels->Labels,
argumentLabels->HasTrailingClosure)) {
labelMismatch = true;
return false;
if (argumentInfo) {
auto args = argFnType->getParams();

SmallVector<FunctionType::Param, 8> argsWithLabels;
argsWithLabels.append(args.begin(), args.end());
FunctionType::relabelParams(argsWithLabels, argumentInfo->Labels);

if (!areConservativelyCompatibleArgumentLabels(
choice, argsWithLabels, argumentInfo->HasTrailingClosure)) {
labelMismatch = true;
return false;
}
}

// Determine the type that this choice will have.
Expand All @@ -5968,7 +5959,7 @@ Type ConstraintSystem::simplifyAppliedOverloads(
switch (filterResult) {
case SolutionKind::Error:
if (labelMismatch && shouldAttemptFixes()) {
argumentLabels = None;
argumentInfo.reset();
goto retry_after_fail;
}

Expand Down Expand Up @@ -6081,16 +6072,20 @@ ConstraintSystem::simplifyApplicableFnConstraint(

};

// If the right-hand side is a type variable, try to simplify the overload
// set.
if (auto typeVar = desugar2->getAs<TypeVariableType>()) {
auto argumentLabels = getArgumentLabels(*this, locator);
Type newType2 =
simplifyAppliedOverloads(typeVar, func1, argumentLabels, locator);
if (!newType2)
return SolutionKind::Error;
// Don't attempt this optimization in "diagnostic mode" because
// in such mode we'd like to attempt all of the available
// overloads regardless of of problems related to missing or
// extraneous labels and/or arguments.
if (!(solverState && shouldAttemptFixes())) {
// If the right-hand side is a type variable,
// try to simplify the overload set.
if (auto typeVar = desugar2->getAs<TypeVariableType>()) {
Type newType2 = simplifyAppliedOverloads(typeVar, func1, locator);
if (!newType2)
return SolutionKind::Error;

desugar2 = newType2->getDesugaredType();
desugar2 = newType2->getDesugaredType();
}
}

// If right-hand side is a type variable, the constraint is unsolved.
Expand Down
Loading