-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ParseableInterface] Expose non-public types required for layout #20004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
0eb2e16
0edc0e6
90a439a
f2f32f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
//=== ShouldPrintForParseableInterface.cpp - Parseable Interface filtering ===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | ||
harlanhaskins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "ShouldPrintForParseableInterface.h" | ||
#include "swift/AST/ASTWalker.h" | ||
#include "swift/AST/TypeWalker.h" | ||
#include "swift/AST/Decl.h" | ||
#include "swift/AST/Module.h" | ||
#include "swift/AST/Type.h" | ||
|
||
using namespace swift; | ||
|
||
bool swift::isPublicOrUsableFromInline(const ValueDecl *VD) { | ||
harlanhaskins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
AccessScope scope = | ||
VD->getFormalAccessScope(/*useDC*/nullptr, | ||
/*treatUsableFromInlineAsPublic*/true); | ||
return scope.isPublic(); | ||
} | ||
|
||
bool swift::contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one I think should probably just move onto AbstractStorageDecl, though maybe without the "isResilient" part. But that doesn't have to change now. |
||
auto *DC = ASD->getDeclContext()->getAsDecl(); | ||
if (!DC) return false; | ||
auto *ND = dyn_cast<NominalTypeDecl>(DC); | ||
if (!ND) return false; | ||
return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); | ||
} | ||
|
||
/// Finds type decls that need to be printed to ensure the layout of public | ||
/// types are correct for non-resilient types. | ||
/// | ||
/// For example: | ||
/// | ||
/// ``` | ||
/// private struct B {} | ||
/// public struct A { | ||
/// internal let b: B | ||
/// } | ||
/// ``` | ||
/// Since `A`'s size depends on `B`, we need to print `B` even though it's | ||
/// private. `B`'s children will also be checked for types that contribute to | ||
/// its layout, and so on. | ||
class FindReferencedNonPublicTypeDecls: public ASTWalker { | ||
using TypeDecls = SmallPtrSetImpl<TypeDecl *>; | ||
|
||
/// Finds all nominal types or typealiases referenced in the type being | ||
/// walked. | ||
class FindTypeDecls: public TypeWalker { | ||
TypeDecls &foundDecls; | ||
public: | ||
FindTypeDecls(TypeDecls &foundDecls): foundDecls(foundDecls) {} | ||
|
||
void addIfNonPublic(TypeDecl *typeDecl) { | ||
if (isPublicOrUsableFromInline(typeDecl)) | ||
return; | ||
foundDecls.insert(typeDecl); | ||
} | ||
|
||
/// Finds all nominal types or typealiases referenced by the | ||
harlanhaskins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Action walkToTypePre(Type type) override { | ||
if (auto nominal = | ||
dyn_cast<NominalOrBoundGenericNominalType>(type.getPointer())) | ||
addIfNonPublic(nominal->getDecl()); | ||
if (auto alias = dyn_cast<NameAliasType>(type.getPointer())) { | ||
// Add the typealias to the found decls | ||
auto decl = alias->getDecl(); | ||
addIfNonPublic(decl); | ||
|
||
// Also walk into the RHS of the typealias to find extra types we need. | ||
decl->getUnderlyingTypeLoc().getType().walk(*this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't the visiting of the decl be responsible for that? |
||
} | ||
return Action::Continue; | ||
} | ||
}; | ||
FindTypeDecls findTypeDecls; | ||
public: | ||
FindReferencedNonPublicTypeDecls(TypeDecls &foundDecls): | ||
findTypeDecls(foundDecls) {} | ||
bool walkToDeclPre(Decl *decl) { | ||
harlanhaskins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
auto asd = dyn_cast<AbstractStorageDecl>(decl); | ||
if (!asd) return true; | ||
if (contributesToParentTypeStorage(asd)) | ||
asd->getInterfaceType().walk(findTypeDecls); | ||
return true; | ||
} | ||
}; | ||
|
||
std::shared_ptr<ShouldPrintForParseableInterface> | ||
ShouldPrintForParseableInterface::create(ModuleDecl *module) { | ||
auto shouldPrint = std::make_shared<ShouldPrintForParseableInterface>(); | ||
|
||
// Walk the module to find stored properties whose type is non-public but | ||
// required to reconstruct the layout of their containing type. | ||
// We need to keep track of these type declarations, so we make sure to | ||
// print them. | ||
auto findTypeDecls = FindReferencedNonPublicTypeDecls( | ||
shouldPrint->referencedNonPublicTypeDecls); | ||
module->walk(findTypeDecls); | ||
return shouldPrint; | ||
} | ||
|
||
bool ShouldPrintForParseableInterface::shouldPrint( | ||
const Decl *D, const PrintOptions &options) { | ||
// Skip anything that isn't 'public' or '@usableFromInline'. | ||
if (auto *VD = dyn_cast<ValueDecl>(D)) { | ||
if (!isPublicOrUsableFromInline(VD)) { | ||
// We do want to print private stored properties, without their | ||
// original names present. | ||
if (auto *ASD = dyn_cast<AbstractStorageDecl>(VD)) | ||
if (contributesToParentTypeStorage(ASD)) | ||
return true; | ||
|
||
// If this is a type that contributes to the layout of any | ||
// types in this module, then we should print it. | ||
if (auto typeDecl = dyn_cast<TypeDecl>(VD)) | ||
return referencedNonPublicTypeDecls.count(typeDecl) != 0; | ||
return false; | ||
} | ||
} | ||
|
||
// Skip extensions that extend things we wouldn't print. | ||
if (auto *ED = dyn_cast<ExtensionDecl>(D)) { | ||
if (!shouldPrint(ED->getExtendedNominal(), options)) | ||
return false; | ||
// FIXME: We also need to check the generic signature for constraints | ||
// that we can't reference. | ||
} | ||
|
||
// Skip typealiases that just redeclare generic parameters. | ||
if (auto *alias = dyn_cast<TypeAliasDecl>(D)) { | ||
if (alias->isImplicit()) { | ||
const Decl *parent = | ||
D->getDeclContext()->getAsDecl(); | ||
if (auto *genericCtx = parent->getAsGenericContext()) { | ||
bool matchesGenericParam = | ||
llvm::any_of(genericCtx->getInnermostGenericParamTypes(), | ||
[alias](const GenericTypeParamType *param) { | ||
return param->getName() == alias->getName(); | ||
}); | ||
if (matchesGenericParam) | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
return ShouldPrintChecker::shouldPrint(D, options); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
//===- ShouldPrintForParseableInterface.h - Parseable Interface filtering -===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H | ||
#define SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H | ||
|
||
#include "swift/AST/PrintOptions.h" | ||
#include "swift/Basic/LLVM.h" | ||
#include "llvm/ADT/SmallPtrSet.h" | ||
|
||
namespace swift { | ||
|
||
class AbstractStorageDecl; | ||
class Decl; | ||
class TypeDecl; | ||
class ValueDecl; | ||
|
||
class ShouldPrintForParseableInterface : public ShouldPrintChecker { | ||
SmallPtrSet<TypeDecl *, 8> referencedNonPublicTypeDecls; | ||
public: | ||
static std::shared_ptr<ShouldPrintForParseableInterface> | ||
create(ModuleDecl *module); | ||
|
||
bool shouldPrint(const Decl *D, const PrintOptions &options) override; | ||
}; | ||
|
||
/// Determines if a declaration is public or has the @usableFromInline | ||
/// attribute. | ||
bool isPublicOrUsableFromInline(const ValueDecl *VD); | ||
|
||
/// Determines if this storage decl resides as a stored instance member | ||
/// of a non-resilient nominal type. | ||
bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD); | ||
|
||
} // end namespace swift | ||
|
||
#endif // !defined(SWIFT_AST_SHOULDPRINTFORPARSEABLEINTERFACE_H) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly starting to think this doesn't belong on PrintOptions at all. Only the reusable or generic ones really deserve factory constructors; the rest should live at their use sites. That doesn't have to change in this PR, but it kind of lines up with the spiltting out to a separate file.