Skip to content

Commit e805100

Browse files
committed
Sema: Fix source compatibility break with default initialization of optional properties
In Swift 4, properties declared with a sugared Optional type, like Int?, have a default value of nil. This can be observed in two ways: - Classes and structs get an implicit no-argument initializer - Designated initializers don't have to initialize this property Note that this did not apply in general to properties where the type was spelled explicitly as Optional<Int>, etc, mostly because of implementation restrictions -- when we check if a type has implicit initializers, we have not realized types for all stored property members yet, and doing so is not really possible without the iterative decl checker. However, in some cases, we *did* perform default initialization for Optional<Int>, because of some code duplication and divergent code paths. A recent refactoring cleaned up some of the mess in this area, but accidentally broke source compatibility with code that relied on the broken Optional<Int> case. Fix this by simulating the old behavior in -swift-version 4, and preserving the more correct behavior in -swift-version 5. Fixes <rdar://problem/35319847>.
1 parent d8c5e79 commit e805100

File tree

5 files changed

+55
-8
lines changed

5 files changed

+55
-8
lines changed

include/swift/AST/Pattern.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,9 @@ class alignas(8) Pattern {
170170

171171
/// Return true if this pattern (or a subpattern) is refutable.
172172
bool isRefutablePattern() const;
173-
174-
173+
174+
bool isNeverDefaultInitializable() const;
175+
175176
/// \brief Mark all vardecls in this pattern as having non-pattern initial
176177
/// values bound into them.
177178
void markHasNonPatternBindingInit() {

lib/AST/Decl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,10 +1187,10 @@ static bool isDefaultInitializable(const TypeRepr *typeRepr) {
11871187

11881188
// @NSManaged properties never get default initialized, nor do debugger
11891189
// variables and immutable properties.
1190-
bool isNeverDefaultInitializable(const Pattern *p) {
1190+
bool Pattern::isNeverDefaultInitializable() const {
11911191
bool result = false;
11921192

1193-
p->forEachVariable([&](const VarDecl *var) {
1193+
forEachVariable([&](const VarDecl *var) {
11941194
if (var->getAttrs().hasAttribute<NSManagedAttr>())
11951195
return;
11961196

@@ -1209,7 +1209,7 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
12091209
if (entry.getInit())
12101210
return true;
12111211

1212-
if (isNeverDefaultInitializable(entry.getPattern()))
1212+
if (entry.getPattern()->isNeverDefaultInitializable())
12131213
return false;
12141214

12151215
// If the pattern is typed as optional (or tuples thereof), it is

lib/Sema/TypeCheckDecl.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,8 +4127,25 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
41274127
TC.checkTypeModifyingDeclAttributes(var);
41284128

41294129
// Decide whether we should suppress default initialization.
4130-
if (!PBD->isDefaultInitializable(i))
4131-
continue;
4130+
//
4131+
// Note: Swift 4 had a bug where properties with a desugared optional
4132+
// type like Optional<Int> had a half-way behavior where sometimes
4133+
// they behave like they are default initialized, and sometimes not.
4134+
//
4135+
// In Swift 5 mode, use the right condition here, and only default
4136+
// initialize properties with a sugared Optional type.
4137+
//
4138+
// (The restriction to sugared types only comes because we don't have
4139+
// the iterative declaration checker yet; so in general, we cannot
4140+
// look at the type of a property at all, and can only look at the
4141+
// TypeRepr, because we haven't validated the property yet.)
4142+
if (TC.Context.isSwiftVersionAtLeast(5)) {
4143+
if (!PBD->isDefaultInitializable(i))
4144+
continue;
4145+
} else {
4146+
if (PBD->getPattern(i)->isNeverDefaultInitializable())
4147+
continue;
4148+
}
41324149

41334150
auto type = PBD->getPattern(i)->getType();
41344151
if (auto defaultInit = buildDefaultInitializer(TC, type)) {

test/Compatibility/default_init.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify -swift-version 4
2+
3+
// Default initialization of variables -- totally broken Swift 4 behavior.
4+
5+
class NotInitializableOptionalClass {
6+
var opt: Optional<Int>
7+
}
8+
9+
struct NotInitializableOptionalStruct { // expected-note {{'init(opt:)' declared here}}
10+
var opt: Optional<Int>
11+
}
12+
13+
func testBadDefaultInit() {
14+
_ = NotInitializableOptionalStruct() // expected-error {{missing argument for parameter 'opt' in call}}
15+
_ = NotInitializableOptionalClass()
16+
}

test/decl/var/default_init.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library %s -verify -swift-version 5
22

33
// Default initialization of variables.
44

@@ -38,3 +38,16 @@ class DefaultInitOfLetProperty {
3838

3939
var global: Int?
4040

41+
class NotInitializableOptionalClass { // expected-error{{class 'NotInitializableOptionalClass' has no initializers}}
42+
// Do not perform default initialization for properties with explicitly-spelled 'Optional'.
43+
var opt: Optional<Int> // expected-note{{stored property 'opt' without initial value prevents synthesized initializers}}
44+
}
45+
46+
struct NotInitializableOptionalStruct { // expected-note {{'init(opt:)' declared here}}
47+
var opt: Optional<Int>
48+
}
49+
50+
func testBadDefaultInit() {
51+
_ = NotInitializableOptionalStruct() // expected-error {{missing argument for parameter 'opt' in call}}
52+
_ = NotInitializableOptionalClass() // expected-error {{'NotInitializableOptionalClass' cannot be constructed because it has no accessible initializers}}
53+
}

0 commit comments

Comments
 (0)