From 9dc0b1674841243110f95cdc9c9d97bcf30c1544 Mon Sep 17 00:00:00 2001 From: Jan Svoboda Date: Thu, 22 Sep 2022 11:31:31 -0700 Subject: [PATCH] [clang][deps] Report module map describing compiled module This patch fixes compilation failure with explicit modules caused by scanner not reporting the module map describing the module whose implementation is being compiled. Below is a breakdown of the attached test case. Note the VFS that makes frameworks "A" and "B" share the same header "shared/H.h". In non-modular build, Clang skips the last import, since the "shared/H.h" header has already been included. During scan (or implicit build), the compiler handles "tu.m" as follows: * `@import B` imports module "B", as expected, * `#import ` is resolved textually (due to `-fmodule-name=A`) to "shared/H.h" (due to the VFS remapping), * `#import ` is resolved to import module "A_Private", since the header "shared/H.h" is already known to be part of that module, and the import is skipped. In the end, the only modular dependency of the TU is "B". In explicit modular build without `-fmodule-name=A`, TU does depend on module "A_Private" properly, not just textually. Clang therefore builds & loads its PCM, and knows to ignore the last import, since "shared/H.h" is known to be part of "A_Private". But with current scanner behavior and `-fmodule-name=A` present, the last import fails during explicit build. Clang doesn't know about "A_Private" (it's included textually) and tries to import "B_Private" instead, which it doesn't know about either (the scanner correctly didn't report it as dependency). This is fixed by reporting the module map describing "A" and matching the semantics of implicit build. Reviewed By: Bigcheese Differential Revision: https://reviews.llvm.org/D134222 --- clang/include/clang/Basic/LangOptions.h | 5 ++ clang/include/clang/Lex/Preprocessor.h | 3 + clang/lib/Lex/Preprocessor.cpp | 7 ++ .../DependencyScanning/ModuleDepCollector.cpp | 9 ++ clang/test/ClangScanDeps/modules-header-sharing.m | 96 ++++++++++++++++++++++ .../Darwin/x86-64/Inputs/dlopen-dlclose-x2.s | 90 -------------------- 6 files changed, 120 insertions(+), 90 deletions(-) create mode 100644 clang/test/ClangScanDeps/modules-header-sharing.m delete mode 100644 compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/dlopen-dlclose-x2.s diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 9facfb0..aa0b6d4 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -505,6 +505,11 @@ public: return getCompilingModule() == CMK_ModuleInterface; } + /// Are we compiling a module implementation? + bool isCompilingModuleImplementation() const { + return !isCompilingModule() && !ModuleName.empty(); + } + /// Do we need to track the owning module for a local declaration? bool trackLocalOwningModule() const { return isCompilingModule() || ModulesLocalVisibility; diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index ae1359c..22fcbf1 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2231,6 +2231,9 @@ public: /// Retrieves the module that we're currently building, if any. Module *getCurrentModule(); + /// Retrieves the module whose implementation we're current compiling, if any. + Module *getCurrentModuleImplementation(); + /// Allocate a new MacroInfo object with the provided SourceLocation. MacroInfo *AllocateMacroInfo(SourceLocation L); diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 1aa2daa..1cc1dc3 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -534,6 +534,13 @@ Module *Preprocessor::getCurrentModule() { return getHeaderSearchInfo().lookupModule(getLangOpts().CurrentModule); } +Module *Preprocessor::getCurrentModuleImplementation() { + if (!getLangOpts().isCompilingModuleImplementation()) + return nullptr; + + return getHeaderSearchInfo().lookupModule(getLangOpts().ModuleName); +} + //===----------------------------------------------------------------------===// // Preprocessor Initialization Methods //===----------------------------------------------------------------------===// diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index bb6673d..52062cd 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -189,6 +189,15 @@ void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) { CI.clearImplicitModuleBuildOptions(); if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) { + Preprocessor &PP = ScanInstance.getPreprocessor(); + if (Module *CurrentModule = PP.getCurrentModuleImplementation()) + if (const FileEntry *CurrentModuleMap = + PP.getHeaderSearchInfo() + .getModuleMap() + .getModuleMapFileForUniquing(CurrentModule)) + CI.getFrontendOpts().ModuleMapFiles.emplace_back( + CurrentModuleMap->getName()); + SmallVector DirectDeps; for (const auto &KV : ModularDeps) if (KV.second->ImportedByMainFile) diff --git a/clang/test/ClangScanDeps/modules-header-sharing.m b/clang/test/ClangScanDeps/modules-header-sharing.m new file mode 100644 index 0000000..0226e6b --- /dev/null +++ b/clang/test/ClangScanDeps/modules-header-sharing.m @@ -0,0 +1,96 @@ +// There are some edge-cases where Clang depends on knowing the module whose implementation it's currently building. +// This test makes sure scanner always reports the corresponding module map. + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- frameworks/A.framework/Modules/module.modulemap +framework module A { umbrella header "A.h" } +//--- frameworks/B.framework/Modules/module.modulemap +framework module B { umbrella header "B.h" } +//--- frameworks/A.framework/Headers/A.h +//--- frameworks/B.framework/Headers/B.h +//--- frameworks/A.framework/Modules/module.private.modulemap +framework module A_Private { umbrella header "A_Private.h" } +//--- frameworks/B.framework/Modules/module.private.modulemap +framework module B_Private { umbrella header "B_Private.h" } +//--- frameworks/A.framework/PrivateHeaders/A_Private.h +#import +//--- frameworks/B.framework/PrivateHeaders/B_Private.h +#import + +//--- shared/H.h + +//--- overlay.json.template +{ + "case-sensitive": "false", + "version": 0, + "roots": [ + { + "contents": [ + { + "external-contents": "DIR/shared/H.h", + "name": "H.h", + "type": "file" + } + ], + "name": "DIR/frameworks/A.framework/PrivateHeaders", + "type": "directory" + }, + { + "contents": [ + { + "external-contents": "DIR/shared/H.h", + "name": "H.h", + "type": "file" + } + ], + "name": "DIR/frameworks/B.framework/PrivateHeaders", + "type": "directory" + } + ] +} + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -fmodule-name=A -ivfsoverlay DIR/overlay.json -F DIR/frameworks -c DIR/tu.m -o DIR/tu.o" +}] + +//--- tu.m +@import B; +#import +#import + +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/overlay.json.template > %t/overlay.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t +// CHECK: { +// CHECK: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/frameworks/A.framework/Modules/module.modulemap", +// CHECK: "-fmodule-name=A", +// CHECK: ], +// CHECK-NEXT: "executable": "clang", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.m", +// CHECK-NEXT: "[[PREFIX]]/shared/H.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/tu.m" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + +// RUN: %deps-to-rsp %t/result.json --module-name=B > %t/B.cc1.rsp +// RUN: %clang @%t/B.cc1.rsp + +// RUN: %deps-to-rsp %t/result.json --tu-index=0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp diff --git a/compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/dlopen-dlclose-x2.s b/compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/dlopen-dlclose-x2.s deleted file mode 100644 index 3fbd177..0000000 --- a/compiler-rt/test/orc/TestCases/Darwin/x86-64/Inputs/dlopen-dlclose-x2.s +++ /dev/null @@ -1,90 +0,0 @@ -// Runs a sequence of dlopen, dlclose, dlopen, dlclose on a library "inits". -// This is intended as a standard harness for testing constructor / destructor -// behavior in the context of a full dlclose and then re-dlopen'ing of the -// inits library. -// -// Compiled from: -// -// int main(int argc, char *argv[]) { -// printf("entering main\n"); -// void *H = dlopen("inits", 0); -// if (!H) { -// printf("failed\n"); -// return -1; -// } -// if (dlclose(H) == -1) { -// printf("failed\n"); -// return -1; -// } -// H = dlopen("inits", 0); -// if (!H) { -// printf("failed\n"); -// return -1; -// } -// if (dlclose(H) == -1) { -// printf("failed\n"); -// return -1; -// } -// printf("leaving main\n"); -// return 0; -//} - - .section __TEXT,__text,regular,pure_instructions - .build_version macos, 13, 0 sdk_version 13, 0 - .globl _main - .p2align 4, 0x90 -_main: - - pushq %r14 - pushq %rbx - pushq %rax - leaq L_str(%rip), %rdi - callq _puts - leaq L_.str.1(%rip), %rdi - xorl %esi, %esi - callq _dlopen - movl $-1, %ebx - leaq L_str.8(%rip), %r14 - testq %rax, %rax - je LBB0_4 - - movq %rax, %rdi - callq _dlclose - cmpl $-1, %eax - je LBB0_4 - - leaq L_.str.1(%rip), %rdi - xorl %esi, %esi - callq _dlopen - testq %rax, %rax - je LBB0_4 - - movq %rax, %rdi - callq _dlclose - xorl %ebx, %ebx - cmpl $-1, %eax - sete %bl - leaq L_str.8(%rip), %rax - leaq L_str.6(%rip), %r14 - cmoveq %rax, %r14 - negl %ebx -LBB0_4: - movq %r14, %rdi - callq _puts - movl %ebx, %eax - addq $8, %rsp - popq %rbx - popq %r14 - retq - - .section __TEXT,__cstring,cstring_literals -L_.str.1: - .asciz "inits" -L_str: - .asciz "entering main" -L_str.6: - .asciz "leaving main" -L_str.8: - .asciz "failed" - -.subsections_via_symbols -- 2.7.4