bool ConstraintSystem::repairFailures()

in lib/Sema/CSSimplify.cpp [3928:5317]


bool ConstraintSystem::repairFailures(
    Type lhs, Type rhs, ConstraintKind matchKind,
    SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
    ConstraintLocatorBuilder locator) {
  SmallVector<LocatorPathElt, 4> path;
  auto anchor = locator.getLocatorParts(path);

  // If there is a missing explicit call it could be:
  //
  // a). Contextual e.g. `let _: R = foo`
  // b). Argument is a function value passed to parameter
  //     which expects its result type e.g. `foo(bar)`
  // c). Assigment destination type matches return type of
  //     of the function value e.g. `foo = bar` or `foo = .bar`
  auto repairByInsertingExplicitCall = [&](Type srcType, Type dstType) -> bool {
    auto fnType = srcType->getAs<FunctionType>();
    if (!fnType)
      return false;

    // If the locator isn't anchored at an expression, or the expression is
    // implicit, don't try to insert an explicit call into the source code.
    auto *loc = getConstraintLocator(locator);
    auto *anchor = getAsExpr(simplifyLocatorToAnchor(loc));
    if (!anchor || anchor->isImplicit())
      return false;

    if (isArgumentOfPatternMatchingOperator(loc))
      return false;

    // Don't attempt this fix for trailing closures.
    if (auto elt = loc->getLastElementAs<LocatorPathElt::ApplyArgToParam>()) {
      auto argumentList = getArgumentList(loc);
      if (argumentList->isTrailingClosureIndex(elt->getArgIdx()))
        return false;
    }

    // If argument is a function type and all of its parameters have
    // default values, let's see whether error is related to missing
    // explicit call.
    if (fnType->getNumParams() > 0) {
      auto overload = findSelectedOverloadFor(anchor);
      if (!(overload && overload->choice.isDecl()))
        return false;

      const auto &choice = overload->choice;
      ParameterListInfo info(fnType->getParams(), choice.getDecl(),
                             hasAppliedSelf(*this, choice));

      if (llvm::any_of(indices(fnType->getParams()),
                       [&info](const unsigned idx) {
                         return !info.hasDefaultArgument(idx);
                       }))
        return false;
    }

    auto resultType = fnType->getResult();
    // If this is situation like `x = { ... }` where closure results in
    // `Void`, let's not suggest to call the closure, because it's most
    // likely not intended.
    if (auto *assignment = getAsExpr<AssignExpr>(anchor)) {
      if (isa<ClosureExpr>(assignment->getSrc()) && resultType->isVoid())
        return false;
    }

    // If left-hand side is a function type but right-hand
    // side isn't, let's check it would be possible to fix
    // this by forming an explicit call.
    auto convertTo = dstType->lookThroughAllOptionalTypes();
    // Right-hand side can't be - a function, a type variable or dependent
    // member, or `Any` (if function conversion to `Any` didn't succeed there
    // is something else going on e.g. problem with escapiness).
    if (convertTo->is<FunctionType>() || convertTo->isTypeVariableOrMember() ||
        convertTo->isAny())
      return false;

    ConstraintKind matchKind;
    if (resultType->is<TypeVariableType>()) {
      matchKind = ConstraintKind::Equal;
    } else {
      matchKind = ConstraintKind::Conversion;
    }

    auto result = matchTypes(resultType, dstType, matchKind,
                             TypeMatchFlags::TMF_ApplyingFix, locator);

    if (result.isSuccess()) {
      conversionsOrFixes.push_back(
          InsertExplicitCall::create(*this, getConstraintLocator(locator)));
      return true;
    }

    return false;
  };

  auto repairByAnyToAnyObjectCast = [&](Type lhs, Type rhs) -> bool {
    if (!(lhs->isAny() && rhs->isAnyObject()))
      return false;

    conversionsOrFixes.push_back(MissingConformance::forContextual(
        *this, lhs, rhs, getConstraintLocator(locator)));
    return true;
  };

  auto repairByTreatingRValueAsLValue = [&](Type lhs, Type rhs) -> bool {
    if (!lhs->is<LValueType>() &&
        (rhs->is<LValueType>() || rhs->is<InOutType>())) {
      // Conversion from l-value to inout in an operator argument
      // position (which doesn't require explicit `&`) decays into
      // a `Bind` of involved object types, same goes for explicit
      // `&` conversion from l-value to inout type.
      //
      // In case of regular argument conversion although explicit `&`
      // is required we still want to diagnose the problem as one
      // about mutability instead of suggesting to add `&` which wouldn't
      // be correct.
      auto kind = (isExpr<InOutExpr>(anchor) ||
                   (rhs->is<InOutType>() &&
                    (matchKind == ConstraintKind::ArgumentConversion ||
                     matchKind == ConstraintKind::OperatorArgumentConversion)))
                      ? ConstraintKind::Bind
                      : matchKind;

      auto result = matchTypes(lhs, rhs->getWithoutSpecifierType(), kind,
                               TMF_ApplyingFix, locator);

      if (result.isSuccess()) {
        // If left side is a hole, let's not record a fix since hole can
        // assume any type and already represents a problem elsewhere in
        // the expression.
        if (lhs->isPlaceholder())
          return true;

        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }
    }

    return false;
  };

  // Check whether given `value` type matches a `RawValue` type of
  // a given raw representable type.
  auto isValueOfRawRepresentable = [&](Type valueType,
                                       Type rawReprType) -> bool {
    // diagnostic is going to suggest failable initializer anyway.
    if (auto objType = rawReprType->getOptionalObjectType())
      rawReprType = objType;

    // If value is optional diagnostic would suggest using `Optional.map` in
    // combination with `<Type>(rawValue: ...)` initializer.
    if (auto objType = valueType->getOptionalObjectType())
      valueType = objType;

    if (rawReprType->isTypeVariableOrMember())
      return false;

    auto rawValue = isRawRepresentable(*this, rawReprType);
    if (!rawValue)
      return false;

    auto result = matchTypes(valueType, rawValue, ConstraintKind::Conversion,
                             TMF_ApplyingFix, locator);
    return !result.isFailure();
  };

  // Check whether given `rawReprType` does indeed conform to `RawRepresentable`
  // and if so check that given `expectedType` matches its `RawValue` type. If
  // that condition holds add a tailored fix which is going to suggest to
  // explicitly construct a raw representable type from a given value type.
  auto repairByConstructingRawRepresentableType =
      [&](Type expectedType, Type rawReprType) -> bool {
    if (!isValueOfRawRepresentable(expectedType, rawReprType))
      return false;

    conversionsOrFixes.push_back(ExplicitlyConstructRawRepresentable::create(
        *this, rawReprType, expectedType, getConstraintLocator(locator)));
    return true;
  };

  // Check whether given `rawReprType` does indeed conform to `RawRepresentable`
  // and if so check that given `expectedType` matches its `RawValue` type. If
  // that condition holds add a tailored fix which is going to suggest to
  // use `.rawValue` associated with given raw representable type to match
  // given expected type.
  auto repairByUsingRawValueOfRawRepresentableType =
      [&](Type rawReprType, Type expectedType) -> bool {
    if (!isValueOfRawRepresentable(expectedType, rawReprType))
      return false;

    conversionsOrFixes.push_back(UseRawValue::create(
        *this, rawReprType, expectedType, getConstraintLocator(locator)));
    return true;
  };

  auto hasConversionOrRestriction = [&](ConversionRestrictionKind kind) {
    return llvm::any_of(conversionsOrFixes,
                        [kind](const RestrictionOrFix correction) {
                          if (auto restriction = correction.getRestriction())
                            return restriction == kind;
                          return false;
                        });
  };

  if (repairArrayLiteralUsedAsDictionary(*this, lhs, rhs, matchKind,
                                         conversionsOrFixes,
                                         getConstraintLocator(locator)))
    return true;

  if (path.empty()) {
    if (!anchor)
      return false;

    if (auto *coercion = getAsExpr<CoerceExpr>(anchor)) {
      // Coercion from T.Type to T.Protocol.
      if (hasConversionOrRestriction(
              ConversionRestrictionKind::MetatypeToExistentialMetatype))
        return false;

      if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
        return false;

      // Let's check whether the sub-expression is an optional type which
      // is possible to unwrap (either by force or `??`) to satisfy the cast,
      // otherwise we'd have to fallback to force downcast.
      if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                  conversionsOrFixes,
                                  getConstraintLocator(coercion->getSubExpr())))
        return true;

      // If the result type of the coercion has an value to optional conversion
      // we can instead suggest the conditional downcast as it is safer in
      // situations like conditional binding.
      auto useConditionalCast =
          llvm::any_of(ConstraintRestrictions, [&](const auto &restriction) {
            Type type1, type2;
            std::tie(type1, type2) = restriction.first;
            auto restrictionKind = restriction.second;

            if (restrictionKind != ConversionRestrictionKind::ValueToOptional)
              return false;

            return rhs->isEqual(type1);
          });

      // Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?').
      if (auto *coerceToCheckCastFix =
              CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast,
                                           getConstraintLocator(locator))) {
        conversionsOrFixes.push_back(coerceToCheckCastFix);
        return true;
      }

      // If it has a deep equality restriction, defer the diagnostic to
      // GenericMismatch.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
          !hasConversionOrRestriction(
              ConversionRestrictionKind::OptionalToOptional)) {
        return false;
      }

      if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
        return false;

      auto *fix = ContextualMismatch::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
      return true;
    }

    // This could be:
    // - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`.
    // - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are
    //   immutable or a subscript e.g. `foo["bar"]! = 42`.
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      return true;

    // If method reference forms a value type of the key path,
    // there is going to be a constraint to match result of the
    // member lookup to the generic parameter `V` of *KeyPath<R, V>
    // type associated with key path expression, which we need to
    // fix-up here unless last component has already a invalid type or
    // instance fix recorded.
    if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) {
      auto i = kpExpr->getComponents().size() - 1;
      auto lastCompLoc = getConstraintLocator(
          locator.withPathElement(LocatorPathElt::KeyPathComponent(i)));
      if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember))
        return true;

      auto lastComponentType = lhs->lookThroughAllOptionalTypes();
      auto keyPathResultType = rhs->lookThroughAllOptionalTypes();

      // Propagate contextual information from/to keypath result type.
      (void)matchTypes(lastComponentType, keyPathResultType, matchKind,
                       TMF_ApplyingFix, getConstraintLocator(locator));

      conversionsOrFixes.push_back(IgnoreContextualType::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
      return true;
    }

    if (auto *ODRE = getAsExpr<OverloadedDeclRefExpr>(anchor)) {
      if (lhs->is<LValueType>()) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }
    }

    if (auto *OEE = getAsExpr<OptionalEvaluationExpr>(anchor)) {
      // If concrete type of the sub-expression can't be converted to the
      // type associated with optional evaluation result it could only be
      // contextual mismatch where type of the top-level expression
      // comes from contextual type or its parent expression.
      //
      // Because result type of the optional evaluation is supposed to
      // represent the type of its sub-expression with added level of
      // optionality if needed.
      auto contextualTy = simplifyType(rhs)->getOptionalObjectType();
      if (!lhs->getOptionalObjectType() && !lhs->hasTypeVariable() &&
          contextualTy && !contextualTy->isTypeVariableOrMember()) {
        conversionsOrFixes.push_back(IgnoreContextualType::create(
            *this, lhs, rhs, getConstraintLocator(OEE->getSubExpr())));
        return true;
      }
    }

    if (auto *AE = getAsExpr<AssignExpr>(anchor)) {
      if (repairByInsertingExplicitCall(lhs, rhs))
        return true;

      if (isa<InOutExpr>(AE->getSrc())) {
        conversionsOrFixes.push_back(
            RemoveAddressOf::create(*this, lhs, rhs,
                                    getConstraintLocator(locator)));
        return true;
      }

      if (repairByAnyToAnyObjectCast(lhs, rhs))
        return true;

      if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
        return true;

      // If destination is `AnyObject` it means that source doesn't conform.
      if (rhs->getWithoutSpecifierType()
              ->lookThroughAllOptionalTypes()
              ->isAnyObject()) {
        conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        return true;
      }

      // An attempt to assign `Int?` to `String?`.
      if (hasConversionOrRestriction(
              ConversionRestrictionKind::OptionalToOptional)) {
        conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        return true;
      }

      // If we are trying to assign e.g. `Array<Int>` to `Array<Float>` let's
      // give solver a chance to determine which generic parameters are
      // mismatched and produce a fix for that.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
        return false;

      // If the situation has to do with protocol composition types and
      // destination doesn't have one of the conformances e.g. source is
      // `X & Y` but destination is only `Y` or vice versa, there is a
      // tailored "missing conformance" fix for that.
      if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
        return false;

      if (hasConversionOrRestriction(
              ConversionRestrictionKind::MetatypeToExistentialMetatype) ||
          hasConversionOrRestriction(
              ConversionRestrictionKind::ExistentialMetatypeToMetatype) ||
          hasConversionOrRestriction(ConversionRestrictionKind::Superclass)) {
        conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        return true;
      }

      if (hasConversionOrRestriction(
              ConversionRestrictionKind::ValueToOptional)) {
        lhs = lhs->lookThroughAllOptionalTypes();
        rhs = rhs->lookThroughAllOptionalTypes();

        // If both object types are functions, let's allow the solver to
        // structurally compare them before trying to fix anything.
        if (lhs->is<FunctionType>() && rhs->is<FunctionType>())
          return false;

        // If either object type is a generic, nominal or existential type
        // it means that follow-up to value-to-optional is going to be:
        //
        // 1. "deep equality" check, which is handled by generic argument(s)
        //    or contextual mismatch fix, or
        // 2. "existential" check, which is handled by a missing conformance
        //    fix.
        if ((lhs->is<BoundGenericType>() && rhs->is<BoundGenericType>()) ||
            (lhs->is<NominalType>() && rhs->is<NominalType>()) ||
            rhs->isAnyExistentialType())
          return false;
      }

      auto *destExpr = AE->getDest();
      // Literal expression as well as call/operator application can't be
      // used as an assignment destination because resulting type is immutable.
      if (isa<ApplyExpr>(destExpr) || isa<LiteralExpr>(destExpr)) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }

      // If destination has a function type, it might either be
      // a property with a function type or a method reference,
      // e.g. `foo.bar = 42` neither can be used if the destination
      // is not l-value.
      auto destType = getType(destExpr);
      auto destTypeVar = destType->getAs<TypeVariableType>();
      bool destIsOrCanBindToLValue =
          destType->is<LValueType>() ||
          (destTypeVar && destTypeVar->getImpl().canBindToLValue());
      if (!destIsOrCanBindToLValue && rhs->is<FunctionType>()) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }

      if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                  conversionsOrFixes, locator))
        return true;

      // `rhs` - is an assignment destination and `lhs` is its source.
      if (repairByConstructingRawRepresentableType(lhs, rhs))
        return true;

      if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
        return true;

      // Let's try to match source and destination types one more
      // time to see whether they line up, if they do - the problem is
      // related to immutability, otherwise it's a type mismatch.
      auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion,
                               TMF_ApplyingFix, locator);
      
      auto *loc = getConstraintLocator(locator);
      if (destIsOrCanBindToLValue || result.isFailure()) {
        // Let this assignment failure be diagnosed by the
        // AllowTupleTypeMismatch fix already recorded.
        if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch))
          return true;

        conversionsOrFixes.push_back(
            IgnoreAssignmentDestinationType::create(*this, lhs, rhs, loc));
      } else {
        conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc));
      }

      return true;
    }

    return false;
  }

  auto elt = path.back();
  switch (elt.getKind()) {
  case ConstraintLocator::LValueConversion: {
    // Ignore l-value conversion element since it has already
    // played its role.
    path.pop_back();
    // If this is a contextual mismatch between l-value types e.g.
    // `@lvalue String vs. @lvalue Int`, let's pretend that it's okay.
    if (!path.empty()) {
      if (path.back().is<LocatorPathElt::ContextualType>()) {
        auto *locator = getConstraintLocator(anchor, path.back());
        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, locator));
        break;
      }

      // If this is a function type param type mismatch in any position,
      // the mismatch we want to report is for the whole structural type.
      auto last = std::find_if(
          path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
            return elt.is<LocatorPathElt::FunctionArgument>();
          });

      if (last != path.rend())
        break;
    }

    LLVM_FALLTHROUGH;
  }

  case ConstraintLocator::ApplyArgToParam: {
    auto loc = getConstraintLocator(locator);

    // Don't attempt to fix an argument being passed to a
    // _OptionalNilComparisonType parameter. Such an overload should only take
    // effect when a nil literal is used in valid code, and doesn't offer any
    // useful fixes for invalid code.
    if (auto *nominal = rhs->getAnyNominal()) {
      if (nominal->isStdlibDecl() &&
          nominal->getName() == getASTContext().Id_OptionalNilComparisonType) {
        return false;
      }
    }

    if (isForCodeCompletion()) {
      // If the argument contains the code completion location, the user has not
      // finished typing out this argument yet. Treat the mismatch as valid so
      // we don't penalize this solution.
      if (auto *arg = getAsExpr(simplifyLocatorToAnchor(loc))) {
        // Ignore synthesized args like $match in implicit pattern match
        // operator calls. Their source location is usually the same as the
        // other (explicit) argument's so source range containment alone isn't
        // sufficient.
        bool isSynthesizedArg = arg->isImplicit() && isa<DeclRefExpr>(arg);
        if (!isSynthesizedArg && containsCodeCompletionLoc(arg) &&
            !lhs->isVoid() && !lhs->isUninhabited())
          return true;
      }
    }

    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    bool isPatternMatching = isArgumentOfPatternMatchingOperator(loc);
    // Let's not suggest force downcasts in pattern-matching context.
    if (!isPatternMatching &&
        repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
      break;

    // Argument is a r-value but parameter expects an l-value e.g.
    //
    // func foo(_ x: inout Int) {}
    // let x: Int = 42
    // foo(x) // `x` can't be converted to `inout Int`.
    //
    // This has to happen before checking for optionality mismatch
    // because otherwise `Int? arg conv inout Int` is going to get
    // fixed as 2 fixes - "force unwrap" + r-value -> l-value mismatch.
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    // If the problem is related to missing unwrap, there is a special
    // fix for that.
    if (lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) {
      // If this is an attempt to check whether optional conforms to a
      // particular protocol, let's do that before attempting to force
      // unwrap the optional.
      if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
        break;

      auto result = matchTypes(lhs->getOptionalObjectType(), rhs, matchKind,
                               TMF_ApplyingFix, locator);

      if (result.isSuccess()) {
        conversionsOrFixes.push_back(
            ForceOptional::create(*this, lhs, rhs, loc));
        break;
      }
    }

    // There is no subtyping between object types of inout argument/parameter.
    if (elt.getKind() == ConstraintLocator::LValueConversion) {
      auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion,
                               TMF_ApplyingFix, locator);

      ConstraintFix *fix = nullptr;
      if (result.isFailure()) {
        // If this is a "destination" argument to a mutating operator
        // like `+=`, let's consider it contextual and only attempt
        // to fix type mismatch on the "source" right-hand side of
        // such operators.
        if (isOperatorArgument(loc) &&
            loc->findLast<LocatorPathElt::ApplyArgToParam>()->getArgIdx() == 0)
          break;

        fix = AllowArgumentMismatch::create(*this, lhs, rhs, loc);
      } else {
        fix = AllowInOutConversion::create(*this, lhs, rhs, loc);
      }

      conversionsOrFixes.push_back(fix);
      break;
    }

    if (elt.getKind() != ConstraintLocator::ApplyArgToParam)
      break;

    // If argument in l-value type and parameter is `inout` or a pointer,
    // let's see if it's generic parameter matches and suggest adding explicit
    // `&`.
    if (lhs->is<LValueType>() &&
        (rhs->is<InOutType>() || rhs->getAnyPointerElementType())) {
      auto baseType = rhs->is<InOutType>() ? rhs->getInOutObjectType()
                                           : rhs->getAnyPointerElementType();

      // Let's use `BindToPointer` constraint here to match up base types
      // of implied `inout` argument and `inout` or pointer parameter.
      // This helps us to avoid implicit conversions associated with
      // `ArgumentConversion` constraint.
      auto result = matchTypes(lhs->getRValueType(), baseType,
                               ConstraintKind::BindToPointerType,
                               TypeMatchFlags::TMF_ApplyingFix, locator);

      if (result.isSuccess()) {
        conversionsOrFixes.push_back(AddAddressOf::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        break;
      }
    }

    // If the argument is inout and the parameter is not inout or a pointer,
    // suggest removing the &.
    if (lhs->is<InOutType>() && !rhs->is<InOutType>()) {
      auto objectType = rhs->lookThroughAllOptionalTypes();
      if (!objectType->getAnyPointerElementType()) {
        auto result = matchTypes(lhs->getInOutObjectType(), rhs,
                                 ConstraintKind::ArgumentConversion,
                                 TypeMatchFlags::TMF_ApplyingFix, locator);

        if (result.isSuccess()) {
          conversionsOrFixes.push_back(RemoveAddressOf::create(
              *this, lhs, rhs, getConstraintLocator(locator)));
          break;
        }
      }
    }

    // If parameter type is `Any` the problem might be related to
    // invalid escapiness of the argument.
    if (rhs->isAny())
      break;

    // If there are any restrictions here we need to wait and let
    // `simplifyRestrictedConstraintImpl` handle them.
    if (llvm::any_of(conversionsOrFixes,
                     [](const RestrictionOrFix &correction) {
                       return bool(correction.getRestriction());
                     }))
      break;

    if (auto *fix = fixPropertyWrapperFailure(
            *this, lhs, loc,
            [&](SelectedOverload overload, VarDecl *decl, Type newBase) {
              // FIXME: There is currently no easy way to avoid attempting
              // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag.
              llvm::SaveAndRestore<ConstraintSystemOptions> options(
                  Options, Options - ConstraintSystemFlags::AllowFixes);

              TypeMatchOptions flags;
              return matchTypes(newBase, rhs, ConstraintKind::Subtype, flags,
                                getConstraintLocator(locator))
                  .isSuccess();
            },
            rhs)) {
      conversionsOrFixes.push_back(fix);
      break;
    }

    // If this is an implicit 'something-to-pointer' conversion
    // it's going to be diagnosed by specialized fix which deals
    // with generic argument mismatches.
    if (matchKind == ConstraintKind::BindToPointerType) {
      if (!rhs->isPlaceholder())
        break;
    }

    // If this is a ~= operator implicitly generated by pattern matching
    // let's not try to fix right-hand side of the operator because it's
    // a correct contextual type.
    if (isPatternMatching &&
        elt.castTo<LocatorPathElt::ApplyArgToParam>().getParamIdx() == 1)
      break;

    if (auto *fix = ExpandArrayIntoVarargs::attempt(*this, lhs, rhs, locator)) {
      conversionsOrFixes.push_back(fix);
      break;
    }

    // If parameter is a collection but argument is not, let's try
    // to try and match collection element type to the argument to
    // produce better diagnostics e.g.:
    //
    // ```
    // func foo<T>(_: [T]) {}
    // foo(1) // expected '[Int]', got 'Int'
    // ```
    if (rhs->isKnownStdlibCollectionType()) {
      std::function<Type(Type)> getArrayOrSetType = [&](Type type) -> Type {
        if (auto eltTy = isArrayType(type))
          return getArrayOrSetType(*eltTy);

        if (auto eltTy = isSetType(type))
          return getArrayOrSetType(*eltTy);

        return type;
      };

      // Let's ignore any optional types associated with element e.g. `[T?]`
      auto rhsEltTy = getArrayOrSetType(rhs)->lookThroughAllOptionalTypes();
      (void)matchTypes(lhs, rhsEltTy, ConstraintKind::Equal, TMF_ApplyingFix,
                       locator);
    }

    // If either type has a placeholder, consider this fixed.
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // `lhs` - is an argument and `rhs` is a parameter type.
    if (repairByConstructingRawRepresentableType(lhs, rhs))
      break;

    if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
      break;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      break;

    {
      auto *calleeLocator = getCalleeLocator(loc);
      if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) {
        if (auto overload = findSelectedOverloadFor(calleeLocator)) {
          if (auto choice = overload->choice.getDeclOrNull()) {
            // If this is an argument of a symetric function/operator let's
            // not fix any position rather than first because we'd just end
            // up with ambiguity instead of reporting an actual problem with
            // mismatched type since each argument can have district bindings.
            if (auto *AFD = dyn_cast<AbstractFunctionDecl>(choice)) {
              auto *paramList = AFD->getParameters();
              auto firstParamType = paramList->get(0)->getInterfaceType();
              if (elt.castTo<LocatorPathElt::ApplyArgToParam>().getParamIdx() >
                      0 &&
                  llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool {
                    return param->getInterfaceType()->isEqual(firstParamType);
                  }))
                return true;
            }
          }
        }
      }
    }

    if (repairOutOfOrderArgumentsInBinaryFunction(*this, conversionsOrFixes,
                                                  loc))
      return true;

    // There is already a remove extraneous arguments fix recorded for this
    // apply arg to param locator, so let's skip the default argument mismatch.
    if (hasFixFor(loc, FixKind::RemoveExtraneousArguments))
      return true;

    conversionsOrFixes.push_back(
        AllowArgumentMismatch::create(*this, lhs, rhs, loc));
    break;
  }

  case ConstraintLocator::KeyPathRoot: {
    // The root mismatch is from base U? to U or a subtype of U in keypath 
    // application so let's suggest an unwrap the optional fix.
    if (auto unwrapFix = UnwrapOptionalBaseKeyPathApplication::attempt(
            *this, lhs, rhs, getConstraintLocator(locator))) {
      conversionsOrFixes.push_back(unwrapFix);
      break;
    }

    conversionsOrFixes.push_back(AllowKeyPathRootTypeMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));

    break;
  }

  case ConstraintLocator::WrappedValue: {
    conversionsOrFixes.push_back(AllowWrappedValueMismatch::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::FunctionArgument: {
    auto *argLoc = getConstraintLocator(
        locator.withPathElement(LocatorPathElt::SynthesizedArgument(0)));

    // Let's drop the last element which points to a single argument
    // and see if this is a contextual mismatch.
    path.pop_back();
    if (path.empty() ||
        !(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
          path.back().getKind() == ConstraintLocator::ContextualType))
      return false;

    auto arg = llvm::find_if(getTypeVariables(),
                             [&argLoc](const TypeVariableType *typeVar) {
                               return typeVar->getImpl().getLocator() == argLoc;
                             });

    // What we have here is a form or tuple splat with no arguments
    // demonstrated by following example:
    //
    // func foo<T: P>(_: T, _: (T.Element) -> Int) {}
    // foo { 42 }
    //
    // In cases like this `T.Element` might be resolved to `Void`
    // which means that we have to try a single empty tuple argument
    // as a narrow exception to SE-0110, see `matchFunctionTypes`.
    //
    // But if `T.Element` didn't get resolved to `Void` we'd like
    // to diagnose this as a missing argument which can't be ignored or
    // a tuple is trying to be inferred as a tuple for destructuring but
    // contextual argument does not match(in this case we remove the extra
    // closure arguments).
    if (arg != getTypeVariables().end()) {
      if (auto argToParamElt =
              path.back().getAs<LocatorPathElt::ApplyArgToParam>()) {
        auto loc = getConstraintLocator(anchor, path);
        auto closureAnchor =
            getAsExpr<ClosureExpr>(simplifyLocatorToAnchor(loc));
        if (rhs->is<TupleType>() && closureAnchor &&
            closureAnchor->getParameters()->size() > 1) {
          auto callee = getCalleeLocator(loc);
          if (auto overload = findSelectedOverloadFor(callee)) {
            auto fnType =
                simplifyType(overload->openedType)->castTo<FunctionType>();
            auto paramIdx = argToParamElt->getParamIdx();
            auto paramType = fnType->getParams()[paramIdx].getParameterType();
            if (auto paramFnType = paramType->getAs<FunctionType>()) {
              conversionsOrFixes.push_back(RemoveExtraneousArguments::create(
                  *this, paramFnType, {}, loc));
              break;
            }
          }
        }
      }

      conversionsOrFixes.push_back(AddMissingArguments::create(
          *this, {SynthesizedArg{0, AnyFunctionType::Param(*arg)}},
          getConstraintLocator(anchor, path)));
      break;
    }

    auto *parentLoc = getConstraintLocator(anchor, path);

    if ((lhs->is<InOutType>() && !rhs->is<InOutType>()) ||
        (!lhs->is<InOutType>() && rhs->is<InOutType>())) {
      // Since `FunctionArgument` as a last locator element represents
      // a single parameter of the function type involved in a conversion
      // to another function type, see `matchFunctionTypes`. If there is already
      // a fix for the this convertion, we can just ignore individual function
      // argument in-out mismatch failure by considered this fixed.
      if (hasFixFor(parentLoc))
        return true;

      // We want to call matchTypes with the default decomposition options
      // in case there are type variables that we couldn't bind due to the
      // inout attribute mismatch.
      auto result = matchTypes(lhs->getInOutObjectType(),
                               rhs->getInOutObjectType(), matchKind,
                               getDefaultDecompositionOptions(TMF_ApplyingFix),
                               locator);

      if (result.isSuccess()) {
        conversionsOrFixes.push_back(AllowInOutConversion::create(*this, lhs,
            rhs, getConstraintLocator(locator)));
        break;
      }
    }

    // In cases like this `FunctionArgument` as a last locator element
    // represents a single parameter of the function type involved in
    // a conversion to another function type, see `matchFunctionTypes`.
    if (parentLoc->isForContextualType() ||
        parentLoc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      // If either type has a placeholder, consider this fixed.
      if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
        return true;

      // If there is a fix associated with contextual conversion or
      // a function type itself, let's ignore argument failure but
      // increase a score.
      if (hasFixFor(parentLoc)) {
        increaseScore(SK_Fix);
        return true;
      }

      // Since there is only one parameter let's give it a chance to diagnose
      // a more specific error in some situations.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) ||
          hasConversionOrRestriction(ConversionRestrictionKind::Existential) ||
          hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
        break;

      conversionsOrFixes.push_back(AllowFunctionTypeMismatch::create(
          *this, lhs, rhs, parentLoc, /*index=*/0));
      break;
    }

    break;
  }

  case ConstraintLocator::TypeParameterRequirement:
  case ConstraintLocator::ConditionalRequirement: {
    // If either type has a placeholder, consider this fixed.
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // If requirement is something like `T == [Int]` let's let
    // type matcher a chance to match generic parameters before
    // recording a fix, because then we'll know exactly how many
    // generic parameters did not match.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    auto *reqLoc = getConstraintLocator(locator);

    if (isFixedRequirement(reqLoc, rhs))
      return true;

    if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path)) {
      recordFixedRequirement(reqLoc, rhs);
      conversionsOrFixes.push_back(fix);
    }
    break;
  }

  case ConstraintLocator::ClosureBody:
  case ConstraintLocator::ClosureResult: {
    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    // If we could record a generic arguments mismatch instead of this fix,
    // don't record a contextual type mismatch here.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    auto *fix = IgnoreContextualType::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::ContextualType: {
    // If either type is a placeholder, consider this fixed
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    // If either side is not yet resolved, it's too early for this fix.
    if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
      break;

    // If there is already a fix for contextual failure, let's not
    // record a duplicate one.
    if (hasFixFor(getConstraintLocator(locator)))
      return true;

    auto purpose = getContextualTypePurpose(anchor);
    if (rhs->isVoid() &&
        (purpose == CTP_ReturnStmt || purpose == CTP_ReturnSingleExpr)) {
      conversionsOrFixes.push_back(
          RemoveReturn::create(*this, lhs, getConstraintLocator(locator)));
      return true;
    }

    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    if (repairByAnyToAnyObjectCast(lhs, rhs))
      break;

    if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
      break;

    // If both types are key path, the only differences
    // between them are mutability and/or root, value type mismatch.
    if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
      auto *fix = KeyPathContextualMismatch::create(
          *this, lhs, rhs, getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
    }

    if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
        isExpr<ClosureExpr>(anchor)) {
      auto *fix = ContextualMismatch::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
    }

    if (purpose == CTP_Initialization && lhs->is<TupleType>() &&
        rhs->is<TupleType>()) {
      auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
                                                 getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
      break;
    }

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      break;

    // Let's wait until both sides are of the same optionality before
    // attempting `.rawValue` fix.
    if (hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional))
      break;

    if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
      break;

    // If there are any restrictions here we need to wait and let
    // `simplifyRestrictedConstraintImpl` handle them.
    if (llvm::any_of(conversionsOrFixes,
                     [](const RestrictionOrFix &correction) {
                       return bool(correction.getRestriction());
                     }))
      break;

    // `lhs` - is an result type and `rhs` is a contextual type.
    if (repairByConstructingRawRepresentableType(lhs, rhs))
      break;

    conversionsOrFixes.push_back(IgnoreContextualType::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::FunctionResult: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      recordAnyTypeVarAsPotentialHole(lhs);
      recordAnyTypeVarAsPotentialHole(rhs);
      return true;
    }

    auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1});
    // If this is a mismatch between contextual type and (trailing)
    // closure with explicitly specified result type let's record it
    // as contextual type mismatch.
    if (loc->isLastElement<LocatorPathElt::ContextualType>() ||
        loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      auto argument = simplifyLocatorToAnchor(loc);
      if (isExpr<ClosureExpr>(argument)) {
        auto *locator =
            getConstraintLocator(argument, ConstraintLocator::ClosureResult);

        if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                    conversionsOrFixes, locator))
          break;

        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, locator));
        break;
      }
    }

    // Handle function result coerce expression wrong type conversion.
    if (isExpr<CoerceExpr>(anchor)) {
      auto *fix =
          ContextualMismatch::create(*this, lhs, rhs, loc);
      conversionsOrFixes.push_back(fix);
      break;
    }
    LLVM_FALLTHROUGH;
  }

  case ConstraintLocator::Member:
  case ConstraintLocator::DynamicLookupResult: {
    // Most likely this is an attempt to use get-only subscript as mutating,
    // or assign a value of a result of function/member ref e.g. `foo() = 42`
    // or `foo.bar = 42`, or `foo.bar()! = 42`.
    if (repairByTreatingRValueAsLValue(rhs, lhs))
      break;

    // `apply argument` -> `arg/param compare` ->
    // `@autoclosure result` -> `function result`
    if (path.size() > 3) {
      const auto &elt = path[path.size() - 2];
      if (elt.getKind() == ConstraintLocator::AutoclosureResult &&
          repairByInsertingExplicitCall(lhs, rhs))
        return true;
    }
    break;
  }

  case ConstraintLocator::AutoclosureResult: {
    if (repairByInsertingExplicitCall(lhs, rhs))
      return true;

    auto isPointerType = [](Type type) -> bool {
      return bool(
          type->lookThroughAllOptionalTypes()->getAnyPointerElementType());
    };

    // Let's see whether this is an implicit conversion to a pointer type
    // which is invalid in @autoclosure context e.g. from `inout`, Array
    // or String.
    if (!isPointerType(lhs) && isPointerType(rhs)) {
      auto result = matchTypes(
          lhs, rhs, ConstraintKind::ArgumentConversion,
          TypeMatchFlags::TMF_ApplyingFix,
          locator.withPathElement(ConstraintLocator::FunctionArgument));

      if (result.isSuccess())
        conversionsOrFixes.push_back(AllowAutoClosurePointerConversion::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
    }

    // In situations like this:
    //
    // struct S<T> {}
    // func foo(_: @autoclosure () -> S<Int>) {}
    // foo(S<String>())
    //
    // Generic type conversion mismatch is a better fix which is going to
    // point to the generic arguments that did not align properly.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    conversionsOrFixes.push_back(AllowArgumentMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::TupleElement: {
    if (isExpr<ArrayExpr>(anchor) || isExpr<DictionaryExpr>(anchor)) {
      // If we could record a generic arguments mismatch instead of this fix,
      // don't record a ContextualMismatch here.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
        break;

      conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
      break;
    }

    // Drop the `tuple element` locator element so that all tuple element
    // mismatches within the same tuple type can be coalesced later.
    auto index = elt.getAs<LocatorPathElt::TupleElement>()->getIndex();
    path.pop_back();

    // Drop the tuple type path elements too, but extract each tuple type first.
    if (!path.empty() && path.back().is<LocatorPathElt::TupleType>()) {
      rhs = path.back().getAs<LocatorPathElt::TupleType>()->getType();
      path.pop_back();
      lhs = path.back().getAs<LocatorPathElt::TupleType>()->getType();
      path.pop_back();
    }

    auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path);

    // Let this fail if it's a contextual mismatch with sequence element types,
    // as there's a special fix for that.
    if (tupleLocator->isLastElement<LocatorPathElt::SequenceElementType>())
      break;

    // Generic argument/requirement failures have a more general fix which
    // is attached to a parent type and aggregates all argument failures
    // into a single fix.
    if (tupleLocator->isLastElement<LocatorPathElt::AnyRequirement>() ||
        tupleLocator->isLastElement<LocatorPathElt::GenericArgument>())
      break;

    // If the mismatch is a part of either optional-to-optional or
    // value-to-optional conversions, let's allow fix refer to a complete
    // top level type and not just a part of it.
    if (tupleLocator->findLast<LocatorPathElt::OptionalPayload>())
      break;

    ConstraintFix *fix;
    if (tupleLocator->isLastElement<LocatorPathElt::FunctionArgument>()) {
      fix = AllowFunctionTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
    } else {
      fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
    }
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::SequenceElementType: {
    // This is going to be diagnosed as `missing conformance`,
    // so no need to create duplicate fixes.
    if (rhs->isExistentialType())
      break;

    // If the types didn't line up, let's allow right-hand side
    // of the conversion (or pattern match) to have holes. This
    // helps when conversion if between a type and a tuple e.g.
    // `Int` vs. `(_, _)`.
    recordAnyTypeVarAsPotentialHole(rhs);

    conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::SubscriptMember: {
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    break;
  }

  case ConstraintLocator::Condition: {
    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      break;

    conversionsOrFixes.push_back(IgnoreContextualType::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::UnresolvedMemberChainResult: {
    // Ignore this mismatch if result is already a hole.
    if (rhs->isPlaceholder())
      return true;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      break;

    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    // If there is a type mismatch here it's contextual e.g.,
    // `let x: E = .foo(42)`, where `.foo` is a member of `E`
    // but produces an incorrect type.
    auto *fix = IgnoreContextualType::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: {
    // If this is an attempt to use readonly IUO as a destination
    // of an assignment e.g.
    //
    // let x: Int! = 0
    // x = 42 <- `x` can be either `Int?` or `Int` but it can't be an l-value.
    if (lhs->is<LValueType>() && !rhs->is<LValueType>()) {
      auto result = matchTypes(lhs->getWithoutSpecifierType(), rhs, matchKind,
                               TMF_ApplyingFix, locator);

      if (result.isSuccess()) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
      }
    }
    break;
  }

  case ConstraintLocator::InstanceType: {
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    break;
  }

  case ConstraintLocator::OptionalPayload: {
    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    break;
  }

  case ConstraintLocator::TernaryBranch: {
    recordAnyTypeVarAsPotentialHole(lhs);
    recordAnyTypeVarAsPotentialHole(rhs);

    // If `if` expression has a contextual type, let's consider it a source of
    // truth and produce a contextual mismatch instead of  per-branch failure,
    // because it's a better pointer than potential then-to-else type mismatch.
    if (auto contextualType =
            getContextualType(anchor, /*forConstraint=*/false)) {
      auto purpose = getContextualTypePurpose(anchor);
      if (contextualType->isEqual(rhs)) {
        auto *loc = getConstraintLocator(
            anchor, LocatorPathElt::ContextualType(purpose));
        if (hasFixFor(loc, FixKind::IgnoreContextualType))
          return true;

        if (contextualType->isVoid() && purpose == CTP_ReturnStmt) {
          conversionsOrFixes.push_back(RemoveReturn::create(*this, lhs, loc));
          break;
        }

        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, loc));
        break;
      }
    }

    // If there is no contextual type, this is most likely a contextual type
    // mismatch between then/else branches of ternary operator.
    conversionsOrFixes.push_back(ContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::PatternMatch: {
    auto *pattern = elt.castTo<LocatorPathElt::PatternMatch>().getPattern();
    bool isMemberMatch =
        lhs->is<FunctionType>() && isa<EnumElementPattern>(pattern);

    // If member reference couldn't be resolved, let's allow pattern
    // to have holes.
    if (rhs->isPlaceholder() && isMemberMatch) {
      recordAnyTypeVarAsPotentialHole(lhs);
      return true;
    }

    // If either type is a placeholder, consider this fixed.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    // If member reference didn't match expected pattern,
    // let's consider that a contextual mismatch.
    if (isMemberMatch) {
      recordAnyTypeVarAsPotentialHole(lhs);
      recordAnyTypeVarAsPotentialHole(rhs);

      conversionsOrFixes.push_back(ContextualMismatch::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
    }

    // `weak` declaration with an explicit non-optional type e.g.
    // `weak x: X = ...` where `X` is a class.
    if (auto *TP = dyn_cast<TypedPattern>(pattern)) {
      if (auto *NP = dyn_cast<NamedPattern>(TP->getSubPattern())) {
        auto *var = NP->getDecl();

        auto ROK = ReferenceOwnership::Strong;
        if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
          ROK = OA->get();

        if (!rhs->getOptionalObjectType() &&
            optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) {
          conversionsOrFixes.push_back(
              AllowNonOptionalWeak::create(*this, getConstraintLocator(NP)));
          break;
        }
      }
    }

    break;
  }

  case ConstraintLocator::GenericArgument: {
    // If any of the types is a placeholder, consider it fixed.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    // Ignoring the generic argument because we may have a generic requirement
    // failure e.g. `String bind T.Element`, so let's drop the generic argument
    // path element and recurse in repairFailures to check and potentially
    // record the requirement failure fix.
    path.pop_back();

    if (path.empty() || !path.back().is<LocatorPathElt::AnyRequirement>())
      break;

    return repairFailures(lhs, rhs, matchKind, conversionsOrFixes,
                          getConstraintLocator(anchor, path));
  }

  case ConstraintLocator::ResultBuilderBodyResult: {
    // If result type of the body couldn't be determined
    // there is going to be other fix available to diagnose
    // the underlying issue.
    if (lhs->isPlaceholder())
      return true;

    conversionsOrFixes.push_back(ContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  default:
    break;
  }

  return !conversionsOrFixes.empty();
}