Skip to content

Commit d3a70ab

Browse files
committed
[CS] Disfavor solutions that were unable to infer the variable type inside a named pattern
1 parent 46f9485 commit d3a70ab

15 files changed

+105
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,8 @@ ERROR(iflet_implicitly_unwraps,none,
42424242
"pattern matching in a condition implicitly unwraps optionals", ())
42434243
ERROR(type_pattern_missing_is,none,
42444244
"'is' keyword required to pattern match against type name", ())
4245+
ERROR(cannot_infer_type_for_variable,none,
4246+
"could not infer type of variable %0", (DeclBaseName))
42454247

42464248

42474249
ERROR(pattern_type_mismatch_context,none,

include/swift/Sema/CSFix.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ enum class FixKind : uint8_t {
301301
/// Resolve type of `nil` by providing a contextual type.
302302
SpecifyContextualTypeForNil,
303303

304+
/// Explicitly specify the type of a named pattern because we were unable to
305+
/// infer it from the expression.
306+
/// Used as a last resort fix with very high impact that should cause fixes
307+
/// inside the expression of the pattern binding decl to be preferred.
308+
SpecifyPatternType,
309+
304310
/// Allow expressions to reference invalid declarations by turning
305311
/// them into holes.
306312
AllowRefToInvalidDecl,
@@ -2766,6 +2772,34 @@ class SpecifyContextualTypeForNil final : public ConstraintFix {
27662772
}
27672773
};
27682774

2775+
class SpecifyPatternType final : public ConstraintFix {
2776+
/// The pattern for which we failed to resolve the variable's type.
2777+
NamedPattern *pattern;
2778+
2779+
SpecifyPatternType(ConstraintSystem &cs, NamedPattern *pattern,
2780+
ConstraintLocator *locator)
2781+
: ConstraintFix(cs, FixKind::SpecifyContextualTypeForNil, locator),
2782+
pattern(pattern) {}
2783+
2784+
public:
2785+
std::string getName() const override {
2786+
return "specify type for pattern match";
2787+
}
2788+
2789+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2790+
2791+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
2792+
return diagnose(*commonFixes.front().first);
2793+
}
2794+
2795+
static SpecifyPatternType *create(ConstraintSystem &cs, NamedPattern *pattern,
2796+
ConstraintLocator *locator);
2797+
2798+
static bool classof(ConstraintFix *fix) {
2799+
return fix->getKind() == FixKind::SpecifyPatternType;
2800+
}
2801+
};
2802+
27692803
class SpecifyTypeForPlaceholder final : public ConstraintFix {
27702804
SpecifyTypeForPlaceholder(ConstraintSystem &cs, ConstraintLocator *locator)
27712805
: ConstraintFix(cs, FixKind::SpecifyTypeForPlaceholder, locator) {}

lib/Sema/CSBindings.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,6 +1989,20 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
19891989
return std::make_pair(fix, /*impact=*/(unsigned)10);
19901990
}
19911991

1992+
if (auto pattern = dyn_cast_or_null<NamedPattern>(
1993+
dstLocator->getAnchor().dyn_cast<Pattern *>())) {
1994+
if (dstLocator->getPath().size() == 1 &&
1995+
dstLocator->isLastElement<LocatorPathElt::NamedPatternDecl>()) {
1996+
// Not being able to infer the type of a variable in a pattern binding
1997+
// decl is more dramatic than anything that could happen inside the
1998+
// expression because we want to preferrably point the diagnostic to a
1999+
// part of the expression that caused us to be unable to infer the
2000+
// variable's type.
2001+
ConstraintFix *fix = SpecifyPatternType::create(cs, pattern, dstLocator);
2002+
return std::make_pair(fix, /*impact=*/(unsigned)20);
2003+
}
2004+
}
2005+
19922006
return None;
19932007
}
19942008

lib/Sema/CSDiagnostics.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7811,6 +7811,12 @@ bool MissingContextualTypeForNil::diagnoseAsError() {
78117811
return true;
78127812
}
78137813

7814+
bool CouldNotInferPatternType::diagnoseAsError() {
7815+
emitDiagnostic(diag::cannot_infer_type_for_variable,
7816+
pattern->getDecl()->getBaseName());
7817+
return true;
7818+
}
7819+
78147820
bool CouldNotInferPlaceholderType::diagnoseAsError() {
78157821
// If this placeholder was explicitly written out by the user, they can maybe
78167822
// fix things by specifying an actual type.

lib/Sema/CSDiagnostics.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,6 +2450,18 @@ class MissingContextualTypeForNil final : public FailureDiagnostic {
24502450
bool diagnoseAsError() override;
24512451
};
24522452

2453+
class CouldNotInferPatternType final : public FailureDiagnostic {
2454+
/// The pattern for which we failed to resolve the variable's type.
2455+
NamedPattern *pattern;
2456+
2457+
public:
2458+
CouldNotInferPatternType(const Solution &solution, NamedPattern *pattern,
2459+
ConstraintLocator *locator)
2460+
: FailureDiagnostic(solution, locator), pattern(pattern) {}
2461+
2462+
bool diagnoseAsError() override;
2463+
};
2464+
24532465
/// Diagnose situations where there is no context to determine the type of a
24542466
/// placeholder, e.g.,
24552467
///

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,17 @@ SpecifyContextualTypeForNil::create(ConstraintSystem &cs,
19671967
return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator);
19681968
}
19691969

1970+
bool SpecifyPatternType::diagnose(const Solution &solution, bool asNote) const {
1971+
CouldNotInferPatternType failure(solution, pattern, getLocator());
1972+
return failure.diagnose(asNote);
1973+
}
1974+
1975+
SpecifyPatternType *SpecifyPatternType::create(ConstraintSystem &cs,
1976+
NamedPattern *pattern,
1977+
ConstraintLocator *locator) {
1978+
return new (cs.getAllocator()) SpecifyPatternType(cs, pattern, locator);
1979+
}
1980+
19701981
bool SpecifyTypeForPlaceholder::diagnose(const Solution &solution,
19711982
bool asNote) const {
19721983
CouldNotInferPlaceholderType failure(solution, getLocator());

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12921,6 +12921,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1292112921
case FixKind::IgnoreInvalidResultBuilderBody:
1292212922
case FixKind::IgnoreResultBuilderWithReturnStmts:
1292312923
case FixKind::SpecifyContextualTypeForNil:
12924+
case FixKind::SpecifyPatternType:
1292412925
case FixKind::AllowRefToInvalidDecl:
1292512926
case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember:
1292612927
case FixKind::AllowCheckedCastCoercibleOptionalType:

lib/Sema/ConstraintLocator.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
598598
out << '@';
599599
expr->getLoc().print(out, *sm);
600600
}
601+
} else if (auto *pattern = anchor.dyn_cast<Pattern *>()) {
602+
out << Pattern::getKindName(pattern->getKind()) << "Pattern";
603+
if (sm) {
604+
out << '@';
605+
pattern->getLoc().print(out, *sm);
606+
}
601607
}
602608

603609
for (auto elt : getPath()) {

test/ClangImporter/MixedSource/can_import_objc_idempotent.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
let (r, s) = square.divided(atDistance: 50, from: .minXEdge)
3333
// expected-error@-1 {{cannot infer contextual base in reference to member 'minXEdge'}}
34+
// expected-error@-2 {{could not infer type of variable 'r'}}
35+
// expected-error@-3 {{could not infer type of variable 's'}}
3436
#endif
3537

3638
#if canImport(MixedWithHeader)

test/NameLookup/name_lookup2.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ func test_varname_binding() {
5555
var ((), (g1, g2), h) = ((), (e, d), e)
5656
var (j, k, l) = callee1()
5757
var (m, n) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
58-
var (o, p, q, r) = callee1() // expected-error{{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, _)', tuples have a different number of elements}}
58+
var (o, p, q, r) = callee1()
59+
// expected-error@-1 {{'(Int, Int, Int)' is not convertible to '(Int, Int, Int, _)', tuples have a different number of elements}}
60+
// expected-error@-2 {{could not infer type of variable 'r'}}
5961
}
6062

6163
//===----------------------------------------------------------------------===//

test/Parse/enum_element_pattern_swift4.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func testE(e: E) {
4343
// expected-note@-1 {{remove associated values to make the pattern match}} {{12-14=}}
4444
case .D(let payload) = e // expected-error {{pattern with associated values does not match enum case 'D'}}
4545
// expected-note@-1 {{remove associated values to make the pattern match}} {{12-25=}}
46+
// expected-error@-2 {{could not infer type of variable 'payload'}}
4647
else { return }
4748
}
4849

test/Parse/matching_patterns.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ enum Voluntary<T> : Equatable {
116116
var n : Voluntary<Int> = .Naught
117117
if case let .Naught(value) = n {} // expected-error{{pattern with associated values does not match enum case 'Naught'}}
118118
// expected-note@-1 {{remove associated values to make the pattern match}} {{20-27=}}
119+
// expected-error@-2 {{could not infer type of variable 'value'}}
119120
if case let .Naught(value1, value2, value3) = n {} // expected-error{{pattern with associated values does not match enum case 'Naught'}}
120121
// expected-note@-1 {{remove associated values to make the pattern match}} {{20-44=}}
122+
// expected-error@-2 {{could not infer type of variable 'value1'}}
123+
// expected-error@-3 {{could not infer type of variable 'value2'}}
124+
// expected-error@-4 {{could not infer type of variable 'value3'}}
121125

122126

123127

@@ -142,7 +146,7 @@ case Voluntary<Int>.Mere,
142146
()
143147
case .Twain,
144148
.Twain(_), // expected-warning {{enum case 'Twain' has 2 associated values; matching them as a tuple is deprecated}}
145-
// expected-note@-69 {{'Twain' declared here}}
149+
// expected-note@-73 {{'Twain' declared here}}
146150
.Twain(_, _),
147151
.Twain(_, _, _): // expected-error{{tuple pattern has the wrong length for tuple type '(Int, Int)'}}
148152
()
@@ -330,3 +334,4 @@ let (responseObject: Int?) = op1
330334
// expected-error @-1 {{expected ',' separator}} {{25-25=,}}
331335
// expected-error @-2 {{expected pattern}}
332336
// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}}
337+
// expected-error @-4 {{could not infer type of variable 'Int'}}

test/expr/expressions.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ func errorRecovery() {
157157

158158
// <rdar://problem/22426860> CrashTracer: [USER] swift at …mous_namespace::ConstraintGenerator::getTypeForPattern + 698
159159
var (g1, g2, g3) = (1, 2) // expected-error {{'(Int, Int)' is not convertible to '(Int, Int, _)', tuples have a different number of elements}}
160+
// expected-error@-1 {{could not infer type of variable 'g3'}}
160161
var (h1, h2) = (1, 2, 3) // expected-error {{'(Int, Int, Int)' is not convertible to '(Int, Int)', tuples have a different number of elements}}
161162
var i: (Bool, Bool) = makeTuple() // expected-error {{cannot convert value of type '(String, Int)' to specified type '(Bool, Bool)'}}
162163
}

test/stmt/foreach.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,13 @@ func testRepeated(ri: RepeatedSequence<Int>) {
231231
func sr_12398(arr1: [Int], arr2: [(a: Int, b: String)]) {
232232
for (x, y) in arr1 {}
233233
// expected-error@-1 {{tuple pattern cannot match values of non-tuple type 'Int'}}
234+
// expected-error@-2 {{could not infer type of variable 'x'}}
235+
// expected-error@-3 {{could not infer type of variable 'y'}}
234236

235237
for (x, y, _) in arr2 {}
236238
// expected-error@-1 {{pattern cannot match values of type '(a: Int, b: String)'}}
239+
// expected-error@-2 {{could not infer type of variable 'x'}}
240+
// expected-error@-3 {{could not infer type of variable 'y'}}
237241
}
238242

239243
// rdar://62339835

test/stmt/if_while_var.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ guard let _ = nonOptionalEnum() else { fatalError() } // expected-error{{initial
5151
let optional: String? = nil
5252
if case let optional? { _ = optional } // expected-error{{variable binding in a condition requires an initializer}}
5353
if case let .some(optional) { _ = optional } // expected-error{{variable binding in a condition requires an initializer}}
54+
// expected-error@-1 {{could not infer type of variable 'optional'}}
5455
if case .some(let optional) { _ = optional } // expected-error{{variable binding in a condition requires an initializer}}
56+
// expected-error@-1 {{could not infer type of variable 'optional'}}
5557

5658
if case let x? = nonOptionalStruct() { _ = x } // expected-error{{'?' pattern cannot match values of type 'NonOptionalStruct'}}
5759
if case let x? = nonOptionalEnum() { _ = x } // expected-error{{'?' pattern cannot match values of type 'NonOptionalEnum'}}

0 commit comments

Comments
 (0)