@@ -317,54 +317,78 @@ static bool findNonMembers(ArrayRef<LookupResultEntry> lookupResults,
317
317
return AllDeclRefs;
318
318
}
319
319
320
+ namespace {
321
+ enum class MemberChainKind {
322
+ OptionalBind, // A 'x?.y' optional binding chain
323
+ UnresolvedMember, // A '.foo.bar' chain
324
+ };
325
+ } // end anonymous namespace
326
+
320
327
// / Find the next element in a chain of members. If this expression is (or
321
328
// / could be) the base of such a chain, this will return \c nullptr.
322
- static Expr *getMemberChainSubExpr (Expr *expr) {
329
+ static Expr *getMemberChainSubExpr (Expr *expr, MemberChainKind kind ) {
323
330
assert (expr && " getMemberChainSubExpr called with null expr!" );
324
- if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
331
+ if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr))
325
332
return UDE->getBase ();
326
- } else if (auto *CE = dyn_cast<CallExpr>(expr)) {
333
+ if (auto *CE = dyn_cast<CallExpr>(expr))
327
334
return CE->getFn ();
328
- } else if (auto *BOE = dyn_cast<BindOptionalExpr>(expr)) {
335
+ if (auto *BOE = dyn_cast<BindOptionalExpr>(expr))
329
336
return BOE->getSubExpr ();
330
- } else if (auto *FVE = dyn_cast<ForceValueExpr>(expr)) {
337
+ if (auto *FVE = dyn_cast<ForceValueExpr>(expr))
331
338
return FVE->getSubExpr ();
332
- } else if (auto *SE = dyn_cast<SubscriptExpr>(expr)) {
339
+ if (auto *SE = dyn_cast<SubscriptExpr>(expr))
333
340
return SE->getBase ();
334
- } else if (auto *DSE = dyn_cast<DotSelfExpr>(expr)) {
341
+ if (auto *DSE = dyn_cast<DotSelfExpr>(expr))
335
342
return DSE->getSubExpr ();
336
- } else if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(expr)) {
343
+ if (auto *USE = dyn_cast<UnresolvedSpecializeExpr>(expr))
337
344
return USE->getSubExpr ();
338
- } else if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr)) {
345
+ if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr))
339
346
return CCE->getBase ();
340
- } else {
341
- return nullptr ;
347
+
348
+ if (kind == MemberChainKind::OptionalBind) {
349
+ // We allow postfix operators to be part of the optional member chain, e.g:
350
+ //
351
+ // for?.bar++
352
+ // x.y?^.foo()
353
+ //
354
+ // Note this behavior is specific to optional chains, we treat e.g
355
+ // `.foo^` as `(.foo)^`.
356
+ if (auto *PO = dyn_cast<PostfixUnaryExpr>(expr))
357
+ return PO->getOperand ();
358
+
359
+ // Unresolved member chains can themselves be nested in optional chains
360
+ // since optional chains can include postfix operators.
361
+ if (auto *UME = dyn_cast<UnresolvedMemberChainResultExpr>(expr))
362
+ return UME->getSubExpr ();
342
363
}
364
+
365
+ return nullptr ;
343
366
}
344
367
345
368
UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase (Expr *expr) {
346
- if (auto *subExpr = getMemberChainSubExpr (expr))
369
+ if (auto *subExpr =
370
+ getMemberChainSubExpr (expr, MemberChainKind::UnresolvedMember)) {
347
371
return getUnresolvedMemberChainBase (subExpr);
348
- else
349
- return dyn_cast<UnresolvedMemberExpr>(expr);
372
+ }
373
+ return dyn_cast<UnresolvedMemberExpr>(expr);
350
374
}
351
375
352
376
static bool isBindOptionalMemberChain (Expr *expr) {
353
- if (isa<BindOptionalExpr>(expr)) {
377
+ if (isa<BindOptionalExpr>(expr))
354
378
return true ;
355
- } else if (auto *base = getMemberChainSubExpr (expr)) {
379
+
380
+ if (auto *base = getMemberChainSubExpr (expr, MemberChainKind::OptionalBind))
356
381
return isBindOptionalMemberChain (base);
357
- } else {
358
- return false ;
359
- }
382
+
383
+ return false ;
360
384
}
361
385
362
386
// / Whether this expression sits at the end of a chain of member accesses.
363
- static bool isMemberChainTail (Expr *expr, Expr *parent) {
387
+ static bool isMemberChainTail (Expr *expr, Expr *parent, MemberChainKind kind ) {
364
388
assert (expr && " isMemberChainTail called with null expr!" );
365
389
// If this expression's parent is not itself part of a chain (or, this expr
366
390
// has no parent expr), this must be the tail of the chain.
367
- return !parent || getMemberChainSubExpr (parent) != expr;
391
+ return !parent || getMemberChainSubExpr (parent, kind ) != expr;
368
392
}
369
393
370
394
static bool isValidForwardReference (ValueDecl *D, DeclContext *DC,
@@ -2641,7 +2665,6 @@ Expr *PreCheckTarget::simplifyTypeConstructionWithLiteralArg(Expr *E) {
2641
2665
// /
2642
2666
// / foo? = newFoo // LHS of the assignment operator
2643
2667
// / foo?.bar += value // LHS of 'assignment: true' precedence group operators.
2644
- // / for?.bar++ // Postfix operator.
2645
2668
// /
2646
2669
// / In such cases, the operand is constructed to be an 'OperatorEvaluationExpr'
2647
2670
// / wrapping the actual operand. This function hoist it and wraps the entire
@@ -2666,12 +2689,6 @@ PreCheckTarget::hoistOptionalEvaluationExprIfNeeded(Expr *expr) {
2666
2689
}
2667
2690
}
2668
2691
}
2669
- } else if (auto *postfixE = dyn_cast<PostfixUnaryExpr>(expr)) {
2670
- if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(postfixE->getOperand ())) {
2671
- postfixE->setOperand (OEE->getSubExpr ());
2672
- OEE->setSubExpr (postfixE);
2673
- return OEE;
2674
- }
2675
2692
}
2676
2693
return nullptr ;
2677
2694
}
@@ -2680,18 +2697,21 @@ Expr *PreCheckTarget::wrapMemberChainIfNeeded(Expr *E) {
2680
2697
auto *parent = Parent.getAsExpr ();
2681
2698
Expr *wrapped = E;
2682
2699
2683
- if (!isMemberChainTail (E, parent))
2700
+ // If the parent is already wrapped, we've already formed the member chain.
2701
+ if (parent && (isa<OptionalEvaluationExpr>(parent) ||
2702
+ isa<UnresolvedMemberChainResultExpr>(parent))) {
2684
2703
return E;
2704
+ }
2685
2705
2686
2706
// If we find an unresolved member chain, wrap it in an
2687
- // UnresolvedMemberChainResultExpr (unless this has already been done) .
2688
- if (auto *UME = TypeChecker::getUnresolvedMemberChainBase (E )) {
2689
- if (!parent || !isa<UnresolvedMemberChainResultExpr>(parent ))
2707
+ // UnresolvedMemberChainResultExpr.
2708
+ if (isMemberChainTail (E, parent, MemberChainKind::UnresolvedMember )) {
2709
+ if (auto *UME = TypeChecker::getUnresolvedMemberChainBase (E ))
2690
2710
wrapped = new (Ctx) UnresolvedMemberChainResultExpr (E, UME);
2691
2711
}
2692
2712
// Wrap optional chain in an OptionalEvaluationExpr.
2693
- if (isBindOptionalMemberChain (E )) {
2694
- if (!parent || !isa<OptionalEvaluationExpr>(parent ))
2713
+ if (isMemberChainTail (E, parent, MemberChainKind::OptionalBind )) {
2714
+ if (isBindOptionalMemberChain (E ))
2695
2715
wrapped = new (Ctx) OptionalEvaluationExpr (wrapped);
2696
2716
}
2697
2717
return wrapped;
0 commit comments