Skip to content

Commit be8b9e5

Browse files
committed
[Constraint solver] Always produce optional types for '?' patterns.
1 parent d2b2a50 commit be8b9e5

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

lib/Sema/CSGen.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,13 +2255,22 @@ namespace {
22552255
}
22562256
return TupleType::get(tupleTypeElts, CS.getASTContext());
22572257
}
2258-
2258+
2259+
case PatternKind::OptionalSome: {
2260+
// The subpattern must have optional type.
2261+
Type subPatternType = getTypeForPattern(
2262+
cast<OptionalSomePattern>(pattern)->getSubPattern(), locator);
2263+
2264+
return OptionalType::get(subPatternType);
2265+
}
2266+
22592267
// Refutable patterns occur when checking the PatternBindingDecls in an
22602268
// if/let or while/let condition. They always require an initial value,
22612269
// so they always allow unspecified types.
2262-
#define PATTERN(Id, Parent)
2263-
#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id:
2264-
#include "swift/AST/PatternNodes.def"
2270+
case PatternKind::Is:
2271+
case PatternKind::EnumElement:
2272+
case PatternKind::Bool:
2273+
case PatternKind::Expr:
22652274
// TODO: we could try harder here, e.g. for enum elements to provide the
22662275
// enum type.
22672276
return CS.createTypeVariable(CS.getConstraintLocator(locator),

lib/Sema/MiscDiagnostics.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3782,8 +3782,15 @@ static void diagDeprecatedObjCSelectors(const DeclContext *dc,
37823782
const_cast<Expr *>(expr)->walk(ObjCSelectorWalker(dc, selectorTy));
37833783
}
37843784

3785-
3786-
3785+
/// Skip over syntactic patterns that aren't typed patterns.
3786+
static Pattern *skipNonTypeSyntacticPatterns(Pattern *pattern) {
3787+
if (auto *pp = dyn_cast<ParenPattern>(pattern))
3788+
return skipNonTypeSyntacticPatterns(pp->getSubPattern());
3789+
if (auto *vp = dyn_cast<VarPattern>(pattern))
3790+
return skipNonTypeSyntacticPatterns(vp->getSubPattern());
3791+
return pattern;
3792+
}
3793+
37873794
/// Diagnose things like this, where 'i' is an Int, not an Int?
37883795
/// if let x: Int = i {
37893796
static void
@@ -3815,7 +3822,19 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
38153822
.highlight(subExpr->getSourceRange());
38163823
return;
38173824
}
3818-
3825+
3826+
// Check for 'if let' to produce a tuned diagnostic.
3827+
if (isa<OptionalSomePattern>(skipNonTypeSyntacticPatterns(p))) {
3828+
ctx.Diags.diagnose(
3829+
cond.getIntroducerLoc(),
3830+
p->isImplicit()
3831+
? diag::condition_optional_element_pattern_not_valid_type
3832+
: diag::optional_element_pattern_not_valid_type,
3833+
subExpr->getType())
3834+
.highlight(subExpr->getSourceRange());
3835+
return;
3836+
}
3837+
38193838
ctx.Diags.diagnose(cond.getIntroducerLoc(),
38203839
diag::optional_check_nonoptional,
38213840
subExpr->getType())

test/stmt/if_while_var.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,25 @@ if var x = foo() {
2323

2424
use(x) // expected-error{{unresolved identifier 'x'}}
2525

26-
if let x = nonOptionalStruct() { } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}}
27-
if let x = nonOptionalEnum() { } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalEnum'}}
26+
if let x = nonOptionalStruct() { _ = x} // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}}
27+
if let x = nonOptionalEnum() { _ = x} // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalEnum'}}
2828

2929
guard let _ = nonOptionalStruct() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalStruct'}}
3030
guard let _ = nonOptionalEnum() else { fatalError() } // expected-error{{initializer for conditional binding must have Optional type, not 'NonOptionalEnum'}}
3131

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

3535
class B {} // expected-note * {{did you mean 'B'?}}
3636
class D : B {}// expected-note * {{did you mean 'D'?}}
3737

3838
// TODO poor recovery in these cases
3939
if let {} // expected-error {{expected '{' after 'if' condition}} expected-error {{pattern matching in a condition requires the 'case' keyword}}
40-
if let x = {} // expected-error{{'{' after 'if'}} expected-error {{variable binding in a condition requires an initializer}} expected-error{{initializer for conditional binding must have Optional type, not '() -> ()'}}
40+
if let x = { } // expected-error{{'{' after 'if'}} expected-error {{variable binding in a condition requires an initializer}} expected-error{{initializer for conditional binding must have Optional type, not '() -> ()'}}
41+
// expected-warning@-1{{value 'x' was defined but never used}}
4142

4243
if let x = foo() {
44+
_ = x
4345
} else {
4446
// TODO: more contextual error? "x is only available on the true branch"?
4547
use(x) // expected-error{{unresolved identifier 'x'}}
@@ -62,8 +64,8 @@ if var x = opt {} // expected-warning {{value 'x' was defined but never used; co
6264

6365
// <rdar://problem/20800015> Fix error message for invalid if-let
6466
let someInteger = 1
65-
if let y = someInteger {} // expected-error {{initializer for conditional binding must have Optional type, not 'Int'}}
66-
if case let y? = someInteger {} // expected-error {{'?' pattern cannot match values of type 'Int'}}
67+
if let y = someInteger { _ = y } // expected-error {{initializer for conditional binding must have Optional type, not 'Int'}}
68+
if case let y? = someInteger { _ = y } // expected-error {{'?' pattern cannot match values of type 'Int'}}
6769

6870
// Test multiple clauses on "if let".
6971
if let x = opt, let y = opt, x != y,

test/stmt/statements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ func testThrowNil() throws {
554554
// Even if the condition fails to typecheck, save it in the AST anyway; the old
555555
// condition may have contained a SequenceExpr.
556556
func r23684220(_ b: Any) {
557-
if let _ = b ?? b {} // expected-error {{initializer for conditional binding must have Optional type, not 'Any'}}
557+
if let _ = b ?? b {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'Any', so the right side is never used}}
558558
}
559559

560560

0 commit comments

Comments
 (0)