Skip to content

Plumb through support for 'usable from inline' imports #16211

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

Merged
Merged
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
3 changes: 3 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,9 @@ SIMPLE_DECL_ATTR(_frozen, Frozen,
OnEnum |
UserInaccessible,
76)
SIMPLE_DECL_ATTR(_usableFromInline, UsableFromInlineImport,
OnImport | UserInaccessible,
77)

#undef TYPE_ATTR
#undef 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
46 changes: 39 additions & 7 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, if these categories already aren't mutually exclusive, I think I'd suggest having UsableFromInline also include Public, as we discussed (which might suggest calling it "ForLinking" or something instead).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift modules are also "imported modules" as far as AST goes, but either way it seems like an implementation detail that's not worth mentioning here. Those functions are virtual for a reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant "Clang imported modules" I guess. I'll change the doc comment in the next PR.

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 @@ -402,11 +415,16 @@ class ModuleDecl : public DeclContext, public TypeDecl {
/// results, with the given access path.
/// \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,
llvm::function_ref<bool(ImportedModule)> fn);
llvm::function_ref<bool(ImportedModule)> fn,
bool includeLinkOnlyModules = false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*sigh* Should this just take an ImportFilter at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not quite an ImportFilter, because it's used to decide if we want to call getImportedModulesForLookup() vs getImportedModulesForLinking(). Since those two are only overridden by ClangModuleUnit and the comments allude to some caching they do, I suspect it could be refactored further, but maybe we can tackle this later.



/// @}

Expand Down Expand Up @@ -638,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 @@ -649,11 +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);
forAllVisibleModules(llvm::function_ref<bool(ModuleDecl::ImportedModule)> fn,
bool includeLinkOnlyModules = false);

/// @}

Expand Down Expand Up @@ -732,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 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
27 changes: 20 additions & 7 deletions include/swift/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,27 +132,40 @@ 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; }

Expand Down
3 changes: 2 additions & 1 deletion 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 = 411; // Last change: copy_block_without_escaping
const uint16_t VERSION_MINOR = 412; // Last change: @usableFromInline import

using DeclIDField = BCFixed<31>;

Expand Down Expand Up @@ -595,6 +595,7 @@ namespace input_block {
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 Down
35 changes: 28 additions & 7 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,11 @@ SourceFile::getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &modu
if (importPair.second.contains(ImportFlags::Exported))
continue;
break;
case ModuleDecl::ImportFilter::ForLinking:
if (!importPair.second.contains(ImportFlags::UsableFromInline) &&
!importPair.second.contains(ImportFlags::Exported))
continue;
break;
}

modules.push_back(importPair.first);
Expand All @@ -985,6 +990,11 @@ void ModuleDecl::getImportedModulesForLookup(
FORWARD(getImportedModulesForLookup, (modules));
}

void ModuleDecl::getImportedModulesForLinking(
SmallVectorImpl<ImportedModule> &modules) const {
FORWARD(getImportedModulesForLinking, (modules));
}

bool ModuleDecl::isSameAccessPath(AccessPathTy lhs, AccessPathTy rhs) {
using AccessPathElem = std::pair<Identifier, SourceLoc>;
if (lhs.size() != rhs.size())
Expand Down Expand Up @@ -1128,11 +1138,15 @@ bool ModuleDecl::isSystemModule() const {
}

bool ModuleDecl::forAllVisibleModules(AccessPathTy thisPath,
llvm::function_ref<bool(ImportedModule)> fn) {
llvm::function_ref<bool(ImportedModule)> fn,
bool includeLinkOnlyModules) {
llvm::SmallSet<ImportedModule, 32, ModuleDecl::OrderImportedModules> visited;
SmallVector<ImportedModule, 32> stack;

getImportedModules(stack, ModuleDecl::ImportFilter::Public);
if (includeLinkOnlyModules)
getImportedModules(stack, ModuleDecl::ImportFilter::ForLinking);
else
getImportedModules(stack, ModuleDecl::ImportFilter::Public);

// Make sure the top-level module is first; we want pre-order-ish traversal.
stack.push_back(ImportedModule(thisPath, this));
Expand All @@ -1158,15 +1172,20 @@ bool ModuleDecl::forAllVisibleModules(AccessPathTy thisPath,
if (!fn(next))
return false;

next.second->getImportedModulesForLookup(stack);
if (includeLinkOnlyModules)
next.second->getImportedModulesForLinking(stack);
else
next.second->getImportedModulesForLookup(stack);
}

return true;
}

bool FileUnit::forAllVisibleModules(
llvm::function_ref<bool(ModuleDecl::ImportedModule)> fn) {
if (!getParentModule()->forAllVisibleModules(ModuleDecl::AccessPathTy(), fn))
llvm::function_ref<bool(ModuleDecl::ImportedModule)> fn,
bool includeLinkOnlyModules) {
if (!getParentModule()->forAllVisibleModules(ModuleDecl::AccessPathTy(), fn,
includeLinkOnlyModules))
return false;

if (auto SF = dyn_cast<SourceFile>(this)) {
Expand All @@ -1175,7 +1194,8 @@ bool FileUnit::forAllVisibleModules(
SmallVector<ModuleDecl::ImportedModule, 4> imports;
SF->getImportedModules(imports, ModuleDecl::ImportFilter::Private);
for (auto importPair : imports)
if (!importPair.second->forAllVisibleModules(importPair.first, fn))
if (!importPair.second->forAllVisibleModules(importPair.first, fn,
includeLinkOnlyModules))
return false;
}

Expand All @@ -1198,7 +1218,8 @@ SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const

next->collectLinkLibraries(callback);
return true;
});
},
/*includeLinkOnlyModules=*/true);
}

bool ModuleDecl::walk(ASTWalker &Walker) {
Expand Down
3 changes: 3 additions & 0 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3049,6 +3049,7 @@ void ClangModuleUnit::getImportedModules(
imports.push_back({ModuleDecl::AccessPathTy(), owner.getStdlibModule()});
break;
case ModuleDecl::ImportFilter::Public:
case ModuleDecl::ImportFilter::ForLinking:
break;
}

Expand All @@ -3058,6 +3059,7 @@ void ClangModuleUnit::getImportedModules(
switch (filter) {
case ModuleDecl::ImportFilter::All:
case ModuleDecl::ImportFilter::Public:
case ModuleDecl::ImportFilter::ForLinking:
imported.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
break;
Expand Down Expand Up @@ -3106,6 +3108,7 @@ void ClangModuleUnit::getImportedModules(
}

case ModuleDecl::ImportFilter::Public:
case ModuleDecl::ImportFilter::ForLinking:
break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/NameBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ void NameBinder::addImport(
ImportOptions options;
if (ID->isExported())
options |= SourceFile::ImportFlags::Exported;
if (ID->isUsableFromInline())
options |= SourceFile::ImportFlags::UsableFromInline;
if (testableAttr)
options |= SourceFile::ImportFlags::Testable;
imports.push_back({ { ID->getDeclPath(), M }, options });
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
IGNORED_ATTR(UIApplicationMain)
IGNORED_ATTR(UnsafeNoObjCTaggedPointer)
IGNORED_ATTR(UsableFromInline)
IGNORED_ATTR(UsableFromInlineImport)
IGNORED_ATTR(WeakLinked)
#undef IGNORED_ATTR

Expand Down Expand Up @@ -840,6 +841,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
IGNORED_ATTR(SynthesizedProtocol)
IGNORED_ATTR(Testable)
IGNORED_ATTR(Transparent)
IGNORED_ATTR(UsableFromInlineImport)
IGNORED_ATTR(WarnUnqualifiedAccess)
IGNORED_ATTR(WeakLinked)
#undef IGNORED_ATTR
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5775,6 +5775,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
UNINTERESTING_ATTR(ClangImporterSynthesizedType)
UNINTERESTING_ATTR(WeakLinked)
UNINTERESTING_ATTR(Frozen)
UNINTERESTING_ATTR(UsableFromInlineImport)

#undef UNINTERESTING_ATTR

void visitAvailableAttr(AvailableAttr *attr) {
Expand Down
18 changes: 15 additions & 3 deletions lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1140,10 +1140,12 @@ ModuleFile::ModuleFile(
unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
switch (kind) {
case input_block::IMPORTED_MODULE: {
bool exported, scoped;
bool exported, usableFromInline, scoped;
input_block::ImportedModuleLayout::readRecord(scratch,
exported, scoped);
Dependencies.push_back({blobData, exported, scoped});
exported,
usableFromInline,
scoped);
Dependencies.push_back({blobData, exported, usableFromInline, scoped});
break;
}
case input_block::LINK_LIBRARY: {
Expand Down Expand Up @@ -1598,6 +1600,13 @@ void ModuleFile::getImportedModules(
continue;

break;

case ModuleDecl::ImportFilter::ForLinking:
// Only include @_exported and @usableFromInline imports.
if (!dep.isExported() && !dep.isUsableFromInline())
continue;

break;
}

assert(dep.isLoaded());
Expand Down Expand Up @@ -1666,6 +1675,9 @@ void ModuleFile::getImportDecls(SmallVectorImpl<Decl *> &Results) {
if (Dep.isExported())
ID->getAttrs().add(
new (Ctx) ExportedAttr(/*IsImplicit=*/false));
if (Dep.isUsableFromInline())
ID->getAttrs().add(
new (Ctx) UsableFromInlineImportAttr(/*IsImplicit=*/true));
ImportDecls.push_back(ID);
}
Bits.ComputedImportDecls = true;
Expand Down
Loading