mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[CSSimplify] Rework how/when mismatches between optional types are fixed
- Don't attempt to insert fixes if there are restrictions present, they'd inform the failures. Inserting fixes too early doesn't help the solver because restriction matching logic would record the same fixes. - Adjust impact of the fixes. Optional conversions shouldn't impact the score in any way because they are not the source of the issue. - Look through one level of optional when failure is related to optional injection. The diagnostic is going to be about underlying type, so there is no reason to print optional on right-hand side.
This commit is contained in:
@@ -3978,8 +3978,19 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
|
||||
path.back().is<LocatorPathElt::GenericArgument>()))
|
||||
path.pop_back();
|
||||
|
||||
auto *fixLoc = getConstraintLocator(anchor, path);
|
||||
// If after looking through optionals and generic arguments
|
||||
// we end up directly on assignment this is a source/destination
|
||||
// type mismatch.
|
||||
if (fixLoc->directlyAt<AssignExpr>()) {
|
||||
auto *fix = IgnoreAssignmentDestinationType::create(
|
||||
*this, type1, type2, fixLoc);
|
||||
return recordFix(fix) ? getTypeMatchFailure(locator)
|
||||
: getTypeMatchSuccess();
|
||||
}
|
||||
|
||||
auto *fix = AllowNonClassTypeToConvertToAnyObject::create(
|
||||
*this, type1, getConstraintLocator(anchor, path));
|
||||
*this, type1, fixLoc);
|
||||
|
||||
return recordFix(fix) ? getTypeMatchFailure(locator)
|
||||
: getTypeMatchSuccess();
|
||||
@@ -5341,6 +5352,9 @@ bool ConstraintSystem::repairFailures(
|
||||
if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
|
||||
return true;
|
||||
|
||||
if (hasAnyRestriction())
|
||||
return false;
|
||||
|
||||
// If destination is `AnyObject` it means that source doesn't conform.
|
||||
if (rhs->getWithoutSpecifierType()
|
||||
->lookThroughAllOptionalTypes()
|
||||
@@ -5350,60 +5364,6 @@ bool ConstraintSystem::repairFailures(
|
||||
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;
|
||||
|
||||
// An attempt to assign `Int?` to `String?`.
|
||||
if (hasConversionOrRestriction(
|
||||
ConversionRestrictionKind::OptionalToOptional)) {
|
||||
conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
|
||||
*this, lhs, rhs, getConstraintLocator(locator)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -14216,14 +14176,23 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch))
|
||||
return true;
|
||||
|
||||
if (restriction == ConversionRestrictionKind::ValueToOptional ||
|
||||
restriction == ConversionRestrictionKind::OptionalToOptional)
|
||||
++impact;
|
||||
if (restriction == ConversionRestrictionKind::ValueToOptional) {
|
||||
// If this is an optional injection we can drop optional from
|
||||
// "to" type since it's not significant for the diagnostic.
|
||||
toType = toType->getOptionalObjectType();
|
||||
}
|
||||
|
||||
auto *fix =
|
||||
loc->isLastElement<LocatorPathElt::ApplyArgToParam>()
|
||||
? AllowArgumentMismatch::create(*this, fromType, toType, loc)
|
||||
: ContextualMismatch::create(*this, fromType, toType, loc);
|
||||
ConstraintFix *fix = nullptr;
|
||||
if (loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
|
||||
fix = AllowArgumentMismatch::create(*this, fromType, toType, loc);
|
||||
} else if (loc->isForAssignment()) {
|
||||
fix = IgnoreAssignmentDestinationType::create(*this, fromType, toType,
|
||||
loc);
|
||||
} else {
|
||||
fix = ContextualMismatch::create(*this, fromType, toType, loc);
|
||||
}
|
||||
|
||||
assert(fix);
|
||||
return !recordFix(fix, impact);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user