//===--- CSDiag.cpp - Constraint Diagnostics ------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements diagnostics for the type checker. // //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" using namespace swift; using namespace constraints; void Failure::dump(SourceManager *sm) const { dump(sm, llvm::errs()); } void Failure::dump(SourceManager *sm, raw_ostream &out) const { out << "("; if (locator) { out << "@"; locator->dump(sm, out); out << ": "; } switch (getKind()) { case DoesNotConformToProtocol: out << getFirstType().getString() << " does not conform to " << getSecondType().getString(); break; case DoesNotHaveMember: out << getFirstType().getString() << " does not have a member named '" << getName() << "'"; break; case DoesNotHaveNonMutatingMember: out << " immutable value of type " << getFirstType().getString() << " only has mutating members named '" << getName() << "'"; break; case FunctionTypesMismatch: out << "function type " << getFirstType().getString() << " is not equal to " << getSecondType().getString(); break; case FunctionAutoclosureMismatch: out << "autoclosure mismatch " << getFirstType().getString() << " vs. " << getSecondType().getString(); break; case FunctionNoReturnMismatch: out << "noreturn attribute mismatch " << getFirstType().getString() << " vs. " << getSecondType().getString(); break; case IsNotMetatype: out << getFirstType().getString() << " is not a metatype"; break; case IsNotArchetype: out << getFirstType().getString() << " is not an archetype"; break; case IsNotClass: out << getFirstType().getString() << " is not a class"; break; case IsNotDynamicLookup: out << getFirstType().getString() << " is not a dynamic lookup value"; break; case TupleNameMismatch: case TupleNamePositionMismatch: case TupleSizeMismatch: case TupleVariadicMismatch: case TupleUnused: out << "mismatched tuple types " << getFirstType().getString() << " and " << getSecondType().getString(); break; case TypesNotConstructible: out << getFirstType().getString() << " is not a constructible argument for " << getSecondType().getString(); break; case TypesNotConvertible: out << getFirstType().getString() << " is not convertible to " << getSecondType().getString(); break; case TypesNotSubtypes: out << getFirstType().getString() << " is not a subtype of " << getSecondType().getString(); break; case TypesNotEqual: out << getFirstType().getString() << " is not equal to " << getSecondType().getString(); break; case IsForbiddenLValue: out << "disallowed l-value binding of " << getFirstType().getString() << " and " << getSecondType().getString(); break; } out << ")\n"; } /// Given a subpath of an old locator, compute its summary flags. static unsigned recomputeSummaryFlags(ConstraintLocator *oldLocator, ArrayRef path) { if (oldLocator->getSummaryFlags() != 0) return ConstraintLocator::getSummaryFlagsForPath(path); return 0; } ConstraintLocator * constraints::simplifyLocator(ConstraintSystem &cs, ConstraintLocator *locator, SourceRange &range1, SourceRange &range2, ConstraintLocator **targetLocator) { // Clear out the target locator result. if (targetLocator) *targetLocator = nullptr; // The path to be tacked on to the target locator to identify the specific // target. Expr *targetAnchor; SmallVector targetPath; auto path = locator->getPath(); auto anchor = locator->getAnchor(); simplifyLocator(anchor, path, targetAnchor, targetPath, range1, range2); // If we have a target anchor, build and simplify the target locator. if (targetLocator && targetAnchor) { SourceRange targetRange1, targetRange2; unsigned targetFlags = recomputeSummaryFlags(locator, targetPath); *targetLocator = simplifyLocator(cs, cs.getConstraintLocator(targetAnchor, targetPath, targetFlags), targetRange1, targetRange2); } // If we didn't simplify anything, just return the input. if (anchor == locator->getAnchor() && path.size() == locator->getPath().size()) { return locator; } // Recompute the summary flags if we had any to begin with. This is // necessary because we might remove e.g. tuple elements from the path. unsigned summaryFlags = recomputeSummaryFlags(locator, path); return cs.getConstraintLocator(anchor, path, summaryFlags); } void constraints::simplifyLocator(Expr *&anchor, ArrayRef &path, Expr *&targetAnchor, SmallVectorImpl &targetPath, SourceRange &range1, SourceRange &range2) { range1 = SourceRange(); range2 = SourceRange(); targetAnchor = nullptr; while (!path.empty()) { switch (path[0].getKind()) { case ConstraintLocator::ApplyArgument: // Extract application argument. if (auto applyExpr = dyn_cast(anchor)) { // The target anchor is the function being called. targetAnchor = applyExpr->getFn(); targetPath.push_back(path[0]); anchor = applyExpr->getArg(); path = path.slice(1); // Look through parentheses around the argument. if (auto paren = dyn_cast(anchor)) anchor = paren->getSubExpr(); continue; } break; case ConstraintLocator::ApplyFunction: // Extract application function. if (auto applyExpr = dyn_cast(anchor)) { // No additional target locator information. targetAnchor = nullptr; targetPath.clear(); anchor = applyExpr->getFn(); path = path.slice(1); continue; } // The unresolved member itself is the function. if (auto unresolvedMember = dyn_cast(anchor)) { if (unresolvedMember->getArgument()) { // No additional target locator information. targetAnchor = nullptr; targetPath.clear(); anchor = unresolvedMember; path = path.slice(1); } continue; } break; case ConstraintLocator::Load: case ConstraintLocator::RvalueAdjustment: case ConstraintLocator::ScalarToTuple: // Loads, rvalue adjustment, and scalar-to-tuple conversions are implicit. path = path.slice(1); continue; case ConstraintLocator::NamedTupleElement: case ConstraintLocator::TupleElement: // Extract tuple element. if (auto tupleExpr = dyn_cast(anchor)) { // Append this extraction to the target locator path. if (targetAnchor) { targetPath.push_back(path[0]); } anchor = tupleExpr->getElement(path[0].getValue()); path = path.slice(1); continue; } break; case ConstraintLocator::MemberRefBase: if (auto dotExpr = dyn_cast(anchor)) { // No additional target locator information. targetAnchor = nullptr; targetPath.clear(); range1 = dotExpr->getNameLoc(); anchor = dotExpr->getBase(); path = path.slice(1); continue; } break; case ConstraintLocator::InterpolationArgument: if (auto interp = dyn_cast(anchor)) { // No additional target locator information. // FIXME: Dig out the constructor we're trying to call? targetAnchor = nullptr; targetPath.clear(); anchor = interp->getSegments()[path[0].getValue()]; path = path.slice(1); continue; } break; case ConstraintLocator::NewArrayConstructor: if (auto newArray = dyn_cast(anchor)) { // No additional target locator information. // FIXME: Dig out the constructor we're trying to call? targetAnchor = nullptr; targetPath.clear(); anchor = newArray->getConstructionFunction(); path = path.slice(1); continue; } break; case ConstraintLocator::AssignSource: if (auto assign = dyn_cast(anchor)) { targetAnchor = assign->getDest(); targetPath.clear(); anchor = assign->getSrc(); path = path.slice(1); continue; } break; default: // FIXME: Lots of other cases to handle. break; } // If we get here, we couldn't simplify the path further. break; } } /// Simplify the given locator down to a specific anchor expression, /// if possible. /// /// \returns the anchor expression if it fully describes the locator, or /// null otherwise. static Expr *simplifyLocatorToAnchor(ConstraintSystem &cs, ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) return nullptr; SourceRange range1, range2; locator = simplifyLocator(cs, locator, range1, range2); if (!locator->getAnchor() || !locator->getPath().empty()) return nullptr; return locator->getAnchor(); } /// Retrieve the argument pattern for the given declaration. /// static Pattern *getParameterPattern(ValueDecl *decl) { if (auto func = dyn_cast(decl)) return func->getBodyParamPatterns()[0]; if (auto constructor = dyn_cast(decl)) return constructor->getBodyParamPatterns()[1]; if (auto subscript = dyn_cast(decl)) return subscript->getIndices(); // FIXME: Variables of function type? return nullptr; } ResolvedLocator constraints::resolveLocatorToDecl( ConstraintSystem &cs, ConstraintLocator *locator, std::function(ConstraintLocator *)> findOvlChoice) { assert(locator && "Null locator"); if (!locator->getAnchor()) return ResolvedLocator(); ValueDecl *decl = nullptr; auto anchor = locator->getAnchor(); // Unwrap any specializations, constructor calls, implicit conversions, and // '.'s. // FIXME: This is brittle. do { if (auto specialize = dyn_cast(anchor)) { anchor = specialize->getSubExpr(); continue; } if (auto implicit = dyn_cast(anchor)) { anchor = implicit->getSubExpr(); continue; } if (auto constructor = dyn_cast(anchor)) { anchor = constructor->getFn(); continue; } if (auto dotSyntax = dyn_cast(anchor)) { anchor = dotSyntax->getRHS(); continue; } if (auto dotSyntax = dyn_cast(anchor)) { anchor = dotSyntax->getFn(); continue; } break; } while (true); if (auto dre = dyn_cast(anchor)) { // Simple case: direct reference to a declaration. decl = dre->getDecl(); } else if (auto mre = dyn_cast(anchor)) { // Simple case: direct reference to a declaration. decl = mre->getMember().getDecl(); } else if (isa(anchor) || isa(anchor) || isa(anchor)) { // Overloaded and unresolved cases: find the resolved overload. auto anchorLocator = cs.getConstraintLocator(anchor); if (auto choice = findOvlChoice(anchorLocator)) { // FIXME: DeclViaDynamic if (choice->getKind() == OverloadChoiceKind::Decl) decl = choice->getDecl(); } } else if (isa(anchor)) { // Unresolved member: find the resolved overload. auto anchorLocator = cs.getConstraintLocator( anchor, ConstraintLocator::UnresolvedMember); if (auto choice = findOvlChoice(anchorLocator)) { // FIXME: DeclViaDynamic if (choice->getKind() == OverloadChoiceKind::Decl) decl = choice->getDecl(); } } else if (auto ctorRef = dyn_cast(anchor)) { decl = ctorRef->getDecl(); } // If we didn't find the declaration, we're out of luck. if (!decl) return ResolvedLocator(); // Use the declaration and the path to produce a more specific result. // FIXME: This is an egregious hack. We'd be far better off // FIXME: Perform deeper path resolution? auto path = locator->getPath(); Pattern *parameterPattern = nullptr; bool impliesFullPattern = false; while (!path.empty()) { switch (path[0].getKind()) { case ConstraintLocator::ApplyArgument: // If we're calling into something that has parameters, dig into the // actual parameter pattern. parameterPattern = getParameterPattern(decl); if (!parameterPattern) break; impliesFullPattern = true; path = path.slice(1); continue; case ConstraintLocator::TupleElement: case ConstraintLocator::NamedTupleElement: if (parameterPattern) { unsigned index = path[0].getValue(); if (auto tuple = dyn_cast( parameterPattern->getSemanticsProvidingPattern())) { parameterPattern = tuple->getFields()[index].getPattern(); impliesFullPattern = false; path = path.slice(1); continue; } parameterPattern = nullptr; } break; case ConstraintLocator::ScalarToTuple: continue; default: break; } break; } // If we have a parameter pattern that refers to a parameter, grab it. if (parameterPattern) { parameterPattern = parameterPattern->getSemanticsProvidingPattern(); if (impliesFullPattern) { if (auto tuple = dyn_cast(parameterPattern)) { if (tuple->getFields().size() == 1) { parameterPattern = tuple->getFields()[0].getPattern(); parameterPattern = parameterPattern->getSemanticsProvidingPattern(); } } } if (auto named = dyn_cast(parameterPattern)) { return ResolvedLocator(named->getDecl()); } } // Otherwise, do the best we can with the declaration we found. if (auto func = dyn_cast(decl)) return ResolvedLocator(func); if (auto constructor = dyn_cast(decl)) return ResolvedLocator(constructor); // FIXME: Deal with the other interesting cases here, e.g., // subscript declarations. return ResolvedLocator(); } /// Emit a note referring to the target of a diagnostic, e.g., the function /// or parameter being used. static void noteTargetOfDiagnostic(ConstraintSystem &cs, const Failure &failure, ConstraintLocator *targetLocator) { // If there's no anchor, there's nothing we can do. if (!targetLocator->getAnchor()) return; // Try to resolve the locator to a particular declaration. auto resolved = resolveLocatorToDecl(cs, targetLocator, [&](ConstraintLocator *locator) -> Optional { for (auto resolved = failure.getResolvedOverloadSets(); resolved; resolved = resolved->Previous) { if (resolved->Locator == locator) return resolved->Choice; } return Nothing; }); // We couldn't resolve the locator to a declaration, so we're done. if (!resolved) return; switch (resolved.getKind()) { case ResolvedLocatorKind::Unresolved: // Can't emit any diagnostic here. return; case ResolvedLocatorKind::Function: { auto name = resolved.getDecl()->getName(); cs.getTypeChecker().diagnose(resolved.getDecl(), name.isOperator()? diag::note_call_to_operator : diag::note_call_to_func, resolved.getDecl()->getName()); return; } case ResolvedLocatorKind::Constructor: // FIXME: Specialize for implicitly-generated constructors. cs.getTypeChecker().diagnose(resolved.getDecl(), diag::note_call_to_initializer); return; case ResolvedLocatorKind::Parameter: cs.getTypeChecker().diagnose(resolved.getDecl(), diag::note_init_parameter, resolved.getDecl()->getName()); return; } } /// \brief Emit a diagnostic for the given failure. /// /// \param cs The constraint system in which the diagnostic was generated. /// \param failure The failure to emit. /// /// \returns true if the diagnostic was emitted successfully. static bool diagnoseFailure(ConstraintSystem &cs, Failure &failure) { // If there's no anchor, we have no location information to use when emitting // the diagnostic. if (!failure.getLocator() || !failure.getLocator()->getAnchor()) return false; SourceRange range1, range2; ConstraintLocator *targetLocator; auto locator = simplifyLocator(cs, failure.getLocator(), range1, range2, &targetLocator); auto &tc = cs.getTypeChecker(); auto anchor = locator->getAnchor(); auto loc = anchor->getLoc(); switch (failure.getKind()) { case Failure::TupleSizeMismatch: { auto tuple1 = failure.getFirstType()->castTo(); auto tuple2 = failure.getSecondType()->castTo(); tc.diagnose(loc, diag::invalid_tuple_size, tuple1, tuple2, tuple1->getFields().size(), tuple2->getFields().size()) .highlight(range1).highlight(range2); break; } case Failure::TupleUnused: tc.diagnose(loc, diag::invalid_tuple_element_unused, failure.getFirstType(), failure.getSecondType()) .highlight(range1).highlight(range2); break; case Failure::TypesNotEqual: case Failure::TypesNotSubtypes: case Failure::TypesNotConvertible: case Failure::TypesNotConstructible: case Failure::FunctionTypesMismatch: tc.diagnose(loc, diag::invalid_relation, failure.getKind() - Failure::TypesNotEqual, failure.getFirstType(), failure.getSecondType()) .highlight(range1).highlight(range2); if (targetLocator) noteTargetOfDiagnostic(cs, failure, targetLocator); break; case Failure::DoesNotHaveMember: case Failure::DoesNotHaveNonMutatingMember: if (auto moduleTy = failure.getFirstType()->getAs()) { tc.diagnose(loc, diag::no_member_of_module, moduleTy->getModule()->Name, failure.getName()) .highlight(range1).highlight(range2); } else { bool IsNoMember = failure.getKind() == Failure::DoesNotHaveMember; tc.diagnose(loc, IsNoMember ? diag::does_not_have_member : diag::does_not_have_non_mutating_member, failure.getFirstType(), failure.getName()) .highlight(range1).highlight(range2); } break; case Failure::DoesNotConformToProtocol: // FIXME: Probably want to do this within the actual solver, because at // this point it's too late to actually recover fully. tc.conformsToProtocol(failure.getFirstType(), failure.getSecondType()->castTo() ->getDecl(), cs.DC, nullptr, loc); if (targetLocator) noteTargetOfDiagnostic(cs, failure, targetLocator); break; case Failure::IsForbiddenLValue: if (auto iotTy = failure.getSecondType()->getAs()) { tc.diagnose(loc, diag::reference_non_inout, iotTy->getObjectType()) .highlight(range1).highlight(range2); return true; } // FIXME: diagnose other cases return false; default: // FIXME: Handle all failure kinds return false; } return true; } /// \brief Determine the number of distinct overload choices in the /// provided set. static unsigned countDistinctOverloads(ArrayRef choices) { llvm::SmallPtrSet uniqueChoices; unsigned result = 0; for (auto choice : choices) { if (uniqueChoices.insert(choice.getOpaqueChoiceSimple())) ++result; } return result; } /// \brief Determine the name of the overload in a set of overload choices. static Identifier getOverloadChoiceName(ArrayRef choices) { for (auto choice : choices) { if (choice.getKind() == OverloadChoiceKind::Decl) return choice.getDecl()->getName(); } return Identifier(); } bool diagnoseAmbiguity(ConstraintSystem &cs, ArrayRef solutions) { // Produce a diff of the solutions. SolutionDiff diff(solutions); // Find the locators which have the largest numbers of distinct overloads. SmallVector mostDistinctOverloads; unsigned maxDistinctOverloads = 0; for (unsigned i = 0, n = diff.overloads.size(); i != n; ++i) { auto &overload = diff.overloads[i]; // If we can't resolve the locator to an anchor expression with no path, // we can't diagnose this well. if (!simplifyLocatorToAnchor(cs, overload.locator)) continue; // If we don't have a name to hang on to, it'll be hard to diagnose this // overload. if (getOverloadChoiceName(overload.choices).empty()) continue; unsigned distinctOverloads = countDistinctOverloads(overload.choices); // We need at least two overloads to make this interesting. if (distinctOverloads < 2) continue; // If we have more distinct overload choices for this locator than for // prior locators, just keep this locator. if (distinctOverloads > maxDistinctOverloads) { maxDistinctOverloads = distinctOverloads; mostDistinctOverloads.clear(); mostDistinctOverloads.push_back(i); continue; } // If we have as many distinct overload choices for this locator as // the best so far, add this locator to the set. if (distinctOverloads == maxDistinctOverloads) { mostDistinctOverloads.push_back(i); continue; } // We have better results. Ignore this one. } // FIXME: Should be able to pick the best locator, e.g., based on some // depth-first numbering of expressions. if (mostDistinctOverloads.size() == 1) { auto &overload = diff.overloads[mostDistinctOverloads[0]]; auto name = getOverloadChoiceName(overload.choices); auto anchor = simplifyLocatorToAnchor(cs, overload.locator); // Emit the ambiguity diagnostic. auto &tc = cs.getTypeChecker(); tc.diagnose(anchor->getLoc(), name.isOperator() ? diag::ambiguous_operator_ref : diag::ambiguous_decl_ref, name); // Emit candidates. for (auto choice : overload.choices) { switch (choice.getKind()) { case OverloadChoiceKind::Decl: case OverloadChoiceKind::DeclViaDynamic: case OverloadChoiceKind::TypeDecl: // FIXME: show deduced types, etc, etc. tc.diagnose(choice.getDecl(), diag::found_candidate); break; case OverloadChoiceKind::BaseType: case OverloadChoiceKind::TupleIndex: // FIXME: Actually diagnose something here. break; } } return true; } // FIXME: If we inferred different types for literals (for example), // could diagnose ambiguity that way as well. return false; } Constraint *getDisjunctionChoice(Constraint *constraint, ConstraintKind kind) { if (constraint->getKind() != ConstraintKind::Disjunction) { return nullptr; } auto nestedConstraints = constraint->getNestedConstraints(); for (auto nestedConstraint : nestedConstraints) { if (nestedConstraint->getKind() == kind) return nestedConstraint; } return nullptr; } Constraint *getComponentConstraint(Constraint *constraint) { if (constraint->getKind() != ConstraintKind::Disjunction) { return constraint; } return constraint->getNestedConstraints().front(); } void ConstraintSystem::diagnoseFailureFromConstraints(Expr *expr) { // If we've been asked for more detailed type-check diagnostics, mine the // system's active and inactive constraints for information on why we could // not find a solution. Constraint *conversionConstraint = nullptr; Constraint *overloadConstraint = nullptr; Constraint *fallbackConstraint = nullptr; Constraint *activeConformanceConstraint = nullptr; Constraint *valueMemberConstraint = nullptr; Constraint *argumentConstraint = nullptr; if(!ActiveConstraints.empty()) { // If any active conformance constraints are in the system, we know that // any inactive constraints are in its service. Capture the constraint and // present this information to the user. auto *constraint = &ActiveConstraints.front(); activeConformanceConstraint = getComponentConstraint(constraint); } while (!InactiveConstraints.empty()) { auto *constraint = &InactiveConstraints.front(); // Capture the first non-disjunction constraint we find. We'll use this // if we can't find a clearer reason for the failure. if (!fallbackConstraint && (constraint->getKind() != ConstraintKind::Disjunction)) { fallbackConstraint = constraint; } // Failed binding constraints point to a missing member. if (!valueMemberConstraint && (constraint->getKind() == ConstraintKind::ValueMember)) { valueMemberConstraint = constraint; } // A missed argument conversion can result in better error messages when // a user passes the wrong arguments to a function application. if (!argumentConstraint) { argumentConstraint = getDisjunctionChoice(constraint, ConstraintKind:: ArgumentTupleConversion); } // Overload resolution failures are often nicely descriptive, so store // off the first one we find. if (!overloadConstraint) { overloadConstraint = getDisjunctionChoice(constraint, ConstraintKind::BindOverload); } // Conversion constraints are also nicely descriptive, so we'll grab the // first one of those as well. if (!conversionConstraint && (constraint->getKind() == ConstraintKind::Conversion || constraint->getKind() == ConstraintKind::ArgumentTupleConversion)) { conversionConstraint = constraint; } InactiveConstraints.pop_front(); } // If no more descriptive constraint was found, use the fallback constraint. if (!(conversionConstraint || overloadConstraint)) { conversionConstraint = fallbackConstraint; } if (argumentConstraint) { TC.diagnose(expr->getLoc(), diag::could_not_convert_argument, argumentConstraint->getFirstType(), argumentConstraint->getSecondType()); } else if (valueMemberConstraint) { auto memberName = valueMemberConstraint->getMember().getBaseName().str(); TC.diagnose(expr->getLoc(), diag::could_not_find_member, memberName); } else if (activeConformanceConstraint) { auto type = activeConformanceConstraint->getSecondType(); TC.diagnose(expr->getLoc(), diag::does_not_conform_to_constraint, type) .highlight(expr->getSourceRange()); } else if (overloadConstraint) { // In the absense of a better conversion constraint failure, point out the // inability to find an appropriate overload. auto overloadChoice = overloadConstraint->getOverloadChoice(); auto overloadName = overloadChoice.getDecl()->getName(); TC.diagnose(expr->getLoc(), diag::cannot_find_appropriate_overload, overloadName.str()); } else if (conversionConstraint) { // Otherwise, if we have a conversion constraint, use that as the basis for // the diagnostic. auto locator = conversionConstraint->getLocator(); auto anchor = locator ? locator->getAnchor() : expr; auto type1 = expr->getType(); auto type2 = conversionConstraint->getSecondType(); if (auto typeVariableType = dyn_cast(type2.getPointer())) { SmallVector bindings; this->getComputedBindings(typeVariableType, bindings); auto binding = bindings.size() ? bindings.front() : Type(); if (!binding.isNull()) { if (binding.getPointer() != type1.getPointer()) type2 = binding; } else { auto impl = typeVariableType->getImpl(); if (auto archetypeType = impl.getArchetype()) { type2 = archetypeType; } else { auto implAnchor = impl.getLocator()->getAnchor(); auto anchorType = implAnchor->getType(); // Don't re-substitute an opened type variable for itself. if (anchorType.getPointer() != type1.getPointer()) type2 = anchorType; } } } if (auto typeVariableType = dyn_cast(type1.getPointer())) { SmallVector bindings; this->getComputedBindings(typeVariableType, bindings); for (auto binding : bindings) { if (type2.getPointer() != binding.getPointer()) { type1 = binding; break; } } } TC.diagnose(anchor->getLoc(), diag::cannot_find_conversion, type1, type2) .highlight(expr->getSourceRange()); } else { // FIXME: Raise an assertion instead. TC.diagnose(expr->getLoc(), diag::constraint_type_check_fail) .highlight(expr->getSourceRange()); } } bool ConstraintSystem::salvage(SmallVectorImpl &viable, Expr *expr) { // If there were any unavoidable failures, emit the first one we can. if (!unavoidableFailures.empty()) { for (auto failure : unavoidableFailures) { if (diagnoseFailure(*this, *failure)) return true; } // FIXME: Crappy generic diagnostic. TC.diagnose(expr->getLoc(), diag::constraint_type_check_fail) .highlight(expr->getSourceRange()); return true; } // There were no unavoidable failures, so attempt to solve again, capturing // any failures that come from our attempts to select overloads or bind // type variables. { viable.clear(); // Set up solver state. SolverState state(*this); state.recordFailures = true; this->solverState = &state; // Solve the system. solve(viable); // Check whether we have a best solution; this can happen if we found // a series of fixes that worked. if (auto best = findBestSolution(viable, /*minimize=*/true)) { if (*best != 0) viable[0] = std::move(viable[*best]); viable.erase(viable.begin() + 1, viable.end()); return false; } // FIXME: If we were able to actually fix things along the way, // we may have to hunt for the best solution. For now, we don't care. // If there are multiple solutions, try to diagnose an ambiguity. if (viable.size() > 1) { if (getASTContext().LangOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); log << "---Ambiguity error: " << viable.size() << " solutions found---\n"; int i = 0; for (auto &solution : viable) { log << "---Ambiguous solution #" << i++ << "---\n"; solution.dump(&TC.Context.SourceMgr, log); log << "\n"; } } if (diagnoseAmbiguity(*this, viable)) { return true; } } // Remove the solver state. this->solverState = nullptr; // Fall through to produce diagnostics. } if (failures.size() == 1) { auto &failure = unavoidableFailures.empty()? *failures.begin() : **unavoidableFailures.begin(); if (diagnoseFailure(*this, failure)) return true; } // If all else fails, attempt to diagnose the failure by looking through the // system's constraints. this->diagnoseFailureFromConstraints(expr); return true; }