diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 134c3b636d3d5..8c3585dfb1fa1 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -125,9 +125,7 @@ namespace swift { /// Resolve imports for a source file generated to adapt a given /// Clang module. - void performImportResolutionForClangMacroBuffer( - SourceFile &SF, ModuleDecl *clangModule - ); + void performImportResolutionForClangMacroBuffer(SourceFile &SF); /// Once type-checking is complete, this instruments code with calls to an /// intrinsic that record the expected values of local variables so they can diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 3de857447660f..d9ba1da51b262 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -2826,6 +2826,17 @@ ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule( if (auto mainModule = SwiftContext.MainModule) { implicitImportInfo = mainModule->getImplicitImportInfo(); } + for (auto *I : underlying->Imports) { + // Make sure that synthesized Swift code in the clang module wrapper + // (e.g. _SwiftifyImport macro expansions) can access the same symbols as + // if it were actually in the clang module + std::string swiftModuleName = isCxxStdModule(I) ? + static_cast(SwiftContext.Id_CxxStdlib) : + I->getFullModuleName(); + ImportPath::Builder importPath(SwiftContext, swiftModuleName, '.'); + UnloadedImportedModule importedModule(importPath.copyTo(SwiftContext), ImportKind::Module); + implicitImportInfo.AdditionalUnloadedImports.push_back(importedModule); + } ClangModuleUnit *file = nullptr; auto wrapper = ModuleDecl::create(name, SwiftContext, implicitImportInfo, [&](ModuleDecl *wrapper, auto addFile) { diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 9f3f146324895..0350edee08f12 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -319,15 +319,14 @@ void swift::performImportResolution(SourceFile &SF) { verify(SF); } -void swift::performImportResolutionForClangMacroBuffer( - SourceFile &SF, ModuleDecl *clangModule -) { +void swift::performImportResolutionForClangMacroBuffer(SourceFile &SF) { // If we've already performed import resolution, bail. if (SF.ASTStage == SourceFile::ImportsResolved) return; + // `getWrapperForModule` has already declared all the implicit clang module + // imports we need ImportResolver resolver(SF); - resolver.addImplicitImport(clangModule); // FIXME: This is a hack that we shouldn't need, but be sure that we can // see the Swift standard library. diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 34b588f915614..17694aecca492 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -1060,10 +1060,8 @@ createMacroSourceFile(std::unique_ptr buffer, /*parsingOpts=*/{}, /*isPrimary=*/false); if (auto parentSourceFile = dc->getParentSourceFile()) macroSourceFile->setImports(parentSourceFile->getImports()); - else if (auto clangModuleUnit = - dyn_cast(dc->getModuleScopeContext())) { - auto clangModule = clangModuleUnit->getParentModule(); - performImportResolutionForClangMacroBuffer(*macroSourceFile, clangModule); + else if (isa(dc->getModuleScopeContext())) { + performImportResolutionForClangMacroBuffer(*macroSourceFile); } return macroSourceFile; } diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-a.h b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-a.h new file mode 100644 index 0000000000000..e31c04ac38ae6 --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-a.h @@ -0,0 +1,3 @@ +#pragma once + +typedef int a_t; diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-b.h b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-b.h new file mode 100644 index 0000000000000..cdcb5f790ced2 --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-b.h @@ -0,0 +1,3 @@ +#pragma once + +typedef int b_t; diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-c.h b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-c.h new file mode 100644 index 0000000000000..a00fe419e07cd --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-c.h @@ -0,0 +1,2 @@ +#pragma once +typedef int c_t; diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-d.h b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-d.h new file mode 100644 index 0000000000000..91d0254316dfc --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-d.h @@ -0,0 +1,2 @@ +#pragma once +typedef int d_t; diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-e.h b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-e.h new file mode 100644 index 0000000000000..eac3d7ddcac46 --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module-e.h @@ -0,0 +1,2 @@ +#pragma once +typedef int e_t; diff --git a/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module.modulemap b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module.modulemap new file mode 100644 index 0000000000000..7c3b96f1111b9 --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/TransitiveModules/module.modulemap @@ -0,0 +1,24 @@ +module ModuleA { + header "module-a.h" + export * +} +module ModuleB { + header "module-b.h" +} +module ModuleOuter { + module ModuleC { + header "module-c.h" + export * + } + explicit module ModuleD { + header "module-d.h" + export * + } +} +module ModuleDeep { + module ModuleDeepNested { + module ModuleDeepNestedNested { + header "module-e.h" + } + } +} diff --git a/test/Interop/C/swiftify-import/Inputs/clang-includes.h b/test/Interop/C/swiftify-import/Inputs/clang-includes.h new file mode 100644 index 0000000000000..096a824680724 --- /dev/null +++ b/test/Interop/C/swiftify-import/Inputs/clang-includes.h @@ -0,0 +1,20 @@ +#pragma once + +#include "TransitiveModules/module-a.h" +#include "TransitiveModules/module-b.h" +#include "TransitiveModules/module-c.h" +#include "TransitiveModules/module-d.h" +#include "TransitiveModules/module-e.h" + +#define __counted_by(x) __attribute__((__counted_by__(x))) +#define __noescape __attribute__((noescape)) + +void basic_include(const a_t *__counted_by(len) p __noescape, a_t len); + +void non_exported_include(const b_t *__counted_by(len) p __noescape, b_t len); + +void submodule_include(const c_t *__counted_by(len) p __noescape, c_t len); + +void explicit_submodule_include(const d_t *__counted_by(len) p __noescape, d_t len); + +void deep_submodule_noexport(const e_t *__counted_by(len) p __noescape, e_t len); diff --git a/test/Interop/C/swiftify-import/Inputs/module.modulemap b/test/Interop/C/swiftify-import/Inputs/module.modulemap index 9e4d82f3ef71c..93416d69a4a97 100644 --- a/test/Interop/C/swiftify-import/Inputs/module.modulemap +++ b/test/Interop/C/swiftify-import/Inputs/module.modulemap @@ -26,3 +26,10 @@ module CommentsClang { header "comments.h" export * } +module ClangIncludesModule { + header "clang-includes.h" + export * +} +module ClangIncludesNoExportModule { + header "clang-includes.h" +} diff --git a/test/Interop/C/swiftify-import/clang-includes-noexport.swift b/test/Interop/C/swiftify-import/clang-includes-noexport.swift new file mode 100644 index 0000000000000..34865880e7572 --- /dev/null +++ b/test/Interop/C/swiftify-import/clang-includes-noexport.swift @@ -0,0 +1,59 @@ +// REQUIRES: swift_feature_SafeInteropWrappers + +// RUN: %target-swift-ide-test -print-module -module-to-print=ClangIncludesNoExportModule -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s + +// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well +// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/ClangIncludesNoExport.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers %s + +import ClangIncludesNoExportModule + +// CHECK: import ModuleA +// CHECK-NEXT: import ModuleB +// CHECK-NOT: import +// CHECK-EMPTY: + +// CHECK-NEXT: func basic_include(_ p: UnsafePointer!, _ len: a_t) +// CHECK-NEXT: func non_exported_include(_ p: UnsafePointer!, _ len: b_t) +// CHECK-NEXT: func submodule_include(_ p: UnsafePointer!, _ len: c_t) +// CHECK-NEXT: func explicit_submodule_include(_ p: UnsafePointer!, _ len: d_t) +// CHECK-NEXT: func deep_submodule_noexport(_ p: UnsafePointer!, _ len: e_t) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func basic_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func deep_submodule_noexport(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func explicit_submodule_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func non_exported_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func submodule_include(_ p: Span) + +public func callBasicInclude(_ p: Span) { + basic_include(p) +} + +public func callNonExported(_ p: Span) { + non_exported_include(p) +} + +public func callSubmoduleInclude(_ p: Span) { + submodule_include(p) +} + +public func callExplicitSubmoduleInclude(_ p: Span) { + explicit_submodule_include(p) +} + +public func callDeepSubmoduleNoexport(_ p: Span) { + deep_submodule_noexport(p) +} diff --git a/test/Interop/C/swiftify-import/clang-includes.swift b/test/Interop/C/swiftify-import/clang-includes.swift new file mode 100644 index 0000000000000..7f89580d794d8 --- /dev/null +++ b/test/Interop/C/swiftify-import/clang-includes.swift @@ -0,0 +1,59 @@ +// REQUIRES: swift_feature_SafeInteropWrappers + +// RUN: %target-swift-ide-test -print-module -module-to-print=ClangIncludesModule -plugin-path %swift-plugin-dir -I %S/Inputs -source-filename=x -enable-experimental-feature SafeInteropWrappers | %FileCheck %s + +// swift-ide-test doesn't currently typecheck the macro expansions, so run the compiler as well +// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/ClangIncludes.swiftmodule -I %S/Inputs -enable-experimental-feature SafeInteropWrappers %s + +import ClangIncludesModule + +// CHECK: @_exported import ModuleA +// CHECK-NEXT: @_exported import ModuleB +// CHECK-NOT: import +// CHECK-EMPTY: + +// CHECK-NEXT: func basic_include(_ p: UnsafePointer!, _ len: a_t) +// CHECK-NEXT: func non_exported_include(_ p: UnsafePointer!, _ len: b_t) +// CHECK-NEXT: func submodule_include(_ p: UnsafePointer!, _ len: c_t) +// CHECK-NEXT: func explicit_submodule_include(_ p: UnsafePointer!, _ len: d_t) +// CHECK-NEXT: func deep_submodule_noexport(_ p: UnsafePointer!, _ len: e_t) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func basic_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func deep_submodule_noexport(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func explicit_submodule_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func non_exported_include(_ p: Span) + +// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop +// CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) +// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func submodule_include(_ p: Span) + +public func callBasicInclude(_ p: Span) { + basic_include(p) +} + +public func callNonExported(_ p: Span) { + non_exported_include(p) +} + +public func callSubmoduleInclude(_ p: Span) { + submodule_include(p) +} + +public func callExplicitSubmoduleInclude(_ p: Span) { + explicit_submodule_include(p) +} + +public func callDeepSubmoduleNoexport(_ p: Span) { + deep_submodule_noexport(p) +}