Author: Danny Milosavljevic Date: 2025-10-20 Subject: Make clang importer heed CPLUS_INCLUDE_PATH --- a/include/swift/ClangImporter/ClangImporter.h 1970-01-01 00:00:01.000000000 +0000 +++ b/include/swift/ClangImporter/ClangImporter.h 2025-10-21 14:31:53.328441417 +0000 @@ -787,6 +787,9 @@ SmallVector, 1> overridenFiles; bool requiresBuiltinHeadersInSystemModules; + + /// Extra arguments to pass to the Clang invocation (e.g., -isystem paths). + std::vector extraArgs; }; /// On Linux, some platform libraries (glibc, libstdc++) are not modularized. --- a/lib/ClangImporter/ClangImporter.cpp 1970-01-01 00:00:01.000000000 +0000 +++ b/lib/ClangImporter/ClangImporter.cpp 2025-10-21 14:34:20.744234737 +0000 @@ -1326,6 +1326,12 @@ importer->requiresBuiltinHeadersInSystemModules = fileMapping.requiresBuiltinHeadersInSystemModules; + // Add extra arguments from file mapping (e.g., C++ stdlib include paths). + ctx.ClangImporterOpts.ExtraArgs.insert( + ctx.ClangImporterOpts.ExtraArgs.end(), + fileMapping.extraArgs.begin(), + fileMapping.extraArgs.end()); + // Create a new Clang compiler invocation. { if (auto ClangArgs = importer->getClangCC1Arguments(ctx, VFS)) --- a/lib/ClangImporter/ClangIncludePaths.cpp 1970-01-01 00:00:01.000000000 +0000 +++ b/lib/ClangImporter/ClangIncludePaths.cpp 2025-10-21 14:32:26.864394627 +0000 @@ -21,6 +21,7 @@ #include "clang/Driver/ToolChain.h" #include "clang/Frontend/CompilerInstance.h" #include "llvm/WindowsDriver/MSVCPaths.h" +#include using namespace swift; @@ -306,9 +307,71 @@ {"cstdlib", "string", "vector"}, vfs)) { cxxStdlibDir = dir.value(); } else { - if (!suppressDiagnostic) - ctx.Diags.diagnose(SourceLoc(), diag::libstdcxx_not_found, triple.str()); - return; + // FALLBACK: Try reading CPLUS_INCLUDE_PATH from environment. + // This is needed because Swift doesn't go through the full clang driver + // command construction that processes environment variables into -cxx-isystem + // arguments. When AddClangCXXStdlibIncludeArgs fails to find GCC (e.g., due + // to --gcc-toolchain pointing to wrong location or GCC detection failing), + // parsedStdlibArgs will be empty and findFirstIncludeDir returns nullopt. + // In Guix, CPLUS_INCLUDE_PATH is set by the build environment. + const char *cplusIncludePath = std::getenv("CPLUS_INCLUDE_PATH"); + if (cplusIncludePath) { + llvm::SmallVector envPaths; + llvm::StringRef(cplusIncludePath).split(envPaths, ':', -1, false); + + for (const auto &envPath : envPaths) { + Path dir(envPath.str()); + // Check if this directory contains C++ stdlib headers + Path testFile(dir); + llvm::sys::path::append(testFile, "cstdlib"); + if (vfs->exists(testFile)) { + llvm::sys::path::remove_dots(dir, /*remove_dot_dot=*/true); + cxxStdlibDir = dir; + break; + } + } + } + + if (cxxStdlibDir.empty()) { + if (!suppressDiagnostic) + ctx.Diags.diagnose(SourceLoc(), diag::libstdcxx_not_found, triple.str()); + return; + } + } + + // ADD INCLUDE PATHS FOR ARCH-SPECIFIC HEADERS: + // GCC's libstdc++ has architecture-specific headers in subdirectories. + // Standard clang driver adds THREE separate -internal-isystem include paths: + // 1. /path/to/gcc/include/c++ (base: cstdlib, string, vector, etc.) + // 2. /path/to/gcc/include/c++/TRIPLE (arch: bits/c++config.h, ext/*, etc.) + // 3. /path/to/gcc/include/c++/backward (backward compatibility headers) + // This allows clang to find both generic headers (cstddef) and arch-specific + // headers (bits/c++config.h) in their respective directories. + // + // We add these three paths as -isystem arguments via fileMapping.extraArgs. + // This is equivalent to what clang's AddClangCXXStdlibIncludeArgs does when + // it calls addSystemInclude three times in Gnu.cpp:addLibStdCXXIncludePaths. + // Using -isystem instead of VFS redirects ensures error messages reference + // real filesystem paths that users can verify exist. + + // 1. Base directory (already found above in cxxStdlibDir) + fileMapping.extraArgs.push_back("-isystem"); + fileMapping.extraArgs.push_back(std::string(cxxStdlibDir)); + + // 2. Architecture-specific subdirectory (e.g., x86_64-unknown-linux-gnu) + Path archSpecificDir(cxxStdlibDir); + llvm::sys::path::append(archSpecificDir, triple.str()); + if (vfs->exists(archSpecificDir)) { + fileMapping.extraArgs.push_back("-isystem"); + fileMapping.extraArgs.push_back(std::string(archSpecificDir)); + } + + // 3. Backward compatibility directory + Path backwardDir(cxxStdlibDir); + llvm::sys::path::append(backwardDir, "backward"); + if (vfs->exists(backwardDir)) { + fileMapping.extraArgs.push_back("-isystem"); + fileMapping.extraArgs.push_back(std::string(backwardDir)); } Path actualModuleMapPath;