Skip to content

Commit ad7e1d0

Browse files
author
Harlan
authored
[InterfaceGen] Print private/internal properties (#19127)
* [Interface] Print private/internal properties All properties which contribute to the storage of a type should be printed, and their names should be hidden from interfaces. Print them with '_' as their name, and teach the parser to recognize these special patterns when parsing interface files. Partially resolves rdar://43810647 * Address review comments * Disable accessor generation for nameless vars * Test to ensure interface files preserve type layout * Ignore attribute differences on Linux
1 parent 3e7e9f3 commit ad7e1d0

File tree

6 files changed

+213
-6
lines changed

6 files changed

+213
-6
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ struct PrintOptions {
322322
/// for optionals that are nested within other optionals.
323323
bool PrintOptionalAsImplicitlyUnwrapped = false;
324324

325+
/// Replaces the name of private and internal properties of types with '_'.
326+
bool OmitNameOfInaccessibleProperties = false;
327+
325328
/// \brief Print dependent types as references into this generic environment.
326329
GenericEnvironment *GenericEnv = nullptr;
327330

lib/AST/ASTPrinter.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,22 @@ void PrintOptions::clearSynthesizedExtension() {
6464
TransformContext.reset();
6565
}
6666

67+
static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
68+
auto *DC = ASD->getDeclContext()->getAsDecl();
69+
if (!DC) return false;
70+
auto *ND = dyn_cast<NominalTypeDecl>(DC);
71+
if (!ND) return false;
72+
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
73+
}
74+
6775
PrintOptions PrintOptions::printTextualInterfaceFile() {
6876
PrintOptions result;
6977
result.PrintLongAttrsOnSeparateLines = true;
7078
result.TypeDefinitions = true;
7179
result.PrintIfConfig = false;
7280
result.FullyQualifiedTypes = true;
7381
result.SkipImports = true;
82+
result.OmitNameOfInaccessibleProperties = true;
7483

7584
class ShouldPrintForTextualInterface : public ShouldPrintChecker {
7685
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
@@ -79,8 +88,14 @@ PrintOptions PrintOptions::printTextualInterfaceFile() {
7988
AccessScope accessScope =
8089
VD->getFormalAccessScope(/*useDC*/nullptr,
8190
/*treatUsableFromInlineAsPublic*/true);
82-
if (!accessScope.isPublic())
91+
if (!accessScope.isPublic()) {
92+
// We do want to print private stored properties, without their
93+
// original names present.
94+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(VD))
95+
if (contributesToParentTypeStorage(ASD))
96+
return true;
8397
return false;
98+
}
8499
}
85100

86101
// Skip typealiases that just redeclare generic parameters.
@@ -821,7 +836,13 @@ void PrintAST::printPattern(const Pattern *pattern) {
821836

822837
case PatternKind::Named: {
823838
auto named = cast<NamedPattern>(pattern);
824-
recordDeclLoc(named->getDecl(), [&]{
839+
auto decl = named->getDecl();
840+
recordDeclLoc(decl, [&]{
841+
if (Options.OmitNameOfInaccessibleProperties &&
842+
contributesToParentTypeStorage(decl) &&
843+
decl->getFormalAccess() < AccessLevel::Public)
844+
Printer << "_";
845+
else
825846
Printer.printName(named->getBoundName());
826847
});
827848
break;
@@ -1362,6 +1383,12 @@ bool ShouldPrintChecker::shouldPrint(const Decl *D,
13621383
return !EED->getSourceRange().isValid();
13631384
}
13641385

1386+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
1387+
if (Options.OmitNameOfInaccessibleProperties &&
1388+
contributesToParentTypeStorage(ASD))
1389+
return true;
1390+
}
1391+
13651392
// Skip declarations that are not accessible.
13661393
if (auto *VD = dyn_cast<ValueDecl>(D)) {
13671394
if (Options.AccessFilter > AccessLevel::Private &&

lib/Parse/ParsePattern.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "swift/AST/ASTWalker.h"
2020
#include "swift/AST/Initializer.h"
21+
#include "swift/AST/Module.h"
2122
#include "swift/Basic/StringExtras.h"
2223
#include "swift/Parse/CodeCompletionCallbacks.h"
2324
#include "swift/Parse/SyntaxParsingContext.h"
@@ -907,22 +908,34 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
907908
///
908909
ParserResult<Pattern> Parser::parsePattern() {
909910
SyntaxParsingContext PatternCtx(SyntaxContext, SyntaxContextKind::Pattern);
911+
bool isLet = (InVarOrLetPattern != IVOLP_InVar);
912+
auto specifier = isLet
913+
? VarDecl::Specifier::Let
914+
: VarDecl::Specifier::Var;
910915
switch (Tok.getKind()) {
911916
case tok::l_paren:
912917
return parsePatternTuple();
913918

914919
case tok::kw__:
920+
// Normally, '_' is invalid in type context for patterns, but they show up
921+
// in interface files as the name for type members that are non-public.
922+
// Treat them as an implicitly synthesized NamedPattern with a nameless
923+
// VarDecl inside.
924+
if (CurDeclContext->isTypeContext() &&
925+
SF.Kind == SourceFileKind::Interface) {
926+
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
927+
auto VD = new (Context) VarDecl(
928+
/*IsStatic*/false, specifier, /*IsCaptureList*/false,
929+
consumeToken(tok::kw__), Identifier(), CurDeclContext);
930+
return makeParserResult(new (Context) NamedPattern(VD, /*implicit*/true));
931+
}
915932
PatternCtx.setCreateSyntax(SyntaxKind::WildcardPattern);
916933
return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));
917934

918935
case tok::identifier: {
919936
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
920937
Identifier name;
921938
SourceLoc loc = consumeIdentifier(&name);
922-
bool isLet = (InVarOrLetPattern != IVOLP_InVar);
923-
auto specifier = isLet
924-
? VarDecl::Specifier::Let
925-
: VarDecl::Specifier::Var;
926939
if (Tok.isIdentifierOrUnderscore() && !Tok.isContextualDeclKeyword())
927940
diagnoseConsecutiveIDs(name.str(), loc, isLet ? "constant" : "variable");
928941

lib/Sema/CodeSynthesis.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,11 @@ static void addOpaqueAccessorToStorage(TypeChecker &TC,
994994

995995
static void addExpectedOpaqueAccessorsToStorage(TypeChecker &TC,
996996
AbstractStorageDecl *storage) {
997+
// Nameless vars from interface files should not have any accessors.
998+
// TODO: Replace this check with a broader check that all storage decls
999+
// from interface files have all their accessors up front.
1000+
if (storage->getBaseName().empty())
1001+
return;
9971002
storage->visitExpectedOpaqueAccessors([&](AccessorKind kind) {
9981003
// If the accessor is already present, there's nothing to do.
9991004
if (storage->getAccessor(kind))
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// Check that importing this module creates the right types
4+
5+
// RUN: %target-swift-frontend -emit-interface-path %t/private-stored-members.swiftinterface -module-name PrivateStoredMembers -emit-module -o %t/PrivateStoredMembers.swiftmodule %S/private-stored-members.swift
6+
// RUN: %target-swift-frontend -emit-ir %s -I %t 2>&1 -DSHOULD_IMPORT | %FileCheck %s --check-prefix CHECK-EXEC --check-prefix CHECK
7+
8+
// Check that the types are also correct when importing a module created from an interface
9+
10+
// RUN: %target-swift-frontend -emit-module -o %t/PrivateStoredMembers.swiftmodule -module-name PrivateStoredMembers %t/private-stored-members.swiftinterface -disable-objc-attr-requires-foundation-module
11+
// RUN: %target-swift-frontend -emit-ir %s -I %t 2>&1 -DSHOULD_IMPORT | %FileCheck %s --check-prefix CHECK-EXEC --check-prefix CHECK
12+
13+
// Check the types generated when the source file is the primary file, and ensure they're the same layout.
14+
15+
// RUN: %target-swift-frontend -emit-ir %S/private-stored-members.swift %s 2>&1 -module-name main | %FileCheck %s --check-prefix CHECK-MAIN --check-prefix CHECK-EXEC
16+
17+
// These two appear out-of-order between run lines
18+
19+
// CHECK-DAG: [[MYCLASS:%T20PrivateStoredMembers7MyClassC]] = type opaque
20+
// CHECK-DAG: [[MYSTRUCT:%T20PrivateStoredMembers8MyStructV]] = type <{ %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V }>
21+
22+
// CHECK-MAIN-DAG: [[MYCLASS:%T4main7MyClassC]] = type <{ %swift.refcounted, %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V }>
23+
// CHECK-MAIN-DAG: [[MYSTRUCT:%T4main8MyStructV]] = type <{ %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V, %TSb, [7 x i8], %Ts5Int64V }>
24+
25+
#if SHOULD_IMPORT
26+
import PrivateStoredMembers
27+
#endif
28+
29+
// CHECK-EXEC: swiftcc void @"$S{{[^ ]+}}8makeUseryyF"() #0 {
30+
public func makeUser() {
31+
let ptr = UnsafeMutablePointer<MyStruct>.allocate(capacity: 1)
32+
// CHECK-EXEC: %.publicEndVar = getelementptr inbounds [[MYSTRUCT]], [[MYSTRUCT]]* %{{[0-9]+}}, i32 0, i32 [[PUBLIC_END_VAR_IDX:9]]
33+
// CHECK-EXEC: %.publicEndVar._value = getelementptr inbounds %Ts5Int64V, %Ts5Int64V* %.publicEndVar, i32 0, i32 0
34+
// CHECK-EXEC: store i64 4, i64* %.publicEndVar._value
35+
ptr.pointee.publicEndVar = 4
36+
37+
// CHECK-EXEC: %.publicEndVar1 = getelementptr inbounds [[MYSTRUCT]], [[MYSTRUCT]]* %{{[0-9]+}}, i32 0, i32 [[PUBLIC_END_VAR_IDX]]
38+
// CHECK-EXEC: %.publicEndVar1._value = getelementptr inbounds %Ts5Int64V, %Ts5Int64V* %.publicEndVar1, i32 0, i32 0
39+
// CHECK-EXEC: [[PUBLIC_END_VAR_LOAD:%[0-9]+]] = load i64, i64* %.publicEndVar1._value, align 8
40+
41+
// CHECK-EXEC: %.publicVar = getelementptr inbounds [[MYSTRUCT]], [[MYSTRUCT]]* %{{[0-9]+}}, i32 0, i32 0
42+
// CHECK-EXEC: %.publicVar._value = getelementptr inbounds %Ts5Int64V, %Ts5Int64V* %.publicVar, i32 0, i32 0
43+
// CHECK-EXEC: store i64 [[PUBLIC_END_VAR_LOAD]], i64* %.publicVar._value, align 8
44+
ptr.pointee.publicVar = ptr.pointee.publicEndVar
45+
ptr.deallocate()
46+
47+
// CHECK-EXEC: %[[MYCLASS_INIT:[0-9]+]] = call swiftcc [[MYCLASS]]* @"$S{{[^ ]+}}7MyClassCACycfC"(%swift.type* swiftself %{{[0-9]+}})
48+
let myClass = MyClass()
49+
50+
// These are uninteresting as they just call into the standard getter and setter.
51+
myClass.publicEndVar = 4
52+
myClass.publicVar = myClass.publicEndVar
53+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -emit-interface-path %t.swiftinterface -emit-module -o /dev/null %s
4+
// RUN: %FileCheck %s < %t.swiftinterface
5+
6+
// RUN: %target-swift-frontend -emit-interface-path %t-resilient.swiftinterface -enable-resilience -emit-module -o /dev/null %s
7+
// RUN: %FileCheck %s --check-prefix RESILIENT < %t-resilient.swiftinterface
8+
9+
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule %t.swiftinterface -disable-objc-attr-requires-foundation-module
10+
// FIXME(rdar44144435): %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -emit-interface-path - | %FileCheck %s
11+
12+
// RUN: %target-swift-frontend -emit-module -o %t/TestResilient.swiftmodule -enable-resilience %t.swiftinterface -disable-objc-attr-requires-foundation-module
13+
// FIXME(rdar44144435): %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -enable-resilience -emit-interface-path - | %FileCheck %s
14+
15+
16+
// CHECK: struct MyStruct {{{$}}
17+
// RESILIENT: struct MyStruct {{{$}}
18+
public struct MyStruct {
19+
// CHECK-NEXT: var publicVar: Int64{{$}}
20+
// RESILIENT-NEXT: var publicVar: Int64{{$}}
21+
public var publicVar: Int64
22+
23+
// CHECK-NEXT: let publicLet: Bool{{$}}
24+
// RESILIENT-NEXT: let publicLet: Bool{{$}}
25+
public let publicLet: Bool
26+
27+
// CHECK-NEXT: internal var _: Int64{{$}}
28+
// RESILIENT-NOT: internal var _: Int64{{$}}
29+
var internalVar: Int64
30+
31+
// CHECK-NEXT: internal let _: Bool{{$}}
32+
// RESILIENT-NOT: internal let _: Bool{{$}}
33+
let internalLet: Bool
34+
35+
// CHECK-NEXT: private var _: Int64{{$}}
36+
// RESILIENT-NOT: private var _: Int64{{$}}
37+
private var privateVar: Int64
38+
39+
// CHECK-NEXT: private let _: Bool{{$}}
40+
// RESILIENT-NOT: private let _: Bool{{$}}
41+
private let privateLet: Bool
42+
43+
// CHECK-NOT: private var
44+
// RESILIENT-NOT: private var
45+
private var computedPrivateVar: Int64 {
46+
return 0
47+
}
48+
49+
// CHECK-NOT: private static var
50+
// RESILIENT-NOT: private static var
51+
private static var staticPrivateVar: Int64 = 0
52+
53+
// CHECK-NEXT: var publicEndVar: Int64{{$}}
54+
// RESILIENT-NEXT: var publicEndVar: Int64{{$}}
55+
public var publicEndVar: Int64 = 0
56+
57+
// CHECK: }{{$}}
58+
// RESILIENT: }{{$}}
59+
}
60+
61+
// CHECK: class MyClass {{{$}}
62+
// RESILIENT: class MyClass {{{$}}
63+
public class MyClass {
64+
// CHECK-NEXT: var publicVar: Int64{{$}}
65+
// RESILIENT-NEXT: var publicVar: Int64{{$}}
66+
public var publicVar: Int64 = 0
67+
68+
// CHECK-NEXT: let publicLet: Bool{{$}}
69+
// RESILIENT-NEXT: let publicLet: Bool{{$}}
70+
public let publicLet: Bool = true
71+
72+
// CHECK-NEXT: internal var _: Int64{{$}}
73+
// RESILIENT-NOT: internal var _: Int64{{$}}
74+
var internalVar: Int64 = 0
75+
76+
// CHECK-NEXT: internal let _: Bool{{$}}
77+
// RESILIENT-NOT: internal let _: Bool{{$}}
78+
let internalLet: Bool = true
79+
80+
// CHECK-NEXT: private var _: Int64{{$}}
81+
// RESILIENT-NOT: private var _: Int64{{$}}
82+
private var privateVar: Int64 = 0
83+
84+
// CHECK-NEXT: private let _: Bool{{$}}
85+
// RESILIENT-NOT: private let _: Bool{{$}}
86+
private let privateLet: Bool = true
87+
88+
// CHECK-NOT: private var
89+
// RESILIENT-NOT: private var
90+
private var computedPrivateVar: Int64 {
91+
return 0
92+
}
93+
94+
// CHECK-NOT: private static var
95+
// RESILIENT-NOT: private static var
96+
private static var staticPrivateVar: Int64 = 0
97+
98+
// CHECK-NEXT: var publicEndVar: Int64{{$}}
99+
// RESILIENT-NEXT: var publicEndVar: Int64{{$}}
100+
public var publicEndVar: Int64 = 0
101+
102+
public init() {}
103+
104+
// CHECK: }{{$}}
105+
// RESILIENT: }{{$}}
106+
}

0 commit comments

Comments
 (0)