SolutionCompareResult ConstraintSystem::compareSolutions(ConstraintSystem &cs, ArrayRef<Solution> solutions, const SolutionDiff &diff, unsigned idx1, unsigned idx2) { if (cs.TC.getLangOpts().DebugConstraintSolver) { auto &log = cs.getASTContext().TypeCheckerDebug->getStream(); log.indent(cs.solverState->depth * 2) << "comparing solutions " << idx1 << " and " << idx2 <<"\n"; } // Whether the solutions are identical. bool identical = true; // Compare the fixed scores by themselves. if (solutions[idx1].getFixedScore() != solutions[idx2].getFixedScore()) { return solutions[idx1].getFixedScore() < solutions[idx2].getFixedScore() ? SolutionCompareResult::Better : SolutionCompareResult::Worse; } // Compute relative score. unsigned score1 = 0; unsigned score2 = 0; auto foundRefinement1 = false; auto foundRefinement2 = false; bool isStdlibOptionalMPlusOperator1 = false; bool isStdlibOptionalMPlusOperator2 = false; // Compare overload sets. for (auto &overload : diff.overloads) { auto choice1 = overload.choices[idx1]; auto choice2 = overload.choices[idx2]; // If the systems made the same choice, there's nothing interesting here. if (sameOverloadChoice(choice1, choice2)) continue; auto decl1 = choice1.getDecl(); auto dc1 = decl1->getDeclContext(); auto decl2 = choice2.getDecl(); auto dc2 = decl2->getDeclContext(); // The two systems are not identical. If the decls in question are distinct // protocol members, let the checks below determine if the two choices are // 'identical' or not. This allows us to structurally unify disparate // protocol members during overload resolution. // FIXME: Along with the FIXME below, this is a hack to work around // problems with restating requirements in protocols. bool decl1InSubprotocol = false; bool decl2InSubprotocol = false; if ((dc1->getContextKind() == DeclContextKind::NominalTypeDecl) && (dc1->getContextKind() == dc2->getContextKind())) { auto ntd1 = dyn_cast<NominalTypeDecl>(dc1); auto ntd2 = dyn_cast<NominalTypeDecl>(dc2); identical = (ntd1 != ntd2) && (ntd1->getKind() == DeclKind::Protocol) && (ntd2->getKind() == DeclKind::Protocol); // FIXME: This hack tells us to prefer members of subprotocols over // those of the protocols they inherit, if all else fails. // If we were properly handling overrides of protocol members when // requirements get restated, it would not be necessary. if (identical) { decl1InSubprotocol = cast<ProtocolDecl>(ntd1)->inheritsFrom( cast<ProtocolDecl>(ntd2)); decl2InSubprotocol = cast<ProtocolDecl>(ntd2)->inheritsFrom( cast<ProtocolDecl>(ntd1)); } } else { identical = false; } // If the kinds of overload choice don't match... if (choice1.getKind() != choice2.getKind()) { identical = false; // A declaration found directly beats any declaration found via dynamic // lookup, bridging, or optional unwrapping. if (choice1.getKind() == OverloadChoiceKind::Decl && (choice2.getKind() == OverloadChoiceKind::DeclViaDynamic || choice2.getKind() == OverloadChoiceKind::DeclViaBridge || choice2.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional)) { ++score1; continue; } if ((choice1.getKind() == OverloadChoiceKind::DeclViaDynamic || choice1.getKind() == OverloadChoiceKind::DeclViaBridge || choice1.getKind() == OverloadChoiceKind::DeclViaUnwrappedOptional) && choice2.getKind() == OverloadChoiceKind::Decl) { ++score2; continue; } continue; } // The kinds of overload choice match, but the contents don't. auto &tc = cs.getTypeChecker(); switch (choice1.getKind()) { case OverloadChoiceKind::TupleIndex: case OverloadChoiceKind::TypeDecl: continue; case OverloadChoiceKind::BaseType: llvm_unreachable("Never considered different"); case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::Decl: case OverloadChoiceKind::DeclViaBridge: case OverloadChoiceKind::DeclViaUnwrappedOptional: break; } // Determine whether one declaration is more specialized than the other. bool firstAsSpecializedAs = false; bool secondAsSpecializedAs = false; if (isDeclAsSpecializedAs(tc, cs.DC, decl1, decl2)) { ++score1; firstAsSpecializedAs = true; } if (isDeclAsSpecializedAs(tc, cs.DC, decl2, decl1)) { ++score2; 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<ConstructorDecl>(decl1)) { if (auto ctor2 = dyn_cast<ConstructorDecl>(decl2)) { if (ctor1->getInitKind() != ctor2->getInitKind()) { if (ctor1->getInitKind() < ctor2->getInitKind()) ++score1; else ++score2; } 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->getResultType(); auto resType2 = ctor2->getResultType(); if (!resType1->isEqual(resType2)) { if (tc.isSubtypeOf(resType1, resType2, cs.DC)) { ++score1; } else if (tc.isSubtypeOf(resType2, resType1, cs.DC)) { ++score2; } } } } } } // 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<TypeDecl>(decl1) && isa<AbstractFunctionDecl>(decl2)) || (isa<AbstractFunctionDecl>(decl1) && isa<TypeDecl>(decl2)))) { if (isa<TypeDecl>(decl1)) ++score2; else ++score1; } // 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; else if (!decl2->isInstanceMember() && decl1->isInstanceMember()) ++score2; else if (isa<VarDecl>(decl1) && isa<FuncDecl>(decl2)) ++score1; else if (isa<VarDecl>(decl2) && isa<FuncDecl>(decl1)) ++score2; // 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; } } // If we still haven't found a refinement, check if there's a parameter- // wise comparison between an empty existential collection and a non- // existential type. if (!(foundRefinement1 && foundRefinement2)) { if (hasEmptyExistentialParameterMismatch(decl1, decl2)) { foundRefinement1 = true; } if (hasEmptyExistentialParameterMismatch(decl2, decl1)) { foundRefinement2 = true; } } // FIXME: The rest of the hack for restating requirements. if (!(foundRefinement1 && foundRefinement2)) { if (identical && decl1InSubprotocol != decl2InSubprotocol) { foundRefinement1 = decl1InSubprotocol; foundRefinement2 = decl2InSubprotocol; } } // FIXME: Lousy hack for ?? to prefer the catamorphism (flattening) // over the mplus (non-flattening) overload if all else is equal. if (decl1->getName().str() == "??") { assert(decl2->getName().str() == "??"); auto check = [](const ValueDecl *VD) -> bool { if (!VD->getModuleContext()->isStdlibModule()) return false; auto fnTy = VD->getType()->castTo<AnyFunctionType>(); if (!fnTy->getResult()->getAnyOptionalObjectType()) return false; // Check that the standard library hasn't added another overload of // the ?? operator. auto inputTupleTy = fnTy->getInput()->castTo<TupleType>(); auto inputTypes = inputTupleTy->getElementTypes(); assert(inputTypes.size() == 2); assert(inputTypes[0]->getAnyOptionalObjectType()); auto autoclosure = inputTypes[1]->castTo<AnyFunctionType>(); assert(autoclosure->isAutoClosure()); auto secondParamTy = autoclosure->getResult(); assert(secondParamTy->getAnyOptionalObjectType()); (void)secondParamTy; return true; }; isStdlibOptionalMPlusOperator1 = check(decl1); isStdlibOptionalMPlusOperator2 = check(decl2); } } // Compare the type variable bindings. auto &tc = cs.getTypeChecker(); for (auto &binding : diff.typeBindings) { // If the type variable isn't one for which we should be looking at the // bindings, don't. if (!binding.typeVar->getImpl().prefersSubtypeBinding()) continue; auto type1 = binding.bindings[idx1]; auto type2 = binding.bindings[idx2]; // Strip any initializers from tuples in the type; they aren't // to be compared. type1 = stripInitializers(type1); type2 = stripInitializers(type2); // If the types are equivalent, there's nothing more to do. if (type1->isEqual(type2)) continue; // If either of the types still contains type variables, we can't // compare them. // FIXME: This is really unfortunate. More type variable sharing // (when it's sane) would help us do much better here. if (type1->hasTypeVariable() || type2->hasTypeVariable()) { identical = false; continue; } // If one type is an implicitly unwrapped optional of the other, // prefer the non-optional. bool type1Better = false; bool type2Better = false; if (auto type1Obj = type1->getImplicitlyUnwrappedOptionalObjectType()) { if (type1Obj->isEqual(type2)) type2Better = true; } if (auto type2Obj = type2->getImplicitlyUnwrappedOptionalObjectType()) { if (type2Obj->isEqual(type1)) type1Better = true; } if (type1Better || type2Better) { if (type1Better) ++score1; if (type2Better) ++score2; continue; } // If one type is a subtype of the other, but not vice-versa, // we prefer the system with the more-constrained type. // FIXME: Collapse this check into the second check. type1Better = tc.isSubtypeOf(type1, type2, cs.DC); type2Better = tc.isSubtypeOf(type2, type1, cs.DC); if (type1Better || type2Better) { if (type1Better) ++score1; if (type2Better) ++score2; // Prefer the unlabeled form of a type. auto unlabeled1 = type1->getUnlabeledType(cs.getASTContext()); auto unlabeled2 = type2->getUnlabeledType(cs.getASTContext()); if (unlabeled1->isEqual(unlabeled2)) { if (type1->isEqual(unlabeled1)) { ++score1; continue; } if (type2->isEqual(unlabeled2)) { ++score2; continue; } } identical = false; continue; } // The systems are not considered equivalent. identical = false; // If one type is convertible to of the other, but not vice-versa. type1Better = tc.isConvertibleTo(type1, type2, cs.DC); type2Better = tc.isConvertibleTo(type2, type1, cs.DC); if (type1Better || type2Better) { if (type1Better) ++score1; if (type2Better) ++score2; continue; } // A concrete type is better than an archetype. // FIXME: Total hack. if (type1->is<ArchetypeType>() != type2->is<ArchetypeType>()) { if (type1->is<ArchetypeType>()) ++score2; else ++score1; continue; } // FIXME: // This terrible hack is in place to support equality comparisons of non- // equatable option types to 'nil'. Until we have a way to constrain a type // variable on "!Equatable", if all other aspects of the overload choices // are equal, favor the overload that does not require an implicit literal // argument conversion to 'nil'. // Post-1.0, we'll need to remove this hack in favor of richer constraint // declarations. if (!(score1 || score2)) { if (auto nominalType2 = type2->getNominalOrBoundGenericNominal()) { if ((nominalType2->getName() == cs.TC.Context.Id_OptionalNilComparisonType)) { ++score1; } } else if (auto nominalType1 = type1->getNominalOrBoundGenericNominal()) { if ((nominalType1->getName() == cs.TC.Context.Id_OptionalNilComparisonType)) { ++score2; } } } } // All other things considered equal, if any overload choice is more // more constrained than the other, increment the score. if (score1 == score2) { if (foundRefinement1) { ++score1; } if (foundRefinement2) { ++score2; } } // FIXME: All other things being equal, prefer the catamorphism (flattening) // overload of ?? over the mplus (non-flattening) overload. if (score1 == score2) { // This is correct: we want to /disprefer/ the mplus. score2 += isStdlibOptionalMPlusOperator1; score1 += isStdlibOptionalMPlusOperator2; } // FIXME: There are type variables and overloads not common to both solutions // that haven't been considered. They make the systems different, but don't // affect ranking. We need to handle this. // If the scores are different, we have a winner. if (score1 != score2) { return score1 > score2? SolutionCompareResult::Better : SolutionCompareResult::Worse; } // Neither system wins; report whether they were identical or not. return identical? SolutionCompareResult::Identical : SolutionCompareResult::Incomparable; }
AssignmentFailure::AssignmentFailure(Expr *destExpr, ConstraintSystem &cs, SourceLoc diagnosticLoc) : FailureDiagnostic(destExpr, cs, cs.getConstraintLocator(destExpr)), Loc(diagnosticLoc), DeclDiagnostic(findDeclDiagonstic(cs.getASTContext(), destExpr)), TypeDiagnostic(diag::assignment_lhs_not_lvalue) {}