Skip to content

Commit d413b24

Browse files
committed
[move-only] Ensure that if we have an allocation that isn't fully initialized (and DI errors on it as such), the move checkers do not run on the allocation.
Previously we would crash. Since we are relatively late in 5.9, my solution is to just turn off the move checker on functions whenever DI would emit an error. If we were earlier in the development cycle, then I would make the error be a per allocation change. rdar://108993297
1 parent 3087afc commit d413b24

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#define DEBUG_TYPE "definite-init"
14+
1415
#include "DIMemoryUseCollector.h"
1516
#include "swift/AST/DiagnosticEngine.h"
1617
#include "swift/AST/DiagnosticsSIL.h"
1718
#include "swift/AST/Expr.h"
1819
#include "swift/AST/Stmt.h"
1920
#include "swift/ClangImporter/ClangModule.h"
2021
#include "swift/SIL/BasicBlockBits.h"
22+
#include "swift/AST/SemanticAttrs.h"
2123
#include "swift/SIL/BasicBlockData.h"
2224
#include "swift/SIL/InstructionUtils.h"
2325
#include "swift/SIL/MemAccessUtils.h"
@@ -1140,7 +1142,15 @@ void LifetimeChecker::doIt() {
11401142
}
11411143

11421144
// If we emitted an error, there is no reason to proceed with load promotion.
1143-
if (!EmittedErrorLocs.empty()) return;
1145+
if (!EmittedErrorLocs.empty()) {
1146+
// Since we failed DI, for now, turn off the move checker on the entire
1147+
// function. With time, we should be able to allow for move checker checks
1148+
// to be emitted on unrelated allocations, but given where we are this is a
1149+
// good enough fix.
1150+
TheMemory.getFunction().addSemanticsAttr(
1151+
semantics::NO_MOVEONLY_DIAGNOSTICS);
1152+
return;
1153+
}
11441154

11451155
// If the memory object has nontrivial type, then any destroy/release of the
11461156
// memory object will destruct the memory. If the memory (or some element
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %target-swift-emit-sil -sil-verify-all -verify -enable-experimental-feature NoImplicitCopy %s
2+
3+
// This testStruct specifically testStructs how DI and the move checkers interact with each other
4+
5+
func testStructSimpleNoInit() {
6+
struct M: ~Copyable {
7+
private let i: Int // expected-note {{'self.i' not initialized}}
8+
9+
// No initialization. Should get DI error and no crash.
10+
init() {
11+
} // expected-error {{return from initializer without initializing all stored properties}}
12+
}
13+
}
14+
15+
func testStructSimplePartialInit() {
16+
struct M: ~Copyable {
17+
private let i: Int
18+
private let i2: Int // expected-note {{'self.i2' not initialized}}
19+
20+
init() {
21+
i = 5
22+
} // expected-error {{return from initializer without initializing all stored properties}}
23+
}
24+
}
25+
26+
func testStructSimplePartialInit2() {
27+
struct M: ~Copyable {
28+
private let i: Int = 5
29+
private let i2: Int // expected-note {{'self.i2' not initialized}}
30+
31+
init() {
32+
} // expected-error {{return from initializer without initializing all stored properties}}
33+
}
34+
}
35+
36+
func testStructGenericNoInit() {
37+
struct M<T>: ~Copyable {
38+
private let i: T // expected-note {{'self.i' not initialized}}
39+
40+
init() {
41+
} // expected-error {{return from initializer without initializing all stored properties}}
42+
}
43+
}
44+
45+
func testStructGenericPartialInit() {
46+
struct M<T>: ~Copyable {
47+
private let i: T
48+
private let i2: T // expected-note {{'self.i2' not initialized}}
49+
50+
init(_ t: T) {
51+
self.i = t
52+
} // expected-error {{return from initializer without initializing all stored properties}}
53+
}
54+
}
55+
56+
func testEnumNoInit() {
57+
enum E : ~Copyable {
58+
case first
59+
case second
60+
61+
init() {
62+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
63+
}
64+
}
65+
66+
func testEnumNoInitWithPayload() {
67+
@_moveOnly struct Empty {}
68+
69+
enum E : ~Copyable {
70+
case first(Empty)
71+
case second
72+
73+
init() {
74+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
75+
}
76+
}
77+
78+
func testEnumNoInitWithGenericPayload() {
79+
@_moveOnly struct Empty {}
80+
81+
enum E<T> : ~Copyable {
82+
case first(Empty)
83+
case second(T)
84+
85+
init() {
86+
} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
87+
}
88+
}

0 commit comments

Comments
 (0)