let CategoryName = "Modules Issue" in {
def err_module_expected_ident : Error<
- "expected a module name after module import">;
+ "expected a module name after module%select{| import}0">;
+def err_unexpected_module_kind : Error<
+ "unexpected module kind %0; expected 'implementation' or 'partition'">;
+def err_attribute_not_module_attr : Error<
+ "%0 attribute cannot be applied to a module">;
def err_attribute_not_import_attr : Error<
"%0 attribute cannot be applied to a module import">;
def err_module_expected_semi : Error<
}
let CategoryName = "Modules Issue" in {
+def err_module_interface_implementation_mismatch : Error<
+ "%select{'module'|'module partition'|'module implementation'}0 declaration "
+ "found while %select{not |not |}0building module interface">;
+def err_current_module_name_mismatch : Error<
+ "module name '%0' specified on command line does not match name of module">;
def err_module_private_specialization : Error<
"%select{template|partial|member}0 specialization cannot be "
"declared __module_private__">;
///
void Initialize();
+ /// Parse the first top-level declaration in a translation unit.
+ bool ParseFirstTopLevelDecl(DeclGroupPtrTy &Result);
+
/// ParseTopLevelDecl - Parse one top-level declaration. Returns true if
/// the EOF was encountered.
bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
//===--------------------------------------------------------------------===//
// Modules
+ DeclGroupPtrTy ParseModuleDecl();
DeclGroupPtrTy ParseModuleImport(SourceLocation AtLoc);
bool parseMisplacedModuleImport();
bool tryParseMisplacedModuleImport() {
return false;
}
+ bool ParseModuleName(
+ SourceLocation UseLoc,
+ SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
+ bool IsImport);
+
//===--------------------------------------------------------------------===//
// C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
ExprResult ParseTypeTrait();
AttributeList *AttrList,
SourceLocation SemiLoc);
+ enum class ModuleDeclKind {
+ Module, ///< 'module X;'
+ Partition, ///< 'module partition X;'
+ Implementation, ///< 'module implementation X;'
+ };
+
+ /// The parser has processed a module-declaration that begins the definition
+ /// of a module interface or implementation.
+ DeclGroupPtrTy ActOnModuleDecl(SourceLocation ModuleLoc, ModuleDeclKind MDK,
+ ModuleIdPath Path);
+
/// \brief The parser has processed a module import declaration.
///
/// \param AtLoc The location of the '@' symbol, if any.
if (External)
External->StartTranslationUnit(Consumer);
- if (P.ParseTopLevelDecl(ADecl)) {
+ if (P.ParseFirstTopLevelDecl(ADecl)) {
if (!External && !S.getLangOpts().CPlusPlus)
P.Diag(diag::ext_empty_translation_unit);
} else {
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds);
}
+bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
+ // C++ Modules TS: module-declaration must be the first declaration in the
+ // file. (There can be no preceding preprocessor directives, but we expect
+ // the lexer to check that.)
+ if (Tok.is(tok::kw_module)) {
+ Result = ParseModuleDecl();
+ return false;
+ }
+ // FIXME: If we're parsing a module interface and we don't have a module
+ // declaration here, diagnose.
+
+ return ParseTopLevelDecl(Result);
+}
+
/// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
/// action tells us to. This returns true if the EOF was encountered.
bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
Braces.consumeClose();
}
+/// Parse a C++ Modules TS module declaration, which appears at the beginning
+/// of a module interface, module partition, or module implementation file.
+///
+/// module-declaration: [Modules TS + P0273R0]
+/// 'module' module-kind[opt] module-name attribute-specifier-seq[opt] ';'
+/// module-kind:
+/// 'implementation'
+/// 'partition'
+///
+/// Note that the module-kind values are context-sensitive keywords.
+Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
+ assert(Tok.is(tok::kw_module) && getLangOpts().ModulesTS &&
+ "should not be parsing a module declaration");
+ SourceLocation ModuleLoc = ConsumeToken();
+
+ // Check for a module-kind.
+ Sema::ModuleDeclKind MDK = Sema::ModuleDeclKind::Module;
+ if (Tok.is(tok::identifier) && NextToken().is(tok::identifier)) {
+ if (Tok.getIdentifierInfo()->isStr("implementation"))
+ MDK = Sema::ModuleDeclKind::Implementation;
+ else if (Tok.getIdentifierInfo()->isStr("partition"))
+ MDK = Sema::ModuleDeclKind::Partition;
+ else {
+ Diag(Tok, diag::err_unexpected_module_kind) << Tok.getIdentifierInfo();
+ SkipUntil(tok::semi);
+ return nullptr;
+ }
+ ConsumeToken();
+ }
+
+ SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
+ if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
+ return nullptr;
+
+ ParsedAttributesWithRange Attrs(AttrFactory);
+ MaybeParseCXX11Attributes(Attrs);
+ // We don't support any module attributes yet.
+ ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_module_attr);
+
+ ExpectAndConsumeSemi(diag::err_module_expected_semi);
+
+ return Actions.ActOnModuleDecl(ModuleLoc, MDK, Path);
+}
+
/// Parse a module import declaration. This is essentially the same for
/// Objective-C and the C++ Modules TS, except for the leading '@' (in ObjC)
/// and the trailing optional attributes (in C++).
///
/// [ObjC] @import declaration:
-/// '@' 'import' (identifier '.')* ';'
+/// '@' 'import' module-name ';'
/// [ModTS] module-import-declaration:
-/// 'module' module-name attribute-specifier-seq[opt] ';'
-/// module-name:
-/// module-name-qualifier[opt] identifier
-/// module-name-qualifier:
-/// module-name-qualifier[opt] identifier '.'
+/// 'import' module-name attribute-specifier-seq[opt] ';'
Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) {
assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import)
: Tok.isObjCAtKeyword(tok::objc_import)) &&
SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc;
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-
- // Parse the module path.
- while (true) {
- if (!Tok.is(tok::identifier)) {
- if (Tok.is(tok::code_completion)) {
- Actions.CodeCompleteModuleImport(ImportLoc, Path);
- cutOffParsing();
- return nullptr;
- }
-
- Diag(Tok, diag::err_module_expected_ident);
- SkipUntil(tok::semi);
- return nullptr;
- }
-
- // Record this part of the module path.
- Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
- ConsumeToken();
-
- if (Tok.isNot(tok::period))
- break;
-
- ConsumeToken();
- }
+ if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
+ return nullptr;
ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
return Actions.ConvertDeclToDeclGroup(Import.get());
}
+/// Parse a C++ Modules TS / Objective-C module name (both forms use the same
+/// grammar).
+///
+/// module-name:
+/// module-name-qualifier[opt] identifier
+/// module-name-qualifier:
+/// module-name-qualifier[opt] identifier '.'
+bool Parser::ParseModuleName(
+ SourceLocation UseLoc,
+ SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
+ bool IsImport) {
+ // Parse the module path.
+ while (true) {
+ if (!Tok.is(tok::identifier)) {
+ if (Tok.is(tok::code_completion)) {
+ Actions.CodeCompleteModuleImport(UseLoc, Path);
+ cutOffParsing();
+ return true;
+ }
+
+ Diag(Tok, diag::err_module_expected_ident) << IsImport;
+ SkipUntil(tok::semi);
+ return true;
+ }
+
+ // Record this part of the module path.
+ Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
+ ConsumeToken();
+
+ if (Tok.isNot(tok::period))
+ return false;
+
+ ConsumeToken();
+ }
+}
+
/// \brief Try recover parser when module annotation appears where it must not
/// be found.
/// \returns false if the recover was successful and parsing may be continued, or
return checkModuleImportContext(*this, M, ImportLoc, CurContext);
}
+Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation ModuleLoc,
+ ModuleDeclKind MDK,
+ ModuleIdPath Path) {
+ // We should see 'module implementation' if and only if we are not compiling
+ // a module interface.
+ if (getLangOpts().CompilingModule ==
+ (MDK == ModuleDeclKind::Implementation)) {
+ Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch)
+ << (unsigned)MDK;
+ return nullptr;
+ }
+
+ // FIXME: Create a ModuleDecl and return it.
+ // FIXME: Teach the lexer to handle this declaration too.
+
+ switch (MDK) {
+ case ModuleDeclKind::Module:
+ // FIXME: Check we're not in a submodule.
+ // FIXME: Set CurrentModule and create a corresponding Module object.
+ return nullptr;
+
+ case ModuleDeclKind::Partition:
+ // FIXME: Check we are in a submodule of the named module.
+ return nullptr;
+
+ case ModuleDeclKind::Implementation:
+ DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, Path);
+ if (Import.isInvalid())
+ return nullptr;
+ ImportDecl *ID = cast<ImportDecl>(Import.get());
+
+ // The current module is whatever we just loaded.
+ //
+ // FIXME: We should probably do this from the lexer rather than waiting
+ // until now, in case we look ahead across something where the current
+ // module matters (eg a #include).
+ auto Name = ID->getImportedModule()->getTopLevelModuleName();
+ if (!getLangOpts().CurrentModule.empty() &&
+ getLangOpts().CurrentModule != Name) {
+ Diag(Path.front().second, diag::err_current_module_name_mismatch)
+ << SourceRange(Path.front().second, Path.back().second)
+ << getLangOpts().CurrentModule;
+ }
+ const_cast<LangOptions&>(getLangOpts()).CurrentModule = Name;
+ return ConvertDeclToDeclGroup(ID);
+ }
+
+ llvm_unreachable("unexpected module decl kind");
+}
+
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ImportLoc,
ModuleIdPath Path) {
// FIXME: we should support importing a submodule within a different submodule
// of the same top-level module. Until we do, make it an error rather than
// silently ignoring the import.
- if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule)
+ // Import-from-implementation is valid in the Modules TS. FIXME: Should we
+ // warn on a redundant import of the current module?
+ if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule &&
+ (getLangOpts().CompilingModule || !getLangOpts().ModulesTS))
Diag(ImportLoc, getLangOpts().CompilingModule
? diag::err_module_self_import
: diag::err_module_import_in_implementation)
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: echo 'int a, b;' > %t/x.h
-// RUN: echo 'module x { header "x.h" module y {} }' > %t/map
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s
+// RUN: echo 'module x { header "x.h" module y {} } module z {}' > %t/map
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME=z
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=2 -DMODULE_KIND=implementation -DMODULE_NAME=x
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=3 -DMODULE_KIND= -DMODULE_NAME=z
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=4 -DMODULE_KIND=partition -DMODULE_NAME=z
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=5 -DMODULE_KIND=elderberry -DMODULE_NAME=z
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME='z [[]]'
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=6 -DMODULE_KIND=implementation -DMODULE_NAME='z [[fancy]]'
+// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \
+// RUN: -DTEST=7 -DMODULE_KIND=implementation -DMODULE_NAME='z [[maybe_unused]]'
-int use_1 = a; // expected-error {{undeclared}}
+module MODULE_KIND MODULE_NAME;
+#if TEST == 3
+// expected-error@-2 {{'module' declaration found while not building module interface}}
+#elif TEST == 4
+// expected-error@-4 {{'module partition' declaration found while not building module interface}}
+#elif TEST == 5
+// expected-error@-6 {{unexpected module kind 'elderberry'}}
+#elif TEST == 6
+// expected-warning@-8 {{unknown attribute 'fancy' ignored}}
+#elif TEST == 7
+// expected-error-re@-10 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
+#endif
+
+int use_1 = a;
+#if TEST != 2
+// expected-error@-2 {{undeclared}}
+#endif
import x;