diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -170,6 +170,7 @@ LANGOPT(Modules , 1, 0, "modules semantics") COMPATIBLE_LANGOPT(ModulesTS , 1, 0, "C++ Modules TS syntax") COMPATIBLE_LANGOPT(CPlusPlusModules, 1, 0, "C++ modules syntax") +COMPATIBLE_LANGOPT(HeaderModules, 1, 0, "Clang header modules") BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None, "compiling a module interface") BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2261,6 +2261,10 @@ LangOpts<"Modules">, Default, PosFlag, NegFlag, BothFlags<[NoXarchOption, CoreOption]>>; +defm header_modules : BoolFOption<"header-modules", + LangOpts<"HeaderModules">, Default, + PosFlag, + NegFlag, BothFlags<[CC1Option, NoDriverOption]>>; def fmodule_maps : Flag <["-"], "fmodule-maps">, Flags<[CoreOption]>, Alias; def fmodule_name_EQ : Joined<["-"], "fmodule-name=">, Group, Flags<[NoXarchOption,CC1Option,CoreOption]>, MetaVarName<"">, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -38,6 +38,7 @@ #include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Types.h" #include "clang/Driver/XRayArgs.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/llvm-config.h" #include "llvm/Option/ArgList.h" @@ -3625,6 +3626,15 @@ HaveModules = true; } + // Disable header modules in C++20 mode, they are not in the C++ standard. + // Unless '-fmodules' was specified explicitly. + if (HaveModules) { + if (HaveClangModules) + CmdArgs.push_back("-fheader-modules"); + else + CmdArgs.push_back("-fno-header-modules"); + } + // -fmodule-maps enables implicit reading of module map files. By default, // this is enabled if we are using Clang's flavor of precompiled modules. if (Args.hasFlag(options::OPT_fimplicit_module_maps, diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2159,7 +2159,8 @@ // Determine whether we should try to import the module for this #include, if // there is one. Don't do so if precompiled module support is disabled or we // are processing this module textually (because we're building the module). - if (Action == Enter && File && SuggestedModule && getLangOpts().Modules && + if (Action == Enter && File && SuggestedModule && + getLangOpts().HeaderModules && !isForModuleBuilding(SuggestedModule.getModule(), getLangOpts().CurrentModule, getLangOpts().ModuleName)) { diff --git a/clang/test/Driver/cpp20-header-module.cpp b/clang/test/Driver/cpp20-header-module.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Driver/cpp20-header-module.cpp @@ -0,0 +1,19 @@ +// Check header modules are not enabled with C++20 standard and -fmodules-ts. +// +// RUN: %clang -fmodules-ts -std=c++20 -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=NO_HEADER_MODULES +// RUN: %clang -std=c++20 -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=NO_HEADER_MODULES +// RUN: %clang -std=c++2a -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=NO_HEADER_MODULES +// RUN: %clang_cl /std:c++latest /Zs -v %s 2>&1 | FileCheck %s --check-prefix=NO_HEADER_MODULES +// +// NO_HEADER_MODULES-NOT: -fheader-modules +// NO_HEADER_MODULES: -cc1 +// NO_HEADER_MODULES-SAME: -fno-header-modules +// +// RUN: %clang -fmodules -fmodules-ts -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=HAS_HEADER_MODULES +// RUN: %clang -fmodules -fmodules-ts -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=HAS_HEADER_MODULES +// RUN: %clang -fmodules -std=c++20 -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=HAS_HEADER_MODULES +// RUN: %clang -fmodules -std=c++2a -fsyntax-only -v %s 2>&1 | FileCheck %s --check-prefix=HAS_HEADER_MODULES +// +// HAS_HEADER_MODULES-NOT: -fno-header-modules +// HAS_HEADER_MODULES: -cc1 +// HAS_HEADER_MODULES: -fheader-modules diff --git a/clang/test/Modules/Inputs/cxx20-and-header-modules/a.h b/clang/test/Modules/Inputs/cxx20-and-header-modules/a.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/cxx20-and-header-modules/a.h @@ -0,0 +1,12 @@ +#ifndef A_HEADER +#define A_HEADER + +#if INCLUDING && BUILDING_MODULE +#error "invalid context" +#endif + +struct X { + int foo() { return 10; } +}; + +#endif diff --git a/clang/test/Modules/Inputs/cxx20-and-header-modules/a.map b/clang/test/Modules/Inputs/cxx20-and-header-modules/a.map new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/cxx20-and-header-modules/a.map @@ -0,0 +1,4 @@ +module A { + header "a.h" + export * +} diff --git a/clang/test/Modules/cxx20-and-header-modules.cpp b/clang/test/Modules/cxx20-and-header-modules.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-and-header-modules.cpp @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// +// Check header modules are disabled by default in C++20 mode. +// RUN: %clang -std=c++20 -fsyntax-only -fno-implicit-modules -fmodules-cache-path=%t -I%S/Inputs/cxx20-and-header-modules -fmodule-map-file=%S/Inputs/cxx20-and-header-modules/a.map %s +// +// Also run in the header modules mode. +// RUN: %clang -std=c++20 -DBUILDING_MODULE -fmodules -fimplicit-modules -fmodules-cache-path=%t -I%S/Inputs/cxx20-and-header-modules -fmodule-map-file=%S/Inputs/cxx20-and-header-modules/a.map %s + +#define INCLUDING 1 +#include "a.h" + +int main() { + return X().foo(); +}