@@ -331,6 +331,8 @@ static Expr *getMemberChainSubExpr(Expr *expr) {
331
331
return FVE->getSubExpr ();
332
332
} else if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
333
333
return SE->getBase ();
334
+ } else if (auto *DSE = dyn_cast<DotSelfExpr>(expr)) {
335
+ return DSE->getSubExpr ();
334
336
} else if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr)) {
335
337
return CCE->getBase ();
336
338
} else {
@@ -345,6 +347,16 @@ UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) {
345
347
return dyn_cast<UnresolvedMemberExpr>(expr);
346
348
}
347
349
350
+ static bool isBindOptionalMemberChain (Expr *expr) {
351
+ if (isa<BindOptionalExpr>(expr)) {
352
+ return true ;
353
+ } else if (auto *base = getMemberChainSubExpr (expr)) {
354
+ return isBindOptionalMemberChain (base);
355
+ } else {
356
+ return false ;
357
+ }
358
+ }
359
+
348
360
// / Whether this expression sits at the end of a chain of member accesses.
349
361
static bool isMemberChainTail (Expr *expr, Expr *parent) {
350
362
assert (expr && " isMemberChainTail called with null expr!" );
@@ -1090,6 +1102,9 @@ class PreCheckTarget final : public ASTWalker {
1090
1102
// / resolution failure, or `nullptr` if transformation is not applicable.
1091
1103
Expr *simplifyTypeConstructionWithLiteralArg (Expr *E);
1092
1104
1105
+ // / Pull some operator expressions into the optional chain.
1106
+ OptionalEvaluationExpr *hoistOptionalEvaluationExprIfNeeded (Expr *E);
1107
+
1093
1108
// / Whether the given expression "looks like" a (possibly sugared) type. For
1094
1109
// / example, `(foo, bar)` "looks like" a type, but `foo + bar` does not.
1095
1110
bool exprLooksLikeAType (Expr *expr);
@@ -1416,22 +1431,6 @@ class PreCheckTarget final : public ASTWalker {
1416
1431
return Action::Continue (expr);
1417
1432
}
1418
1433
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
-
1435
1434
// Check if there are any BindOptionalExpr in the tree which
1436
1435
// wrap DiscardAssignmentExpr, such situation corresponds to syntax
1437
1436
// like - `_? = <value>`, since it doesn't really make
@@ -1471,16 +1470,27 @@ class PreCheckTarget final : public ASTWalker {
1471
1470
return Action::Continue (result);
1472
1471
}
1473
1472
1474
- // If we find an unresolved member chain, wrap it in an
1475
- // UnresolvedMemberChainResultExpr (unless this has already been done).
1473
+ if (auto *OEE = hoistOptionalEvaluationExprIfNeeded (expr)) {
1474
+ return Action::Continue (OEE);
1475
+ }
1476
+
1476
1477
auto *parent = Parent.getAsExpr ();
1477
1478
if (isMemberChainTail (expr, parent)) {
1479
+ Expr *wrapped = expr;
1480
+ // If we find an unresolved member chain, wrap it in an
1481
+ // UnresolvedMemberChainResultExpr (unless this has already been done).
1478
1482
if (auto *UME = TypeChecker::getUnresolvedMemberChainBase (expr)) {
1479
1483
if (!parent || !isa<UnresolvedMemberChainResultExpr>(parent)) {
1480
- auto *chain = new (ctx) UnresolvedMemberChainResultExpr (expr, UME);
1481
- return Action::Continue (chain);
1484
+ wrapped = new (ctx) UnresolvedMemberChainResultExpr (expr, UME);
1482
1485
}
1483
1486
}
1487
+ // Wrap optional chain in an OptionalEvaluationExpr.
1488
+ if (isBindOptionalMemberChain (expr)) {
1489
+ if (!parent || !isa<OptionalEvaluationExpr>(parent)) {
1490
+ wrapped = new (ctx) OptionalEvaluationExpr (wrapped);
1491
+ }
1492
+ }
1493
+ expr = wrapped;
1484
1494
}
1485
1495
return Action::Continue (expr);
1486
1496
}
@@ -2624,6 +2634,45 @@ Expr *PreCheckTarget::simplifyTypeConstructionWithLiteralArg(Expr *E) {
2624
2634
: nullptr ;
2625
2635
}
2626
2636
2637
+ // / Pull some operator expressions into the optional chain if needed.
2638
+ // /
2639
+ // / foo? = newFoo // LHS of the assignment operator
2640
+ // / foo?.bar += value // LHS of 'assignment: true' precedence group operators.
2641
+ // / for?.bar++ // Postfix operator.
2642
+ // /
2643
+ // / In such cases, the operand is constructed to be an 'OperatorEvaluationExpr'
2644
+ // / wrapping the actual operand. This function hoist it and wraps the entire
2645
+ // / expression with it. Returns the result 'OperatorEvaluationExpr', or nullptr
2646
+ // / if 'expr' didn't match the condition.
2647
+ OptionalEvaluationExpr *
2648
+ PreCheckTarget::hoistOptionalEvaluationExprIfNeeded (Expr *expr) {
2649
+ if (auto *assignE = dyn_cast<AssignExpr>(expr)) {
2650
+ if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(assignE->getDest ())) {
2651
+ assignE->setDest (OEE->getSubExpr ());
2652
+ OEE->setSubExpr (assignE);
2653
+ return OEE;
2654
+ }
2655
+ } else if (auto *binaryE = dyn_cast<BinaryExpr>(expr)) {
2656
+ if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(binaryE->getLHS ())) {
2657
+ if (auto *precedence = TypeChecker::lookupPrecedenceGroupForInfixOperator (
2658
+ DC, binaryE, /* diagnose=*/ false )) {
2659
+ if (precedence->isAssignment ()) {
2660
+ binaryE->getArgs ()->setExpr (0 , OEE->getSubExpr ());
2661
+ OEE->setSubExpr (binaryE);
2662
+ return OEE;
2663
+ }
2664
+ }
2665
+ }
2666
+ } else if (auto *postfixE = dyn_cast<PostfixUnaryExpr>(expr)) {
2667
+ if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(postfixE->getOperand ())) {
2668
+ postfixE->setOperand (OEE->getSubExpr ());
2669
+ OEE->setSubExpr (postfixE);
2670
+ return OEE;
2671
+ }
2672
+ }
2673
+ return nullptr ;
2674
+ }
2675
+
2627
2676
bool ConstraintSystem::preCheckTarget (SyntacticElementTarget &target) {
2628
2677
auto *DC = target.getDeclContext ();
2629
2678
auto &ctx = DC->getASTContext ();
0 commit comments