Skip to content

Commit 8e8e4d0

Browse files
committed
[Parse/Sema] Move OptionalEvaluationExpr wrapping to PreCheckTarget
This simplify the Parser diagnostics and some type checker logic.
1 parent 9461af4 commit 8e8e4d0

File tree

6 files changed

+60
-112
lines changed

6 files changed

+60
-112
lines changed

include/swift/Parse/Parser.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,8 +1774,7 @@ class Parser {
17741774
bool isExprBasic);
17751775
ParserResult<Expr> parseExprPostfixSuffix(ParserResult<Expr> inner,
17761776
bool isExprBasic,
1777-
bool periodHasKeyPathBehavior,
1778-
bool &hasBindOptional);
1777+
bool periodHasKeyPathBehavior);
17791778
ParserResult<Expr> parseExprPostfix(Diag<> ID, bool isExprBasic);
17801779
ParserResult<Expr> parseExprPrimary(Diag<> ID, bool isExprBasic);
17811780
ParserResult<Expr> parseExprUnary(Diag<> ID, bool isExprBasic);

lib/Parse/ParseExpr.cpp

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -748,13 +748,11 @@ ParserResult<Expr> Parser::parseExprKeyPath() {
748748
}
749749

750750
auto inner = makeParserResult(new (Context) KeyPathDotExpr(dotLoc));
751-
bool unusedHasBindOptional = false;
752751

753752
// Inside a keypath's path, the period always behaves normally: the key path
754753
// behavior is only the separation between type and path.
755754
pathResult = parseExprPostfixSuffix(inner, /*isExprBasic=*/true,
756-
/*periodHasKeyPathBehavior=*/false,
757-
unusedHasBindOptional);
755+
/*periodHasKeyPathBehavior=*/false);
758756
parseStatus |= pathResult;
759757
}
760758

@@ -1229,10 +1227,7 @@ getMagicIdentifierLiteralKind(tok Kind, const LangOptions &Opts) {
12291227

12301228
ParserResult<Expr>
12311229
Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
1232-
bool periodHasKeyPathBehavior,
1233-
bool &hasBindOptional) {
1234-
hasBindOptional = false;
1235-
1230+
bool periodHasKeyPathBehavior) {
12361231
// Handle suffix expressions.
12371232
while (1) {
12381233
// FIXME: Better recovery.
@@ -1318,9 +1313,8 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
13181313
// In this case, we want to consume the trailing closure because
13191314
// otherwise it will get parsed as a get-set clause on a variable
13201315
// declared by `baseExpr.<complete>` which is clearly wrong.
1321-
bool hasBindOptional = false;
13221316
parseExprPostfixSuffix(makeParserResult(CCExpr), isExprBasic,
1323-
periodHasKeyPathBehavior, hasBindOptional);
1317+
periodHasKeyPathBehavior);
13241318

13251319
return makeParserCodeCompletionResult(CCExpr);
13261320
}
@@ -1410,7 +1404,6 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
14101404
if (consumeIf(tok::question_postfix)) {
14111405
Result = makeParserResult(Result, new (Context) BindOptionalExpr(
14121406
Result.get(), TokLoc, /*depth*/ 0));
1413-
hasBindOptional = true;
14141407
continue;
14151408
}
14161409

@@ -1479,19 +1472,14 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
14791472
}
14801473

14811474
llvm::TinyPtrVector<ASTNode> activeElements;
1482-
llvm::SmallPtrSet<Expr *, 4> exprsWithBindOptional;
14831475
auto ICD = parseIfConfig(
14841476
IfConfigContext::PostfixExpr,
14851477
[&](bool isActive) {
14861478
// Although we know the '#if' body starts with period,
14871479
// '#elseif'/'#else' bodies might start with invalid tokens.
14881480
if (isAtStartOfPostfixExprSuffix() || Tok.is(tok::pound_if)) {
1489-
bool exprHasBindOptional = false;
14901481
auto expr = parseExprPostfixSuffix(Result, isExprBasic,
1491-
periodHasKeyPathBehavior,
1492-
exprHasBindOptional);
1493-
if (exprHasBindOptional)
1494-
exprsWithBindOptional.insert(expr.get());
1482+
periodHasKeyPathBehavior);
14951483

14961484
if (isActive)
14971485
activeElements.push_back(expr.get());
@@ -1513,7 +1501,6 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
15131501
if (SourceMgr.rangeContainsIDEInspectionTarget(charRange) &&
15141502
L->isCodeCompletion())
15151503
status.setHasCodeCompletion();
1516-
hasBindOptional |= exprsWithBindOptional.contains(expr);
15171504
Result = makeParserResult(status, expr);
15181505
continue;
15191506
}
@@ -1609,20 +1596,8 @@ ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
16091596
if (Result.isNull())
16101597
return Result;
16111598

1612-
bool hasBindOptional = false;
1613-
Result = parseExprPostfixSuffix(Result, isExprBasic,
1614-
/*periodHasKeyPathBehavior=*/InSwiftKeyPath,
1615-
hasBindOptional);
1616-
if (Result.isParseErrorOrHasCompletion() || Result.hasCodeCompletion())
1617-
return Result;
1618-
1619-
// If we had a ? suffix expression, bind the entire postfix chain
1620-
// within an OptionalEvaluationExpr.
1621-
if (hasBindOptional) {
1622-
Result = makeParserResult(new (Context) OptionalEvaluationExpr(Result.get()));
1623-
}
1624-
1625-
return Result;
1599+
return parseExprPostfixSuffix(Result, isExprBasic,
1600+
/*periodHasKeyPathBehavior=*/InSwiftKeyPath);
16261601
}
16271602

16281603
/// parseExprPrimary

lib/Sema/PreCheckTarget.cpp

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ static Expr *getMemberChainSubExpr(Expr *expr) {
331331
return FVE->getSubExpr();
332332
} else if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
333333
return SE->getBase();
334+
} else if (auto *DSE = dyn_cast<DotSelfExpr>(expr)) {
335+
return DSE->getSubExpr();
336+
} else if (auto *PUE = dyn_cast<PostfixUnaryExpr>(expr)) {
337+
return PUE->getOperand();
334338
} else if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr)) {
335339
return CCE->getBase();
336340
} else {
@@ -345,6 +349,16 @@ UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) {
345349
return dyn_cast<UnresolvedMemberExpr>(expr);
346350
}
347351

352+
static bool isBindOptionalMemberChain(Expr *expr) {
353+
if (isa<BindOptionalExpr>(expr)) {
354+
return true;
355+
} else if (auto *base = getMemberChainSubExpr(expr)) {
356+
return isBindOptionalMemberChain(base);
357+
} else {
358+
return false;
359+
}
360+
}
361+
348362
/// Whether this expression sits at the end of a chain of member accesses.
349363
static bool isMemberChainTail(Expr *expr, Expr *parent) {
350364
assert(expr && "isMemberChainTail called with null expr!");
@@ -1416,22 +1430,6 @@ class PreCheckTarget final : public ASTWalker {
14161430
return Action::Continue(expr);
14171431
}
14181432

1419-
// Double check if there are any BindOptionalExpr remaining in the
1420-
// tree (see comment below for more details), if there are no BOE
1421-
// expressions remaining remove OptionalEvaluationExpr from the tree.
1422-
if (auto OEE = dyn_cast<OptionalEvaluationExpr>(expr)) {
1423-
bool hasBindOptional = false;
1424-
OEE->forEachChildExpr([&](Expr *expr) -> Expr * {
1425-
if (isa<BindOptionalExpr>(expr))
1426-
hasBindOptional = true;
1427-
// If at least a single BOE was found, no reason
1428-
// to walk any further in the tree.
1429-
return hasBindOptional ? nullptr : expr;
1430-
});
1431-
1432-
return Action::Continue(hasBindOptional ? OEE : OEE->getSubExpr());
1433-
}
1434-
14351433
// Check if there are any BindOptionalExpr in the tree which
14361434
// wrap DiscardAssignmentExpr, such situation corresponds to syntax
14371435
// like - `_? = <value>`, since it doesn't really make
@@ -1471,16 +1469,45 @@ class PreCheckTarget final : public ASTWalker {
14711469
return Action::Continue(result);
14721470
}
14731471

1474-
// If we find an unresolved member chain, wrap it in an
1475-
// UnresolvedMemberChainResultExpr (unless this has already been done).
1472+
// If this is an assignment operator, and the left operand is an optional
1473+
// evaluation, pull the operator into the chain.
1474+
if (auto *binExpr = dyn_cast<BinaryExpr>(expr)) {
1475+
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(binExpr->getLHS())) {
1476+
if (auto *precedence =
1477+
TypeChecker::lookupPrecedenceGroupForInfixOperator(
1478+
DC, binExpr, /*diagnose=*/false)) {
1479+
if (precedence->isAssignment()) {
1480+
binExpr->getArgs()->setExpr(0, OEE->getSubExpr());
1481+
OEE->setSubExpr(binExpr);
1482+
return Action::Continue(OEE);
1483+
}
1484+
}
1485+
}
1486+
} else if (auto *assignExpr = dyn_cast<AssignExpr>(expr)) {
1487+
if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(assignExpr->getDest())) {
1488+
assignExpr->setDest(OEE->getSubExpr());
1489+
OEE->setSubExpr(assignExpr);
1490+
return Action::Continue(OEE);
1491+
}
1492+
}
1493+
14761494
auto *parent = Parent.getAsExpr();
14771495
if (isMemberChainTail(expr, parent)) {
1496+
Expr *wrapped = expr;
1497+
// If we find an unresolved member chain, wrap it in an
1498+
// UnresolvedMemberChainResultExpr (unless this has already been done).
14781499
if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) {
14791500
if (!parent || !isa<UnresolvedMemberChainResultExpr>(parent)) {
1480-
auto *chain = new (ctx) UnresolvedMemberChainResultExpr(expr, UME);
1481-
return Action::Continue(chain);
1501+
wrapped = new (ctx) UnresolvedMemberChainResultExpr(expr, UME);
1502+
}
1503+
}
1504+
// Wrap optional chain in an OptionalEvaluationExpr.
1505+
if (isBindOptionalMemberChain(expr)) {
1506+
if (!parent || !isa<OptionalEvaluationExpr>(parent)) {
1507+
wrapped = new (ctx) OptionalEvaluationExpr(wrapped);
14821508
}
14831509
}
1510+
expr = wrapped;
14841511
}
14851512
return Action::Continue(expr);
14861513
}

lib/Sema/TypeCheckExpr.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -215,17 +215,6 @@ static Expr *makeBinOp(ASTContext &Ctx, Expr *Op, Expr *LHS, Expr *RHS,
215215
await->setSubExpr(sub);
216216
return await;
217217
}
218-
219-
// If this is an assignment operator, and the left operand is an optional
220-
// evaluation, pull the operator into the chain.
221-
if (opPrecedence && opPrecedence->isAssignment()) {
222-
if (auto optEval = dyn_cast<OptionalEvaluationExpr>(LHS)) {
223-
auto sub = makeBinOp(Ctx, Op, optEval->getSubExpr(), RHS,
224-
opPrecedence, isEndOfSequence);
225-
optEval->setSubExpr(sub);
226-
return optEval;
227-
}
228-
}
229218

230219
// If the right operand is a try or await, it's an error unless the operator
231220
// is an assignment or conditional operator and there's nothing to

lib/Sema/TypeCheckPattern.cpp

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -397,53 +397,12 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
397397
E->getRParenLoc());
398398
}
399399

400-
Pattern *convertBindingsToOptionalSome(Expr *E) {
401-
auto *expr = E->getSemanticsProvidingExpr();
402-
auto *bindExpr = dyn_cast<BindOptionalExpr>(expr);
403-
if (!bindExpr) {
404-
// Let's see if this expression prefixed with any number of '?'
405-
// has any other disjoint 'BindOptionalExpr' inside of it, if so,
406-
// we need to wrap such sub-expression into `OptionalEvaluationExpr`.
407-
bool hasDisjointChaining = false;
408-
expr->forEachChildExpr([&](Expr *subExpr) -> Expr * {
409-
// If there is `OptionalEvaluationExpr` in the AST
410-
// it means that all of possible `BindOptionalExpr`
411-
// which follow are covered by it.
412-
if (isa<OptionalEvaluationExpr>(subExpr))
413-
return nullptr;
414-
415-
if (isa<BindOptionalExpr>(subExpr)) {
416-
hasDisjointChaining = true;
417-
return nullptr;
418-
}
419-
420-
return subExpr;
421-
});
422-
423-
if (hasDisjointChaining)
424-
E = new (Context) OptionalEvaluationExpr(E);
425-
426-
return getSubExprPattern(E);
427-
}
428-
429-
auto *subExpr = convertBindingsToOptionalSome(bindExpr->getSubExpr());
430-
return OptionalSomePattern::create(Context, subExpr,
431-
bindExpr->getQuestionLoc());
400+
// Convert a x? to OptionalSome pattern.
401+
Pattern *visitBindOptionalExpr(BindOptionalExpr *E) {
402+
return OptionalSomePattern::create(
403+
Context, getSubExprPattern(E->getSubExpr()), E->getQuestionLoc());
432404
}
433405

434-
// Convert a x? to OptionalSome pattern. In the AST form, this will look like
435-
// an OptionalEvaluationExpr with an immediate BindOptionalExpr inside of it.
436-
Pattern *visitOptionalEvaluationExpr(OptionalEvaluationExpr *E) {
437-
auto *subExpr = E->getSubExpr();
438-
// We only handle the case where one or more bind expressions are subexprs
439-
// of the optional evaluation. Other cases are not simple postfix ?'s.
440-
if (!isa<BindOptionalExpr>(subExpr->getSemanticsProvidingExpr()))
441-
return nullptr;
442-
443-
return convertBindingsToOptionalSome(subExpr);
444-
}
445-
446-
447406
// Unresolved member syntax '.Element' forms an EnumElement pattern. The
448407
// element will be resolved when we type-check the pattern.
449408
Pattern *visitUnresolvedMemberExpr(UnresolvedMemberExpr *ume) {

validation-test/compiler_crashers_2_fixed/issue-55435.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ class ContainerTransition {
66
var viewControllers: [Int: String]?
77
func completeTransition() {
88
viewControllers?[Int//.max
9-
// expected-error@-1 {{no exact matches in call to subscript}}
10-
// expected-note@-2 {{found candidate with type '(Int.Type) -> Dictionary<Int, String>.SubSequence' (aka '(Int.Type) -> Slice<Dictionary<Int, String>>')}}
11-
// expected-note@-3 {{to match this opening '['}}
9+
// expected-error@-1 {{cannot convert value of type 'Int.Type' to expected argument type 'Int'}}
10+
// expected-note@-2 {{to match this opening '['}}
1211
} // expected-error {{expected ']' in expression list}}
1312
}

0 commit comments

Comments
 (0)