Skip to content

Commit f25e92f

Browse files
authored
Merge pull request #70319 from hamishknight/context-switch
[Sema] Correctly re-contextualize `if`/`switch` exprs in lazy vars
2 parents 828f589 + 29dac43 commit f25e92f

File tree

5 files changed

+219
-27
lines changed

5 files changed

+219
-27
lines changed

include/swift/AST/Pattern.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ class EnumElementPattern : public Pattern {
566566
void setSubPattern(Pattern *p) { SubPattern = p; }
567567

568568
DeclContext *getDeclContext() const { return DC; }
569+
void setDeclContext(DeclContext *newDC) { DC = newDC; }
569570

570571
DeclNameRef getName() const { return Name; }
571572

@@ -706,6 +707,12 @@ class ExprPattern : public Pattern {
706707

707708
DeclContext *getDeclContext() const { return DC; }
708709

710+
void setDeclContext(DeclContext *newDC) {
711+
DC = newDC;
712+
if (MatchVar)
713+
MatchVar->setDeclContext(newDC);
714+
}
715+
709716
/// The match expression if it has been computed, \c nullptr otherwise.
710717
/// Should only be used by the ASTDumper and ASTWalker.
711718
Expr *getCachedMatchExpr() const { return MatchExpr; }

lib/Sema/MiscDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3912,7 +3912,8 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
39123912
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
39133913
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(E)) {
39143914
// Diagnose a SingleValueStmtExpr in a context that we do not currently
3915-
// support.
3915+
// support. If we start allowing these in arbitrary places, we'll need
3916+
// to ensure that autoclosures correctly contextualize them.
39163917
if (!ValidSingleValueStmtExprs.contains(SVE)) {
39173918
Diags.diagnose(SVE->getLoc(), diag::single_value_stmt_out_of_place,
39183919
SVE->getStmt()->getKind());

lib/Sema/TypeCheckStorage.cpp

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,9 @@ synthesizeReadCoroutineGetterBody(AccessorDecl *getter, ASTContext &ctx) {
15521552
namespace {
15531553
/// This ASTWalker explores an expression tree looking for expressions (which
15541554
/// are DeclContext's) and changes their parent DeclContext to NewDC.
1555+
/// TODO: We ought to consider merging this with
1556+
/// ContextualizeClosuresAndMacros, or better yet removing it in favor of
1557+
/// avoiding the recontextualization for lazy vars.
15551558
class RecontextualizeClosures : public ASTWalker {
15561559
DeclContext *NewDC;
15571560
public:
@@ -1567,34 +1570,42 @@ namespace {
15671570
CE->setParent(NewDC);
15681571
return Action::SkipChildren(E);
15691572
}
1570-
1571-
if (auto CLE = dyn_cast<CaptureListExpr>(E)) {
1572-
// Make sure to recontextualize any decls in the capture list as well.
1573-
for (auto &CLE : CLE->getCaptureList()) {
1574-
CLE.getVar()->setDeclContext(NewDC);
1575-
CLE.PBD->setDeclContext(NewDC);
1576-
}
1577-
}
1578-
1579-
// Unlike a closure, a TapExpr is not a DeclContext, so we need to
1580-
// recontextualize its variable and then anything else in its body.
1581-
// FIXME: Might be better to change walkToDeclPre() and walkToStmtPre()
1582-
// below, but I don't know what other effects that might have.
1583-
if (auto TE = dyn_cast<TapExpr>(E)) {
1584-
TE->getVar()->setDeclContext(NewDC);
1585-
for (auto node : TE->getBody()->getElements())
1586-
node.walk(RecontextualizeClosures(NewDC));
1587-
}
15881573

15891574
return Action::Continue(E);
15901575
}
15911576

1592-
/// We don't want to recurse into declarations or statements.
1593-
PreWalkAction walkToDeclPre(Decl *) override {
1594-
return Action::SkipChildren();
1577+
PreWalkResult<Pattern *> walkToPatternPre(Pattern *P) override {
1578+
if (auto *EP = dyn_cast<ExprPattern>(P))
1579+
EP->setDeclContext(NewDC);
1580+
if (auto *EP = dyn_cast<EnumElementPattern>(P))
1581+
EP->setDeclContext(NewDC);
1582+
1583+
return Action::Continue(P);
15951584
}
1585+
15961586
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
1597-
return Action::SkipChildren(S);
1587+
// The ASTWalker doesn't walk the case body variables, contextualize them
1588+
// ourselves.
1589+
if (auto *CS = dyn_cast<CaseStmt>(S)) {
1590+
for (auto *CaseVar : CS->getCaseBodyVariablesOrEmptyArray())
1591+
CaseVar->setDeclContext(NewDC);
1592+
}
1593+
return Action::Continue(S);
1594+
}
1595+
1596+
PreWalkAction walkToDeclPre(Decl *D) override {
1597+
D->setDeclContext(NewDC);
1598+
1599+
// Auxiliary decls need to have their contexts adjusted too.
1600+
if (auto *VD = dyn_cast<VarDecl>(D)) {
1601+
VD->visitAuxiliaryDecls([&](VarDecl *D) {
1602+
D->setDeclContext(NewDC);
1603+
});
1604+
}
1605+
1606+
// Skip walking the children of any Decls that are also DeclContexts,
1607+
// they will already have the right parent.
1608+
return Action::SkipChildrenIf(isa<DeclContext>(D));
15981609
}
15991610
};
16001611
} // end anonymous namespace

test/SILGen/if_expr.swift

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2-
// RUN: %target-swift-emit-ir %s
1+
// RUN: %target-swift-emit-silgen -enable-experimental-feature ThenStatements %s | %FileCheck %s
2+
// RUN: %target-swift-emit-ir -enable-experimental-feature ThenStatements %s
3+
4+
// Needed for experimental features
5+
// REQUIRES: asserts
36

47
func foo() -> Int {
58
if .random() { 1 } else { 2 }
@@ -580,3 +583,79 @@ func testConditionalCast<T>(_ x: Any) -> T? {
580583
nil
581584
}
582585
}
586+
587+
@propertyWrapper
588+
struct Wrapper<T> {
589+
var wrappedValue: T
590+
}
591+
592+
// rdar://119158202 - Make sure we correctly contextualize local bindings.
593+
func testLazyLocal(_ x: Int?) {
594+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1aL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
595+
lazy var a = if let x { x } else { 0 }
596+
_ = a
597+
598+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1bL_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
599+
lazy var b = if .random() {
600+
let x = ""
601+
then x
602+
} else {
603+
""
604+
}
605+
_ = b
606+
607+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1cL_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
608+
lazy var c = if .random() {
609+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1cL_SSvg1xL2_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
610+
lazy var x = ""
611+
then x
612+
} else {
613+
""
614+
}
615+
_ = c
616+
617+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1dL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
618+
lazy var d = if .random() {
619+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1dL_Sivg1yL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
620+
lazy var y = if let x { x } else { 0 }
621+
then y
622+
} else {
623+
0
624+
}
625+
_ = d
626+
627+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr13testLazyLocalyySiSgF1eL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
628+
lazy var e = if .random() {
629+
@Wrapper
630+
var x = 0
631+
then x
632+
} else {
633+
0
634+
}
635+
_ = e
636+
}
637+
638+
struct LazyProp {
639+
var a: Int?
640+
641+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s7if_expr8LazyPropV1bSivg : $@convention(method) (@inout LazyProp) -> Int
642+
lazy var b = if let a { a } else { 0 }
643+
644+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s7if_expr8LazyPropV1cSivg : $@convention(method) (@inout LazyProp) -> Int
645+
lazy var c = if .random() {
646+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr8LazyPropV1cSivg1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
647+
lazy var x = 0
648+
then x
649+
} else {
650+
0
651+
}
652+
653+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s7if_expr8LazyPropV1dSivg : $@convention(method) (@inout LazyProp) -> Int
654+
lazy var d = if .random() {
655+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s7if_expr8LazyPropV1dSivg1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, @inout_aliasable LazyProp) -> Int
656+
lazy var x = if case let a? = a { a } else { 0 }
657+
then x
658+
} else {
659+
0
660+
}
661+
}

test/SILGen/switch_expr.swift

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2-
// RUN: %target-swift-emit-ir %s
1+
// RUN: %target-swift-emit-silgen -enable-experimental-feature ThenStatements %s | %FileCheck %s
2+
// RUN: %target-swift-emit-ir -enable-experimental-feature ThenStatements %s
3+
4+
// Needed for experimental features
5+
// REQUIRES: asserts
36

47
func foo() -> Int {
58
switch Bool.random() {
@@ -775,3 +778,94 @@ func testConditionalCast<T>(_ x: Any, _ y: Int) -> T? {
775778
x as? T
776779
}
777780
}
781+
782+
@propertyWrapper
783+
struct Wrapper<T> {
784+
var wrappedValue: T
785+
}
786+
787+
// rdar://119158202 - Make sure we correctly contextualize local bindings.
788+
func testLazyLocal(_ x: Int?) {
789+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1aL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
790+
lazy var a = switch x { case let x?: x case nil: 0 }
791+
_ = a
792+
793+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1bL_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
794+
lazy var b = switch Bool.random() {
795+
case true:
796+
let x = ""
797+
then x
798+
case false:
799+
""
800+
}
801+
_ = b
802+
803+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1cL_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
804+
lazy var c = switch Bool.random() {
805+
case true:
806+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1cL_SSvg1xL3_SSvg : $@convention(thin) (@guaranteed { var Optional<String> }) -> @owned String
807+
lazy var x = ""
808+
then x
809+
case false:
810+
""
811+
}
812+
_ = c
813+
814+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1dL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
815+
lazy var d = switch Bool.random() {
816+
case true:
817+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1dL_Sivg1yL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, Optional<Int>) -> Int
818+
lazy var y = switch x { case let x?: x case nil: 0 }
819+
then y
820+
case false:
821+
0
822+
}
823+
_ = d
824+
825+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1eL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
826+
lazy var e = switch Bool.random() {
827+
case true:
828+
@Wrapper
829+
var x = 0
830+
then x
831+
case false:
832+
0
833+
}
834+
_ = e
835+
836+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr13testLazyLocalyySiSgF1fL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
837+
lazy var f = switch 0 {
838+
case 1:
839+
1
840+
default:
841+
0
842+
}
843+
_ = f
844+
}
845+
846+
struct LazyProp {
847+
var a: Int?
848+
849+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s11switch_expr8LazyPropV1bSivg : $@convention(method) (@inout LazyProp) -> Int
850+
lazy var b = switch a { case let a?: a case nil: 0 }
851+
852+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s11switch_expr8LazyPropV1cSivg : $@convention(method) (@inout LazyProp) -> Int
853+
lazy var c = switch Bool.random() {
854+
case true:
855+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr8LazyPropV1cSivg1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
856+
lazy var x = 0
857+
then x
858+
case false:
859+
0
860+
}
861+
862+
// CHECK-LABEL: sil hidden [lazy_getter] [noinline] [ossa] @$s11switch_expr8LazyPropV1dSivg : $@convention(method) (@inout LazyProp) -> Int
863+
lazy var d = switch Bool.random() {
864+
case true:
865+
// CHECK-LABEL: sil private [lazy_getter] [noinline] [ossa] @$s11switch_expr8LazyPropV1dSivg1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }, @inout_aliasable LazyProp) -> Int
866+
lazy var x = switch a { case let a?: a case nil: 0 }
867+
then x
868+
case false:
869+
0
870+
}
871+
}

0 commit comments

Comments
 (0)