Skip to content

Commit 5c06e85

Browse files
authored
Merge pull request #10911 from slavapestov/raii-scrubbers-are-bad-and-should-die-4.0
Fix regression with lazy properties and add another test case [4.0]
2 parents e79af69 + a0d7055 commit 5c06e85

File tree

9 files changed

+142
-87
lines changed

9 files changed

+142
-87
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8969,6 +8969,8 @@ diagnoseAmbiguousMultiStatementClosure(ClosureExpr *closure) {
89698969
if (hasUnresolvedParams)
89708970
continue;
89718971

8972+
CS->TC.preCheckExpression(resultExpr, CS->DC);
8973+
89728974
// Obtain type of the result expression without applying solutions,
89738975
// because otherwise this might result in leaking of type variables,
89748976
// since we are not resetting result statement and if expression is

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,16 +1600,16 @@ CleanupIllFormedExpressionRAII::~CleanupIllFormedExpressionRAII() {
16001600

16011601
/// Pre-check the expression, validating any types that occur in the
16021602
/// expression and folding sequence expressions.
1603-
static bool preCheckExpression(TypeChecker &tc, Expr *&expr, DeclContext *dc) {
1604-
PreCheckExpression preCheck(tc, dc);
1603+
bool TypeChecker::preCheckExpression(Expr *&expr, DeclContext *dc) {
1604+
PreCheckExpression preCheck(*this, dc);
16051605
// Perform the pre-check.
16061606
if (auto result = expr->walk(preCheck)) {
16071607
expr = result;
16081608
return false;
16091609
}
16101610

16111611
// Pre-check failed. Clean up and return.
1612-
CleanupIllFormedExpressionRAII::doIt(expr, tc.Context);
1612+
CleanupIllFormedExpressionRAII::doIt(expr, Context);
16131613
return true;
16141614
}
16151615

@@ -1645,11 +1645,6 @@ solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
16451645
ExprTypeCheckListener *listener, ConstraintSystem &cs,
16461646
SmallVectorImpl<Solution> &viable,
16471647
TypeCheckExprOptions options) {
1648-
// First, pre-check the expression, validating any types that occur in the
1649-
// expression and folding sequence expressions.
1650-
if (preCheckExpression(*this, expr, dc))
1651-
return true;
1652-
16531648
// Attempt to solve the constraint system.
16541649
auto solution = cs.solve(expr,
16551650
convertType,
@@ -1707,6 +1702,7 @@ namespace {
17071702
llvm::SmallVector<Expr*,4> Exprs;
17081703
llvm::SmallVector<TypeLoc*, 4> TypeLocs;
17091704
llvm::SmallVector<Pattern*, 4> Patterns;
1705+
llvm::SmallVector<VarDecl*, 4> Vars;
17101706
public:
17111707

17121708
ExprCleanser(Expr *E) {
@@ -1729,6 +1725,13 @@ namespace {
17291725
return { true, P };
17301726
}
17311727

1728+
bool walkToDeclPre(Decl *D) override {
1729+
if (auto VD = dyn_cast<VarDecl>(D))
1730+
TS->Vars.push_back(VD);
1731+
1732+
return true;
1733+
}
1734+
17321735
// Don't walk into statements. This handles the BraceStmt in
17331736
// non-single-expr closures, so we don't walk into their body.
17341737
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
@@ -1757,6 +1760,13 @@ namespace {
17571760
if (P->hasType() && P->getType()->hasTypeVariable())
17581761
P->setType(Type());
17591762
}
1763+
1764+
for (auto VD : Vars) {
1765+
if (VD->hasType() && VD->getType()->hasTypeVariable()) {
1766+
VD->setType(Type());
1767+
VD->setInterfaceType(Type());
1768+
}
1769+
}
17601770
}
17611771
};
17621772
} // end anonymous namespace
@@ -1770,6 +1780,11 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
17701780
ConstraintSystem *baseCS) {
17711781
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
17721782

1783+
// First, pre-check the expression, validating any types that occur in the
1784+
// expression and folding sequence expressions.
1785+
if (preCheckExpression(expr, dc))
1786+
return true;
1787+
17731788
// Construct a constraint system from this expression.
17741789
ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes;
17751790
if (options.contains(TypeCheckExprFlags::PreferForceUnwrapToOptional))
@@ -1839,8 +1854,10 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
18391854
return true;
18401855
}
18411856

1842-
if (options.contains(TypeCheckExprFlags::SkipApplyingSolution))
1857+
if (options.contains(TypeCheckExprFlags::SkipApplyingSolution)) {
1858+
cleanup.disable();
18431859
return false;
1860+
}
18441861

18451862
// Apply the solution to the expression.
18461863
bool isDiscarded = options.contains(TypeCheckExprFlags::IsDiscarded);

lib/Sema/TypeChecker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,9 @@ static Optional<Type> getTypeOfCompletionContextExpr(
844844
CompletionTypeCheckKind kind,
845845
Expr *&parsedExpr,
846846
ConcreteDeclRef &referencedDecl) {
847+
if (TC.preCheckExpression(parsedExpr, DC))
848+
return None;
849+
847850
switch (kind) {
848851
case CompletionTypeCheckKind::Normal:
849852
// Handle below.

lib/Sema/TypeChecker.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,9 +1489,16 @@ class TypeChecker final : public LazyResolver {
14891489
SubstitutionList SelfInterfaceSubs,
14901490
SubstitutionList SelfContextSubs);
14911491

1492+
/// Pre-check the expression, validating any types that occur in the
1493+
/// expression and folding sequence expressions.
1494+
bool preCheckExpression(Expr *&expr, DeclContext *dc);
1495+
14921496
/// Sets up and solves the constraint system \p cs to type check the given
14931497
/// expression.
14941498
///
1499+
/// The expression should have already been pre-checked with
1500+
/// preCheckExpression().
1501+
///
14951502
/// \returns true if an error occurred, false otherwise.
14961503
///
14971504
/// \see typeCheckExpression

test/IDE/complete_crashes.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=BAD_MEMBERS_1 | %FileCheck %s -check-prefix=BAD_MEMBERS_1
22
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=BAD_MEMBERS_2 | %FileCheck %s -check-prefix=BAD_MEMBERS_2
3-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLOSURE_CALLED_IN_PLACE_1 | %FileCheck %s -check-prefix=WITH_GLOBAL
3+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CLOSURE_CALLED_IN_PLACE_1 | %FileCheck %s -check-prefix=WITH_GLOBAL_INT
44
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_28991372 | %FileCheck %s -check-prefix=RDAR_28991372
55

66
class BadMembers1 {
@@ -37,17 +37,23 @@ func badMembers2(_ a: BadMembers2) {
3737

3838
func globalFunc() {}
3939

40+
func globalFuncInt() -> Int { return 0 }
41+
4042
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=LET_COMPUTED | %FileCheck %s -check-prefix=WITH_GLOBAL
4143
class C {
4244
let x : Int { #^LET_COMPUTED^# }
4345
}
4446

4547
// WITH_GLOBAL: Begin completions
46-
// WITH_GLOBAL-DAG: Decl[FreeFunction]/CurrModule: globalFunc()[#Void#]{{; name=.+$}}
48+
// WITH_GLOBAL-DAG: Decl[FreeFunction]/CurrModule: globalFunc()[#Void#]; name=globalFunc()
4749
// WITH_GLOBAL: End completions
4850

4951
({ x in 2+x })(#^CLOSURE_CALLED_IN_PLACE_1^#
5052

53+
// WITH_GLOBAL_INT: Begin completions
54+
// WITH_GLOBAL_INT-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Identical]: globalFuncInt()[#Int#]; name=globalFuncInt()
55+
// WITH_GLOBAL_INT: End completions
56+
5157
// rdar://19634354
5258
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_19634354
5359
while true {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
4+
import StdlibUnittest
5+
6+
var LazyPropertyTestSuite = TestSuite("LazyProperty")
7+
8+
var lazyPropertyInitialized = 0
9+
var lazyPropertyInitialized2 = 0
10+
var lazyPropertyInitialized3 = 0
11+
12+
func lazyInitFunction() -> Int {
13+
lazyPropertyInitialized += 1
14+
return 0
15+
}
16+
17+
class LazyPropertyClass {
18+
var id : Int
19+
lazy var lazyProperty = lazyInitFunction()
20+
21+
lazy var lazyProperty2: Int = {
22+
lazyPropertyInitialized2 += 1
23+
return 0
24+
}()
25+
26+
lazy var lazyProperty3: Int! = {
27+
lazyPropertyInitialized3 += 1
28+
return 0
29+
}()
30+
31+
init(_ ident : Int) {
32+
id = ident
33+
}
34+
}
35+
36+
LazyPropertyTestSuite.test("Basic") {
37+
var a = LazyPropertyClass(1)
38+
39+
expectEqual(0, lazyPropertyInitialized)
40+
_ = a.lazyProperty
41+
expectEqual(1, lazyPropertyInitialized)
42+
_ = a.lazyProperty
43+
44+
a.lazyProperty = 42 // nothing interesting happens
45+
46+
expectEqual(0, lazyPropertyInitialized2)
47+
_ = a.lazyProperty2
48+
expectEqual(1, lazyPropertyInitialized2)
49+
50+
a = LazyPropertyClass(2)
51+
52+
a = LazyPropertyClass(3)
53+
a.lazyProperty = 42
54+
expectEqual(1, lazyPropertyInitialized)
55+
56+
expectEqual(0, lazyPropertyInitialized3)
57+
expectEqual(0, a.lazyProperty3)
58+
expectEqual(1, lazyPropertyInitialized3)
59+
60+
a.lazyProperty3 = nil
61+
expectEqual(nil, a.lazyProperty3)
62+
expectEqual(1, lazyPropertyInitialized3)
63+
}
64+
65+
// Swift 3 had a bogus 'property resetting' behavior,
66+
// but we don't allow that anymore.
67+
68+
LazyPropertyTestSuite.test("Reset") {
69+
70+
}
71+
72+
runAllTests()

test/Interpreter/properties.swift

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -173,63 +173,6 @@ func test() {
173173
}
174174
test()
175175

176-
func lazyInitFunction() -> Int {
177-
print("lazy property initialized")
178-
return 0
179-
}
180-
181-
182-
class LazyPropertyClass {
183-
var id : Int
184-
lazy var lazyProperty = lazyInitFunction()
185-
186-
lazy var lazyProperty2 : Int = {
187-
print("other lazy property initialized")
188-
return 0
189-
}()
190-
191-
192-
init(_ ident : Int) {
193-
id = ident
194-
print("LazyPropertyClass.init #\(id)")
195-
}
196-
197-
deinit {
198-
print("LazyPropertyClass.deinit #\(id)")
199-
}
200-
201-
202-
}
203-
204-
205-
func testLazyProperties() {
206-
print("testLazyPropertiesStart") // CHECK: testLazyPropertiesStart
207-
if true {
208-
var a = LazyPropertyClass(1) // CHECK-NEXT: LazyPropertyClass.init #1
209-
_ = a.lazyProperty // CHECK-NEXT: lazy property initialized
210-
_ = a.lazyProperty // executed only once, lazy init not called again.
211-
a.lazyProperty = 42 // nothing interesting happens
212-
_ = a.lazyProperty2 // CHECK-NEXT: other lazy property initialized
213-
214-
// CHECK-NEXT: LazyPropertyClass.init #2
215-
// CHECK-NEXT: LazyPropertyClass.deinit #1
216-
a = LazyPropertyClass(2)
217-
218-
a = LazyPropertyClass(3)
219-
a.lazyProperty = 42 // Store don't trigger lazy init.
220-
221-
// CHECK-NEXT: LazyPropertyClass.init #3
222-
// CHECK-NEXT: LazyPropertyClass.deinit #2
223-
// CHECK-NEXT: LazyPropertyClass.deinit #3
224-
}
225-
print("testLazyPropertiesDone") // CHECK: testLazyPropertiesDone
226-
}
227-
228-
229-
230-
testLazyProperties()
231-
232-
233176

234177
/// rdar://16805609 - <rdar://problem/16805609> Providing a 'didSet' in a generic override doesn't work
235178
class rdar16805609Base<T> {

test/SILGen/decls.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -167,22 +167,3 @@ struct StructWithStaticVar {
167167
init() {
168168
}
169169
}
170-
171-
// <rdar://problem/17405715> lazy property crashes silgen of implicit memberwise initializer
172-
// CHECK-LABEL: // decls.StructWithLazyField.init
173-
// CHECK-NEXT: sil hidden @_T05decls19StructWithLazyFieldVACSiSg4once_tcfC : $@convention(method) (Optional<Int>, @thin StructWithLazyField.Type) -> @owned StructWithLazyField {
174-
struct StructWithLazyField {
175-
lazy var once : Int = 42
176-
let someProp = "Some value"
177-
}
178-
179-
// <rdar://problem/21057425> Crash while compiling attached test-app.
180-
// CHECK-LABEL: // decls.test21057425
181-
func test21057425() {
182-
var x = 0, y: Int = 0
183-
}
184-
185-
func useImplicitDecls() {
186-
_ = StructWithLazyField(once: 55)
187-
}
188-

test/SILGen/lazy_properties.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-silgen -primary-file %s | %FileCheck %s
2+
3+
// <rdar://problem/17405715> lazy property crashes silgen of implicit memberwise initializer
4+
// CHECK-LABEL: // lazy_properties.StructWithLazyField.init
5+
// CHECK-NEXT: sil hidden @_T015lazy_properties19StructWithLazyFieldVACSiSg4once_tcfC : $@convention(method) (Optional<Int>, @thin StructWithLazyField.Type) -> @owned StructWithLazyField {
6+
struct StructWithLazyField {
7+
lazy var once : Int = 42
8+
let someProp = "Some value"
9+
}
10+
11+
// <rdar://problem/21057425> Crash while compiling attached test-app.
12+
// CHECK-LABEL: // lazy_properties.test21057425
13+
func test21057425() {
14+
var x = 0, y: Int = 0
15+
}
16+
17+
// Anonymous closure parameters in lazy initializer crash SILGen
18+
19+
// CHECK-LABEL: sil hidden @_T015lazy_properties22HasAnonymousParametersV1xSifg : $@convention(method) (@inout HasAnonymousParameters) -> Int
20+
// CHECK-LABEL: sil private @_T015lazy_properties22HasAnonymousParametersV1xSifgS2icfU_ : $@convention(thin) (Int) -> Int
21+
22+
struct HasAnonymousParameters {
23+
lazy var x = { $0 }(0)
24+
}

0 commit comments

Comments
 (0)