Skip to content

Infer imports for linking #16317

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
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
21 changes: 14 additions & 7 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ class ModuleDecl : public DeclContext, public TypeDecl {
// @_exported only.
Public,

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

// @_usableFromInline and @_exported only.
Expand All @@ -373,8 +373,11 @@ class ModuleDecl : public DeclContext, public TypeDecl {
void
getImportedModulesForLookup(SmallVectorImpl<ImportedModule> &imports) const;

/// Extension of the above hack. Identical to getImportedModulesForLookup()
/// for imported modules, otherwise also includes @usableFromInline imports.
/// Looks up which modules are imported by this module, ignoring any that
/// definitely don't represent distinct libraries.
///
/// This is a performance hack for the ClangImporter. Do not use for
/// anything but linking. May go away in the future.
void
getImportedModulesForLinking(SmallVectorImpl<ImportedModule> &imports) const;

Expand Down Expand Up @@ -767,10 +770,6 @@ class SourceFile final : public FileUnit {
/// This source file has access to testable declarations in the imported
/// module.
Testable = 0x2,

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

/// \see ImportFlags
Expand All @@ -785,6 +784,9 @@ class SourceFile final : public FileUnit {
/// This is filled in by the Name Binding phase.
ArrayRef<std::pair<ModuleDecl::ImportedModule, ImportOptions>> Imports;

/// A list of modules where declarations were used in inline functions.
llvm::SetVector<ModuleDecl *> ModulesUsedFromInline;

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

bool hasTestableImport(const ModuleDecl *module) const;

void markUsableFromInline(ModuleDecl *module);
ArrayRef<ModuleDecl *> getUsableFromInlineModules() const {
return ModulesUsedFromInline.getArrayRef();
}

void clearLookupCache();

void cacheVisibleDecls(SmallVectorImpl<ValueDecl *> &&globals) const;
Expand Down
24 changes: 22 additions & 2 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -975,14 +975,28 @@ SourceFile::getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &modu
continue;
break;
case ModuleDecl::ImportFilter::ForLinking:
if (!importPair.second.contains(ImportFlags::UsableFromInline) &&
!importPair.second.contains(ImportFlags::Exported))
if (!importPair.second.contains(ImportFlags::Exported))
continue;
break;
}

modules.push_back(importPair.first);
}

switch (filter) {
case ModuleDecl::ImportFilter::Public:
case ModuleDecl::ImportFilter::Private:
break;

case ModuleDecl::ImportFilter::All:
case ModuleDecl::ImportFilter::ForLinking:
// Treat UsableAsInline modules like imports.
// This isn't strictly necessary because we would have already found them
// through one of the modules listed above, but it's better for testing.
for (ModuleDecl *module : getUsableFromInlineModules())
modules.emplace_back(ModuleDecl::AccessPathTy(), module);
break;
}
}

void ModuleDecl::getImportedModulesForLookup(
Expand Down Expand Up @@ -1287,6 +1301,12 @@ bool SourceFile::hasTestableImport(const swift::ModuleDecl *module) const {
});
}

void SourceFile::markUsableFromInline(ModuleDecl *module) {
if (module == getParentModule())
return;
ModulesUsedFromInline.insert(module);
}

void SourceFile::clearLookupCache() {
if (!Cache)
return;
Expand Down
8 changes: 6 additions & 2 deletions lib/Sema/NameBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,19 @@ 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 });

if (topLevelModule != M)
imports.push_back({ { ID->getDeclPath(), topLevelModule }, options });

if (ID->isUsableFromInline()) {
// This is a bit unfortunate; everywhere else in this function, we're not
// actually modifying the SourceFile yet, just building a list for later.
SF.markUsableFromInline(topLevelModule);
}

if (ID->getImportKind() != ImportKind::Module) {
// If we're importing a specific decl, validate the import kind.
using namespace namelookup;
Expand Down
9 changes: 7 additions & 2 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2509,12 +2509,17 @@ bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R,
return true;
}

if (FragileKind)
if (R.isValid())
if (FragileKind) {
if (R.isValid()) {
if (TC.diagnoseInlinableDeclRef(R.Start, D, DC, *FragileKind,
TreatUsableFromInlineAsPublic))
return true;

auto *SF = cast<SourceFile>(DC->getModuleScopeContext());
SF->markUsableFromInline(D->getModuleContext());
}
}

if (TC.diagnoseExplicitUnavailability(D, R, DC, call))
return true;

Expand Down
4 changes: 2 additions & 2 deletions lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1588,8 +1588,8 @@ void ModuleFile::getImportedModules(
break;

case ModuleDecl::ImportFilter::Private:
// Skip @_exported imports.
if (dep.isExported())
// Skip otherwise-labeled imports.
if (dep.isExported() || dep.isUsableFromInline())
continue;

break;
Expand Down
8 changes: 8 additions & 0 deletions test/Serialization/Inputs/autolinking_module_inferred.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@_exported import autolinking_public
import autolinking_other // inferred as @_usableFromInline
import autolinking_other2
import autolinking_private

public func bfunc(x: Int = afunc(), y: Int = afunc2()) {
cfunc()
}
1 change: 1 addition & 0 deletions test/Serialization/Inputs/autolinking_other2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@_exported import autolinking_other3
1 change: 1 addition & 0 deletions test/Serialization/Inputs/autolinking_other3.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public func afunc2() -> Int { return 0 }
35 changes: 35 additions & 0 deletions test/Serialization/autolinking-inlinable-inferred.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_public.swift -emit-module-path %t/autolinking_public.swiftmodule -module-link-name autolinking_public -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other3.swift -emit-module-path %t/autolinking_other3.swiftmodule -module-link-name autolinking_other3 -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other2.swift -emit-module-path %t/autolinking_other2.swiftmodule -module-link-name autolinking_other2 -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other.swift -emit-module-path %t/autolinking_other.swiftmodule -module-link-name autolinking_other -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_private.swift -emit-module-path %t/autolinking_private.swiftmodule -module-link-name autolinking_private -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_module_inferred.swift -emit-module-path %t/autolinking_module_inferred.swiftmodule -module-link-name autolinking_module_inferred -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-ir %s -I %t -swift-version 4 | %FileCheck %s

// This test is identical to autolinking-inlinable, except we also rely on the
// '@_usableFromInline' attribute getting inferred correctly on the import.

// Linux uses a different autolinking mechanism, based on
// swift-autolink-extract. This file tests the Darwin mechanism.
// UNSUPPORTED: OS=linux-gnu
// UNSUPPORTED: OS=linux-gnueabihf
// UNSUPPORTED: OS=freebsd
// UNSUPPORTED: OS=linux-androideabi

import autolinking_module_inferred

bfunc()

// Note: we don't autolink autolinking_private even though autolinking_module imports it also.

// CHECK: !llvm.linker.options = !{[[SWIFTCORE:![0-9]+]], [[SWIFTONONESUPPORT:![0-9]+]], [[MODULE:![0-9]+]], [[PUBLIC:![0-9]+]], [[OTHER3:![0-9]+]], [[OTHER:![0-9]+]], [[OBJC:![0-9]+]]}

// CHECK: [[SWIFTCORE]] = !{!"-lswiftCore"}
// CHECK: [[SWIFTONONESUPPORT]] = !{!"-lswiftSwiftOnoneSupport"}
// CHECK: [[MODULE]] = !{!"-lautolinking_module_inferred"}
// CHECK: [[PUBLIC]] = !{!"-lautolinking_public"}
// CHECK: [[OTHER3]] = !{!"-lautolinking_other3"}
// CHECK: [[OTHER]] = !{!"-lautolinking_other"}
// CHECK: [[OBJC]] = !{!"-lobjc"}
4 changes: 3 additions & 1 deletion test/Serialization/autolinking-inlinable.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_public.swift -emit-module-path %t/autolinking_public.swiftmodule -module-link-name autolinking_public -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other.swift -emit-module-path %t/autolinking_other.swiftmodule -module-link-name autolinking_other -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other3.swift -emit-module-path %t/autolinking_other3.swiftmodule -module-link-name autolinking_other3 -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other2.swift -emit-module-path %t/autolinking_other2.swiftmodule -module-link-name autolinking_other2 -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_other.swift -emit-module-path %t/autolinking_other.swiftmodule -module-link-name autolinking_other -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_private.swift -emit-module-path %t/autolinking_private.swiftmodule -module-link-name autolinking_private -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-module %S/Inputs/autolinking_module.swift -emit-module-path %t/autolinking_module.swiftmodule -module-link-name autolinking_module -I %t -swift-version 4
// RUN: %target-swift-frontend -emit-ir %s -I %t -swift-version 4 | %FileCheck %s
Expand Down