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();
}