Slide 6
Slide 6 text
// The kinds of overload choice match, but the contents don't.
switch (choice1.getKind()) {
case OverloadChoiceKind::TupleIndex:
case OverloadChoiceKind::MaterializePack:
case OverloadChoiceKind::ExtractFunctionIsolation:
continue;
case OverloadChoiceKind::KeyPathApplication:
llvm_unreachable("Never considered different");
case OverloadChoiceKind::DeclViaDynamic:
case OverloadChoiceKind::Decl:
case OverloadChoiceKind::DeclViaBridge:
case OverloadChoiceKind::DeclViaUnwrappedOptional:
case OverloadChoiceKind::DynamicMemberLookup:
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
break;
}
// We don't apply some ranking rules to overloads found through dynamic
// lookup in order to keep a few potentially ill-formed cases ambiguous.
bool isDynamicOverloadComparison =
choice1.getKind() == OverloadChoiceKind::DeclViaDynamic &&
choice2.getKind() == OverloadChoiceKind::DeclViaDynamic;
// Determine whether one declaration is more specialized than the other.
bool firstAsSpecializedAs = false;
bool secondAsSpecializedAs = false;
if (isDeclAsSpecializedAs(cs.DC, decl1, decl2,
isDynamicOverloadComparison,
/*allowMissingConformances=*/false)) {
score1 += weight;
firstAsSpecializedAs = true;
}
if (isDeclAsSpecializedAs(cs.DC, decl2, decl1,
isDynamicOverloadComparison,
/*allowMissingConformances=*/false)) {
score2 += weight;
secondAsSpecializedAs = true;
}
// If each is as specialized as the other, and both are constructors,
// check the constructor kind.
if (firstAsSpecializedAs && secondAsSpecializedAs) {
if (auto ctor1 = dyn_cast(decl1)) {
if (auto ctor2 = dyn_cast(decl2)) {
if (ctor1->getInitKind() != ctor2->getInitKind()) {
if (ctor1->getInitKind() < ctor2->getInitKind())
score1 += weight;
else
score2 += weight;
} else if (ctor1->getInitKind() ==
CtorInitializerKind::Convenience) {
// If both are convenience initializers, and the instance type of
// one is a subtype of the other's, favor the subtype constructor.
auto resType1 = ctor1->mapTypeIntoContext(
ctor1->getResultInterfaceType());
auto resType2 = ctor2->mapTypeIntoContext(
ctor2->getResultInterfaceType());
if (!resType1->isEqual(resType2)) {
if (TypeChecker::isSubtypeOf(resType1, resType2, cs.DC)) {
score1 += weight;
} else if (TypeChecker::isSubtypeOf(resType2, resType1, cs.DC)) {
score2 += weight;
}
}
}
}
}
}
// If both declarations come from Clang, and one is a type and the other
// is a function, prefer the function.
if (decl1->hasClangNode() &&
decl2->hasClangNode() &&
((isa(decl1) &&
isa(decl2)) ||
(isa(decl1) &&
isa(decl2)))) {
if (isa(decl1))
score2 += weight;
else
score1 += weight;
}
// A class member is always better than a curried instance member.
// If the members agree on instance-ness, a property is better than a
// method (because a method is usually immediately invoked).
if (!decl1->isInstanceMember() && decl2->isInstanceMember())
score1 += weight;
else if (!decl2->isInstanceMember() && decl1->isInstanceMember())
score2 += weight;
else if (isa(decl1) && isa(decl2))
score1 += weight;
else if (isa(decl2) && isa(decl1))
score2 += weight;
// If both are class properties with the same name, prefer
// the one attached to the subclass because it could only be
// found if requested directly.
if (!decl1->isInstanceMember() && !decl2->isInstanceMember()) {
if (isa(decl1) && isa(decl2)) {
auto *nominal1 = dc1->getSelfNominalTypeDecl();
auto *nominal2 = dc2->getSelfNominalTypeDecl();
if (nominal1 && nominal2 && nominal1 != nominal2) {
auto base1 = nominal1->getDeclaredType();
auto base2 = nominal2->getDeclaredType();
if (isNominallySuperclassOf(base1, base2))
score2 += weight;
if (isNominallySuperclassOf(base2, base1))
score1 += weight;
}
}
}
// If we haven't found a refinement, record whether one overload is in
// any way more constrained than another. We'll only utilize this
// information in the case of a potential ambiguity.
if (!(foundRefinement1 && foundRefinement2)) {
if (isDeclMoreConstrainedThan(decl1, decl2)) {
foundRefinement1 = true;
}
if (isDeclMoreConstrainedThan(decl2, decl1)) {
foundRefinement2 = true;
}
}
// FIXME: The rest of the hack for restating requirements.
if (!(foundRefinement1 && foundRefinement2)) {
if (identical && decl1InSubprotocol != decl2InSubprotocol) {
foundRefinement1 = decl1InSubprotocol;
foundRefinement2 = decl2InSubprotocol;
}
}
// Swift 4.1 compatibility hack: If everything else is considered equal,
// favour a property on a concrete type over a protocol property member.
//
// This hack is required due to changes in shadowing behaviour where a
// protocol property member will no longer shadow a property on a concrete
// type, which created unintentional ambiguities in 4.2. This hack ensures
// we at least keep these cases unambiguous in Swift 5 under Swift 4
// compatibility mode. Don't however apply this hack for decls found through
// dynamic lookup, as we want the user to have to disambiguate those.
//
// This is intentionally narrow in order to best preserve source
// compatibility under Swift 4 mode by ensuring we don't introduce any new
// ambiguities. This will become a more general "is more specialised" rule
// in Swift 5 mode.
if (!cs.getASTContext().isSwiftVersionAtLeast(5) &&
choice1.getKind() != OverloadChoiceKind::DeclViaDynamic &&
choice2.getKind() != OverloadChoiceKind::DeclViaDynamic &&
isa(decl1) && isa(decl2)) {
auto *nominal1 = dc1->getSelfNominalTypeDecl();
auto *nominal2 = dc2->getSelfNominalTypeDecl();
if (nominal1 && nominal2 && nominal1 != nominal2) {
isVarAndNotProtocol1 = !isa(nominal1);
isVarAndNotProtocol2 = !isa(nominal2);
}
}
// FIXME: Lousy hack for ?? to prefer the catamorphism (flattening)
// over the mplus (non-flattening) overload if all else is equal.
if (decl1->getBaseName() == "??") {
assert(decl2->getBaseName() == "??");
auto check = [](const ValueDecl *VD) -> bool {
if (!VD->getModuleContext()->isStdlibModule())
return false;
auto fnTy = VD->getInterfaceType()->castTo();
if (!fnTy->getResult()->getOptionalObjectType())
return false;
// Check that the standard library hasn't added another overload of
// the ?? operator.
auto params = fnTy->getParams();
assert(params.size() == 2);
auto param1 = params[0].getParameterType();
auto param2 = params[1].getParameterType()->castTo();
assert(param1->getOptionalObjectType());
assert(params[1].isAutoClosure());
assert(param2->getResult()->getOptionalObjectType());
(void) param1;
(void) param2;
return true;
};
isStdlibOptionalMPlusOperator1 = check(decl1);
isStdlibOptionalMPlusOperator2 = check(decl2);
}
}
// Compare the type variable bindings.
llvm::DenseMap typeDiff;
const auto &bindings1 = solutions[idx1].typeBindings;
const auto &bindings2 = solutions[idx2].typeBindings;
for (const auto &binding1 : bindings1) {
auto *typeVar = binding1.first;
auto *loc = typeVar->getImpl().getLocator();