Skip to content

Commit 6a86a62

Browse files
authored
Merge pull request #12762 from slavapestov/infinite-fractal-of-corner-cases
Sema: Fix source compatibility break with default initialization of optional properties
2 parents 92c32a8 + e805100 commit 6a86a62

File tree

8 files changed

+89
-20
lines changed

8 files changed

+89
-20
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: 35 additions & 13 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)) {
@@ -8540,16 +8557,20 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
85408557

85418558
// Bail out if we're validating one of our constructors already; we'll
85428559
// revisit the issue later.
8543-
bool alreadyValidatingCtor = false;
8544-
for (auto member : decl->getMembers()) {
8545-
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
8546-
validateDecl(ctor);
8547-
if (!ctor->hasValidSignature())
8548-
alreadyValidatingCtor = true;
8560+
if (isa<ClassDecl>(decl)) {
8561+
bool alreadyValidatingCtor = false;
8562+
for (auto member : decl->getMembers()) {
8563+
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
8564+
validateDecl(ctor);
8565+
if (!ctor->hasValidSignature())
8566+
alreadyValidatingCtor = true;
8567+
}
85498568
}
8569+
if (alreadyValidatingCtor)
8570+
return;
85508571
}
8551-
if (alreadyValidatingCtor)
8552-
return;
8572+
8573+
decl->setAddedImplicitInitializers();
85538574

85548575
// Check whether there is a user-declared constructor or an instance
85558576
// variable.
@@ -8558,7 +8579,6 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
85588579
bool SuppressMemberwiseInitializer = false;
85598580
bool FoundSynthesizedInit = false;
85608581
bool FoundDesignatedInit = false;
8561-
decl->setAddedImplicitInitializers();
85628582

85638583
// Before we look for constructors, we need to make sure that all synthesized
85648584
// initializers are properly synthesized.
@@ -8600,6 +8620,9 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
86008620
FoundDesignatedInit = true;
86018621
}
86028622

8623+
if (isa<StructDecl>(decl))
8624+
continue;
8625+
86038626
if (!ctor->isInvalid())
86048627
initializerParamTypes.insert(getInitializerParamType(ctor));
86058628

@@ -8816,8 +8839,7 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
88168839
if (auto ref = conformsToProtocol(targetType, protocol, target,
88178840
ConformanceCheckFlags::Used,
88188841
SourceLoc())) {
8819-
if (auto *conformance =
8820-
dyn_cast_or_null<NormalProtocolConformance>(ref->getConcrete())) {
8842+
if (auto *conformance = ref->getConcrete()->getRootNormalConformance()) {
88218843
if (conformance->isIncomplete()) {
88228844
// Check conformance, forcing synthesis.
88238845
//

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3628,6 +3628,11 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
36283628
if (genericDecl->getGenericParams())
36293629
continue;
36303630

3631+
// Skip typealiases with an unbound generic type as their underlying type.
3632+
if (auto *typeAliasDecl = dyn_cast<TypeAliasDecl>(candidate.first))
3633+
if (typeAliasDecl->getDeclaredInterfaceType()->is<UnboundGenericType>())
3634+
continue;
3635+
36313636
// Check this type against the protocol requirements.
36323637
if (auto checkResult = checkTypeWitness(TC, DC, Proto, assocType,
36333638
candidate.second)) {

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/protocol/conforms/failure.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ struct A: OptionSet {
100100
init() { } // expected-note 2{{candidate has non-matching type '()'}}
101101
}
102102

103+
// Type witness cannot have its own generic parameters
103104
// FIXME: Crappy diagnostic
104105
protocol PA {
105-
associatedtype A // expected-note 2 {{protocol requires nested type 'A'; do you want to add it?}}
106+
associatedtype A // expected-note 3 {{protocol requires nested type 'A'; do you want to add it?}}
106107
}
107108

108109
struct BadCase1 : PA { // expected-error {{type 'BadCase1' does not conform to protocol 'PA'}}
@@ -113,6 +114,13 @@ struct BadCase2 : PA { // expected-error {{type 'BadCase2' does not conform to p
113114
typealias A<T> = T
114115
}
115116

117+
// Variation on the above
118+
struct G<T> {}
119+
120+
struct BadCase3 : PA { // expected-error {{type 'BadCase3' does not conform to protocol 'PA'}}
121+
typealias A = G
122+
}
123+
116124
// rdar://problem/32215763
117125
extension UInt32: ExpressibleByStringLiteral {}
118126
// expected-error@-1 {{type 'UInt32' does not conform to protocol 'ExpressibleByStringLiteral'}}

test/decl/protocol/special/coding/struct_codable_member_type_lookup.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,7 @@ extension C.Inner {
645645
case value
646646
}
647647
}
648+
649+
struct GenericCodableStruct<T : Codable> : Codable {}
650+
651+
func foo(_: GenericCodableStruct<Int>.CodingKeys) // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}

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)