30 changes: 30 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExportDecl>(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,
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LinkageSpecDecl>(DC)) {
if (!D->isModulePrivate() && DC && !DC->isFileContext() &&
!isa<LinkageSpecDecl>(DC) && !isa<ExportDecl>(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.
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LinkageSpecDecl>(Ctx))
Ctx = Ctx->getParent();
Ctx = Ctx->getRedeclContext();

// C++ [temp]p2:
// A template-declaration can appear only as a namespace scope or
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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());
Expand Down
20 changes: 20 additions & 0 deletions clang/test/CodeGenCXX/modules-ts.cppm
Original file line number Diff line number Diff line change
@@ -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.
43 changes: 27 additions & 16 deletions clang/test/Parser/cxx-modules-interface.cppm
Original file line number Diff line number Diff line change
@@ -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
68 changes: 68 additions & 0 deletions clang/test/SemaCXX/modules-ts.cppm
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down