Skip to content

Commit 231e333

Browse files
authored
Merge pull request #76981 from rintaro/precheck-optional-eval-expr
[Parse/Sema] Move OptionalEvaluationExpr wrapping to PreCheckTarget
2 parents 85c9a89 + ffeaa71 commit 231e333

File tree

7 files changed

+91
-112
lines changed

7 files changed

+91
-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: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ 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();
334336
} else if (auto *CCE = dyn_cast<CodeCompletionExpr>(expr)) {
335337
return CCE->getBase();
336338
} else {
@@ -345,6 +347,16 @@ UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) {
345347
return dyn_cast<UnresolvedMemberExpr>(expr);
346348
}
347349

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+
348360
/// Whether this expression sits at the end of a chain of member accesses.
349361
static bool isMemberChainTail(Expr *expr, Expr *parent) {
350362
assert(expr && "isMemberChainTail called with null expr!");
@@ -1090,6 +1102,9 @@ class PreCheckTarget final : public ASTWalker {
10901102
/// resolution failure, or `nullptr` if transformation is not applicable.
10911103
Expr *simplifyTypeConstructionWithLiteralArg(Expr *E);
10921104

1105+
/// Pull some operator expressions into the optional chain.
1106+
OptionalEvaluationExpr *hoistOptionalEvaluationExprIfNeeded(Expr *E);
1107+
10931108
/// Whether the given expression "looks like" a (possibly sugared) type. For
10941109
/// example, `(foo, bar)` "looks like" a type, but `foo + bar` does not.
10951110
bool exprLooksLikeAType(Expr *expr);
@@ -1416,22 +1431,6 @@ class PreCheckTarget final : public ASTWalker {
14161431
return Action::Continue(expr);
14171432
}
14181433

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-
14351434
// Check if there are any BindOptionalExpr in the tree which
14361435
// wrap DiscardAssignmentExpr, such situation corresponds to syntax
14371436
// like - `_? = <value>`, since it doesn't really make
@@ -1471,16 +1470,27 @@ class PreCheckTarget final : public ASTWalker {
14711470
return Action::Continue(result);
14721471
}
14731472

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+
14761477
auto *parent = Parent.getAsExpr();
14771478
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).
14781482
if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) {
14791483
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);
14821485
}
14831486
}
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;
14841494
}
14851495
return Action::Continue(expr);
14861496
}
@@ -2624,6 +2634,45 @@ Expr *PreCheckTarget::simplifyTypeConstructionWithLiteralArg(Expr *E) {
26242634
: nullptr;
26252635
}
26262636

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

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) {

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 member chain.
379+
postfix operator ^
380+
postfix func ^ (_ lhs: ImplicitMembers) -> Int { 0 }
381+
func acceptInt(_ x: Int) {}
382+
func postfixOpIsNotAMemberChain() {
383+
acceptInt(.implicit.another^)
384+
}

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)