Skip to content

[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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ struct PrintOptions {
/// consistent and well-formed.
///
/// \see swift::emitParseableInterface
static PrintOptions printParseableInterfaceFile();
static PrintOptions printParseableInterfaceFile(ModuleDecl *module);

static PrintOptions printModuleInterface();
static PrintOptions printTypeInterface(Type T);
Expand Down
62 changes: 3 additions & 59 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
#include "ShouldPrintForParseableInterface.h"
#include <algorithm>
#include <queue>

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

static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
AccessScope scope =
VD->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
return scope.isPublic();
}

static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
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();
}

PrintOptions PrintOptions::printParseableInterfaceFile() {
PrintOptions PrintOptions::printParseableInterfaceFile(ModuleDecl *module) {
Copy link
Contributor

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.

PrintOptions result;
result.PrintLongAttrsOnSeparateLines = true;
result.TypeDefinitions = true;
Expand All @@ -100,50 +86,8 @@ PrintOptions PrintOptions::printParseableInterfaceFile() {
printer << " " << AFD->getInlinableBodyText(scratch);
};

class ShouldPrintForParseableInterface : public ShouldPrintChecker {
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
// 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;
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);
}
};
result.CurrentPrintabilityChecker =
std::make_shared<ShouldPrintForParseableInterface>();
ShouldPrintForParseableInterface::create(module);

// FIXME: We don't really need 'public' on everything; we could just change
// the default to 'public' and mark the 'internal' things.
Expand Down
1 change: 1 addition & 0 deletions lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_swift_library(swiftAST STATIC
RawComment.cpp
RequirementEnvironment.cpp
SyntaxASTMap.cpp
ShouldPrintForParseableInterface.cpp
SILLayout.cpp
Stmt.cpp
SubstitutionMap.cpp
Expand Down
155 changes: 155 additions & 0 deletions lib/AST/ShouldPrintForParseableInterface.cpp
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
// 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) {
AccessScope scope =
VD->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
return scope.isPublic();
}

bool swift::contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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
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);
Copy link
Contributor

Choose a reason for hiding this comment

The 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) {
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);
}
46 changes: 46 additions & 0 deletions lib/AST/ShouldPrintForParseableInterface.h
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)
3 changes: 2 additions & 1 deletion lib/Frontend/ParseableInterfaceSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ bool swift::emitParseableInterface(raw_ostream &out,
printToolVersionAndFlagsComment(out, Opts, M);
printImports(out, M);

const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile();
const PrintOptions printOptions =
PrintOptions::printParseableInterfaceFile(M);
SmallVector<Decl *, 16> topLevelDecls;
M->getTopLevelDecls(topLevelDecls);
for (const Decl *D : topLevelDecls) {
Expand Down
Loading