[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:
Pavel Yaskevich
2020-02-04 08:53:04 -08:00
parent 9a058274ae
commit 0103041a30
3 changed files with 85 additions and 29 deletions

View File

@@ -2077,8 +2077,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
// will also fail (because type can't satisfy it), and it's // will also fail (because type can't satisfy it), and it's
// more interesting for diagnostics. // more interesting for diagnostics.
auto req = last->getAs<LocatorPathElt::AnyRequirement>(); auto req = last->getAs<LocatorPathElt::AnyRequirement>();
if (type1->isHole() || (req && if (!req)
req->getRequirementKind() == RequirementKind::Superclass)) return getTypeMatchFailure(locator);
if (type1->isHole() ||
req->getRequirementKind() == RequirementKind::Superclass)
return getTypeMatchSuccess(); return getTypeMatchSuccess();
auto *fix = fixRequirementFailure(*this, type1, type2, locator); auto *fix = fixRequirementFailure(*this, type1, type2, locator);
@@ -2171,6 +2174,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
if (last->is<LocatorPathElt::FunctionResult>()) if (last->is<LocatorPathElt::FunctionResult>())
return getTypeMatchFailure(locator); 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 } else { // There are no elements in the path
auto *anchor = locator.getAnchor(); auto *anchor = locator.getAnchor();
if (!(anchor && if (!(anchor &&
@@ -2814,6 +2822,14 @@ bool ConstraintSystem::repairFailures(
return false; return false;
if (auto *coercion = dyn_cast<CoerceExpr>(anchor)) { 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 // Let's check whether the sub-expression is an optional type which
// is possible to unwrap (either by force or `??`) to satisfy the cast, // is possible to unwrap (either by force or `??`) to satisfy the cast,
// otherwise we'd have to fallback to force downcast. // otherwise we'd have to fallback to force downcast.
@@ -2839,10 +2855,11 @@ bool ConstraintSystem::repairFailures(
if (hasConversionOrRestriction(ConversionRestrictionKind::Existential)) if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
return false; return false;
auto *fix = ContextualMismatch::create(*this, lhs, rhs, auto *fix = ContextualMismatch::create(*this, lhs, rhs,
getConstraintLocator(locator)); getConstraintLocator(locator));
conversionsOrFixes.push_back(fix); conversionsOrFixes.push_back(fix);
return true;
} }
// This could be: // This could be:
@@ -3486,15 +3503,17 @@ bool ConstraintSystem::repairFailures(
// If there is a deep equality, superclass restriction // If there is a deep equality, superclass restriction
// already recorded, let's not add bother ignoring // already recorded, let's not add bother ignoring
// contextual type, because actual fix is going to // contextual type, because actual fix is going to
// be perform once restriction is applied. // be performed once restriction is applied.
if (llvm::any_of(conversionsOrFixes, if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
[](const RestrictionOrFix &entry) -> bool { break;
return entry.IsRestriction &&
(entry.getRestriction() == if (hasConversionOrRestriction(
ConversionRestrictionKind::Superclass || ConversionRestrictionKind::MetatypeToExistentialMetatype))
entry.getRestriction() == break;
ConversionRestrictionKind::DeepEquality);
})) if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
!hasConversionOrRestriction(
ConversionRestrictionKind::OptionalToOptional))
break; break;
conversionsOrFixes.push_back(IgnoreContextualType::create( conversionsOrFixes.push_back(IgnoreContextualType::create(
@@ -8232,15 +8251,42 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; 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) { switch (restriction) {
// for $< in { <, <c, <oc }: // for $< in { <, <c, <oc }:
// T_i $< U_i ===> (T_i...) $< (U_i...) // T_i $< U_i ===> (T_i...) $< (U_i...)
case ConversionRestrictionKind::DeepEquality: case ConversionRestrictionKind::DeepEquality:
return matchDeepEqualityTypes(type1, type2, locator); return matchDeepEqualityTypes(type1, type2, locator);
case ConversionRestrictionKind::Superclass: case ConversionRestrictionKind::Superclass: {
addContextualScore(); 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 }: // for $< in { <, <c, <oc }:
// T $< U, U : P_i ===> T $< protocol<P_i...> // T $< U, U : P_i ===> T $< protocol<P_i...>
@@ -8255,15 +8301,23 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
// P : Q ===> T.Protocol $< Q.Type // P : Q ===> T.Protocol $< Q.Type
// for P protocol, Q protocol, // for P protocol, Q protocol,
// P $< Q ===> P.Type $< Q.Type // P $< Q ===> P.Type $< Q.Type
case ConversionRestrictionKind::MetatypeToExistentialMetatype: case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
addContextualScore(); addContextualScore();
return matchExistentialTypes( auto instanceTy1 = type1->getMetatypeInstanceType();
type1->castTo<MetatypeType>()->getInstanceType(), auto instanceTy2 = type2->getMetatypeInstanceType();
type2->castTo<ExistentialMetatypeType>()->getInstanceType(),
ConstraintKind::ConformsTo, auto result = matchExistentialTypes(
subflags, instanceTy1, instanceTy2, ConstraintKind::ConformsTo, subflags,
locator.withPathElement(ConstraintLocator::InstanceType)); locator.withPathElement(ConstraintLocator::InstanceType));
if (!(shouldAttemptFixes() && result.isFailure()))
return result;
return fixContextualFailure(type1, type2, locator)
? getTypeMatchSuccess()
: getTypeMatchFailure(locator);
}
// for $< in { <, <c, <oc }: // for $< in { <, <c, <oc }:
// for P protocol, C class, D class, // for P protocol, C class, D class,
@@ -8278,13 +8332,16 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
if (!superclass1) if (!superclass1)
return SolutionKind::Error; return SolutionKind::Error;
return matchTypes( auto result =
superclass1, matchTypes(superclass1, instance2, ConstraintKind::Subtype, subflags,
instance2, locator.withPathElement(ConstraintLocator::InstanceType));
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 }: // for $< in { <, <c, <oc }:
// T $< U ===> T $< U? // T $< U ===> T $< U?

View File

@@ -243,7 +243,6 @@ let _: String = try? optProducer?.produceInt() // expected-error {{cannot conver
let _: Int?? = try? optProducer?.produceInt() // good let _: Int?? = try? optProducer?.produceInt() // good
let _: Int? = try? optProducer?.produceIntNoThrowing() // expected-error {{cannot convert value of type 'Int??' to specified type 'Int?'}} 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?.produceIntNoThrowing() // expected-warning {{no calls to throwing functions occur within 'try' expression}}
let _: Int? = (try? optProducer?.produceAny()) as? Int // good let _: Int? = (try? optProducer?.produceAny()) as? Int // good

View File

@@ -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 _: 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 = 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 = 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-error {{value of type 'Unrelated' does not conform to 'Derived & P2' in coercion}}
let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}} let _ = Unrelated() as? Derived & P2 // expected-warning {{always fails}}