Skip to content

Commit 28a1dc7

Browse files
authored
Use 'fileprivate' for synthesized members of 'private' decls (#15980)
Since 'private' means "limit to the enclosing scope (and extensions thereof)", putting it on a member means that the member can't be accessed everywhere the type might show up. That's normally a good thing, but it's not the desired effect for synthesized members used for derived conformances, and when it comes to class initializers this actually violates AST invariants. rdar://problem/39478298
1 parent 076ef91 commit 28a1dc7

File tree

9 files changed

+71
-13
lines changed

9 files changed

+71
-13
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,8 +2304,14 @@ class ValueDecl : public Decl {
23042304
bool treatUsableFromInlineAsPublic = false) const;
23052305

23062306

2307-
/// Copy the formal access level and @usableFromInline attribute from source.
2308-
void copyFormalAccessFrom(ValueDecl *source);
2307+
/// Copy the formal access level and @usableFromInline attribute from
2308+
/// \p source.
2309+
///
2310+
/// If \p sourceIsParentContext is true, an access level of \c private will
2311+
/// be copied as \c fileprivate, to ensure that this declaration will be
2312+
/// available everywhere \p source is.
2313+
void copyFormalAccessFrom(const ValueDecl *source,
2314+
bool sourceIsParentContext = false);
23092315

23102316
/// Returns the access level that actually controls how a declaration should
23112317
/// be emitted and may be used.

lib/AST/Decl.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,9 +2259,24 @@ ValueDecl::getFormalAccessScope(const DeclContext *useDC,
22592259
llvm_unreachable("unknown access level");
22602260
}
22612261

2262-
void ValueDecl::copyFormalAccessFrom(ValueDecl *source) {
2262+
void ValueDecl::copyFormalAccessFrom(const ValueDecl *source,
2263+
bool sourceIsParentContext) {
22632264
if (!hasAccess()) {
2264-
setAccess(source->getFormalAccess());
2265+
AccessLevel access = source->getFormalAccess();
2266+
2267+
// To make something have the same access as a 'private' parent, it has to
2268+
// be 'fileprivate' or greater.
2269+
if (sourceIsParentContext && access == AccessLevel::Private)
2270+
access = AccessLevel::FilePrivate;
2271+
2272+
// Only certain declarations can be 'open'.
2273+
if (access == AccessLevel::Open && !isPotentiallyOverridable()) {
2274+
assert(!isa<ClassDecl>(this) &&
2275+
"copying 'open' onto a class has complications");
2276+
access = AccessLevel::Public;
2277+
}
2278+
2279+
setAccess(access);
22652280
}
22662281

22672282
// Inherit the @usableFromInline attribute.
@@ -2816,7 +2831,7 @@ void ClassDecl::addImplicitDestructor() {
28162831
setHasDestructor();
28172832

28182833
// Propagate access control and versioned-ness.
2819-
DD->copyFormalAccessFrom(this);
2834+
DD->copyFormalAccessFrom(this, /*sourceIsParentContext*/true);
28202835

28212836
// Wire up generic environment of DD.
28222837
DD->setGenericEnvironment(getGenericEnvironmentOfContext());

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ static FuncDecl *deriveEncodable_encode(TypeChecker &tc, Decl *parentDecl,
767767

768768
encodeDecl->setInterfaceType(interfaceType);
769769
encodeDecl->setValidationStarted();
770-
encodeDecl->setAccess(target->getFormalAccess());
770+
encodeDecl->copyFormalAccessFrom(target, /*sourceIsParentContext*/true);
771771

772772
tc.Context.addSynthesizedDecl(encodeDecl);
773773

@@ -1106,7 +1106,7 @@ static ValueDecl *deriveDecodable_init(TypeChecker &tc, Decl *parentDecl,
11061106
initDecl->setInterfaceType(interfaceType);
11071107
initDecl->setValidationStarted();
11081108
initDecl->setInitializerInterfaceType(initializerType);
1109-
initDecl->setAccess(target->getFormalAccess());
1109+
initDecl->copyFormalAccessFrom(target, /*sourceIsParentContext*/true);
11101110

11111111
tc.Context.addSynthesizedDecl(initDecl);
11121112

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl,
655655
FunctionType::ExtInfo());
656656
}
657657
eqDecl->setInterfaceType(interfaceTy);
658-
eqDecl->copyFormalAccessFrom(typeDecl);
658+
eqDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true);
659659
eqDecl->setValidationStarted();
660660

661661
tc.Context.addSynthesizedDecl(eqDecl);
@@ -1044,15 +1044,15 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl,
10441044

10451045
getterDecl->setInterfaceType(interfaceType);
10461046
getterDecl->setValidationStarted();
1047-
getterDecl->copyFormalAccessFrom(typeDecl);
1047+
getterDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true);
10481048

10491049
// Finish creating the property.
10501050
hashValueDecl->setImplicit();
10511051
hashValueDecl->setInterfaceType(intType);
10521052
hashValueDecl->setValidationStarted();
10531053
hashValueDecl->makeComputed(SourceLoc(), getterDecl,
10541054
nullptr, nullptr, SourceLoc());
1055-
hashValueDecl->copyFormalAccessFrom(typeDecl);
1055+
hashValueDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true);
10561056

10571057
Pattern *hashValuePat = new (C) NamedPattern(hashValueDecl, /*implicit*/true);
10581058
hashValuePat->setType(intType);

lib/Sema/DerivedConformanceRawRepresentable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ static ConstructorDecl *deriveRawRepresentable_init(TypeChecker &tc,
340340
}
341341
initDecl->setInterfaceType(allocIfaceType);
342342
initDecl->setInitializerInterfaceType(initIfaceType);
343-
initDecl->copyFormalAccessFrom(enumDecl);
343+
initDecl->copyFormalAccessFrom(enumDecl, /*sourceIsParentContext*/true);
344344
initDecl->setValidationStarted();
345345

346346
tc.Context.addSynthesizedDecl(initDecl);

lib/Sema/DerivedConformances.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ DerivedConformance::declareDerivedProperty(TypeChecker &tc, Decl *parentDecl,
303303
/*IsCaptureList*/false, SourceLoc(), name,
304304
propertyContextType, parentDC);
305305
propDecl->setImplicit();
306-
propDecl->copyFormalAccessFrom(typeDecl);
306+
propDecl->copyFormalAccessFrom(typeDecl, /*sourceIsParentContext*/true);
307307
propDecl->setInterfaceType(propertyInterfaceType);
308308
propDecl->setValidationStarted();
309309

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7605,7 +7605,7 @@ void TypeChecker::validateDecl(ValueDecl *D) {
76057605
&& "Decl parsing must prevent destructors outside of types!");
76067606

76077607
checkDeclAttributesEarly(DD);
7608-
DD->copyFormalAccessFrom(enclosingClass);
7608+
DD->copyFormalAccessFrom(enclosingClass, /*sourceIsParentContext*/true);
76097609

76107610
configureImplicitSelf(*this, DD);
76117611

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s
2+
3+
// Check that synthesized members show up as 'fileprivate', not 'private.
4+
5+
// CHECK-LABEL: private struct PrivateConformer : Hashable {
6+
private struct PrivateConformer: Hashable {
7+
var value: Int
8+
// CHECK-DAG: fileprivate var hashValue: Int { get }
9+
// CHECK-DAG: @_implements(Equatable, ==(_:_:)) fileprivate static func __derived_struct_equals
10+
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,30 @@ let _ = SimpleChildClass.encode(to:)
5050
// The synthesized CodingKeys type should not be accessible from outside the
5151
// class.
5252
let _ = SimpleChildClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}
53+
54+
// Check access level issues around 'private'.
55+
private class PrivateClass: Codable {
56+
var x: Int = 1
57+
}
58+
59+
private class PrivateClassChild: PrivateClass {}
60+
61+
_ = PrivateClass.init(from:)
62+
_ = PrivateClass.encode(to:)
63+
_ = PrivateClassChild.init(from:)
64+
_ = PrivateClassChild.encode(to:)
65+
66+
_ = PrivateClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}
67+
68+
// Check access level issues around 'open'.
69+
open class OpenClass: Codable {
70+
var x: Int = 1
71+
}
72+
open class OpenClassChild: OpenClass {}
73+
74+
_ = OpenClass.init(from:)
75+
_ = OpenClass.encode(to:)
76+
_ = OpenClassChild.init(from:)
77+
_ = OpenClassChild.encode(to:)
78+
79+
_ = OpenClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}

0 commit comments

Comments
 (0)