Skip to content

Commit 0eb2e16

Browse files
author
Harlan Haskins
committed
[ParseableInterface] Expose non-public types required for layout
This patch does a pass over the module looking for stored properties that contribute to the parent's storage but that use non-public-or-usableFromInline types. It marks these types for printing, while also hiding their non-required members. It also splits off ShouldPrintForParseableInterface into its own file, because it's grown significantly since it was first introduced in ASTPrinter.
1 parent d20fdf5 commit 0eb2e16

File tree

7 files changed

+299
-61
lines changed

7 files changed

+299
-61
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ struct PrintOptions {
455455
/// consistent and well-formed.
456456
///
457457
/// \see swift::emitParseableInterface
458-
static PrintOptions printParseableInterfaceFile();
458+
static PrintOptions printParseableInterfaceFile(ModuleDecl *module);
459459

460460
static PrintOptions printModuleInterface();
461461
static PrintOptions printTypeInterface(Type T);

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "llvm/Support/ConvertUTF.h"
4848
#include "llvm/Support/SaveAndRestore.h"
4949
#include "llvm/Support/raw_ostream.h"
50+
#include "ShouldPrintForParseableInterface.h"
5051
#include <algorithm>
5152
#include <queue>
5253

@@ -64,22 +65,7 @@ void PrintOptions::clearSynthesizedExtension() {
6465
TransformContext.reset();
6566
}
6667

67-
static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
68-
AccessScope scope =
69-
VD->getFormalAccessScope(/*useDC*/nullptr,
70-
/*treatUsableFromInlineAsPublic*/true);
71-
return scope.isPublic();
72-
}
73-
74-
static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
75-
auto *DC = ASD->getDeclContext()->getAsDecl();
76-
if (!DC) return false;
77-
auto *ND = dyn_cast<NominalTypeDecl>(DC);
78-
if (!ND) return false;
79-
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
80-
}
81-
82-
PrintOptions PrintOptions::printParseableInterfaceFile() {
68+
PrintOptions PrintOptions::printParseableInterfaceFile(ModuleDecl *module) {
8369
PrintOptions result;
8470
result.PrintLongAttrsOnSeparateLines = true;
8571
result.TypeDefinitions = true;
@@ -100,50 +86,8 @@ PrintOptions PrintOptions::printParseableInterfaceFile() {
10086
printer << " " << AFD->getInlinableBodyText(scratch);
10187
};
10288

103-
class ShouldPrintForParseableInterface : public ShouldPrintChecker {
104-
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
105-
// Skip anything that isn't 'public' or '@usableFromInline'.
106-
if (auto *VD = dyn_cast<ValueDecl>(D)) {
107-
if (!isPublicOrUsableFromInline(VD)) {
108-
// We do want to print private stored properties, without their
109-
// original names present.
110-
if (auto *ASD = dyn_cast<AbstractStorageDecl>(VD))
111-
if (contributesToParentTypeStorage(ASD))
112-
return true;
113-
return false;
114-
}
115-
}
116-
117-
// Skip extensions that extend things we wouldn't print.
118-
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
119-
if (!shouldPrint(ED->getExtendedNominal(), options))
120-
return false;
121-
// FIXME: We also need to check the generic signature for constraints
122-
// that we can't reference.
123-
}
124-
125-
// Skip typealiases that just redeclare generic parameters.
126-
if (auto *alias = dyn_cast<TypeAliasDecl>(D)) {
127-
if (alias->isImplicit()) {
128-
const Decl *parent =
129-
D->getDeclContext()->getAsDecl();
130-
if (auto *genericCtx = parent->getAsGenericContext()) {
131-
bool matchesGenericParam =
132-
llvm::any_of(genericCtx->getInnermostGenericParamTypes(),
133-
[alias](const GenericTypeParamType *param) {
134-
return param->getName() == alias->getName();
135-
});
136-
if (matchesGenericParam)
137-
return false;
138-
}
139-
}
140-
}
141-
142-
return ShouldPrintChecker::shouldPrint(D, options);
143-
}
144-
};
14589
result.CurrentPrintabilityChecker =
146-
std::make_shared<ShouldPrintForParseableInterface>();
90+
ShouldPrintForParseableInterface::create(module);
14791

14892
// FIXME: We don't really need 'public' on everything; we could just change
14993
// the default to 'public' and mark the 'internal' things.

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ add_swift_library(swiftAST STATIC
5050
RawComment.cpp
5151
RequirementEnvironment.cpp
5252
SyntaxASTMap.cpp
53+
ShouldPrintForParseableInterface.cpp
5354
SILLayout.cpp
5455
Stmt.cpp
5556
SubstitutionMap.cpp
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//=== ShouldPrintForParseableInterface.cpp - Parseable Interface filtering ===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "ShouldPrintForParseableInterface.h"
14+
#include "swift/AST/ASTWalker.h"
15+
#include "swift/AST/TypeWalker.h"
16+
#include "swift/AST/Decl.h"
17+
#include "swift/AST/Module.h"
18+
#include "swift/AST/Type.h"
19+
20+
using namespace swift;
21+
22+
bool swift::isPublicOrUsableFromInline(const ValueDecl *VD) {
23+
AccessScope scope =
24+
VD->getFormalAccessScope(/*useDC*/nullptr,
25+
/*treatUsableFromInlineAsPublic*/true);
26+
return scope.isPublic();
27+
}
28+
29+
bool swift::contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
30+
auto *DC = ASD->getDeclContext()->getAsDecl();
31+
if (!DC) return false;
32+
auto *ND = dyn_cast<NominalTypeDecl>(DC);
33+
if (!ND) return false;
34+
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic();
35+
}
36+
37+
/// Finds type decls that need to be printed to ensure the layout of public
38+
/// types are correct for non-resilient types.
39+
///
40+
/// For example:
41+
///
42+
/// ```
43+
/// private struct B {}
44+
/// public struct A {
45+
/// internal let b: B
46+
/// }
47+
/// ```
48+
/// Since `A`'s size depends on `B`, we need to print `B` even though it's
49+
/// private. `B`'s children will also be checked for types that contribute to
50+
/// its layout, and so on.
51+
class FindReferencedNonPublicTypeDecls: public ASTWalker {
52+
using TypeDecls = SmallPtrSetImpl<TypeDecl *>;
53+
54+
/// Finds all nominal types or typealiases referenced in the type being
55+
/// walked.
56+
class FindTypeDecls: public TypeWalker {
57+
TypeDecls &foundDecls;
58+
public:
59+
FindTypeDecls(TypeDecls &foundDecls): foundDecls(foundDecls) {}
60+
61+
void addIfNonPublic(TypeDecl *typeDecl) {
62+
if (isPublicOrUsableFromInline(typeDecl))
63+
return;
64+
foundDecls.insert(typeDecl);
65+
}
66+
67+
/// Finds all nominal types or typealiases referenced by the
68+
Action walkToTypePre(Type type) override {
69+
if (auto nominal =
70+
dyn_cast<NominalOrBoundGenericNominalType>(type.getPointer()))
71+
addIfNonPublic(nominal->getDecl());
72+
if (auto alias = dyn_cast<NameAliasType>(type.getPointer())) {
73+
// Add the typealias to the found decls
74+
auto decl = alias->getDecl();
75+
addIfNonPublic(decl);
76+
77+
// Also walk into the RHS of the typealias to find extra types we need.
78+
decl->getUnderlyingTypeLoc().getType().walk(*this);
79+
}
80+
return Action::Continue;
81+
}
82+
};
83+
FindTypeDecls findTypeDecls;
84+
public:
85+
FindReferencedNonPublicTypeDecls(TypeDecls &foundDecls):
86+
findTypeDecls(foundDecls) {}
87+
bool walkToDeclPre(Decl *decl) {
88+
auto asd = dyn_cast<AbstractStorageDecl>(decl);
89+
if (!asd) return true;
90+
if (contributesToParentTypeStorage(asd))
91+
asd->getInterfaceType().walk(findTypeDecls);
92+
return true;
93+
}
94+
};
95+
96+
std::shared_ptr<ShouldPrintForParseableInterface>
97+
ShouldPrintForParseableInterface::create(ModuleDecl *module) {
98+
auto shouldPrint = std::make_shared<ShouldPrintForParseableInterface>();
99+
100+
// Walk the module to find stored properties whose type is non-public but
101+
// required to reconstruct the layout of their containing type.
102+
// We need to keep track of these type declarations, so we make sure to
103+
// print them.
104+
auto findTypeDecls = FindReferencedNonPublicTypeDecls(
105+
shouldPrint->referencedNonPublicTypeDecls);
106+
module->walk(findTypeDecls);
107+
return shouldPrint;
108+
}
109+
110+
bool ShouldPrintForParseableInterface::shouldPrint(
111+
const Decl *D, const PrintOptions &options) {
112+
// Skip anything that isn't 'public' or '@usableFromInline'.
113+
if (auto *VD = dyn_cast<ValueDecl>(D)) {
114+
if (!isPublicOrUsableFromInline(VD)) {
115+
// We do want to print private stored properties, without their
116+
// original names present.
117+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(VD))
118+
if (contributesToParentTypeStorage(ASD))
119+
return true;
120+
121+
// If this is a type that contributes to the layout of any
122+
// types in this module, then we should print it.
123+
if (auto typeDecl = dyn_cast<TypeDecl>(VD))
124+
return referencedNonPublicTypeDecls.count(typeDecl) != 0;
125+
return false;
126+
}
127+
}
128+
129+
// Skip extensions that extend things we wouldn't print.
130+
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
131+
if (!shouldPrint(ED->getExtendedNominal(), options))
132+
return false;
133+
// FIXME: We also need to check the generic signature for constraints
134+
// that we can't reference.
135+
}
136+
137+
// Skip typealiases that just redeclare generic parameters.
138+
if (auto *alias = dyn_cast<TypeAliasDecl>(D)) {
139+
if (alias->isImplicit()) {
140+
const Decl *parent =
141+
D->getDeclContext()->getAsDecl();
142+
if (auto *genericCtx = parent->getAsGenericContext()) {
143+
bool matchesGenericParam =
144+
llvm::any_of(genericCtx->getInnermostGenericParamTypes(),
145+
[alias](const GenericTypeParamType *param) {
146+
return param->getName() == alias->getName();
147+
});
148+
if (matchesGenericParam)
149+
return false;
150+
}
151+
}
152+
}
153+
154+
return ShouldPrintChecker::shouldPrint(D, options);
155+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===- ShouldPrintForParseableInterface.h - Parseable Interface filtering -===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H
14+
#define SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H
15+
16+
#include "swift/AST/PrintOptions.h"
17+
#include "swift/Basic/LLVM.h"
18+
#include "llvm/ADT/SmallPtrSet.h"
19+
20+
namespace swift {
21+
22+
class AbstractStorageDecl;
23+
class Decl;
24+
class TypeDecl;
25+
class ValueDecl;
26+
27+
class ShouldPrintForParseableInterface : public ShouldPrintChecker {
28+
SmallPtrSet<TypeDecl *, 8> referencedNonPublicTypeDecls;
29+
public:
30+
static std::shared_ptr<ShouldPrintForParseableInterface>
31+
create(ModuleDecl *module);
32+
33+
bool shouldPrint(const Decl *D, const PrintOptions &options) override;
34+
};
35+
36+
/// Determines if a declaration is public or has the @usableFromInline
37+
/// attribute.
38+
bool isPublicOrUsableFromInline(const ValueDecl *VD);
39+
40+
/// Determines if this storage decl resides as a stored instance member
41+
/// of a non-resilient nominal type.
42+
bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD);
43+
44+
} // end namespace swift
45+
46+
#endif // !defined(SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H)

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,8 @@ bool swift::emitParseableInterface(raw_ostream &out,
408408
printToolVersionAndFlagsComment(out, Opts, M);
409409
printImports(out, M);
410410

411-
const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile();
411+
const PrintOptions printOptions =
412+
PrintOptions::printParseableInterfaceFile(M);
412413
SmallVector<Decl *, 16> topLevelDecls;
413414
M->getTopLevelDecls(topLevelDecls);
414415
for (const Decl *D : topLevelDecls) {

0 commit comments

Comments
 (0)