Skip to content

Commit 46b64d4

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 908f105 commit 46b64d4

File tree

8 files changed

+29
-28
lines changed

8 files changed

+29
-28
lines changed

include/swift/Parse/Parser.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,7 @@ class Parser {
13571357
ParserResult<Pattern> parsePatternTuple();
13581358

13591359
ParserResult<Pattern>
1360-
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> P,
1361-
bool isOptional);
1360+
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> P);
13621361
ParserResult<Pattern> parseMatchingPattern(bool isExprBasic);
13631362
ParserResult<Pattern> parseMatchingPatternAsLetOrVar(bool isLet,
13641363
SourceLoc VarLoc,

lib/Parse/ParsePattern.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,8 +1110,7 @@ ParserResult<Pattern> Parser::parsePatternTuple() {
11101110
/// pattern-type-annotation ::= (':' type)?
11111111
///
11121112
ParserResult<Pattern> Parser::
1113-
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
1114-
bool isOptional) {
1113+
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result) {
11151114
if (!Tok.is(tok::colon))
11161115
return result;
11171116

@@ -1138,15 +1137,6 @@ parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
11381137
if (!repr)
11391138
repr = new (Context) ErrorTypeRepr(PreviousLoc);
11401139

1141-
// In an if-let, the actual type of the expression is Optional of whatever
1142-
// was written.
1143-
// FIXME: This is not good, `TypeRepr`s are supposed to represent what the
1144-
// user actually wrote in source (that's why they don't have any `isImplicit`
1145-
// bit). This synthesized `OptionalTypeRepr` leads to workarounds in other
1146-
// parts where we want to reason about the types as perceived by the user.
1147-
if (isOptional)
1148-
repr = new (Context) OptionalTypeRepr(repr, SourceLoc());
1149-
11501140
return makeParserResult(status, new (Context) TypedPattern(P, repr));
11511141
}
11521142

lib/Parse/ParseStmt.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,8 +1498,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
14981498
P->setImplicit();
14991499
}
15001500

1501-
ThePattern = parseOptionalPatternTypeAnnotation(ThePattern,
1502-
BindingKindStr != "case");
1501+
ThePattern = parseOptionalPatternTypeAnnotation(ThePattern);
15031502
if (ThePattern.hasCodeCompletion()) {
15041503
Status.setHasCodeCompletion();
15051504

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

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4295,7 +4295,7 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization(
42954295
bool bindPatternVarsOneWay) {
42964296
// Determine the contextual type for the initialization.
42974297
TypeLoc contextualType;
4298-
if (!isa<OptionalSomePattern>(pattern) &&
4298+
if (!(isa<OptionalSomePattern>(pattern) && !pattern->isImplicit()) &&
42994299
patternType && !patternType->isHole()) {
43004300
contextualType = TypeLoc::withoutLoc(patternType);
43014301

lib/Sema/ConstraintSystem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,8 @@ class SolutionApplicationTarget {
14551455
bool isOptionalSomePatternInit() const {
14561456
return kind == Kind::expression &&
14571457
expression.contextualPurpose == CTP_Initialization &&
1458-
isa<OptionalSomePattern>(expression.pattern);
1458+
isa<OptionalSomePattern>(expression.pattern) &&
1459+
!expression.pattern->isImplicit();
14591460
}
14601461

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

lib/Sema/MiscDiagnostics.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,10 +3791,9 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
37913791
// checking for a type, which forced it to be promoted to a double optional
37923792
// type.
37933793
if (auto ooType = subExpr->getType()->getOptionalObjectType()) {
3794-
if (auto TP = dyn_cast<TypedPattern>(p))
3794+
if (auto OSP = dyn_cast<OptionalSomePattern>(p)) {
37953795
// Check for 'if let' to produce a tuned diagnostic.
3796-
if (isa<OptionalSomePattern>(TP->getSubPattern()) &&
3797-
TP->getSubPattern()->isImplicit()) {
3796+
if (auto *TP = dyn_cast<TypedPattern>(OSP->getSubPattern())) {
37983797
ctx.Diags.diagnose(cond.getIntroducerLoc(),
37993798
diag::optional_check_promotion,
38003799
subExpr->getType())
@@ -3803,6 +3802,7 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
38033802
ooType->getString());
38043803
return;
38053804
}
3805+
}
38063806
ctx.Diags.diagnose(cond.getIntroducerLoc(),
38073807
diag::optional_pattern_match_promotion,
38083808
subExpr->getType(), cond.getInitializer()->getType())

lib/Sema/TypeCheckPattern.cpp

Lines changed: 18 additions & 7 deletions
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-
true);
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(), /*implicit*/ true);
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);
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.

test/stmt/statements.swift

Lines changed: 1 addition & 0 deletions
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)