iains created this revision.
Herald added a project: All.
iains added a reviewer: ChuanqiXu.
iains added a subscriber: clang-modules.
iains published this revision for review.
iains added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

this came up during discussion of other header unit constraints, we had not 
implemented it yet.
found a test case where we'd accidentally broken this rule too.


[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.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D140261

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDecl.cpp
  clang/test/CXX/module/module.import/p6.cpp
  clang/test/CodeGenCXX/module-initializer-header.cppm

Index: clang/test/CodeGenCXX/module-initializer-header.cppm
===================================================================
--- clang/test/CodeGenCXX/module-initializer-header.cppm
+++ clang/test/CodeGenCXX/module-initializer-header.cppm
@@ -8,24 +8,24 @@
 //
 //--- header.h
 int foo();
-int i = foo();
+static int i = foo();
 
 //--- M.cppm
 module;
 import "header.h";
 export module M;
 
-// CHECK: @i = {{.*}}global i32 0
+// CHECK: @_ZL1i = {{.*}}global i32 0
 // CHECK: void @__cxx_global_var_init()
 // CHECK-NEXT: entry:
 // CHECK-NEXT:  %call = call noundef{{.*}} i32 @_Z3foov()
-// CHECK-NEXT:  store i32 %call, ptr @i  
+// CHECK-NEXT:  store i32 %call, ptr @_ZL1i  
 
 //--- Use.cpp
 import "header.h";
 
-// CHECK: @i = {{.*}}global i32 0
+// CHECK: @_ZL1i = {{.*}}global i32 0
 // CHECK: void @__cxx_global_var_init()
 // CHECK-NEXT: entry:
 // CHECK-NEXT:  %call = call noundef{{.*}} i32 @_Z3foov()
-// CHECK-NEXT:  store i32 %call, ptr @i  
+// CHECK-NEXT:  store i32 %call, ptr @_ZL1i  
Index: clang/test/CXX/module/module.import/p6.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.import/p6.cpp
@@ -0,0 +1,24 @@
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -x c++-header %t/bad-header-unit.h \
+// RUN:  -emit-header-unit -o %t/bad-header-unit.pcm -verify
+
+//--- bad-header-unit.h
+
+inline int ok_foo () { return 0;}
+
+static int ok_bar ();
+
+int ok_decl ();
+
+int bad_def () { return 2;}  // expected-error {{non-inline external definitions are not permitted in C++ header units}}
+
+inline int ok_inline_var = 1;
+
+static int ok_static_var;
+
+int ok_var_decl;
+
+int bad_var_definition = 3;  // expected-error {{non-inline external definitions are not permitted in C++ header units}}
+
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -12952,6 +12952,17 @@
       VDecl->setInvalidDecl();
   }
 
+  // C++ [module.import/6] external definitions are not permitted in header
+  // units.
+  if (getLangOpts().CPlusPlus20 && getLangOpts().CPlusPlusModules &&
+      !ModuleScopes.empty() && ModuleScopes.back().Module->isHeaderUnit()) {
+    if (VDecl->getFormalLinkage() == Linkage::ExternalLinkage &&
+        !VDecl->isInline()) {
+      Diag(VDecl->getLocation(), diag::err_extern_def_in_header_unit);
+      VDecl->setInvalidDecl();
+    }
+  }
+
   // If adding the initializer will turn this declaration into a definition,
   // and we already have a definition for this variable, diagnose or otherwise
   // handle the situation.
@@ -15100,6 +15111,17 @@
     }
   }
 
+  // C++ [module.import/6] external definitions are not permitted in header
+  // units.
+  if (getLangOpts().CPlusPlus20 && getLangOpts().CPlusPlusModules &&
+      !ModuleScopes.empty() && ModuleScopes.back().Module->isHeaderUnit()) {
+    if (FD->getFormalLinkage() == Linkage::ExternalLinkage &&
+        !FD->isInlined()) {
+      Diag(FD->getLocation(), diag::err_extern_def_in_header_unit);
+      FD->setInvalidDecl();
+    }
+  }
+
   // Ensure that the function's exception specification is instantiated.
   if (const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>())
     ResolveExceptionSpec(D->getLocation(), FPT);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11233,6 +11233,8 @@
   "add 'export' here if this is intended to be a module interface unit">;
 def err_invalid_module_name : Error<
   "%0 is %select{an invalid|a reserved}1 name for a module">;
+def err_extern_def_in_header_unit : Error<
+  "non-inline external definitions are not permitted in C++ header units">;
 
 def ext_equivalent_internal_linkage_decl_in_modules : ExtWarn<
   "ambiguous use of internal linkage declaration %0 defined in multiple modules">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to