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.");
}