Skip to content

Commit 1b0eed2

Browse files
committed
[CS] Better propagate holes from contextual types
Eagerly bind invalid type references to holes and propagate contextual type holes in `repairFailures`. This avoids some unnecessary diagnostics in cases where we have an invalid contextual type.
1 parent b4fe5df commit 1b0eed2

File tree

4 files changed

+63
-5
lines changed

4 files changed

+63
-5
lines changed

lib/Sema/CSGen.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,8 +1716,13 @@ namespace {
17161716
CS.recordFix(
17171717
IgnoreInvalidASTNode::create(CS, CS.getConstraintLocator(locator)));
17181718

1719-
return CS.createTypeVariable(CS.getConstraintLocator(repr),
1720-
TVO_CanBindToHole);
1719+
// Immediately bind the result to a hole since we know it's invalid and
1720+
// want it to propagate rather than allowing other type variables to
1721+
// become holes.
1722+
auto *tv = CS.createTypeVariable(CS.getConstraintLocator(repr),
1723+
TVO_CanBindToHole);
1724+
CS.recordTypeVariablesAsHoles(tv);
1725+
return tv;
17211726
}
17221727
// Diagnose top-level usages of placeholder types.
17231728
if (auto *ty = dyn_cast<PlaceholderTypeRepr>(repr->getWithoutParens())) {

lib/Sema/CSSimplify.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5569,6 +5569,17 @@ bool ConstraintSystem::repairFailures(
55695569
return true;
55705570
};
55715571

5572+
// Propagate a hole from one type to another. This is useful for contextual
5573+
// types since type resolution forms a top-level ErrorType for types with
5574+
// nested errors, e.g `S<@error_type>` becomes `@error_type`. As such, when
5575+
// matching `S<$T0> == $T1` where `$T1` is a hole from a contextual type, we
5576+
// want to eagerly turn `$T0` into a hole since it's likely that `$T1` would
5577+
// have provided the contextual info for it.
5578+
auto tryPropagateHole = [&](Type from, Type to) {
5579+
if (from->isPlaceholder() && to->hasTypeVariable())
5580+
recordTypeVariablesAsHoles(to);
5581+
};
5582+
55725583
if (path.empty()) {
55735584
if (!anchor)
55745585
return false;
@@ -6342,6 +6353,13 @@ bool ConstraintSystem::repairFailures(
63426353

63436354
case ConstraintLocator::ClosureBody:
63446355
case ConstraintLocator::ClosureResult: {
6356+
// If either type is a placeholder, consider this fixed, eagerly propagating
6357+
// a hole from the contextual type.
6358+
if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
6359+
tryPropagateHole(rhs, lhs);
6360+
return true;
6361+
}
6362+
63456363
if (repairByInsertingExplicitCall(lhs, rhs))
63466364
break;
63476365

@@ -6361,9 +6379,12 @@ bool ConstraintSystem::repairFailures(
63616379
}
63626380

63636381
case ConstraintLocator::ContextualType: {
6364-
// If either type is a placeholder, consider this fixed
6365-
if (lhs->isPlaceholder() || rhs->isPlaceholder())
6382+
// If either type is a placeholder, consider this fixed, eagerly propagating
6383+
// a hole from the contextual type.
6384+
if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
6385+
tryPropagateHole(rhs, lhs);
63666386
return true;
6387+
}
63676388

63686389
// If either side is not yet resolved, it's too early for this fix.
63696390
if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
@@ -7002,6 +7023,13 @@ bool ConstraintSystem::repairFailures(
70027023
}
70037024

70047025
case ConstraintLocator::CoercionOperand: {
7026+
// If either type is a placeholder, consider this fixed, eagerly propagating
7027+
// a hole from the contextual type.
7028+
if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
7029+
tryPropagateHole(rhs, lhs);
7030+
return true;
7031+
}
7032+
70057033
auto *coercion = castToExpr<CoerceExpr>(anchor);
70067034

70077035
// Coercion from T.Type to T.Protocol.

test/Constraints/generics.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,3 +1088,28 @@ do {
10881088
f(x, { $0 }, { _ in Task {} }) // expected-warning {{result of 'Task<E>' initializer is unused}}
10891089
}
10901090
}
1091+
1092+
func testHolePropagation() {
1093+
struct S<T: P> {}
1094+
struct R {}
1095+
1096+
// The hole from the contextual type should propagate such that we don't
1097+
// complain about not being able to infer 'T'.
1098+
_ = { () -> S<R> in S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1099+
_ = { () -> S<R> in return S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1100+
_ = { () -> S<R> in (); return S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1101+
1102+
let _: () -> S<R> = { S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1103+
let _: () -> S<R> = { return S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1104+
let _: () -> S<R> = { (); return S() } // expected-error {{type 'R' does not conform to protocol 'P'}}
1105+
1106+
_ = { S() }() as S<R> // expected-error {{type 'R' does not conform to protocol 'P'}}
1107+
_ = { return S() }() as S<R> // expected-error {{type 'R' does not conform to protocol 'P'}}
1108+
_ = { (); return S() } as () -> S<R> // expected-error {{type 'R' does not conform to protocol 'P'}}
1109+
1110+
func makeT<T>() -> T {}
1111+
1112+
_ = { () -> (S<R>, Int) in (makeT(), 0) } // expected-error {{type 'R' does not conform to protocol 'P'}}
1113+
_ = { () -> (S<R>, Int) in return (makeT(), 0) } // expected-error {{type 'R' does not conform to protocol 'P'}}
1114+
_ = { () -> (S<R>, Int) in (); return (makeT(), 0) } // expected-error {{type 'R' does not conform to protocol 'P'}}
1115+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::simplifySameShapeConstraint(swift::Type, swift::Type, swift::optionset::OptionSet<swift::constraints::ConstraintSystem::TypeMatchFlags, unsigned int>, swift::constraints::ConstraintLocatorBuilder)","signatureAssert":"Assertion failed: (isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"), function cast"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
struct a < each b {
44
c(d : repeat each b) {
55
c(preacidte : b) {

0 commit comments

Comments
 (0)