mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[ConstraintSystem] Tidy up restriction handling in contextual failures
Delay "fixing" contextual conversion failures until restriction is applied this helps to tidy up logic for superclass and existential conversions. Too bad we have to "fix" in `simplifyRestrictedConstraintImpl` now but we can't really do much about that because diagnostics need both top-level types to be useful.
This commit is contained in:
@@ -2077,8 +2077,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
|
||||
// will also fail (because type can't satisfy it), and it's
|
||||
// more interesting for diagnostics.
|
||||
auto req = last->getAs<LocatorPathElt::AnyRequirement>();
|
||||
if (type1->isHole() || (req &&
|
||||
req->getRequirementKind() == RequirementKind::Superclass))
|
||||
if (!req)
|
||||
return getTypeMatchFailure(locator);
|
||||
|
||||
if (type1->isHole() ||
|
||||
req->getRequirementKind() == RequirementKind::Superclass)
|
||||
return getTypeMatchSuccess();
|
||||
|
||||
auto *fix = fixRequirementFailure(*this, type1, type2, locator);
|
||||
@@ -2171,6 +2174,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
|
||||
if (last->is<LocatorPathElt::FunctionResult>())
|
||||
return getTypeMatchFailure(locator);
|
||||
|
||||
// If instance types didn't line up correctly, let's produce a
|
||||
// diagnostic which mentions them together with their metatypes.
|
||||
if (last->is<LocatorPathElt::InstanceType>())
|
||||
return getTypeMatchFailure(locator);
|
||||
|
||||
} else { // There are no elements in the path
|
||||
auto *anchor = locator.getAnchor();
|
||||
if (!(anchor &&
|
||||
@@ -2814,6 +2822,14 @@ bool ConstraintSystem::repairFailures(
|
||||
return false;
|
||||
|
||||
if (auto *coercion = dyn_cast<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.
|
||||
@@ -2843,6 +2859,7 @@ bool ConstraintSystem::repairFailures(
|
||||
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
|
||||
getConstraintLocator(locator));
|
||||
conversionsOrFixes.push_back(fix);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This could be:
|
||||
@@ -3486,15 +3503,17 @@ bool ConstraintSystem::repairFailures(
|
||||
// If there is a deep equality, superclass restriction
|
||||
// already recorded, let's not add bother ignoring
|
||||
// contextual type, because actual fix is going to
|
||||
// be perform once restriction is applied.
|
||||
if (llvm::any_of(conversionsOrFixes,
|
||||
[](const RestrictionOrFix &entry) -> bool {
|
||||
return entry.IsRestriction &&
|
||||
(entry.getRestriction() ==
|
||||
ConversionRestrictionKind::Superclass ||
|
||||
entry.getRestriction() ==
|
||||
ConversionRestrictionKind::DeepEquality);
|
||||
}))
|
||||
// be performed once restriction is applied.
|
||||
if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
|
||||
break;
|
||||
|
||||
if (hasConversionOrRestriction(
|
||||
ConversionRestrictionKind::MetatypeToExistentialMetatype))
|
||||
break;
|
||||
|
||||
if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
|
||||
!hasConversionOrRestriction(
|
||||
ConversionRestrictionKind::OptionalToOptional))
|
||||
break;
|
||||
|
||||
conversionsOrFixes.push_back(IgnoreContextualType::create(
|
||||
@@ -8232,15 +8251,42 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
|
||||
};
|
||||
|
||||
auto fixContextualFailure = [&](Type fromType, Type toType,
|
||||
ConstraintLocatorBuilder locator) -> bool {
|
||||
auto *loc = getConstraintLocator(locator);
|
||||
if (loc->isForAssignment() || loc->isForCoercion() ||
|
||||
loc->isForContextualType()) {
|
||||
if (restriction == ConversionRestrictionKind::Superclass) {
|
||||
if (auto *fix =
|
||||
CoerceToCheckedCast::attempt(*this, fromType, toType, loc))
|
||||
return !recordFix(fix);
|
||||
}
|
||||
|
||||
auto *fix = ContextualMismatch::create(*this, fromType, toType, loc);
|
||||
return !recordFix(fix);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
switch (restriction) {
|
||||
// for $< in { <, <c, <oc }:
|
||||
// T_i $< U_i ===> (T_i...) $< (U_i...)
|
||||
case ConversionRestrictionKind::DeepEquality:
|
||||
return matchDeepEqualityTypes(type1, type2, locator);
|
||||
|
||||
case ConversionRestrictionKind::Superclass:
|
||||
case ConversionRestrictionKind::Superclass: {
|
||||
addContextualScore();
|
||||
return matchSuperclassTypes(type1, type2, subflags, locator);
|
||||
|
||||
auto result = matchSuperclassTypes(type1, type2, subflags, locator);
|
||||
|
||||
if (!(shouldAttemptFixes() && result.isFailure()))
|
||||
return result;
|
||||
|
||||
return fixContextualFailure(type1, type2, locator)
|
||||
? getTypeMatchSuccess()
|
||||
: getTypeMatchFailure(locator);
|
||||
}
|
||||
|
||||
// for $< in { <, <c, <oc }:
|
||||
// T $< U, U : P_i ===> T $< protocol<P_i...>
|
||||
@@ -8255,16 +8301,24 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
// P : Q ===> T.Protocol $< Q.Type
|
||||
// for P protocol, Q protocol,
|
||||
// P $< Q ===> P.Type $< Q.Type
|
||||
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
|
||||
case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
|
||||
addContextualScore();
|
||||
|
||||
return matchExistentialTypes(
|
||||
type1->castTo<MetatypeType>()->getInstanceType(),
|
||||
type2->castTo<ExistentialMetatypeType>()->getInstanceType(),
|
||||
ConstraintKind::ConformsTo,
|
||||
subflags,
|
||||
auto instanceTy1 = type1->getMetatypeInstanceType();
|
||||
auto instanceTy2 = type2->getMetatypeInstanceType();
|
||||
|
||||
auto result = matchExistentialTypes(
|
||||
instanceTy1, instanceTy2, ConstraintKind::ConformsTo, subflags,
|
||||
locator.withPathElement(ConstraintLocator::InstanceType));
|
||||
|
||||
if (!(shouldAttemptFixes() && result.isFailure()))
|
||||
return result;
|
||||
|
||||
return fixContextualFailure(type1, type2, locator)
|
||||
? getTypeMatchSuccess()
|
||||
: getTypeMatchFailure(locator);
|
||||
}
|
||||
|
||||
// for $< in { <, <c, <oc }:
|
||||
// for P protocol, C class, D class,
|
||||
// (P & C) : D ===> (P & C).Type $< D.Type
|
||||
@@ -8278,13 +8332,16 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
if (!superclass1)
|
||||
return SolutionKind::Error;
|
||||
|
||||
return matchTypes(
|
||||
superclass1,
|
||||
instance2,
|
||||
ConstraintKind::Subtype,
|
||||
subflags,
|
||||
auto result =
|
||||
matchTypes(superclass1, instance2, ConstraintKind::Subtype, subflags,
|
||||
locator.withPathElement(ConstraintLocator::InstanceType));
|
||||
|
||||
if (!(shouldAttemptFixes() && result.isFailure()))
|
||||
return result;
|
||||
|
||||
return fixContextualFailure(type1, type2, locator)
|
||||
? getTypeMatchSuccess()
|
||||
: getTypeMatchFailure(locator);
|
||||
}
|
||||
// for $< in { <, <c, <oc }:
|
||||
// T $< U ===> T $< U?
|
||||
|
||||
@@ -243,7 +243,6 @@ let _: String = try? optProducer?.produceInt() // expected-error {{cannot conver
|
||||
let _: Int?? = try? optProducer?.produceInt() // good
|
||||
|
||||
let _: Int? = try? optProducer?.produceIntNoThrowing() // expected-error {{cannot convert value of type 'Int??' to specified type 'Int?'}}
|
||||
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}
|
||||
let _: Int?? = try? optProducer?.produceIntNoThrowing() // expected-warning {{no calls to throwing functions occur within 'try' expression}}
|
||||
|
||||
let _: Int? = (try? optProducer?.produceAny()) as? Int // good
|
||||
|
||||
@@ -103,7 +103,7 @@ func basicSubtyping(
|
||||
let _: P3 = baseAndP2 // expected-error {{value of type 'Base<Int> & P2' does not conform to specified type 'P3'}}
|
||||
let _: Derived = baseAndP1 // expected-error {{cannot convert value of type 'Base<Int> & P1' to specified type 'Derived'}}
|
||||
let _: Derived = baseAndP2 // expected-error {{cannot convert value of type 'Base<Int> & P2' to specified type 'Derived'}}
|
||||
let _: Derived & P2 = baseAndP2 // expected-error {{value of type 'Base<Int> & P2' does not conform to specified type 'Derived & P2'}}
|
||||
let _: Derived & P2 = baseAndP2 // expected-error {{cannot convert value of type 'Base<Int> & P2' to specified type 'Derived'}}
|
||||
|
||||
let _ = Unrelated() as Derived & P2 // expected-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}}
|
||||
let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}}
|
||||
|
||||
Reference in New Issue
Block a user