Skip to content

Commit 31242bc

Browse files
committed
Remove The Parser Hack For If-Let
The parser used to rewrite if let x: T into if let x: T? This transformation is correct at face value, but relied on being able to construct TypeReprs with bogus source locations. Instead of having the parser kick semantic analysis into shape, let's perform this reinterpretation when we resolve if-let patterns in statement conditions.
1 parent 14ff43e commit 31242bc

File tree

8 files changed

+29
-28
lines changed

8 files changed

+29
-28
lines changed

Diff for: include/swift/Parse/Parser.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -1359,8 +1359,7 @@ class Parser {
13591359
ParserResult<Pattern> parsePatternTuple();
13601360

13611361
ParserResult<Pattern>
1362-
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> P,
1363-
bool isOptional);
1362+
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> P);
13641363
ParserResult<Pattern> parseMatchingPattern(bool isExprBasic);
13651364
ParserResult<Pattern> parseMatchingPatternAsLetOrVar(bool isLet,
13661365
SourceLoc VarLoc,

Diff for: lib/Parse/ParsePattern.cpp

+1-11
Original file line numberDiff line numberDiff line change
@@ -1124,8 +1124,7 @@ ParserResult<Pattern> Parser::parsePatternTuple() {
11241124
/// pattern-type-annotation ::= (':' type)?
11251125
///
11261126
ParserResult<Pattern> Parser::
1127-
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
1128-
bool isOptional) {
1127+
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result) {
11291128
if (!Tok.is(tok::colon))
11301129
return result;
11311130

@@ -1152,15 +1151,6 @@ parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
11521151
if (!repr)
11531152
repr = new (Context) ErrorTypeRepr(PreviousLoc);
11541153

1155-
// In an if-let, the actual type of the expression is Optional of whatever
1156-
// was written.
1157-
// FIXME: This is not good, `TypeRepr`s are supposed to represent what the
1158-
// user actually wrote in source (that's why they don't have any `isImplicit`
1159-
// bit). This synthesized `OptionalTypeRepr` leads to workarounds in other
1160-
// parts where we want to reason about the types as perceived by the user.
1161-
if (isOptional)
1162-
repr = new (Context) OptionalTypeRepr(repr, SourceLoc());
1163-
11641154
return makeParserResult(status, new (Context) TypedPattern(P, repr));
11651155
}
11661156

Diff for: lib/Parse/ParseStmt.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -1502,8 +1502,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
15021502
P->setImplicit();
15031503
}
15041504

1505-
ThePattern = parseOptionalPatternTypeAnnotation(ThePattern,
1506-
BindingKindStr != "case");
1505+
ThePattern = parseOptionalPatternTypeAnnotation(ThePattern);
15071506
if (ThePattern.hasCodeCompletion())
15081507
Status.setHasCodeCompletion();
15091508

@@ -2123,7 +2122,7 @@ ParserResult<Stmt> Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) {
21232122
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
21242123
T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
21252124
pattern = parseMatchingPattern(/*isExprBasic*/true);
2126-
pattern = parseOptionalPatternTypeAnnotation(pattern, /*isOptional*/false);
2125+
pattern = parseOptionalPatternTypeAnnotation(pattern);
21272126
} else if (!IsCStyleFor || Tok.is(tok::kw_var)) {
21282127
// Change the parser state to know that the pattern we're about to parse is
21292128
// implicitly mutable. Bound variables can be changed to mutable explicitly

Diff for: lib/Sema/ConstraintSystem.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -4278,7 +4278,7 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization(
42784278
bool bindPatternVarsOneWay) {
42794279
// Determine the contextual type for the initialization.
42804280
TypeLoc contextualType;
4281-
if (!isa<OptionalSomePattern>(pattern) &&
4281+
if (!(isa<OptionalSomePattern>(pattern) && !pattern->isImplicit()) &&
42824282
patternType && !patternType->isHole()) {
42834283
contextualType = TypeLoc::withoutLoc(patternType);
42844284

Diff for: lib/Sema/ConstraintSystem.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,8 @@ class SolutionApplicationTarget {
14341434
bool isOptionalSomePatternInit() const {
14351435
return kind == Kind::expression &&
14361436
expression.contextualPurpose == CTP_Initialization &&
1437-
isa<OptionalSomePattern>(expression.pattern);
1437+
isa<OptionalSomePattern>(expression.pattern) &&
1438+
!expression.pattern->isImplicit();
14381439
}
14391440

14401441
/// Whether to bind the types of any variables within the pattern via

Diff for: lib/Sema/MiscDiagnostics.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -3752,10 +3752,9 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
37523752
// checking for a type, which forced it to be promoted to a double optional
37533753
// type.
37543754
if (auto ooType = subExpr->getType()->getOptionalObjectType()) {
3755-
if (auto TP = dyn_cast<TypedPattern>(p))
3755+
if (auto OSP = dyn_cast<OptionalSomePattern>(p)) {
37563756
// Check for 'if let' to produce a tuned diagnostic.
3757-
if (isa<OptionalSomePattern>(TP->getSubPattern()) &&
3758-
TP->getSubPattern()->isImplicit()) {
3757+
if (auto *TP = dyn_cast<TypedPattern>(OSP->getSubPattern())) {
37593758
ctx.Diags.diagnose(cond.getIntroducerLoc(),
37603759
diag::optional_check_promotion,
37613760
subExpr->getType())
@@ -3764,6 +3763,7 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
37643763
ooType->getString());
37653764
return;
37663765
}
3766+
}
37673767
ctx.Diags.diagnose(cond.getIntroducerLoc(),
37683768
diag::optional_pattern_match_promotion,
37693769
subExpr->getType(), cond.getInitializer()->getType())

Diff for: lib/Sema/TypeCheckPattern.cpp

+18-7
Original file line numberDiff line numberDiff line change
@@ -655,12 +655,8 @@ Pattern *TypeChecker::resolvePattern(Pattern *P, DeclContext *DC,
655655

656656
// "if let" implicitly looks inside of an optional, so wrap it in an
657657
// OptionalSome pattern.
658-
InnerP = new (Context) OptionalSomePattern(InnerP, InnerP->getEndLoc());
659-
InnerP->setImplicit();
660-
if (auto *TP = dyn_cast<TypedPattern>(P))
661-
TP->setSubPattern(InnerP);
662-
else
663-
P = InnerP;
658+
P = new (Context) OptionalSomePattern(P, P->getEndLoc());
659+
P->setImplicit();
664660
}
665661

666662
return P;
@@ -811,9 +807,24 @@ Type PatternTypeRequest::evaluate(Evaluator &evaluator,
811807
//
812808
// Refutable patterns occur when checking the PatternBindingDecls in if/let,
813809
// while/let, and let/else conditions.
810+
case PatternKind::OptionalSome: {
811+
// Annotated if-let patterns are rewritten by TypeChecker::resolvePattern
812+
// to have an enclosing implicit (...)? pattern. If we can resolve the inner
813+
// typed pattern, the resulting pattern must have optional type.
814+
auto somePat = cast<OptionalSomePattern>(P);
815+
if (somePat->isImplicit() && isa<TypedPattern>(somePat->getSubPattern())) {
816+
auto resolution = TypeResolution::forContextual(dc, options);
817+
TypedPattern *TP = cast<TypedPattern>(somePat->getSubPattern());
818+
auto type = validateTypedPattern(resolution, TP, options);
819+
if (type && !type->hasError()) {
820+
return OptionalType::get(type);
821+
}
822+
}
823+
LLVM_FALLTHROUGH;
824+
}
825+
814826
case PatternKind::Is:
815827
case PatternKind::EnumElement:
816-
case PatternKind::OptionalSome:
817828
case PatternKind::Bool:
818829
case PatternKind::Expr:
819830
// In a let/else, these always require an initial value to match against.

Diff for: test/stmt/statements.swift

+1
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ func testThrowNil() throws {
556556
// condition may have contained a SequenceExpr.
557557
func r23684220(_ b: Any) {
558558
if let _ = b ?? b {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'Any', so the right side is never used}}
559+
// expected-error@-1 {{initializer for conditional binding must have Optional type, not 'Any'}}
559560
}
560561

561562

0 commit comments

Comments
 (0)