diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/DeclBase.cpp | 16 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 167 |
4 files changed, 164 insertions, 25 deletions
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index e114a87c8e3..5a6d85cd00c 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -430,22 +430,6 @@ bool Decl::isReferenced() const { return false; } -bool Decl::isExported() const { - if (isModulePrivate()) - return false; - // Namespaces are always exported. - if (isa<TranslationUnitDecl>(this) || isa<NamespaceDecl>(this)) - return true; - // Otherwise, this is a strictly lexical check. - for (auto *DC = getLexicalDeclContext(); DC; DC = DC->getLexicalParent()) { - if (cast<Decl>(DC)->isModulePrivate()) - return false; - if (isa<ExportDecl>(DC)) - return true; - } - return false; -} - ExternalSourceSymbolAttr *Decl::getExternalSourceSymbolAttr() const { const Decl *Definition = nullptr; if (auto *ID = dyn_cast<ObjCInterfaceDecl>(this)) { diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 9159f4d6826..7d2fcbd3e1b 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -437,9 +437,10 @@ Decl *Parser::ParseExportDeclaration() { // The Modules TS draft says "An export-declaration shall declare at least one // entity", but the intent is that it shall contain at least one declaration. - if (Tok.is(tok::r_brace)) + if (Tok.is(tok::r_brace) && getLangOpts().ModulesTS) { Diag(ExportLoc, diag::err_export_empty) << SourceRange(ExportLoc, Tok.getLocation()); + } while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9f9e117ab9f..797bbbeca95 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9037,6 +9037,9 @@ void Sema::ActOnFinishNamespaceDef(Decl *Dcl, SourceLocation RBrace) { PopDeclContext(); if (Namespc->hasAttr<VisibilityAttr>()) PopPragmaVisibility(true, RBrace); + // If this namespace contains an export-declaration, export it now. + if (DeferredExportedNamespaces.erase(Namespc)) + Dcl->setModuleOwnershipKind(Decl::ModuleOwnershipKind::VisibleWhenImported); } CXXRecordDecl *Sema::getStdBadAlloc() const { diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 43d8e47856d..68c2286cf49 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -330,6 +330,14 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path); } +/// Determine whether \p D is lexically within an export-declaration. +static const ExportDecl *getEnclosingExportDecl(const Decl *D) { + for (auto *DC = D->getLexicalDeclContext(); DC; DC = DC->getLexicalParent()) + if (auto *ED = dyn_cast<ExportDecl>(DC)) + return ED; + return nullptr; +} + DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, @@ -384,7 +392,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // Re-export the module if needed. if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) { - if (ExportLoc.isValid() || Import->isExported()) + if (ExportLoc.isValid() || getEnclosingExportDecl(Import)) getCurrentModule()->Exports.emplace_back(Mod, false); } else if (ExportLoc.isValid()) { Diag(ExportLoc, diag::err_export_not_in_module_interface); @@ -516,11 +524,13 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, SourceLocation LBraceLoc) { ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc); - // C++20 [module.interface]p1: + // Set this temporarily so we know the export-declaration was braced. + D->setRBraceLoc(LBraceLoc); + + // C++2a [module.interface]p1: // An export-declaration shall appear only [...] in the purview of a module // interface unit. An export-declaration shall not appear directly or - // indirectly within an unnamed namespace or a private-module-fragment. - // FIXME: Check for the unnamed namespace case. + // indirectly within [...] a private-module-fragment. if (ModuleScopes.empty() || !ModuleScopes.back().Module->isModulePurview()) { Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0; } else if (!ModuleScopes.back().ModuleInterface) { @@ -534,10 +544,35 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, Diag(ModuleScopes.back().BeginLoc, diag::note_private_module_fragment); } + for (const DeclContext *DC = CurContext; DC; DC = DC->getLexicalParent()) { + if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) { + // An export-declaration shall not appear directly or indirectly within + // an unnamed namespace [...] + if (ND->isAnonymousNamespace()) { + Diag(ExportLoc, diag::err_export_within_anonymous_namespace); + Diag(ND->getLocation(), diag::note_anonymous_namespace); + // Don't diagnose internal-linkage declarations in this region. + D->setInvalidDecl(); + break; + } + + // A declaration is exported if it is [...] a namespace-definition + // that contains an exported declaration. + // + // Defer exporting the namespace until after we leave it, in order to + // avoid marking all subsequent declarations in the namespace as exported. + if (!DeferredExportedNamespaces.insert(ND).second) + break; + } + } + // [...] its declaration or declaration-seq shall not contain an // export-declaration. - if (D->isExported()) + if (auto *ED = getEnclosingExportDecl(D)) { Diag(ExportLoc, diag::err_export_within_export); + if (ED->hasBraces()) + Diag(ED->getLocation(), diag::note_export); + } CurContext->addDecl(D); PushDeclContext(S, D); @@ -545,15 +580,131 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, return D; } +static bool checkExportedDeclContext(Sema &S, DeclContext *DC, + SourceLocation BlockStart); + +namespace { +enum class UnnamedDeclKind { + Empty, + StaticAssert, + Asm, + UsingDirective, + Context +}; +} + +static llvm::Optional<UnnamedDeclKind> getUnnamedDeclKind(Decl *D) { + if (isa<EmptyDecl>(D)) + return UnnamedDeclKind::Empty; + if (isa<StaticAssertDecl>(D)) + return UnnamedDeclKind::StaticAssert; + if (isa<FileScopeAsmDecl>(D)) + return UnnamedDeclKind::Asm; + if (isa<UsingDirectiveDecl>(D)) + return UnnamedDeclKind::UsingDirective; + // Everything else either introduces one or more names or is ill-formed. + return llvm::None; +} + +unsigned getUnnamedDeclDiag(UnnamedDeclKind UDK, bool InBlock) { + switch (UDK) { + case UnnamedDeclKind::Empty: + case UnnamedDeclKind::StaticAssert: + // Allow empty-declarations and static_asserts in an export block as an + // extension. + return InBlock ? diag::ext_export_no_name_block : diag::err_export_no_name; + + case UnnamedDeclKind::UsingDirective: + // Allow exporting using-directives as an extension. + return diag::ext_export_using_directive; + + case UnnamedDeclKind::Context: + // Allow exporting DeclContexts that transitively contain no declarations + // as an extension. + return diag::ext_export_no_names; + + case UnnamedDeclKind::Asm: + return diag::err_export_no_name; + } + llvm_unreachable("unknown kind"); +} + +static void diagExportedUnnamedDecl(Sema &S, UnnamedDeclKind UDK, Decl *D, + SourceLocation BlockStart) { + S.Diag(D->getLocation(), getUnnamedDeclDiag(UDK, BlockStart.isValid())) + << (unsigned)UDK; + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); +} + +/// Check that it's valid to export \p D. +static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { + // C++2a [module.interface]p3: + // An exported declaration shall declare at least one name + if (auto UDK = getUnnamedDeclKind(D)) + diagExportedUnnamedDecl(S, *UDK, D, BlockStart); + + // [...] shall not declare a name with internal linkage. + if (auto *ND = dyn_cast<NamedDecl>(D)) { + // Don't diagnose anonymous union objects; we'll diagnose their members + // instead. + if (ND->getDeclName() && ND->getFormalLinkage() == InternalLinkage) { + S.Diag(ND->getLocation(), diag::err_export_internal) << ND; + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); + } + } + + // C++2a [module.interface]p5: + // all entities to which all of the using-declarators ultimately refer + // shall have been introduced with a name having external linkage + if (auto *USD = dyn_cast<UsingShadowDecl>(D)) { + NamedDecl *Target = USD->getUnderlyingDecl(); + if (Target->getFormalLinkage() == InternalLinkage) { + S.Diag(USD->getLocation(), diag::err_export_using_internal) << Target; + S.Diag(Target->getLocation(), diag::note_using_decl_target); + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); + } + } + + // Recurse into namespace-scope DeclContexts. (Only namespace-scope + // declarations are exported.) + if (auto *DC = dyn_cast<DeclContext>(D)) + if (DC->getRedeclContext()->isFileContext() && !isa<EnumDecl>(D)) + return checkExportedDeclContext(S, DC, BlockStart); + return false; +} + +/// Check that it's valid to export all the declarations in \p DC. +static bool checkExportedDeclContext(Sema &S, DeclContext *DC, + SourceLocation BlockStart) { + bool AllUnnamed = true; + for (auto *D : DC->decls()) + AllUnnamed &= checkExportedDecl(S, D, BlockStart); + return AllUnnamed; +} + /// Complete the definition of an export declaration. Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) { auto *ED = cast<ExportDecl>(D); if (RBraceLoc.isValid()) ED->setRBraceLoc(RBraceLoc); - // FIXME: Diagnose export of internal-linkage declaration (including - // anonymous namespace). - PopDeclContext(); + + if (!D->isInvalidDecl()) { + SourceLocation BlockStart = + ED->hasBraces() ? ED->getBeginLoc() : SourceLocation(); + for (auto *Child : ED->decls()) { + if (checkExportedDecl(*this, Child, BlockStart)) { + // If a top-level child is a linkage-spec declaration, it might contain + // no declarations (transitively), in which case it's ill-formed. + diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, + BlockStart); + } + } + } + return D; } |