Skip to content

Commit e365b6b

Browse files
Implicitly untuple patterns if applicable and add warnings.
1 parent 8eaf81b commit e365b6b

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3389,6 +3389,18 @@ ERROR(pattern_type_mismatch_context,none,
33893389

33903390
ERROR(tuple_pattern_in_non_tuple_context,none,
33913391
"tuple pattern cannot match values of the non-tuple type %0", (Type))
3392+
WARNING(matching_pattern_with_many_assoc_values, none,
3393+
"cannot match several associated values at once, "
3394+
"implicitly tupling the associated values and trying to match that "
3395+
"instead", ())
3396+
WARNING(matching_tuple_pattern_with_many_assoc_values,none,
3397+
"a tuple pattern cannot match several associated values at once, "
3398+
"implicitly tupling the associated values and trying to match "
3399+
"that instead", ())
3400+
WARNING(matching_many_patterns_with_tupled_assoc_value,none,
3401+
"the enum case has a single tuple as an associated value, but "
3402+
"there are several patterns here, implicitly tupling the patterns "
3403+
"and trying to match that instead", ())
33923404
ERROR(closure_argument_list_tuple,none,
33933405
"contextual closure type %0 expects %1 argument%s1, "
33943406
"but %2 %select{were|was}3 used in closure body", (Type, unsigned, unsigned, bool))

lib/Sema/TypeCheckPattern.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,46 @@ bool TypeChecker::typeCheckPattern(Pattern *P, DeclContext *dc,
10201020
llvm_unreachable("bad pattern kind!");
10211021
}
10221022

1023+
namespace {
1024+
1025+
/// We need to allow particular matches for backwards compatibility, so we
1026+
/// "repair" the pattern if needed, so that the exhaustiveness checker receives
1027+
/// well-formed input. Also emit diagnostics warning the user to fix their code.
1028+
///
1029+
/// See SR-11160 and SR-11212 for more discussion.
1030+
//
1031+
// type ~ (T1, ..., Tn) (n >= 2)
1032+
// 1a. pat ~ ((P1, ..., Pm)) (m >= 2)
1033+
// 1b. pat
1034+
// type ~ ((T1, ..., Tn)) (n >= 2)
1035+
// 2. pat ~ (P1, ..., Pm) (m >= 2)
1036+
void implicitlyUntuplePatternIfApplicable(TypeChecker *TC,
1037+
Pattern *&enumElementInnerPat,
1038+
Type enumPayloadType) {
1039+
if (auto *tupleType = dyn_cast<TupleType>(enumPayloadType.getPointer())) {
1040+
if (tupleType->getNumElements() >= 2
1041+
&& enumElementInnerPat->getKind() == PatternKind::Paren) {
1042+
auto *semantic = enumElementInnerPat->getSemanticsProvidingPattern();
1043+
if (auto *tuplePattern = dyn_cast<TuplePattern>(semantic)) {
1044+
if (tuplePattern->getNumElements() >= 2) {
1045+
TC->diagnose(tuplePattern->getLoc(),
1046+
diag::matching_tuple_pattern_with_many_assoc_values);
1047+
enumElementInnerPat = semantic;
1048+
}
1049+
} else {
1050+
TC->diagnose(enumElementInnerPat->getLoc(),
1051+
diag::matching_pattern_with_many_assoc_values);
1052+
}
1053+
}
1054+
} else if (auto *tupleType = enumPayloadType->getAs<TupleType>()) {
1055+
if (tupleType->getNumElements() >= 2
1056+
&& enumElementInnerPat->getKind() == PatternKind::Tuple)
1057+
TC->diagnose(enumElementInnerPat->getLoc(),
1058+
diag::matching_many_patterns_with_tupled_assoc_value);
1059+
}
1060+
}
1061+
}
1062+
10231063
/// Perform top-down type coercion on the given pattern.
10241064
bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution,
10251065
Type type,
@@ -1513,6 +1553,9 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution,
15131553
auto newSubOptions = subOptions;
15141554
newSubOptions.setContext(TypeResolverContext::EnumPatternPayload);
15151555
newSubOptions |= TypeResolutionFlags::FromNonInferredPattern;
1556+
1557+
::implicitlyUntuplePatternIfApplicable(this, sub, elementType);
1558+
15161559
if (coercePatternToType(sub, resolution, elementType, newSubOptions))
15171560
return true;
15181561
EEP->setSubPattern(sub);

test/Parse/matching_patterns.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ enum Voluntary<T> : Equatable {
100100
()
101101

102102
case .Twain(), // expected-error{{tuple pattern has the wrong length for tuple type '(T, T)'}}
103-
.Twain(_),
103+
.Twain(_), // expected-warning {{cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
104104
.Twain(_, _),
105105
.Twain(_, _, _): // expected-error{{tuple pattern has the wrong length for tuple type '(T, T)'}}
106106
()
@@ -143,7 +143,7 @@ case Voluntary<Int>.Mere,
143143
.Mere(_):
144144
()
145145
case .Twain,
146-
.Twain(_),
146+
.Twain(_), // expected-warning {{cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
147147
.Twain(_, _),
148148
.Twain(_, _, _): // expected-error{{tuple pattern has the wrong length for tuple type '(Int, Int)'}}
149149
()

test/Sema/exhaustive_switch.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,8 +1215,7 @@ func sr11160_extra() {
12151215
}
12161216

12171217
// SR-11212 tests: Some of the tests here rely on compiler bugs related to
1218-
// implicit (un)tupling in patterns. When you add a warning for the erroneous
1219-
// cases, feel free to add expected notes as appropriate.
1218+
// implicit (un)tupling in patterns.
12201219
enum SR11212Tests {
12211220

12221221
enum Untupled {
@@ -1226,12 +1225,14 @@ enum SR11212Tests {
12261225
func sr11212_content_untupled_pattern_tupled(u: Untupled) -> (Int, Int) {
12271226
switch u {
12281227
case .upair((let x, let y)): return (x, y)
1228+
// expected-warning@-1 {{a tuple pattern cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
12291229
}
12301230
}
12311231

12321232
func sr11212_content_untupled_pattern_tupled_nested(u: Untupled) -> (Int, Int) {
12331233
switch u {
12341234
case .upair(let (x, y)): return (x, y)
1235+
// expected-warning@-1 {{a tuple pattern cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
12351236
}
12361237
}
12371238

@@ -1244,6 +1245,7 @@ enum SR11212Tests {
12441245
func sr11212_content_untupled_pattern_ambiguous(u: Untupled) -> (Int, Int) {
12451246
switch u {
12461247
case .upair(let u_): return u_
1248+
// expected-warning@-1 {{cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
12471249
}
12481250
}
12491251

@@ -1266,6 +1268,7 @@ enum SR11212Tests {
12661268
func sr11212_content_tupled_pattern_untupled(t: Tupled) -> (Int, Int) {
12671269
switch t {
12681270
case .tpair(let x, let y): return (x, y)
1271+
// expected-warning@-1 {{the enum case has a single tuple as an associated value, but there are several patterns here, implicitly tupling the patterns and trying to match that instead}}
12691272
}
12701273
}
12711274

@@ -1294,6 +1297,7 @@ enum SR11212Tests {
12941297
func sr11212_content_generic_pattern_untupled(b: Box<(Int, Int)>) -> (Int, Int) {
12951298
switch b {
12961299
case .box(let x, let y): return (x, y)
1300+
// expected-warning@-1 {{the enum case has a single tuple as an associated value, but there are several patterns here, implicitly tupling the patterns and trying to match that instead}}
12971301
}
12981302
}
12991303

test/decl/enum/enumtest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ func testDirection() {
275275
i = x
276276
break
277277

278-
case .NorthEast(let x):
278+
case .NorthEast(let x): // expected-warning {{cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead}}
279279
i = x.distanceEast
280280
break
281281
}

0 commit comments

Comments
 (0)