r279196 - C++ Modules TS: support parsing the 'module' declaration (including extensions

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 18 18:43:06 PDT 2016


Author: rsmith
Date: Thu Aug 18 20:43:06 2016
New Revision: 279196

URL: http://llvm.org/viewvc/llvm-project?rev=279196&view=rev
Log:
C++ Modules TS: support parsing the 'module' declaration (including extensions
from p0273r0 approved by EWG). We'll eventually need to handle this from the
lexer as well, in order to disallow preprocessor directives preceding the
module declaration and to support macro import.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseAST.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/Parser/cxx-modules-import.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Thu Aug 18 20:43:06 2016
@@ -1022,7 +1022,11 @@ def warn_pragma_unroll_cuda_value_in_par
 
 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<

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Aug 18 20:43:06 2016
@@ -8462,6 +8462,11 @@ def note_related_result_type_explicit :
 }
 
 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__">;

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Aug 18 20:43:06 2016
@@ -278,6 +278,9 @@ public:
   ///
   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);
@@ -2656,6 +2659,7 @@ private:
 
   //===--------------------------------------------------------------------===//
   // Modules
+  DeclGroupPtrTy ParseModuleDecl();
   DeclGroupPtrTy ParseModuleImport(SourceLocation AtLoc);
   bool parseMisplacedModuleImport();
   bool tryParseMisplacedModuleImport() {
@@ -2666,6 +2670,11 @@ private:
     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();

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Aug 18 20:43:06 2016
@@ -1859,6 +1859,17 @@ public:
                               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.

Modified: cfe/trunk/lib/Parse/ParseAST.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseAST.cpp?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseAST.cpp (original)
+++ cfe/trunk/lib/Parse/ParseAST.cpp Thu Aug 18 20:43:06 2016
@@ -147,7 +147,7 @@ void clang::ParseAST(Sema &S, bool Print
   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 {

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Thu Aug 18 20:43:06 2016
@@ -537,6 +537,20 @@ void Parser::LateTemplateParserCleanupCa
   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) {
@@ -2000,18 +2014,58 @@ void Parser::ParseMicrosoftIfExistsExter
   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)) &&
@@ -2020,30 +2074,8 @@ Parser::DeclGroupPtrTy Parser::ParseModu
   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);
@@ -2064,6 +2096,42 @@ Parser::DeclGroupPtrTy Parser::ParseModu
   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

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Aug 18 20:43:06 2016
@@ -15178,6 +15178,56 @@ void Sema::diagnoseMisplacedModuleImport
   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) {
@@ -15194,7 +15244,10 @@ DeclResult Sema::ActOnModuleImport(Sourc
   // 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)

Modified: cfe/trunk/test/Parser/cxx-modules-import.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-modules-import.cpp?rev=279196&r1=279195&r2=279196&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-modules-import.cpp (original)
+++ cfe/trunk/test/Parser/cxx-modules-import.cpp Thu Aug 18 20:43:06 2016
@@ -1,10 +1,41 @@
 // 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 at -2 {{'module' declaration found while not building module interface}}
+#elif TEST == 4
+// expected-error at -4 {{'module partition' declaration found while not building module interface}}
+#elif TEST == 5
+// expected-error at -6 {{unexpected module kind 'elderberry'}}
+#elif TEST == 6
+// expected-warning at -8 {{unknown attribute 'fancy' ignored}}
+#elif TEST == 7
+// expected-error-re at -10 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
+#endif
+
+int use_1 = a;
+#if TEST != 2
+// expected-error at -2 {{undeclared}}
+#endif
 
 import x;
 




More information about the cfe-commits mailing list