]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Follow module grammar more closely [PR110808]
authorNathaniel Shead <nathanieloshead@gmail.com>
Fri, 24 Nov 2023 05:26:43 +0000 (16:26 +1100)
committerNathaniel Shead <nathanieloshead@gmail.com>
Sun, 7 Jan 2024 10:04:17 +0000 (21:04 +1100)
This patch cleans up the parsing of module-declarations and
import-declarations to more closely follow the grammar defined by the
standard.

For instance, currently we allow declarations like 'import A:B', even
from an unrelated source file (not part of module A), which causes
errors in merging declarations. However, the syntax in [module.import]
doesn't even allow this form of import, so this patch prevents this from
parsing at all and avoids the error that way.

Additionally, we sometimes allow statements like 'import :X' or
'module :X' even when not in a named module, and this causes segfaults,
so we disallow this too.

PR c++/110808

gcc/cp/ChangeLog:

* parser.cc (cp_parser_module_name): Rewrite to handle
module-names and module-partitions independently.
(cp_parser_module_partition): New function.
(cp_parser_module_declaration): Parse module partitions
explicitly. Don't change state if parsing module decl failed.
(cp_parser_import_declaration): Handle different kinds of
import-declarations locally.

gcc/testsuite/ChangeLog:

* g++.dg/modules/part-hdr-1_c.C: Fix syntax.
* g++.dg/modules/part-mac-1_c.C: Likewise.
* g++.dg/modules/mod-invalid-1.C: New test.
* g++.dg/modules/part-8_a.C: New test.
* g++.dg/modules/part-8_b.C: New test.
* g++.dg/modules/part-8_c.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
gcc/cp/parser.cc
gcc/testsuite/g++.dg/modules/mod-invalid-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/part-8_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/part-8_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/part-8_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/part-hdr-1_c.C
gcc/testsuite/g++.dg/modules/part-mac-1_c.C

index 37536faf2cf7f0a6575bc870c5fff41f067b2a37..bc1683bef34486be1b5b86cbca180b6d6a96d200 100644 (file)
@@ -14894,58 +14894,64 @@ cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p,
 
 /* Modules */
 
-/* Parse a module-name,
-   identifier
-   module-name . identifier
-   header-name
+/* Parse a module-name or module-partition.
 
-   Returns a pointer to module object, NULL.   */
+   module-name:
+     module-name-qualifier [opt] identifier
 
-static module_state *
-cp_parser_module_name (cp_parser *parser)
-{
-  cp_token *token = cp_lexer_peek_token (parser->lexer);
-  if (token->type == CPP_HEADER_NAME)
-    {
-      cp_lexer_consume_token (parser->lexer);
+   module-partition:
+     : module-name-qualifier [opt] identifier
 
-      return get_module (token->u.value);
-    }
+   module-name-qualifier:
+     identifier .
+     module-name-qualifier identifier .
 
-  module_state *parent = NULL;
-  bool partitioned = false;
-  if (token->type == CPP_COLON && named_module_p ())
-    {
-      partitioned = true;
-      cp_lexer_consume_token (parser->lexer);
-    }
+   Returns a pointer to the module object, or NULL on failure.
+   For PARTITION_P, PARENT is the module this is a partition of.  */
+
+static module_state *
+cp_parser_module_name (cp_parser *parser, bool partition_p = false,
+                      module_state *parent = NULL)
+{
+  if (partition_p
+      && cp_lexer_consume_token (parser->lexer)->type != CPP_COLON)
+    return NULL;
 
   for (;;)
     {
       if (cp_lexer_peek_token (parser->lexer)->type != CPP_NAME)
        {
-         cp_parser_error (parser, "expected module-name");
-         break;
+         if (partition_p)
+           cp_parser_error (parser, "expected module-partition");
+         else
+           cp_parser_error (parser, "expected module-name");
+         return NULL;
        }
 
       tree name = cp_lexer_consume_token (parser->lexer)->u.value;
-      parent = get_module (name, parent, partitioned);
-      token = cp_lexer_peek_token (parser->lexer);
-      if (!partitioned && token->type == CPP_COLON)
-       partitioned = true;
-      else if (token->type != CPP_DOT)
+      parent = get_module (name, parent, partition_p);
+      if (cp_lexer_peek_token (parser->lexer)->type != CPP_DOT)
        break;
 
       cp_lexer_consume_token (parser->lexer);
-   }
+    }
 
   return parent;
 }
 
+/* Parse a module-partition.  Defers to cp_parser_module_name.  */
+
+static module_state *
+cp_parser_module_partition (cp_parser *parser, module_state *parent = NULL)
+{
+  return cp_parser_module_name (parser, /*partition_p=*/true, parent);
+}
+
 /* Named module-declaration
      __module ; PRAGMA_EOL
-     __module private ; PRAGMA_EOL (unimplemented)
-     [__export] __module module-name attr-spec-seq-opt ; PRAGMA_EOL
+     __module : private ; PRAGMA_EOL (unimplemented)
+     [__export] __module module-name module-partition [opt]
+        attr-spec-seq-opt ; PRAGMA_EOL
 */
 
 static module_parse
@@ -15003,9 +15009,12 @@ cp_parser_module_declaration (cp_parser *parser, module_parse mp_state,
   else
     {
       module_state *mod = cp_parser_module_name (parser);
+      if (mod && cp_lexer_peek_token (parser->lexer)->type == CPP_COLON)
+       mod = cp_parser_module_partition (parser, mod);
       tree attrs = cp_parser_attributes_opt (parser);
 
-      mp_state = MP_PURVIEW_IMPORTS;
+      if (mod)
+       mp_state = MP_PURVIEW_IMPORTS;
       if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
        goto skip_eol;
 
@@ -15017,7 +15026,10 @@ cp_parser_module_declaration (cp_parser *parser, module_parse mp_state,
 }
 
 /* Import-declaration
-   [__export] __import module-name attr-spec-seq-opt ; PRAGMA_EOL */
+   __import module-name attr-spec-seq-opt ; PRAGMA_EOL
+   __import module-partition attr-spec-seq-opt ; PRAGMA_EOL
+   __import header-name attr-spec-seq-opt ; PRAGMA_EOL
+*/
 
 static void
 cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,
@@ -15045,7 +15057,27 @@ cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,
     }
   else
     {
-      module_state *mod = cp_parser_module_name (parser);
+      module_state *mod = NULL;
+      cp_token *next = cp_lexer_peek_token (parser->lexer);
+      if (next->type == CPP_HEADER_NAME)
+       {
+         cp_lexer_consume_token (parser->lexer);
+         mod = get_module (next->u.value);
+       }
+      else if (next->type == CPP_COLON)
+       {
+         /* An import specifying a module-partition shall only appear after the
+            module-declaration in a module unit: [module.import]/4.  */
+         if (named_module_p ()
+             && (mp_state == MP_PURVIEW_IMPORTS
+                 || mp_state == MP_PRIVATE_IMPORTS))
+           mod = cp_parser_module_partition (parser);
+         else
+           error_at (next->location, "import specifying a module-partition"
+                     " must appear after a named module-declaration");
+       }
+      else
+       mod = cp_parser_module_name (parser);
       tree attrs = cp_parser_attributes_opt (parser);
 
       if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
diff --git a/gcc/testsuite/g++.dg/modules/mod-invalid-1.C b/gcc/testsuite/g++.dg/modules/mod-invalid-1.C
new file mode 100644 (file)
index 0000000..fadaba0
--- /dev/null
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules-ts" }
+
+module;
+
+module :foo;  // { dg-error "expected module-name" }
+
+import :foo;  // { dg-error "import specifying a module-partition must appear after a named module-declaration" }
diff --git a/gcc/testsuite/g++.dg/modules/part-8_a.C b/gcc/testsuite/g++.dg/modules/part-8_a.C
new file mode 100644 (file)
index 0000000..09f956f
--- /dev/null
@@ -0,0 +1,6 @@
+// PR c++/110808
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi group:tres }
+
+export module group:tres;
+int mul() { return 0; }
diff --git a/gcc/testsuite/g++.dg/modules/part-8_b.C b/gcc/testsuite/g++.dg/modules/part-8_b.C
new file mode 100644 (file)
index 0000000..1ade029
--- /dev/null
@@ -0,0 +1,6 @@
+// PR c++/110808
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi group }
+
+export module group;
+export import :tres;
diff --git a/gcc/testsuite/g++.dg/modules/part-8_c.C b/gcc/testsuite/g++.dg/modules/part-8_c.C
new file mode 100644 (file)
index 0000000..2351f28
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/110808
+// { dg-additional-options "-fmodules-ts" }
+
+import group:tres;  // { dg-error "expected .;." }
+
+int main() {
+  return mul();  // { dg-error "not declared" }
+}
index 78a53d2fda3af27bece9ab78bc8879e9ac66dbf8..db57adcef44dd29847f31793f4ec0512ab926ca1 100644 (file)
@@ -2,4 +2,4 @@
 // { dg-module-cmi {mod} }
 
 export module mod;
-import mod:impl;
+import :impl;
index 78a53d2fda3af27bece9ab78bc8879e9ac66dbf8..db57adcef44dd29847f31793f4ec0512ab926ca1 100644 (file)
@@ -2,4 +2,4 @@
 // { dg-module-cmi {mod} }
 
 export module mod;
-import mod:impl;
+import :impl;