Skip to content

[4.2 EARLY] Autolink libraries referenced from default arguments and @inlinable bodies #16332

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
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ SIMPLE_DECL_ATTR(_weakLinked, WeakLinked,

SIMPLE_DECL_ATTR(_frozen, Frozen, OnEnum | UserInaccessible, 76)

SIMPLE_DECL_ATTR(_usableFromInline, UsableFromInlineImport,
OnImport | UserInaccessible,
77)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1549,10 +1549,19 @@ class ImportDecl final : public Decl,
return static_cast<ImportKind>(Bits.ImportDecl.ImportKind);
}

// An exported import is visible to name lookup from other modules, and is
// autolinked when the containing module is autolinked.
bool isExported() const {
return getAttrs().hasAttribute<ExportedAttr>();
}

// A usable from inline import is autolinked but not visible to name lookup.
// This attribute is inferred when type checking inlinable and default
// argument bodies.
bool isUsableFromInline() const {
return getAttrs().hasAttribute<UsableFromInlineImportAttr>();
}

ModuleDecl *getModule() const { return Mod; }
void setModule(ModuleDecl *M) { Mod = M; }

Expand Down
97 changes: 41 additions & 56 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,17 @@ class ModuleDecl : public DeclContext, public TypeDecl {

/// \sa getImportedModules
enum class ImportFilter {
// Everything.
All,

// @_exported only.
Public,
Private

// Not @_exported only. Also includes @_usableFromInline.
Private,

// @_usableFromInline and @_exported only.
ForLinking
};

/// Looks up which modules are imported by this module.
Expand All @@ -360,11 +368,16 @@ class ModuleDecl : public DeclContext, public TypeDecl {
/// Looks up which modules are imported by this module, ignoring any that
/// won't contain top-level decls.
///
/// This is a performance hack. Do not use for anything but name lookup.
/// May go away in the future.
/// This is a performance hack for the ClangImporter. Do not use for
/// anything but name lookup. May go away in the future.
void
getImportedModulesForLookup(SmallVectorImpl<ImportedModule> &imports) const;

/// Extension of the above hack. Identical to getImportedModulesForLookup()
/// for imported modules, otherwise also includes @usableFromInline imports.
void
getImportedModulesForLinking(SmallVectorImpl<ImportedModule> &imports) const;

/// Finds all top-level decls of this module.
///
/// This does a simple local lookup, not recursively looking through imports.
Expand Down Expand Up @@ -400,45 +413,18 @@ class ModuleDecl : public DeclContext, public TypeDecl {
///
/// \param topLevelAccessPath If present, include the top-level module in the
/// results, with the given access path.
/// \param includePrivateTopLevelImports If true, imports listed in all
/// file units within this module are traversed. Otherwise (the
/// default), only re-exported imports are traversed.
/// \param fn A callback of type bool(ImportedModule) or void(ImportedModule).
/// Return \c false to abort iteration.
/// \param includeLinkOnlyModules Include modules that are not visible to
/// name lookup but must be linked in because inlinable code can
/// reference their symbols.
///
/// \return True if the traversal ran to completion, false if it ended early
/// due to the callback.
bool forAllVisibleModules(AccessPathTy topLevelAccessPath,
bool includePrivateTopLevelImports,
llvm::function_ref<bool(ImportedModule)> fn);
llvm::function_ref<bool(ImportedModule)> fn,
bool includeLinkOnlyModules = false);

bool forAllVisibleModules(AccessPathTy topLevelAccessPath,
bool includePrivateTopLevelImports,
llvm::function_ref<void(ImportedModule)> fn) {
return forAllVisibleModules(topLevelAccessPath,
includePrivateTopLevelImports,
[=](const ImportedModule &import) -> bool {
fn(import);
return true;
});
}

template <typename Fn>
bool forAllVisibleModules(AccessPathTy topLevelAccessPath,
bool includePrivateTopLevelImports,
Fn &&fn) {
using RetTy = typename std::result_of<Fn(ImportedModule)>::type;
llvm::function_ref<RetTy(ImportedModule)> wrapped{std::forward<Fn>(fn)};
return forAllVisibleModules(topLevelAccessPath,
includePrivateTopLevelImports,
wrapped);
}

template <typename Fn>
bool forAllVisibleModules(AccessPathTy topLevelAccessPath, Fn &&fn) {
return forAllVisibleModules(topLevelAccessPath, false,
std::forward<Fn>(fn));
}

/// @}

Expand Down Expand Up @@ -670,6 +656,12 @@ class FileUnit : public DeclContext {
return getImportedModules(imports, ModuleDecl::ImportFilter::Public);
}

/// \see ModuleDecl::getImportedModulesForLinking
virtual void getImportedModulesForLinking(
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const {
return getImportedModules(imports, ModuleDecl::ImportFilter::ForLinking);
}

/// Generates the list of libraries needed to link this file, based on its
/// imports.
virtual void
Expand All @@ -681,28 +673,15 @@ class FileUnit : public DeclContext {
///
/// \param fn A callback of type bool(ImportedModule) or void(ImportedModule).
/// Return \c false to abort iteration.
/// \param includeLinkOnlyModules Include modules that are not visible to
/// name lookup but must be linked in because inlinable code can
/// reference their symbols.
///
/// \return True if the traversal ran to completion, false if it ended early
/// due to the callback.
bool
forAllVisibleModules(llvm::function_ref<bool(ModuleDecl::ImportedModule)> fn);

bool
forAllVisibleModules(llvm::function_ref<void(ModuleDecl::ImportedModule)> fn) {
return forAllVisibleModules([=](ModuleDecl::ImportedModule import) -> bool {
fn(import);
return true;
});
}

template <typename Fn>
bool forAllVisibleModules(Fn &&fn) {
using RetTy = typename std::result_of<Fn(ModuleDecl::ImportedModule)>::type;
llvm::function_ref<RetTy(ModuleDecl::ImportedModule)> wrapped{
std::forward<Fn>(fn)
};
return forAllVisibleModules(wrapped);
}
forAllVisibleModules(llvm::function_ref<bool(ModuleDecl::ImportedModule)> fn,
bool includeLinkOnlyModules = false);

/// @}

Expand Down Expand Up @@ -781,13 +760,17 @@ class SourceFile final : public FileUnit {
};

/// Possible attributes for imports in source files.
enum class ImportFlags {
enum class ImportFlags : uint8_t {
/// The imported module is exposed to anyone who imports the parent module.
Exported = 0x1,

/// This source file has access to testable declarations in the imported
/// module.
Testable = 0x2
Testable = 0x2,

/// Modules that depend on the module containing this source file will
/// autolink this dependency.
UsableFromInline = 0x4,
};

/// \see ImportFlags
Expand All @@ -800,7 +783,7 @@ class SourceFile final : public FileUnit {
/// This is the list of modules that are imported by this module.
///
/// This is filled in by the Name Binding phase.
ArrayRef<std::pair<ModuleDecl::ImportedModule, ImportOptions>> Imports;
MutableArrayRef<std::pair<ModuleDecl::ImportedModule, ImportOptions>> Imports;

/// A unique identifier representing this file; used to mark private decls
/// within the file to keep them from conflicting with other files in the
Expand Down Expand Up @@ -906,6 +889,8 @@ class SourceFile final : public FileUnit {

bool hasTestableImport(const ModuleDecl *module) const;

void markUsableFromInlineImport(const ModuleDecl *module);

void clearLookupCache();

void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
Expand Down
6 changes: 6 additions & 0 deletions include/swift/ClangImporter/ClangModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ class ClangModuleUnit final : public LoadedFile {
virtual void getImportedModulesForLookup(
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const override;

virtual void getImportedModulesForLinking(
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const override {
// In C, anything that's linkable is visible to the source language.
return getImportedModulesForLookup(imports);
}

virtual void
collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const override;

Expand Down
14 changes: 0 additions & 14 deletions include/swift/IRGen/IRGenPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,6 @@ createIRGenModule(SILModule *SILMod, StringRef OutputFilename,
/// Delete the IRGenModule and IRGenerator obtained by the above call.
void deleteIRGenModule(std::pair<IRGenerator *, IRGenModule *> &Module);

/// Collect the set of libraries to autolink against by mining the
/// external definitions stored in an AST context.
///
/// This entire thing is a hack that we shouldn't need, but it reflects the
/// fact that we can end up referencing something via an external definition
/// (e.g., an imported Clang declaration) indirectly via the Clang importer
/// that would not be visible directly. In such cases, we would fail to
/// link against the shared library that defines the entity or an overlay that
/// is needed as part of its import from Clang (e.g., the Foundation overlay
/// is needed when bridging NSString, even if there is no other mention of
/// an entity from the Foundation overlay).
llvm::SmallVector<LinkLibrary, 4> collectLinkLibrariesFromExternals(
ASTContext &ctx);

} // end namespace irgen
} // end namespace swift

Expand Down
35 changes: 20 additions & 15 deletions include/swift/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,32 +132,43 @@ class ModuleFile
const StringRef RawPath;

private:
unsigned IsExported : 1;
const unsigned IsHeader : 1;
const unsigned IsExported : 1;
const unsigned IsUsableFromInline : 1;
const unsigned IsScoped : 1;

Dependency(StringRef path, bool isHeader, bool exported, bool isScoped)
: RawPath(path), IsExported(exported), IsHeader(isHeader),
IsScoped(isScoped) {}
Dependency(bool isHeader,
StringRef path, bool exported,
bool isUsableFromInline, bool isScoped)
: RawPath(path),
IsHeader(isHeader),
IsExported(exported),
IsUsableFromInline(isUsableFromInline),
IsScoped(isScoped) {
assert(!(IsExported && IsUsableFromInline));
assert(!(IsHeader && IsScoped));
}

public:
Dependency(StringRef path, bool exported, bool isScoped)
: Dependency(path, false, exported, isScoped) {}
Dependency(StringRef path, bool exported, bool isUsableFromInline,
bool isScoped)
: Dependency(false, path, exported, isUsableFromInline, isScoped) {}

static Dependency forHeader(StringRef headerPath, bool exported) {
return Dependency(headerPath, true, exported, false);
return Dependency(true, headerPath, exported,
/*isUsableFromInline=*/false,
/*isScoped=*/false);
}

bool isLoaded() const {
return Import.second != nullptr;
}

bool isExported() const { return IsExported; }
bool isUsableFromInline() const { return IsUsableFromInline; }
bool isHeader() const { return IsHeader; }
bool isScoped() const { return IsScoped; }

void forceExported() { IsExported = true; }

std::string getPrettyPrintedPath() const;
};

Expand Down Expand Up @@ -399,12 +410,6 @@ class ModuleFile
/// Whether this module file comes from a framework.
unsigned IsFramework : 1;

/// THIS SETTING IS OBSOLETE BUT IS STILL USED BY OLDER MODULES.
///
/// Whether this module has a shadowed module that's part of its public
/// interface.
unsigned HasUnderlyingModule : 1;

/// Whether or not ImportDecls is valid.
unsigned ComputedImportDecls : 1;

Expand Down
10 changes: 3 additions & 7 deletions include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t VERSION_MINOR = 409; // Last change: standalone requirement subs
const uint16_t VERSION_MINOR = 410; // Last change: @usableFromInline import

using DeclIDField = BCFixed<31>;

Expand Down Expand Up @@ -583,13 +583,14 @@ namespace input_block {
LINK_LIBRARY,
IMPORTED_HEADER,
IMPORTED_HEADER_CONTENTS,
MODULE_FLAGS,
MODULE_FLAGS, // [unused]
SEARCH_PATH
};

using ImportedModuleLayout = BCRecordLayout<
IMPORTED_MODULE,
BCFixed<1>, // exported?
BCFixed<1>, // usable from inlinable functions?
BCFixed<1>, // scoped?
BCBlob // module name, with submodule path pieces separated by \0s.
// If the 'scoped' flag is set, the final path piece is an access
Expand All @@ -616,11 +617,6 @@ namespace input_block {
BCBlob
>;

using ModuleFlagsLayout = BCRecordLayout<
MODULE_FLAGS,
BCFixed<1> // has underlying module? [[UNUSED]]
>;

using SearchPathLayout = BCRecordLayout<
SEARCH_PATH,
BCFixed<1>, // framework?
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2348,6 +2348,7 @@ void ASTContext::diagnoseAttrsRequiringFoundation(SourceFile &SF) {
SF.forAllVisibleModules([&](ModuleDecl::ImportedModule import) {
if (import.second->getName() == Id_Foundation)
ImportsFoundationModule = true;
return true;
});

if (ImportsFoundationModule)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/LookupVisibleDecls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ static void doDynamicLookup(VisibleDeclConsumer &Consumer,
CurrDC->getParentSourceFile()->forAllVisibleModules(
[&](ModuleDecl::ImportedModule Import) {
Import.second->lookupClassMembers(Import.first, ConsumerWrapper);
return true;
});
}

Expand Down
Loading