mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user