Merge pull request #76354 from xedin/improve-mismatch-diagnostics-in-optional-context

[CSSimplify] Rework how/when mismatches between optional types are fixed
This commit is contained in:
Pavel Yaskevich
2024-09-11 10:14:19 -07:00
committed by GitHub
31 changed files with 152 additions and 178 deletions

View File

@@ -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.
@@ -14062,26 +14022,9 @@ ConstraintSystem::simplifyLValueObjectConstraint(
if (!shouldAttemptFixes())
return SolutionKind::Error;
auto assessImpact = [&]() -> unsigned {
// If this is a projection of a member reference
// let's check whether the member is unconditionally
// settable, if so than it's a problem with its base.
if (locator.directlyAt<UnresolvedDotExpr>()) {
auto *memberLoc = getConstraintLocator(locator.getAnchor(),
ConstraintLocator::Member);
if (auto selected = findSelectedOverloadFor(memberLoc)) {
if (auto *storage = dyn_cast_or_null<AbstractStorageDecl>(
selected->choice.getDeclOrNull())) {
return storage->isSettable(nullptr) ? 1 : 2;
}
}
}
return 2;
};
if (recordFix(
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)),
assessImpact()))
auto *fixLoc = getConstraintLocator(locator);
if (recordFix(TreatRValueAsLValue::create(*this, fixLoc),
TreatRValueAsLValue::assessImpact(*this, fixLoc)))
return SolutionKind::Error;
lvalueTy = LValueType::get(lvalueTy);
@@ -14215,14 +14158,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);
}
@@ -15228,21 +15180,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
}
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.
impact += (locator.endsWith<LocatorPathElt::FunctionResult>() ||
locator.endsWith<LocatorPathElt::SubscriptMember>())
? 1
: 0;
// An overload choice that isn't settable is least interesting for diagnosis.
if (auto overload = findSelectedOverloadFor(getCalleeLocator(fix->getLocator()))) {
if (auto *var = dyn_cast_or_null<VarDecl>(overload->choice.getDeclOrNull())) {
impact += !var->isSettableInSwift(DC) ? 1 : 0;
}
}
unsigned impact =
TreatRValueAsLValue::assessImpact(*this, fix->getLocator());
return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
}