diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6eaf7b9435491..bebcfbd6df959 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13816,13 +13816,20 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { VDecl->setInvalidDecl(); } - // C++ [module.import/6] external definitions are not permitted in header - // units. + // C++ [module.import/6] + // ... + // A header unit shall not contain a definition of a non-inline function or + // variable whose name has external linkage. + // + // We choose to allow weak & selectany definitions, as they are common in + // headers, and have semantics similar to inline definitions which are allowed + // in header units. if (getLangOpts().CPlusPlusModules && currentModuleIsHeaderUnit() && !VDecl->isInvalidDecl() && VDecl->isThisDeclarationADefinition() && VDecl->getFormalLinkage() == Linkage::External && !VDecl->isInline() && !VDecl->isTemplated() && !isa(VDecl) && - !VDecl->getInstantiatedFromStaticDataMember()) { + !VDecl->getInstantiatedFromStaticDataMember() && + !(VDecl->hasAttr() || VDecl->hasAttr())) { Diag(VDecl->getLocation(), diag::err_extern_def_in_header_unit); VDecl->setInvalidDecl(); } @@ -16153,16 +16160,24 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, } } - // C++ [module.import/6] external definitions are not permitted in header - // units. Deleted and Defaulted functions are implicitly inline (but the + // C++ [module.import/6] + // ... + // A header unit shall not contain a definition of a non-inline function or + // variable whose name has external linkage. + // + // Deleted and Defaulted functions are implicitly inline (but the // inline state is not set at this point, so check the BodyKind explicitly). + // We choose to allow weak & selectany definitions, as they are common in + // headers, and have semantics similar to inline definitions which are allowed + // in header units. // FIXME: Consider an alternate location for the test where the inlined() // state is complete. if (getLangOpts().CPlusPlusModules && currentModuleIsHeaderUnit() && !FD->isInvalidDecl() && !FD->isInlined() && BodyKind != FnBodyKind::Delete && BodyKind != FnBodyKind::Default && FD->getFormalLinkage() == Linkage::External && !FD->isTemplated() && - !FD->isTemplateInstantiation()) { + !FD->isTemplateInstantiation() && + !(FD->hasAttr() || FD->hasAttr())) { assert(FD->isThisDeclarationADefinition()); Diag(FD->getLocation(), diag::err_extern_def_in_header_unit); FD->setInvalidDecl(); diff --git a/clang/test/CXX/module/module.import/p6.cpp b/clang/test/CXX/module/module.import/p6.cpp index cb2d799e5b565..9e378a5fe7759 100644 --- a/clang/test/CXX/module/module.import/p6.cpp +++ b/clang/test/CXX/module/module.import/p6.cpp @@ -3,6 +3,9 @@ // 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 +// RUN: %clang_cc1 -std=c++20 -x c++-header %t/bad-header-unit-declspec.h \ +// RUN: -emit-header-unit -o %t/bad-header-unit.pcm -verify \ +// RUN: -fdeclspec //--- bad-header-unit.h @@ -77,3 +80,13 @@ template bool b() { } inline bool B = b(); + +__attribute__((weak)) int weak_fun_definition() { return 42; } + +__attribute__((weak)) int weak_var_definition = 42; + +//--- bad-header-unit-declspec.h + +/* The cases below should compile without diagnostics. */ + +__declspec(selectany) int selectany_var_definition = 42; // expected-no-diagnostics