Skip to content

Commit d118e5c

Browse files
authored
Allow 'public' classes to have 'internal' required initializers (#14775)
They're already not subclassable publicly, so it's okay for the initializer to not be available to cross-module clients, just like if it were non-'required'. This allows constructing a class instance chosen at runtime /within/ the module without having to expose the existence of the constructor to everybody. rdar://problem/22845087
1 parent 79655d9 commit d118e5c

File tree

5 files changed

+40
-14
lines changed

5 files changed

+40
-14
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ CHANGELOG
2323
Swift 5.0
2424
---------
2525

26+
* Public classes may now have internal `required` initializers. The rule for
27+
`required` initializers is that they must be available everywhere the class
28+
can be subclassed, but previously we said that `required` initializers on
29+
public classes needed to be public themselves. (This limitation is a holdover
30+
from before the introduction of the open/public distinction in Swift 3.)
31+
2632
* C macros containing casts are no longer imported to Swift if the type in the
2733
cast is unavailable or deprecated, or produces some other diagnostic when
2834
referenced. (These macros were already only imported under very limited

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3180,7 +3180,9 @@ NOTE(required_initializer_here,none,
31803180
"'required' initializer is declared in superclass here", ())
31813181

31823182
ERROR(required_initializer_not_accessible,none,
3183-
"'required' initializer must be as accessible as its enclosing type", ())
3183+
"'required' initializer must be accessible wherever class %0 can be "
3184+
"subclassed",
3185+
(DeclName))
31843186
ERROR(required_initializer_missing_keyword,none,
31853187
"'required' modifier must be present on all overrides of a required "
31863188
"initializer", ())

lib/Sema/TypeCheckDecl.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7290,13 +7290,23 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
72907290
if (CD->isRequired()) {
72917291
if (auto nominal = CD->getDeclContext()
72927292
->getAsNominalTypeOrNominalTypeExtensionContext()) {
7293-
auto requiredAccess = std::min(nominal->getFormalAccess(),
7294-
AccessLevel::Public);
7295-
if (requiredAccess == AccessLevel::Private)
7293+
AccessLevel requiredAccess;
7294+
switch (nominal->getFormalAccess()) {
7295+
case AccessLevel::Open:
7296+
requiredAccess = AccessLevel::Public;
7297+
break;
7298+
case AccessLevel::Public:
7299+
case AccessLevel::Internal:
7300+
requiredAccess = AccessLevel::Internal;
7301+
break;
7302+
case AccessLevel::FilePrivate:
7303+
case AccessLevel::Private:
72967304
requiredAccess = AccessLevel::FilePrivate;
7305+
break;
7306+
}
72977307
if (CD->getFormalAccess() < requiredAccess) {
7298-
auto diag = TC.diagnose(CD,
7299-
diag::required_initializer_not_accessible);
7308+
auto diag = TC.diagnose(CD, diag::required_initializer_not_accessible,
7309+
nominal->getFullName());
73007310
fixItAccess(diag, CD, requiredAccess);
73017311
}
73027312
}

test/Compatibility/accessibility.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ internal extension Base {
165165
}
166166

167167
public class PublicSub: Base {
168-
required init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-12=public }}
168+
private required init() {} // expected-error {{'required' initializer must be accessible wherever class 'PublicSub' can be subclassed}} {{3-10=internal}}
169169
override func foo() {} // expected-error {{overriding instance method must be as accessible as the declaration it overrides}} {{12-12=public }}
170170
override var bar: Int { // expected-error {{overriding var must be as accessible as the declaration it overrides}} {{12-12=public }}
171171
get { return 0 }
@@ -174,8 +174,12 @@ public class PublicSub: Base {
174174
override subscript () -> () { return () } // expected-error {{overriding subscript must be as accessible as the declaration it overrides}} {{12-12=public }}
175175
}
176176

177+
public class PublicSubGood: Base {
178+
required init() {} // okay
179+
}
180+
177181
internal class InternalSub: Base {
178-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=internal}}
182+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'InternalSub' can be subclassed}} {{12-19=internal}}
179183
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=internal}}
180184
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=internal}}
181185
get { return 0 }
@@ -207,7 +211,7 @@ internal class InternalSubPrivateSet: Base {
207211
}
208212

209213
fileprivate class FilePrivateSub: Base {
210-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=fileprivate}}
214+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'FilePrivateSub' can be subclassed}} {{12-19=fileprivate}}
211215
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=fileprivate}}
212216
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=fileprivate}}
213217
get { return 0 }
@@ -249,7 +253,7 @@ fileprivate class FilePrivateSubPrivateSet: Base {
249253
}
250254

251255
private class PrivateSub: Base {
252-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=fileprivate}}
256+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'PrivateSub' can be subclassed}} {{12-19=fileprivate}}
253257
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=fileprivate}}
254258
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=fileprivate}}
255259
get { return 0 }

test/Sema/accessibility.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ internal extension Base {
165165
}
166166

167167
public class PublicSub: Base {
168-
required init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-12=public }}
168+
private required init() {} // expected-error {{'required' initializer must be accessible wherever class 'PublicSub' can be subclassed}} {{3-10=internal}}
169169
override func foo() {} // expected-error {{overriding instance method must be as accessible as the declaration it overrides}} {{12-12=public }}
170170
override var bar: Int { // expected-error {{overriding var must be as accessible as the declaration it overrides}} {{12-12=public }}
171171
get { return 0 }
@@ -174,8 +174,12 @@ public class PublicSub: Base {
174174
override subscript () -> () { return () } // expected-error {{overriding subscript must be as accessible as the declaration it overrides}} {{12-12=public }}
175175
}
176176

177+
public class PublicSubGood: Base {
178+
required init() {} // okay
179+
}
180+
177181
internal class InternalSub: Base {
178-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=internal}}
182+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'InternalSub' can be subclassed}} {{12-19=internal}}
179183
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=internal}}
180184
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=internal}}
181185
get { return 0 }
@@ -207,7 +211,7 @@ internal class InternalSubPrivateSet: Base {
207211
}
208212

209213
fileprivate class FilePrivateSub: Base {
210-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=fileprivate}}
214+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'FilePrivateSub' can be subclassed}} {{12-19=fileprivate}}
211215
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=fileprivate}}
212216
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=fileprivate}}
213217
get { return 0 }
@@ -249,7 +253,7 @@ fileprivate class FilePrivateSubPrivateSet: Base {
249253
}
250254

251255
private class PrivateSub: Base {
252-
required private init() {} // expected-error {{'required' initializer must be as accessible as its enclosing type}} {{12-19=fileprivate}}
256+
required private init() {} // expected-error {{'required' initializer must be accessible wherever class 'PrivateSub' can be subclassed}} {{12-19=fileprivate}}
253257
private override func foo() {} // expected-error {{overriding instance method must be as accessible as its enclosing type}} {{3-10=fileprivate}}
254258
private override var bar: Int { // expected-error {{overriding var must be as accessible as its enclosing type}} {{3-10=fileprivate}}
255259
get { return 0 }

0 commit comments

Comments
 (0)