mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
SIL: Serialize bodies of local functions inside @_transparent functions
A transparent function might be deserialized and inlined into a function in another module, which would cause problems if the function referenced local functions. Previously we would force local functions to have public linkage instead, which worked, but was not resilient if the body of the transparent function changed in the module that contained it. Add a library evolution test ensuring that such a change is resilient now. Part of https://bugs.swift.org/browse/SR-267.
This commit is contained in:
@@ -240,34 +240,6 @@ Optional<AnyFunctionRef> SILDeclRef::getAnyFunctionRef() const {
|
||||
return AnyFunctionRef(loc.get<AbstractClosureExpr*>());
|
||||
}
|
||||
|
||||
static SILLinkage getLinkageForLocalContext(DeclContext *dc) {
|
||||
auto isClangImported = [](AbstractFunctionDecl *fn) -> bool {
|
||||
if (fn->hasClangNode())
|
||||
return true;
|
||||
if (auto func = dyn_cast<FuncDecl>(fn))
|
||||
if (auto storage = func->getAccessorStorageDecl())
|
||||
return storage->hasClangNode();
|
||||
return false;
|
||||
};
|
||||
|
||||
while (!dc->isModuleScopeContext()) {
|
||||
// Local definitions in transparent contexts are forced public because
|
||||
// external references to them can be exposed by mandatory inlining.
|
||||
// For Clang-imported decls, though, the closure should get re-synthesized
|
||||
// on use.
|
||||
if (auto fn = dyn_cast<AbstractFunctionDecl>(dc))
|
||||
if (fn->isTransparent() && !isClangImported(fn))
|
||||
return SILLinkage::Public;
|
||||
// Check that this local context is not itself in a local transparent
|
||||
// context.
|
||||
dc = dc->getParent();
|
||||
}
|
||||
|
||||
// FIXME: Once we have access control at the AST level, we should not assume
|
||||
// shared always, but rather base it off of the local decl context.
|
||||
return SILLinkage::Shared;
|
||||
}
|
||||
|
||||
bool SILDeclRef::isThunk() const {
|
||||
return isCurried || isForeignToNativeThunk() || isNativeToForeignThunk();
|
||||
}
|
||||
@@ -300,6 +272,8 @@ bool SILDeclRef::isClangGenerated() const {
|
||||
|
||||
auto clangNode = getDecl()->getClangNode().getAsDecl();
|
||||
if (auto nd = dyn_cast_or_null<clang::NamedDecl>(clangNode)) {
|
||||
// ie, 'static inline' functions for which we must ask Clang to emit a body
|
||||
// for explicitly
|
||||
if (!nd->isExternallyVisible())
|
||||
return true;
|
||||
}
|
||||
@@ -308,25 +282,26 @@ bool SILDeclRef::isClangGenerated() const {
|
||||
}
|
||||
|
||||
SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
|
||||
// Anonymous functions have local linkage.
|
||||
if (auto closure = getAbstractClosureExpr())
|
||||
return getLinkageForLocalContext(closure->getParent());
|
||||
// Anonymous functions have shared linkage.
|
||||
// FIXME: This should really be the linkage of the parent function.
|
||||
if (getAbstractClosureExpr())
|
||||
return SILLinkage::Shared;
|
||||
|
||||
// Native function-local declarations have local linkage.
|
||||
// Native function-local declarations have shared linkage.
|
||||
// FIXME: @objc declarations should be too, but we currently have no way
|
||||
// of marking them "used" other than making them external.
|
||||
ValueDecl *d = getDecl();
|
||||
DeclContext *moduleContext = d->getDeclContext();
|
||||
while (!moduleContext->isModuleScopeContext()) {
|
||||
if (!isForeign && moduleContext->isLocalContext())
|
||||
return getLinkageForLocalContext(moduleContext);
|
||||
return SILLinkage::Shared;
|
||||
moduleContext = moduleContext->getParent();
|
||||
}
|
||||
|
||||
// Currying and calling convention thunks have shared linkage.
|
||||
if (isThunk())
|
||||
// If a function declares a @_cdecl name, its native-to-foreign thunk is
|
||||
// exported with the visibility of the function.
|
||||
// If a function declares a @_cdecl name, its native-to-foreign thunk
|
||||
// is exported with the visibility of the function.
|
||||
if (!isNativeToForeignThunk() || !d->getAttrs().hasAttribute<CDeclAttr>())
|
||||
return SILLinkage::Shared;
|
||||
|
||||
@@ -383,6 +358,36 @@ bool SILDeclRef::isTransparent() const {
|
||||
return hasDecl() ? getDecl()->isTransparent() : false;
|
||||
}
|
||||
|
||||
/// \brief True if the function should have its body serialized.
|
||||
bool SILDeclRef::isFragile() const {
|
||||
DeclContext *dc;
|
||||
if (auto closure = getAbstractClosureExpr())
|
||||
dc = closure->getLocalContext();
|
||||
else
|
||||
dc = getDecl()->getDeclContext();
|
||||
|
||||
while (!dc->isModuleScopeContext()) {
|
||||
// Local definitions in transparent contexts are fragile because
|
||||
// external references to them can be exposed by mandatory inlining.
|
||||
if (auto fn = dyn_cast<AbstractFunctionDecl>(dc))
|
||||
if (fn->isTransparent() &&
|
||||
fn->getEffectiveAccess() == Accessibility::Public)
|
||||
return true;
|
||||
// Check that this local context is not itself in a local transparent
|
||||
// context.
|
||||
dc = dc->getParent();
|
||||
}
|
||||
|
||||
// Externally-visible transparent functions are fragile.
|
||||
if (hasDecl())
|
||||
if (auto fn = dyn_cast<AbstractFunctionDecl>(getDecl()))
|
||||
if (fn->isTransparent() &&
|
||||
fn->getEffectiveAccess() == Accessibility::Public)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief True if the function has noinline attribute.
|
||||
bool SILDeclRef::isNoinline() const {
|
||||
if (!hasDecl())
|
||||
|
||||
Reference in New Issue
Block a user