Skip to content

Commit 335668b

Browse files
committed
[C++20][Modules] Do not allow non-inline external definitions in header units.
[module.import/6] last sentence: A header unit shall not contain a definition of a non-inline function or variable whose name has external linkage. Differential Revision: https://reviews.llvm.org/D140261
1 parent 25acbf3 commit 335668b

File tree

6 files changed

+59
-5
lines changed

6 files changed

+59
-5
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ code bases.
187187
- ``-p`` is rejected for all targets which are not AIX or OpenBSD. ``-p`` led
188188
to an ``-Wunused-command-line-argument`` warning in previous releases.
189189

190+
- Clang now diagnoses non-inline externally-visible definitions in C++
191+
standard header units as per ``[module.import/6]``. Previously, in Clang-15,
192+
these definitions were allowed. Note that such definitions are ODR
193+
violations if the header is included more than once.
194+
190195
What's New in Clang |release|?
191196
==============================
192197
Some of the major new features and improvements to Clang are listed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11236,6 +11236,8 @@ def note_not_module_interface_add_export : Note<
1123611236
"add 'export' here if this is intended to be a module interface unit">;
1123711237
def err_invalid_module_name : Error<
1123811238
"%0 is %select{an invalid|a reserved}1 name for a module">;
11239+
def err_extern_def_in_header_unit : Error<
11240+
"non-inline external definitions are not permitted in C++ header units">;
1123911241

1124011242
def ext_equivalent_internal_linkage_decl_in_modules : ExtWarn<
1124111243
"ambiguous use of internal linkage declaration %0 defined in multiple modules">,

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,12 @@ class Sema final {
23252325
return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface;
23262326
}
23272327

2328+
/// Is the module scope we are in a C++ Header Unit?
2329+
bool currentModuleIsHeaderUnit() const {
2330+
return ModuleScopes.empty() ? false
2331+
: ModuleScopes.back().Module->isHeaderUnit();
2332+
}
2333+
23282334
/// Get the module owning an entity.
23292335
Module *getOwningModule(const Decl *Entity) {
23302336
return Entity->getOwningModule();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13071,6 +13071,15 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1307113071
VDecl->setInvalidDecl();
1307213072
}
1307313073

13074+
// C++ [module.import/6] external definitions are not permitted in header
13075+
// units.
13076+
if (getLangOpts().CPlusPlusModules && currentModuleIsHeaderUnit() &&
13077+
VDecl->getFormalLinkage() == Linkage::ExternalLinkage &&
13078+
!VDecl->isInline()) {
13079+
Diag(VDecl->getLocation(), diag::err_extern_def_in_header_unit);
13080+
VDecl->setInvalidDecl();
13081+
}
13082+
1307413083
// If adding the initializer will turn this declaration into a definition,
1307513084
// and we already have a definition for this variable, diagnose or otherwise
1307613085
// handle the situation.
@@ -15224,6 +15233,14 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
1522415233
}
1522515234
}
1522615235

15236+
// C++ [module.import/6] external definitions are not permitted in header
15237+
// units.
15238+
if (getLangOpts().CPlusPlusModules && currentModuleIsHeaderUnit() &&
15239+
FD->getFormalLinkage() == Linkage::ExternalLinkage && !FD->isInlined()) {
15240+
Diag(FD->getLocation(), diag::err_extern_def_in_header_unit);
15241+
FD->setInvalidDecl();
15242+
}
15243+
1522715244
// Ensure that the function's exception specification is instantiated.
1522815245
if (const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>())
1522915246
ResolveExceptionSpec(D->getLocation(), FPT);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: mkdir -p %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -std=c++20 -x c++-header %t/bad-header-unit.h \
5+
// RUN: -emit-header-unit -o %t/bad-header-unit.pcm -verify
6+
7+
//--- bad-header-unit.h
8+
9+
inline int ok_foo () { return 0;}
10+
11+
static int ok_bar ();
12+
13+
int ok_decl ();
14+
15+
int bad_def () { return 2;} // expected-error {{non-inline external definitions are not permitted in C++ header units}}
16+
17+
inline int ok_inline_var = 1;
18+
19+
static int ok_static_var;
20+
21+
int ok_var_decl;
22+
23+
int bad_var_definition = 3; // expected-error {{non-inline external definitions are not permitted in C++ header units}}
24+

clang/test/CodeGenCXX/module-initializer-header.cppm

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@
88
//
99
//--- header.h
1010
int foo();
11-
int i = foo();
11+
static int i = foo();
1212

1313
//--- M.cppm
1414
module;
1515
import "header.h";
1616
export module M;
1717

18-
// CHECK: @i = {{.*}}global i32 0
18+
// CHECK: @_ZL1i = {{.*}}global i32 0
1919
// CHECK: void @__cxx_global_var_init()
2020
// CHECK-NEXT: entry:
2121
// CHECK-NEXT: %call = call noundef{{.*}} i32 @_Z3foov()
22-
// CHECK-NEXT: store i32 %call, ptr @i
22+
// CHECK-NEXT: store i32 %call, ptr @_ZL1i
2323

2424
//--- Use.cpp
2525
import "header.h";
2626

27-
// CHECK: @i = {{.*}}global i32 0
27+
// CHECK: @_ZL1i = {{.*}}global i32 0
2828
// CHECK: void @__cxx_global_var_init()
2929
// CHECK-NEXT: entry:
3030
// CHECK-NEXT: %call = call noundef{{.*}} i32 @_Z3foov()
31-
// CHECK-NEXT: store i32 %call, ptr @i
31+
// CHECK-NEXT: store i32 %call, ptr @_ZL1i

0 commit comments

Comments
 (0)