Skip to content

Commit d650917

Browse files
committed
[Sema] Ensure postfix operator is not a part of implicit member chain
But a part of optional chain.
1 parent 8e8e4d0 commit d650917

File tree

2 files changed

+53
-22
lines changed

2 files changed

+53
-22
lines changed

lib/Sema/PreCheckTarget.cpp

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,6 @@ static Expr *getMemberChainSubExpr(Expr *expr) {
333333
return SE->getBase();
334334
} else if (auto *DSE = dyn_cast<DotSelfExpr>(expr)) {
335335
return DSE->getSubExpr();
336-
} else if (auto *PUE = dyn_cast<PostfixUnaryExpr>(expr)) {
337-
return PUE->getOperand();
338336
} else if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr)) {
339337
return CCE->getBase();
340338
} else {
@@ -1104,6 +1102,9 @@ class PreCheckTarget final : public ASTWalker {
11041102
/// resolution failure, or `nullptr` if transformation is not applicable.
11051103
Expr *simplifyTypeConstructionWithLiteralArg(Expr *E);
11061104

1105+
/// Pull some operator expressions into the optional chain.
1106+
OptionalEvaluationExpr *hoistOptionalEvaluationExprIfNeeded(Expr *E);
1107+
11071108
/// Whether the given expression "looks like" a (possibly sugared) type. For
11081109
/// example, `(foo, bar)` "looks like" a type, but `foo + bar` does not.
11091110
bool exprLooksLikeAType(Expr *expr);
@@ -1469,26 +1470,8 @@ class PreCheckTarget final : public ASTWalker {
14691470
return Action::Continue(result);
14701471
}
14711472

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-
}
1473+
if (auto *OEE = hoistOptionalEvaluationExprIfNeeded(expr)) {
1474+
return Action::Continue(OEE);
14921475
}
14931476

14941477
auto *parent = Parent.getAsExpr();
@@ -2651,6 +2634,45 @@ Expr *PreCheckTarget::simplifyTypeConstructionWithLiteralArg(Expr *E) {
26512634
: nullptr;
26522635
}
26532636

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+
26542676
bool ConstraintSystem::preCheckTarget(SyntacticElementTarget &target) {
26552677
auto *DC = target.getDeclContext();
26562678
auto &ctx = DC->getASTContext();

test/expr/delayed-ident/member_chains.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ let _: ImplicitMembers = .implicit[funcOptional: ()]()!.another
223223
let _: ImplicitMembers? = .implicit[funcOptional: ()]()?.another
224224
let _: ImplicitMembers = .implicit[optionalFunc: ()]!().another
225225
let _: ImplicitMembers? = .implicit[optionalFunc: ()]?().another
226+
let _: ImplicitMembers = .implicit.self
226227

227228
func implicit(_ i: inout ImplicitMembers) {
228229
if i == .implicit {}
@@ -373,3 +374,11 @@ func rdar68094328() {
373374
foo(C.bar, .init(string: str).baz(str: "")) // Ok
374375
}
375376
}
377+
378+
// Ensure postfix operator is not a part of implicit operator chain.
379+
postfix operator ^
380+
postfix func ^ (_ lhs: ImplicitMembers) -> Int { 0 }
381+
func acceptInt(_ x: Int) {}
382+
func postfixOpIsNotAMemberChain() {
383+
acceptInt(.implicit.another^)
384+
}

0 commit comments

Comments
 (0)