Skip to content

Commit e8db752

Browse files
committed
[modules] PR20507: Avoid silent textual inclusion.
Summary: If a module was unavailable (either a missing requirement on the module being imported, or a missing file anywhere in the top-level module (and not dominated by an unsatisfied `requires`)), we would silently treat inclusions as textual. This would cause all manner of crazy and confusing errors (and would also silently "work" sometimes, making the problem difficult to track down). I'm really not a fan of the `M->isAvailable(getLangOpts(), getTargetInfo(), Requirement, MissingHeader)` function; it seems to do too many things at once, but for now I've done things in a sort of awkward way. The changes to test/Modules/Inputs/declare-use/module.map were necessitated because the thing that was meant to be tested there (introduced in r197805) was predicated on silently falling back to textual inclusion, which we no longer do. The changes to test/Modules/Inputs/macro-reexport/module.modulemap are just an overlooked missing header that seems to have been missing since this code was committed (r213922), which is now caught. Reviewers: rsmith, benlangmuir, djasper Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D10423 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@245228 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 19d5024 commit e8db752

16 files changed

+127
-10
lines changed

include/clang/Basic/DiagnosticCommonKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ def err_module_not_built : Error<"could not build module '%0'">, DefaultFatal;
8484
def err_module_build_disabled: Error<
8585
"module '%0' is needed but has not been provided, and implicit use of module "
8686
"files is disabled">, DefaultFatal;
87+
def err_module_unavailable : Error<
88+
"module '%0' %select{is incompatible with|requires}1 feature '%2'">;
89+
def err_module_header_missing : Error<
90+
"%select{|umbrella }0header '%1' not found">;
8791
def err_module_lock_failure : Error<
8892
"could not acquire lock file for module '%0'">, DefaultFatal;
8993
def err_module_lock_timeout : Error<

include/clang/Basic/DiagnosticFrontendKinds.td

-4
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,6 @@ def err_no_submodule_suggest : Error<
183183
"no submodule named %0 in module '%1'; did you mean '%2'?">;
184184
def warn_missing_submodule : Warning<"missing submodule '%0'">,
185185
InGroup<IncompleteUmbrella>;
186-
def err_module_unavailable : Error<
187-
"module '%0' %select{is incompatible with|requires}1 feature '%2'">;
188-
def err_module_header_missing : Error<
189-
"%select{|umbrella }0header '%1' not found">;
190186
def err_module_cannot_create_includes : Error<
191187
"cannot create includes file for module %0: %1">;
192188
def warn_module_config_macro_undef : Warning<

include/clang/Basic/DiagnosticLexKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ def warn_mmap_unknown_attribute : Warning<"unknown attribute '%0'">,
623623
def warn_auto_module_import : Warning<
624624
"treating #%select{include|import|include_next|__include_macros}0 as an "
625625
"import of module '%1'">, InGroup<AutoImport>, DefaultIgnore;
626+
def note_implicit_top_level_module_import_here : Note<
627+
"submodule of top-level module '%0' implicitly imported here">;
626628
def warn_uncovered_module_header : Warning<
627629
"umbrella header for module '%0' does not include header '%1'">,
628630
InGroup<IncompleteUmbrella>;

lib/Lex/ModuleMap.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
320320

321321
static bool isBetterKnownHeader(const ModuleMap::KnownHeader &New,
322322
const ModuleMap::KnownHeader &Old) {
323+
// Prefer available modules.
324+
if (New.getModule()->isAvailable() && !Old.getModule()->isAvailable())
325+
return true;
326+
323327
// Prefer a public header over a private header.
324328
if ((New.getRole() & ModuleMap::PrivateHeader) !=
325329
(Old.getRole() & ModuleMap::PrivateHeader))
@@ -349,9 +353,6 @@ ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File) {
349353
// Prefer a header from the current module over all others.
350354
if (H.getModule()->getTopLevelModule() == CompilingModule)
351355
return MakeResult(H);
352-
// Cannot use a module if it is unavailable.
353-
if (!H.getModule()->isAvailable())
354-
continue;
355356
if (!Result || isBetterKnownHeader(H, Result))
356357
Result = H;
357358
}

lib/Lex/PPDirectives.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,29 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
16741674
getLangOpts().CurrentModule &&
16751675
SuggestedModule.getModule()->getTopLevelModuleName() !=
16761676
getLangOpts().ImplementationOfModule) {
1677+
1678+
// If this include corresponds to a module but that module is
1679+
// unavailable, diagnose the situation and bail out.
1680+
if (!SuggestedModule.getModule()->isAvailable()) {
1681+
clang::Module::Requirement Requirement;
1682+
clang::Module::UnresolvedHeaderDirective MissingHeader;
1683+
Module *M = SuggestedModule.getModule();
1684+
// Identify the cause.
1685+
(void)M->isAvailable(getLangOpts(), getTargetInfo(), Requirement,
1686+
MissingHeader);
1687+
if (MissingHeader.FileNameLoc.isValid()) {
1688+
Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing)
1689+
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
1690+
} else {
1691+
Diag(M->DefinitionLoc, diag::err_module_unavailable)
1692+
<< M->getFullModuleName() << Requirement.second << Requirement.first;
1693+
}
1694+
Diag(FilenameTok.getLocation(),
1695+
diag::note_implicit_top_level_module_import_here)
1696+
<< M->getTopLevelModuleName();
1697+
return;
1698+
}
1699+
16771700
// Compute the module access path corresponding to this module.
16781701
// FIXME: Should we have a second loadModule() overload to avoid this
16791702
// extra lookup step?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// missing_header/not_missing.h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// missing_requirement.h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module missing_header {
2+
module missing { header "missing_header/missing.h" }
3+
module error_importing_this { header "missing_header/not_missing.h" }
4+
}
5+
6+
module nonrequired_missing_header {
7+
module unsatisfied_requires {
8+
requires nonexistent_feature
9+
header "nonrequired_missing_header/missing.h"
10+
}
11+
module fine_to_import {
12+
header "nonrequired_missing_header/not_missing.h"
13+
}
14+
}
15+
16+
module missing_requirement {
17+
requires nonexistent_feature
18+
header "missing_requirement.h"
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// nonrequired_missing_header/not_missing.h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// nonrequired_missing_header/requires_feature_you_dont_have.h
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#pragma once
2+
int available;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// There is some order-dependence to how clang chooses modules, so make
2+
// sure that both the first and last modules here are ones that would
3+
// cause a test failure if they were picked.
4+
5+
module unavailable_before {
6+
requires nonexistent_feature
7+
header "available-is-better.h"
8+
}
9+
10+
module available {
11+
header "available-is-better.h"
12+
}
13+
14+
module unavailable_after {
15+
requires nonexistent_feature
16+
header "available-is-better.h"
17+
}

test/Modules/Inputs/declare-use/module.map

-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ module XD {
2020

2121
module XE {
2222
header "e.h"
23-
header "unavailable.h"
2423
use XA
2524
use XB
2625
}
2726

2827
module XF {
2928
header "f.h"
30-
header "unavailable.h"
3129
use XA
3230
use XB
3331
}

test/Modules/Inputs/macro-reexport/module.modulemap

-1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ module e {
1919
}
2020
module f {
2121
module f1 { header "f1.h" export * }
22-
module f2 { header "f2.h" export * }
2322
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: rm -rf %t
2+
// RUN: not %clang_cc1 -x c++ -Rmodule-build -DMISSING_HEADER -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/auto-import-unavailable %s 2>&1 | FileCheck %s --check-prefix=MISSING-HEADER
3+
// RUN: %clang_cc1 -x c++ -Rmodule-build -DNONREQUIRED_MISSING_HEADER -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/auto-import-unavailable %s 2>&1 | FileCheck %s --check-prefix=NONREQUIRED-MISSING-HEADER
4+
// RUN: not %clang_cc1 -x c++ -Rmodule-build -DMISSING_REQUIREMENT -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/auto-import-unavailable %s 2>&1 | FileCheck %s --check-prefix=MISSING-REQUIREMENT
5+
6+
#ifdef MISSING_HEADER
7+
8+
// Even if the header we ask for is not missing, if the top-level module
9+
// containing it has a missing header, then the whole top-level is
10+
// unavailable and we issue an error.
11+
12+
// MISSING-HEADER: module.modulemap:2:27: error: header 'missing_header/missing.h' not found
13+
// MISSING-HEADER-DAG: auto-import-unavailable.cpp:[[@LINE+1]]:10: note: submodule of top-level module 'missing_header' implicitly imported here
14+
#include "missing_header/not_missing.h"
15+
16+
// We should not attempt to build the module.
17+
// MISSING-HEADER-NOT: remark: building module
18+
19+
#endif // #ifdef MISSING_HEADER
20+
21+
22+
#ifdef NONREQUIRED_MISSING_HEADER
23+
24+
// However, if the missing header is dominated by an unsatisfied
25+
// `requires`, then that is acceptable.
26+
// This also tests that an unsatisfied `requires` elsewhere in the
27+
// top-level module doesn't affect an available module.
28+
29+
// NONREQUIRED-MISSING-HEADER: auto-import-unavailable.cpp:[[@LINE+2]]:10: remark: building module 'nonrequired_missing_header'
30+
// NONREQUIRED-MISSING-HEADER: auto-import-unavailable.cpp:[[@LINE+1]]:10: remark: finished building module 'nonrequired_missing_header'
31+
#include "nonrequired_missing_header/not_missing.h"
32+
33+
#endif // #ifdef NONREQUIRED_MISSING_HEADER
34+
35+
36+
#ifdef MISSING_REQUIREMENT
37+
38+
// If the header is unavailable due to a missing requirement, an error
39+
// should be emitted if a user tries to include it.
40+
41+
// MISSING-REQUIREMENT:module.modulemap:16:8: error: module 'missing_requirement' requires feature 'nonexistent_feature'
42+
// MISSING-REQUIREMENT: auto-import-unavailable.cpp:[[@LINE+1]]:10: note: submodule of top-level module 'missing_requirement' implicitly imported here
43+
#include "missing_requirement.h"
44+
45+
// MISSING-REQUIREMENT-NOT: remark: building module
46+
47+
#endif // #ifdef MISSING_REQUIREMENT

test/Modules/available-is-better.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: rm -rf %t
2+
// RUN: %clang_cc1 -x c++ -Rmodule-build -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/available-is-better %s 2>&1 | FileCheck %s
3+
4+
#include "available-is-better.h"
5+
// CHECK: remark: building module

0 commit comments

Comments
 (0)