From 8df390f9ebc87702ae2b6155e5d81477c481db6f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 8 Sep 2016 23:14:54 +0000 Subject: [PATCH] C++ Modules TS: Add parsing and some semantic analysis support for export-declarations. These don't yet have an effect on name visibility; we still export everything by default. llvm-svn: 280999 --- clang/include/clang/AST/Decl.h | 49 ++++++++++++++++ clang/include/clang/AST/DeclBase.h | 5 +- clang/include/clang/AST/RecursiveASTVisitor.h | 2 + clang/include/clang/Basic/DeclNodes.td | 1 + clang/include/clang/Basic/DiagnosticParseKinds.td | 2 + clang/include/clang/Parse/Parser.h | 1 + clang/include/clang/Sema/Sema.h | 5 ++ clang/include/clang/Sema/Template.h | 1 + clang/include/clang/Serialization/ASTBitCodes.h | 2 + clang/lib/AST/Decl.cpp | 15 +++++ clang/lib/AST/DeclBase.cpp | 12 ++-- clang/lib/CodeGen/CGDecl.cpp | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 31 ++++++----- clang/lib/CodeGen/CodeGenModule.h | 2 +- clang/lib/Parse/ParseDeclCXX.cpp | 47 ++++++++++++++++ clang/lib/Parse/Parser.cpp | 8 ++- clang/lib/Sema/SemaDecl.cpp | 30 ++++++++++ clang/lib/Sema/SemaLookup.cpp | 4 +- clang/lib/Sema/SemaTemplate.cpp | 4 +- clang/lib/Serialization/ASTCommon.cpp | 2 + clang/lib/Serialization/ASTReaderDecl.cpp | 9 +++ clang/lib/Serialization/ASTWriterDecl.cpp | 7 +++ clang/test/CodeGenCXX/modules-ts.cppm | 20 +++++++ clang/test/Parser/cxx-modules-interface.cppm | 43 ++++++++------ clang/test/SemaCXX/modules-ts.cppm | 68 +++++++++++++++++++++++ clang/tools/libclang/CIndex.cpp | 1 + 26 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 clang/test/CodeGenCXX/modules-ts.cppm create mode 100644 clang/test/SemaCXX/modules-ts.cppm diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 77bbdb2..90d8727 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3792,6 +3792,55 @@ public: static bool classofKind(Kind K) { return K == Import; } }; +/// \brief Represents a C++ Modules TS module export declaration. +/// +/// For example: +/// \code +/// export void foo(); +/// \endcode +class ExportDecl final : public Decl, public DeclContext { + virtual void anchor(); +private: + /// \brief The source location for the right brace (if valid). + SourceLocation RBraceLoc; + + ExportDecl(DeclContext *DC, SourceLocation ExportLoc) + : Decl(Export, DC, ExportLoc), DeclContext(Export), + RBraceLoc(SourceLocation()) { } + + friend class ASTDeclReader; + +public: + static ExportDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation ExportLoc); + static ExportDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + SourceLocation getExportLoc() const { return getLocation(); } + SourceLocation getRBraceLoc() const { return RBraceLoc; } + void setRBraceLoc(SourceLocation L) { RBraceLoc = L; } + + SourceLocation getLocEnd() const LLVM_READONLY { + if (RBraceLoc.isValid()) + return RBraceLoc; + // No braces: get the end location of the (only) declaration in context + // (if present). + return decls_empty() ? getLocation() : decls_begin()->getLocEnd(); + } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(getLocation(), getLocEnd()); + } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Export; } + static DeclContext *castToDeclContext(const ExportDecl *D) { + return static_cast(const_cast(D)); + } + static ExportDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast(const_cast(DC)); + } +}; + /// \brief Represents an empty-declaration. class EmptyDecl : public Decl { virtual void anchor(); diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index f2912a1..970ba8a 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -33,6 +33,7 @@ class DeclContext; class DeclarationName; class DependentDiagnostic; class EnumDecl; +class ExportDecl; class FunctionDecl; class FunctionType; enum Linkage : unsigned char; @@ -1135,6 +1136,7 @@ public: /// ObjCMethodDecl /// ObjCContainerDecl /// LinkageSpecDecl +/// ExportDecl /// BlockDecl /// OMPDeclareReductionDecl /// @@ -1279,7 +1281,8 @@ public: /// \brief Test whether the context supports looking up names. bool isLookupContext() const { - return !isFunctionOrMethod() && DeclKind != Decl::LinkageSpec; + return !isFunctionOrMethod() && DeclKind != Decl::LinkageSpec && + DeclKind != Decl::Export; } bool isFileContext() const { diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 84abbb7..0f01033 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1388,6 +1388,8 @@ DEF_TRAVERSE_DECL(ClassScopeFunctionSpecializationDecl, { DEF_TRAVERSE_DECL(LinkageSpecDecl, {}) +DEF_TRAVERSE_DECL(ExportDecl, {}) + DEF_TRAVERSE_DECL(ObjCPropertyImplDecl, {// FIXME: implement this }) diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index f29c399..6da2c2c 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -80,6 +80,7 @@ def Named : Decl<1>; def ObjCProperty : DDecl; def ObjCCompatibleAlias : DDecl; def LinkageSpec : Decl, DeclContext; +def Export : Decl, DeclContext; def ObjCPropertyImpl : Decl; def FileScopeAsm : Decl; def AccessSpec : Decl; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 1ee2510..e13f221 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1040,6 +1040,8 @@ def err_attribute_not_import_attr : Error< def err_module_expected_semi : Error< "expected ';' after module name">; def err_missing_before_module_end : Error<"expected %0 at end of module">; + +def err_export_empty : Error<"export declaration cannot be empty">; } let CategoryName = "Generics Issue" in { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index d5d885f..b0b9b63 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2412,6 +2412,7 @@ private: ParsedAttributes& attrs, BalancedDelimiterTracker &Tracker); Decl *ParseLinkage(ParsingDeclSpec &DS, unsigned Context); + Decl *ParseExportDeclaration(); Decl *ParseUsingDirectiveOrDeclaration(unsigned Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index dfe9e36..ccf4663 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1922,6 +1922,11 @@ public: SourceLocation DeclLoc, ArrayRef Modules, MissingImportKind MIK, bool Recover); + Decl *ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, + SourceLocation LBraceLoc); + Decl *ActOnFinishExportDecl(Scope *S, Decl *ExportDecl, + SourceLocation RBraceLoc); + /// \brief We've found a use of a templated declaration that would trigger an /// implicit instantiation. Check that any relevant explicit specializations /// and partial specializations are visible, and diagnose if not. diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index cda82a6..0d12880 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -410,6 +410,7 @@ namespace clang { #define OBJCCONTAINER(DERIVED, BASE) #define FILESCOPEASM(DERIVED, BASE) #define IMPORT(DERIVED, BASE) +#define EXPORT(DERIVED, BASE) #define LINKAGESPEC(DERIVED, BASE) #define OBJCCOMPATIBLEALIAS(DERIVED, BASE) #define OBJCMETHOD(DERIVED, BASE) diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 47678a1..510c1c4 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1103,6 +1103,8 @@ namespace clang { DECL_UNRESOLVED_USING_TYPENAME, /// \brief A LinkageSpecDecl record. DECL_LINKAGE_SPEC, + /// \brief An ExportDecl record. + DECL_EXPORT, /// \brief A CXXRecordDecl record. DECL_CXX_RECORD, /// \brief A CXXMethodDecl record. diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index cfdd557..fec7df0 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4310,3 +4310,18 @@ SourceRange ImportDecl::getSourceRange() const { return SourceRange(getLocation(), getIdentifierLocs().back()); } + +//===----------------------------------------------------------------------===// +// ExportDecl Implementation +//===----------------------------------------------------------------------===// + +void ExportDecl::anchor() {} + +ExportDecl *ExportDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation ExportLoc) { + return new (C, DC) ExportDecl(DC, ExportLoc); +} + +ExportDecl *ExportDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) ExportDecl(nullptr, SourceLocation()); +} diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 4586d4b..4d4e725 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -672,6 +672,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case FriendTemplate: case AccessSpec: case LinkageSpec: + case Export: case FileScopeAsm: case StaticAssert: case ObjCPropertyImpl: @@ -957,7 +958,7 @@ bool DeclContext::isDependentContext() const { bool DeclContext::isTransparentContext() const { if (DeclKind == Decl::Enum) return !cast(this)->isScoped(); - else if (DeclKind == Decl::LinkageSpec) + else if (DeclKind == Decl::LinkageSpec || DeclKind == Decl::Export) return true; return false; @@ -996,6 +997,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::TranslationUnit: case Decl::ExternCContext: case Decl::LinkageSpec: + case Decl::Export: case Decl::Block: case Decl::Captured: case Decl::OMPDeclareReduction: @@ -1408,8 +1410,8 @@ NamedDecl *const DeclContextLookupResult::SingleElementDummyList = nullptr; DeclContext::lookup_result DeclContext::lookup(DeclarationName Name) const { - assert(DeclKind != Decl::LinkageSpec && - "Should not perform lookups into linkage specs!"); + assert(DeclKind != Decl::LinkageSpec && DeclKind != Decl::Export && + "should not perform lookups into transparent contexts"); // If we have an external source, ensure that any later redeclarations of this // context have been loaded, since they may add names to the result of this @@ -1472,8 +1474,8 @@ DeclContext::lookup(DeclarationName Name) const { DeclContext::lookup_result DeclContext::noload_lookup(DeclarationName Name) { - assert(DeclKind != Decl::LinkageSpec && - "Should not perform lookups into linkage specs!"); + assert(DeclKind != Decl::LinkageSpec && DeclKind != Decl::Export && + "should not perform lookups into transparent contexts"); DeclContext *PrimaryContext = getPrimaryContext(); if (PrimaryContext != this) diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 5b4017c..a792047 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -78,6 +78,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::PragmaDetectMismatch: case Decl::AccessSpec: case Decl::LinkageSpec: + case Decl::Export: case Decl::ObjCPropertyImpl: case Decl::FileScopeAsm: case Decl::Friend: diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index e296a4c..dad49f5 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3754,17 +3754,6 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { D->setHasNonZeroConstructors(true); } -/// EmitNamespace - Emit all declarations in a namespace. -void CodeGenModule::EmitNamespace(const NamespaceDecl *ND) { - for (auto *I : ND->decls()) { - if (const auto *VD = dyn_cast(I)) - if (VD->getTemplateSpecializationKind() != TSK_ExplicitSpecialization && - VD->getTemplateSpecializationKind() != TSK_Undeclared) - continue; - EmitTopLevelDecl(I); - } -} - // EmitLinkageSpec - Emit all declarations in a linkage spec. void CodeGenModule::EmitLinkageSpec(const LinkageSpecDecl *LSD) { if (LSD->getLanguage() != LinkageSpecDecl::lang_c && @@ -3773,13 +3762,21 @@ void CodeGenModule::EmitLinkageSpec(const LinkageSpecDecl *LSD) { return; } - for (auto *I : LSD->decls()) { - // Meta-data for ObjC class includes references to implemented methods. - // Generate class's method definitions first. + EmitDeclContext(LSD); +} + +void CodeGenModule::EmitDeclContext(const DeclContext *DC) { + for (auto *I : DC->decls()) { + // Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope + // are themselves considered "top-level", so EmitTopLevelDecl on an + // ObjCImplDecl does not recursively visit them. We need to do that in + // case they're nested inside another construct (LinkageSpecDecl / + // ExportDecl) that does stop them from being considered "top-level". if (auto *OID = dyn_cast(I)) { for (auto *M : OID->methods()) EmitTopLevelDecl(M); } + EmitTopLevelDecl(I); } } @@ -3825,7 +3822,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { // C++ Decls case Decl::Namespace: - EmitNamespace(cast(D)); + EmitDeclContext(cast(D)); break; case Decl::CXXRecord: // Emit any static data members, they may be definitions. @@ -3976,6 +3973,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; } + case Decl::Export: + EmitDeclContext(cast(D)); + break; + case Decl::OMPThreadPrivate: EmitOMPThreadPrivateDecl(cast(D)); break; diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index ed18156..a069ce1 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1182,7 +1182,7 @@ private: // C++ related functions. - void EmitNamespace(const NamespaceDecl *D); + void EmitDeclContext(const DeclContext *DC); void EmitLinkageSpec(const LinkageSpecDecl *D); void CompleteDIClassType(const CXXMethodDecl* D); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index ed99fed..f7349b1 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -372,6 +372,53 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, unsigned Context) { : nullptr; } +/// Parse a C++ Modules TS export-declaration. +/// +/// export-declaration: +/// 'export' declaration +/// 'export' '{' declaration-seq[opt] '}' +/// +Decl *Parser::ParseExportDeclaration() { + assert(Tok.is(tok::kw_export)); + SourceLocation ExportLoc = ConsumeToken(); + + ParseScope ExportScope(this, Scope::DeclScope); + Decl *ExportDecl = Actions.ActOnStartExportDecl( + getCurScope(), ExportLoc, + Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation()); + + if (Tok.isNot(tok::l_brace)) { + // FIXME: Factor out a ParseExternalDeclarationWithAttrs. + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + MaybeParseMicrosoftAttributes(Attrs); + ParseExternalDeclaration(Attrs); + return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl, + SourceLocation()); + } + + BalancedDelimiterTracker T(*this, tok::l_brace); + T.consumeOpen(); + + // 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)) + Diag(ExportLoc, diag::err_export_empty) + << SourceRange(ExportLoc, Tok.getLocation()); + + while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && + Tok.isNot(tok::eof)) { + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + MaybeParseMicrosoftAttributes(Attrs); + ParseExternalDeclaration(Attrs); + } + + T.consumeClose(); + return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl, + T.getCloseLocation()); +} + /// ParseUsingDirectiveOrDeclaration - Parse C++ using using-declaration or /// using-directive. Assumes that current token is 'using'. Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0a54ea0..8cc97e5 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -770,11 +770,17 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, : Sema::PCC_Namespace); cutOffParsing(); return nullptr; + case tok::kw_export: + if (getLangOpts().ModulesTS) { + SingleDecl = ParseExportDeclaration(); + break; + } + // This must be 'export template'. Parse it so we can diagnose our lack + // of support. case tok::kw_using: case tok::kw_namespace: case tok::kw_typedef: case tok::kw_template: - case tok::kw_export: // As in 'export template' case tok::kw_static_assert: case tok::kw__Static_assert: // A function definition cannot start with any of these keywords. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e6381fb..773d00b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15422,6 +15422,36 @@ void Sema::createImplicitModuleImportForErrorRecovery(SourceLocation Loc, VisibleModules.setVisible(Mod, Loc); } +/// We have parsed the start of an export declaration, including the '{' +/// (if present). +Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, + SourceLocation LBraceLoc) { + // FIXME: C++ Modules TS: + // An export-declaration [...] shall not contain more than one + // export keyword. + // + // The intent here is that an export-declaration cannot appear within another + // export-declaration. + + ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc); + CurContext->addDecl(D); + PushDeclContext(S, D); + return D; +} + +/// Complete the definition of an export declaration. +Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) { + auto *ED = cast(D); + if (RBraceLoc.isValid()) + ED->setRBraceLoc(RBraceLoc); + + // FIXME: Diagnose export of internal-linkage declaration (including + // anonymous namespace). + + PopDeclContext(); + return D; +} + void Sema::ActOnPragmaRedefineExtname(IdentifierInfo* Name, IdentifierInfo* AliasName, SourceLocation PragmaLoc, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 40ad686..044a3aa 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1538,8 +1538,8 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { // If this declaration is not at namespace scope nor module-private, // then it is visible if its lexical parent has a visible definition. DeclContext *DC = D->getLexicalDeclContext(); - if (!D->isModulePrivate() && - DC && !DC->isFileContext() && !isa(DC)) { + if (!D->isModulePrivate() && DC && !DC->isFileContext() && + !isa(DC) && !isa(DC)) { // For a parameter, check whether our current template declaration's // lexical context is visible, not whether there's some other visible // definition of it, because parameters aren't "within" the definition. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 7d57329..bef3620 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5892,9 +5892,7 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { if (Ctx && Ctx->isExternCContext()) return Diag(TemplateParams->getTemplateLoc(), diag::err_template_linkage) << TemplateParams->getSourceRange(); - - while (Ctx && isa(Ctx)) - Ctx = Ctx->getParent(); + Ctx = Ctx->getRedeclContext(); // C++ [temp]p2: // A template-declaration can appear only as a namespace scope or diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 07d0c1c..79ccffc 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -183,6 +183,7 @@ serialization::getDefinitiveDeclContext(const DeclContext *DC) { case Decl::ExternCContext: case Decl::Namespace: case Decl::LinkageSpec: + case Decl::Export: return nullptr; // C/C++ tag types can only be defined in one place. @@ -291,6 +292,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::ObjCProperty: case Decl::ObjCCompatibleAlias: case Decl::LinkageSpec: + case Decl::Export: case Decl::ObjCPropertyImpl: case Decl::PragmaComment: case Decl::PragmaDetectMismatch: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 620e1fe..3cc5404 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -327,6 +327,7 @@ namespace clang { void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D); void VisitLinkageSpecDecl(LinkageSpecDecl *D); + void VisitExportDecl(ExportDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *AD); void VisitImportDecl(ImportDecl *D); void VisitAccessSpecDecl(AccessSpecDecl *D); @@ -1366,6 +1367,11 @@ void ASTDeclReader::VisitLinkageSpecDecl(LinkageSpecDecl *D) { D->setRBraceLoc(ReadSourceLocation(Record, Idx)); } +void ASTDeclReader::VisitExportDecl(ExportDecl *D) { + VisitDecl(D); + D->RBraceLoc = ReadSourceLocation(Record, Idx); +} + void ASTDeclReader::VisitLabelDecl(LabelDecl *D) { VisitNamedDecl(D); D->setLocStart(ReadSourceLocation(Record, Idx)); @@ -3266,6 +3272,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_LINKAGE_SPEC: D = LinkageSpecDecl::CreateDeserialized(Context, ID); break; + case DECL_EXPORT: + D = ExportDecl::CreateDeserialized(Context, ID); + break; case DECL_LABEL: D = LabelDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 158123b..a923d27 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -110,6 +110,7 @@ namespace clang { void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D); void VisitLinkageSpecDecl(LinkageSpecDecl *D); + void VisitExportDecl(ExportDecl *D); void VisitFileScopeAsmDecl(FileScopeAsmDecl *D); void VisitImportDecl(ImportDecl *D); void VisitAccessSpecDecl(AccessSpecDecl *D); @@ -1080,6 +1081,12 @@ void ASTDeclWriter::VisitLinkageSpecDecl(LinkageSpecDecl *D) { Code = serialization::DECL_LINKAGE_SPEC; } +void ASTDeclWriter::VisitExportDecl(ExportDecl *D) { + VisitDecl(D); + Record.AddSourceLocation(D->getRBraceLoc()); + Code = serialization::DECL_EXPORT; +} + void ASTDeclWriter::VisitLabelDecl(LabelDecl *D) { VisitNamedDecl(D); Record.AddSourceLocation(D->getLocStart()); diff --git a/clang/test/CodeGenCXX/modules-ts.cppm b/clang/test/CodeGenCXX/modules-ts.cppm new file mode 100644 index 0000000..da3bcb2 --- /dev/null +++ b/clang/test/CodeGenCXX/modules-ts.cppm @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fmodules-ts -std=c++1z -triple=x86_64-linux-gnu -emit-module-interface %s -o %t.pcm +// RUN: %clang_cc1 -fmodules-ts -std=c++1z -triple=x86_64-linux-gnu %t.pcm -emit-llvm -o - | FileCheck %s + +module FooBar; + +export { + // CHECK-LABEL: define i32 @_Z1fv( + int f() { return 0; } +} + +// FIXME: Emit global variables and their initializers with this TU. +// Emit an initialization function that other TUs can call, with guard variable. + +// FIXME: Mangle non-exported symbols so they don't collide with +// non-exported symbols from other modules? + +// FIXME: Formally-internal-linkage symbols that are used from an exported +// symbol need a mangled name and external linkage. + +// FIXME: const-qualified variables don't have implicit internal linkage when owned by a module. diff --git a/clang/test/Parser/cxx-modules-interface.cppm b/clang/test/Parser/cxx-modules-interface.cppm index 8874771..f7835bd 100644 --- a/clang/test/Parser/cxx-modules-interface.cppm +++ b/clang/test/Parser/cxx-modules-interface.cppm @@ -1,21 +1,32 @@ -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -DTEST=0 -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -Dmodule=int -DTEST=1 -// RUN: not %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -DTEST=2 2>&1 | FileCheck %s --check-prefix=CHECK-2 -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -verify -Dfoo=bar -DTEST=3 +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -Dmodule=int -DERRORS -#if TEST == 0 +module foo; +#ifndef ERRORS // expected-no-diagnostics -#endif +#else +// expected-error@-4 {{expected module declaration at start of module interface}} -module foo; -#if TEST == 1 -// expected-error@-2 {{expected module declaration at start of module interface}} -#elif TEST == 2 -// CHECK-2: error: redefinition of module 'foo' -#endif +// FIXME: support 'export module X;' and 'export { int n; module X; }' +// FIXME: proclaimed-ownership-declarations? + +export { + int a; + int b; +} +export int c; + +namespace N { + export void f() {} +} + +export struct T {} t; -int n; -#if TEST == 3 -// expected-error@-2 {{redefinition of 'n'}} -// expected-note@-3 {{previous}} +struct S { + export int n; // expected-error {{expected member name or ';'}} + export static int n; // expected-error {{expected member name or ';'}} +}; +void f() { + export int n; // expected-error {{expected expression}} +} #endif diff --git a/clang/test/SemaCXX/modules-ts.cppm b/clang/test/SemaCXX/modules-ts.cppm new file mode 100644 index 0000000..61e9e43 --- /dev/null +++ b/clang/test/SemaCXX/modules-ts.cppm @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -DTEST=0 +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -o %t.pcm -verify -Dmodule=int -DTEST=1 +// RUN: not %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -DTEST=2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -emit-module-interface %s -fmodule-file=%t.pcm -o %t.pcm -verify -Dfoo=bar -DTEST=3 + +#if TEST == 0 +// expected-no-diagnostics +#endif + +module foo; +#if TEST == 1 +// expected-error@-2 {{expected module declaration at start of module interface}} +#elif TEST == 2 +// CHECK-2: error: redefinition of module 'foo' +#endif + +static int m; // ok, internal linkage, so no redefinition error +int n; +#if TEST == 3 +// expected-error@-2 {{redefinition of '}} +// expected-note@-3 {{previous}} +#endif + +#if TEST == 0 +export { + int a; + int b; + constexpr int *p = &n; +} +export int c; + +namespace N { + export void f() {} +} + +export struct T {} t; +#elif TEST == 3 +int use_a = a; // expected-error {{declaration of 'a' must be imported from module 'foo' before it is required}} +// expected-note@-13 {{previous}} + +#undef foo +import foo; + +export {} // expected-error {{export declaration cannot be empty}} +export { ; } +export { static_assert(true); } + +// FIXME: These diagnostics are not very good. +export import foo; // expected-error {{expected unqualified-id}} +export { import foo; } // expected-error {{expected unqualified-id}} + +int use_b = b; +int use_n = n; // FIXME: this should not be visible, because it is not exported + +extern int n; +static_assert(&n == p); // FIXME: these are not the same entity +#endif + + +#if TEST == 1 +struct S { + export int n; // expected-error {{expected member name or ';'}} + export static int n; // expected-error {{expected member name or ';'}} +}; +#endif + +// FIXME: Exports of declarations without external linkage are disallowed. +// Exports of declarations with non-external-linkage types are disallowed. diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 22a17ad..0cc03c7 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5635,6 +5635,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::ObjCImplementation: case Decl::AccessSpec: case Decl::LinkageSpec: + case Decl::Export: case Decl::ObjCPropertyImpl: case Decl::FileScopeAsm: case Decl::StaticAssert: -- 2.7.4