ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint()

in lib/Sema/CSSimplify.cpp [11871:12258]


ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
    ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind,
    TypeMatchOptions flags, ConstraintLocatorBuilder locator) {

  // Try with the fix.
  TypeMatchOptions subflags =
    getDefaultDecompositionOptions(flags) | TMF_ApplyingFix;
  switch (fix->getKind()) {
  case FixKind::ForceOptional: {
    SmallVector<Type, 4> unwraps1;
    type1->lookThroughAllOptionalTypes(unwraps1);

    SmallVector<Type, 4> unwraps2;
    type2->lookThroughAllOptionalTypes(unwraps2);

    auto impact = unwraps1.size() != unwraps2.size()
                      ? unwraps1.size() - unwraps2.size()
                      : 1;
    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::UnwrapOptionalBase:
  case FixKind::UnwrapOptionalBaseWithOptionalResult: {
    if (recordFix(fix))
      return SolutionKind::Error;

    type1 = simplifyType(type1);
    type2 = simplifyType(type2);

    // Explicitly preserve l-valueness of an unwrapped member type.
    if (!type1->is<LValueType>() && type2->is<LValueType>())
      type1 = LValueType::get(type1);

    // First type already appropriately set.
    return matchTypes(type1, type2, matchKind, subflags, locator);
  }

  case FixKind::ForceDowncast:
    // These work whenever they are suggested.
    if (recordFix(fix))
      return SolutionKind::Error;

    return SolutionKind::Solved;

  case FixKind::AddressOf: {
    // Assume that '&' was applied to the first type, turning an lvalue into
    // an inout.
    auto result = matchTypes(InOutType::get(type1->getRValueType()), type2,
                             matchKind, subflags, locator);
    if (result == SolutionKind::Solved)
      if (recordFix(fix))
        return SolutionKind::Error;

    return result;
  }

  case FixKind::AutoClosureForwarding: {
    if (recordFix(fix))
      return SolutionKind::Error;
    return matchTypes(type1, type2, matchKind, subflags, locator);
  }

  case FixKind::AllowTupleTypeMismatch: {
    if (fix->getAs<AllowTupleTypeMismatch>()->isElementMismatch()) {
      auto *locator = fix->getLocator();
      if (recordFix(fix, /*impact*/locator->isForContextualType() ? 5 : 1))
        return SolutionKind::Error;
      return SolutionKind::Solved;
    }
    auto lhs = type1->castTo<TupleType>();
    auto rhs = type2->castTo<TupleType>();
    // Create a new tuple type the size of the smaller tuple with elements
    // from the larger tuple whenever either side contains a type variable.
    // For example (A, $0, B, $2) and (X, Y, $1) produces: (X, $0, B).
    // This allows us to guarentee that the types will match, and all
    // type variables will get bound to something as long as we default
    // excess types in the larger tuple to Any. In the prior example,
    // when the tuples (X, Y, $1) and (X, $0, B) get matched, $0 is equated
    // to Y, $1 is equated to B, and $2 is defaulted to Any.
    auto lhsLarger = lhs->getNumElements() >= rhs->getNumElements();
    auto isLabelingFailure = lhs->getNumElements() == rhs->getNumElements();
    auto larger = lhsLarger ? lhs : rhs;
    auto smaller = lhsLarger ? rhs : lhs;
    llvm::SmallVector<TupleTypeElt, 4> newTupleTypes;

    for (unsigned i = 0; i < larger->getNumElements(); ++i) {
      auto largerElt = larger->getElement(i);
      if (i < smaller->getNumElements()) {
        auto smallerElt = smaller->getElement(i);
        if (isLabelingFailure)
          newTupleTypes.push_back(TupleTypeElt(largerElt.getType()));
        else if (largerElt.getType()->isTypeVariableOrMember() ||
            smallerElt.getType()->isTypeVariableOrMember())
          newTupleTypes.push_back(largerElt);
        else
          newTupleTypes.push_back(smallerElt);
      } else {
        if (largerElt.getType()->isTypeVariableOrMember())
          recordAnyTypeVarAsPotentialHole(largerElt.getType());
      }
    }
    auto matchingType =
        TupleType::get(newTupleTypes, getASTContext())->castTo<TupleType>();
    if (recordFix(fix))
      return SolutionKind::Error;
    return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator);
  }

  case FixKind::AllowFunctionTypeMismatch: {
    if (recordFix(fix, /*impact=*/5))
      return SolutionKind::Error;
    return SolutionKind::Solved;
  }

  case FixKind::TreatEphemeralAsNonEphemeral: {
    auto *theFix = static_cast<TreatEphemeralAsNonEphemeral *>(fix);
    // If we have a non-ephemeral locator for an ephemeral conversion, make a
    // note of the fix.
    auto conversion = theFix->getConversionKind();
    switch (isConversionEphemeral(conversion, locator)) {
    case ConversionEphemeralness::Ephemeral:
      // Record the fix with an impact of zero. This ensures that non-ephemeral
      // diagnostics don't impact solver behavior.
      if (recordFix(fix, /*impact*/ 0))
        return SolutionKind::Error;

      return SolutionKind::Solved;
    case ConversionEphemeralness::Unresolved:
    case ConversionEphemeralness::NonEphemeral:
      // FIXME: The unresolved case should form an unsolved constraint rather
      // than being treated as fully solved. This will require a way to connect
      // the unsolved constraint to the type variable for the unresolved
      // overload such that the fix gets re-activated when the overload is
      // bound.
      return SolutionKind::Solved;
    }
  }

  case FixKind::InsertCall:
  case FixKind::RemoveReturn:
  case FixKind::RemoveAddressOf:
  case FixKind::AddMissingArguments:
  case FixKind::MoveOutOfOrderArgument:
  case FixKind::SkipUnhandledConstructInResultBuilder:
  case FixKind::UsePropertyWrapper:
  case FixKind::UseWrappedValue:
  case FixKind::AllowInvalidPropertyWrapperType:
  case FixKind::RemoveProjectedValueArgument:
  case FixKind::ExpandArrayIntoVarargs:
  case FixKind::UseRawValue:
  case FixKind::SpecifyBaseTypeForContextualMember:
  case FixKind::CoerceToCheckedCast:
  case FixKind::SpecifyObjectLiteralTypeImport:
  case FixKind::AllowKeyPathRootTypeMismatch:
  case FixKind::UnwrapOptionalBaseKeyPathApplication:
  case FixKind::AllowCoercionToForceCast:
  case FixKind::SpecifyKeyPathRootType:
  case FixKind::SpecifyLabelToAssociateTrailingClosure:
  case FixKind::AllowKeyPathWithoutComponents:
  case FixKind::IgnoreInvalidResultBuilderBody:
  case FixKind::IgnoreResultBuilderWithReturnStmts:
  case FixKind::SpecifyContextualTypeForNil:
  case FixKind::AllowRefToInvalidDecl:
  case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember:
  case FixKind::AllowCheckedCastCoercibleOptionalType:
  case FixKind::AllowNoopCheckedCast:
  case FixKind::AllowUnsupportedRuntimeCheckedCast:
  case FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype:
  case FixKind::AllowWrappedValueMismatch:
  case FixKind::RemoveExtraneousArguments:
  case FixKind::SpecifyTypeForPlaceholder:
  case FixKind::AllowAutoClosurePointerConversion:
  case FixKind::IgnoreKeyPathContextualMismatch:
  case FixKind::NotCompileTimeConst: {
    return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::ExplicitlyConstructRawRepresentable: {
    // Let's increase impact of this fix for binary operators because
    // it's possible to get both `.rawValue` and construction fixes for
    // different overloads of a binary operator and `.rawValue` is a
    // better fix because raw representable has a failable constructor.
    return recordFix(fix,
                     /*impact=*/isExpr<BinaryExpr>(locator.getAnchor()) ? 2 : 1)
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  case FixKind::TreatRValueAsLValue: {
    unsigned impact = 1;
    // If this is an attempt to use result of a function/subscript call as
    // an l-value, it has to have an increased impact because it's either
    // a function - which is completely incorrect, or it's a get-only
    // subscript, which requires changes to declaration to become mutable.
    if (auto last = locator.last()) {
      impact += (last->is<LocatorPathElt::FunctionResult>() ||
                 last->is<LocatorPathElt::SubscriptMember>())
                    ? 1
                    : 0;
    }

    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::AddConformance:
  case FixKind::SkipSameTypeRequirement:
  case FixKind::SkipSuperclassRequirement: {
    return recordFix(fix, assessRequirementFailureImpact(*this, type1,
                                                         fix->getLocator()))
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  case FixKind::AllowArgumentTypeMismatch: {
    auto impact = 2;
    // If there are any other argument mismatches already detected for this
    // call, we increase the score even higher so more argument fixes means
    // less viable is the overload.
    if (llvm::any_of(getFixes(), [&](const ConstraintFix *fix) {
          auto *fixLocator = fix->getLocator();
          return fixLocator->findLast<LocatorPathElt::ApplyArgToParam>()
                     ? fixLocator->getAnchor() == locator.getAnchor()
                     : false;
        }))
      impact += 3;

    // Passing a closure to a parameter that doesn't expect one should
    // be scored lower because there might be an overload that expects
    // a closure but has other issues e.g. wrong number of parameters.
    if (!type2->lookThroughAllOptionalTypes()->is<FunctionType>()) {
      auto argument = simplifyLocatorToAnchor(fix->getLocator());
      if (isExpr<ClosureExpr>(argument)) {
        impact += 2;
      }
    }

    // De-prioritize `Builtin.RawPointer` and `OpaquePointer` parameters
    // because they usually clash with generic parameter mismatches e.g.
    //
    // let ptr: UnsafePointer<String> = ...
    // _ = UnsafePointer<Int>(ups)
    //
    // Here initializer overloads have both `Builtin.RawPointer` and
    // `OpaquePointer` variants, but the actual issue is that generic argument
    // `String` doesn't match `Int`.
    {
      if (type2->is<BuiltinRawPointerType>())
        impact += 1;

      if (type2->getAnyNominal() == getASTContext().getOpaquePointerDecl())
        impact += 1;
    }

    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::TreatArrayLiteralAsDictionary: {
    ArrayExpr *AE = getAsExpr<ArrayExpr>(fix->getAnchor());
    assert(AE);

    // If the array was empty, there's nothing to do.
    if (AE->getNumElements() == 0)
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;

    // For arrays with a single element, match the element type to the
    // dictionary's key type.
    SmallVector<Type, 2> optionals;
    auto dictTy = type2->lookThroughAllOptionalTypes(optionals);

    // If the fix is worse than the best solution, there's no point continuing.
    if (recordFix(fix, optionals.size() + 1))
      return SolutionKind::Error;

    // Extract the dictionary key type.
    ProtocolDecl *dictionaryProto =
        Context.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
    auto keyAssocTy = dictionaryProto->getAssociatedType(Context.Id_Key);
    auto valueBaseTy = createTypeVariable(getConstraintLocator(locator),
                                      TVO_CanBindToLValue |
                                        TVO_CanBindToNoEscape |
                                        TVO_CanBindToHole);
    assignFixedType(valueBaseTy, dictTy);
    auto dictionaryKeyTy = DependentMemberType::get(valueBaseTy, keyAssocTy);

    // Extract the array element type.
    auto elemTy = isArrayType(type1);

    ConstraintLocator *elemLoc = getConstraintLocator(AE->getElement(0));
    ConstraintKind kind = isDictionaryType(dictTy)
        ? ConstraintKind::Conversion
        : ConstraintKind::Equal;
    return matchTypes(*elemTy, dictionaryKeyTy, kind, subflags, elemLoc);
  }

  case FixKind::ContextualMismatch:
  case FixKind::IgnoreContextualType:
  case FixKind::IgnoreAssignmentDestinationType:
  case FixKind::AllowConversionThroughInOut:
  case FixKind::IgnoreCollectionElementContextualMismatch: {
    auto impact = 1;

    auto locator = fix->getLocator();
    if (auto branchElt =
            locator->getLastElementAs<LocatorPathElt::TernaryBranch>()) {
      // If this is `else` branch of a ternary operator, let's
      // increase its impact to eliminate the chance of ambiguity.
      //
      // Branches are connected through two `subtype` constraints
      // to a common type variable with represents their join, which
      // means that result would attempt a type from each side if
      // one is available and that would result in two fixes - one for
      // each mismatched branch.
      if (branchElt->forElse())
        impact = 10;
    }

    if (recordFix(fix, impact))
      return SolutionKind::Error;

    if (auto *fnType1 = type1->getAs<FunctionType>()) {
      // If this is a contextual mismatch between two
      // function types which we couldn't find a more
      // specific fix for. Let's assume that such types
      // are competely disjoint and adjust impact of
      // the fix accordingly.
      if (auto *fnType2 = type2->getAs<FunctionType>()) {
        increaseScore(SK_Fix, 10);
      } else {
        // If type produced by expression is a function type
        // with result type matching contextual, it should have
        // been diagnosed as "missing explicit call", let's
        // increase the score to make sure that we don't impede that.
        auto result = matchTypes(fnType1->getResult(), type2, matchKind,
                                 TMF_ApplyingFix, locator);
        if (result == SolutionKind::Solved)
          increaseScore(SK_Fix);
      }
    }

    return SolutionKind::Solved;
  }

  case FixKind::AllowNonOptionalWeak: {
    if (recordFix(fix))
      return SolutionKind::Error;

    (void)matchTypes(type1, OptionalType::get(type2),
                     ConstraintKind::Conversion,
                     TypeMatchFlags::TMF_ApplyingFix, locator);

    return SolutionKind::Solved;
  }

  case FixKind::UseSubscriptOperator:
  case FixKind::ExplicitlyEscaping:
  case FixKind::MarkGlobalActorFunction:
  case FixKind::RelabelArguments:
  case FixKind::RemoveCall:
  case FixKind::RemoveUnwrap:
  case FixKind::DefineMemberBasedOnUse:
  case FixKind::AllowMemberRefOnExistential:
  case FixKind::AllowTypeOrInstanceMember:
  case FixKind::AllowInvalidPartialApplication:
  case FixKind::AllowInvalidInitRef:
  case FixKind::AllowClosureParameterDestructuring:
  case FixKind::AllowInaccessibleMember:
  case FixKind::AllowAnyObjectKeyPathRoot:
  case FixKind::AllowMultiArgFuncKeyPathMismatch:
  case FixKind::TreatKeyPathSubscriptIndexAsHashable:
  case FixKind::AllowInvalidRefInKeyPath:
  case FixKind::DefaultGenericArgument:
  case FixKind::GenericArgumentsMismatch:
  case FixKind::AllowMutatingMemberOnRValueBase:
  case FixKind::AllowTupleSplatForSingleParameter:
  case FixKind::AllowNonClassTypeToConvertToAnyObject:
  case FixKind::SpecifyClosureParameterType:
  case FixKind::SpecifyClosureReturnType:
  case FixKind::AddQualifierToAccessTopLevelName:
  case FixKind::AddSendableAttribute:
  case FixKind::DropThrowsAttribute:
  case FixKind::DropAsyncAttribute:
  case FixKind::AllowSwiftToCPointerConversion:
  case FixKind::AllowTupleLabelMismatch:
    llvm_unreachable("handled elsewhere");
  }

  llvm_unreachable("Unhandled FixKind in switch.");
}