diff mbox series

c++, v2: Attempt to implement C++26 P3034R1 - Module Declarations Shouldn't be Macros [PR114461]

Message ID ZyD/tAVnWeMsCFms@tucnak
State New
Headers show
Series c++, v2: Attempt to implement C++26 P3034R1 - Module Declarations Shouldn't be Macros [PR114461] | expand

Commit Message

Jakub Jelinek Oct. 29, 2024, 3:31 p.m. UTC
On Fri, Oct 25, 2024 at 12:52:41PM -0400, Jason Merrill wrote:
> This does seem like a hole in the wording.  I think the clear intent is that
> the name/partition must neither be macro-expanded nor come from macro
> expansion.

I'll defer filing the DR and figuring out the right wording for the standard
to you/WG21.

> > The patch below implements what is above described as the first variant
> > of the first issue resolution, i.e. disables expansion of as many tokens
> > as could be in the valid module name and module partition syntax, but
> > as soon as it e.g. sees two adjacent identifiers, the second one can be
> > macro expanded.  So, effectively:
> > #define SEMI ;
> > export module SEMI
> > used to be valid and isn't anymore,
> > #define FOO bar
> > export module FOO;
> > isn't valid,
> > #define COLON :
> > export module COLON private;
> > isn't valid,
> > #define BAR baz
> > export module foo.bar:baz.qux.BAR;
> > isn't valid,
> 
> Agreed.

Ok.

> > but
> > #define BAZ .qux
> > export module foo BAZ;
> > or
> > #define QUX [[]]
> > export module foo QUX;
> > or
> > #define FREDDY :garply
> > export module foo FREDDY;
> > or
> > #define GARPLY private
> > module : GARPLY;
> > etc. is.
> 
> I think QUX is valid, but the others are intended to be invalid.

I've changed the patch so that the BAZ and FREDDY cases above
are also diagnosed as invalid, so cases where a module name or module
partition name is followed by an identifier defined as object-like or
function-like macro and where after macro expansion the first
non-padding/comment token is dot or colon (so, either an object-like
or function-like macro whose expansion starts with . or : or one
which expands to nothing and . or : following that macro either directly
or from some other macro expansion.
E.g. one could have
#define A [[vendor::attr(1 ? 2
#define B 3)]]
export module foo.bar A : B;
which I think ought to be valid and expanded as
export module foo.bar [[vendor::attr(1 ? 2 : 3)]];
or
#define A
#define B baz
export module foo.bar A : B;
which would preprocess into
export module foo.bar:baz;
and I think that is against the intent of the paper where the quick
scanners couldn't figure this out without actually performing preprocessing.
Unfortunately, trying to enter macro context inside of
cpp_maybe_module_directive doesn't seem to work well, so I've instead added
a flag and let cpp_get_token_1 to diagnose it later (but still during
preprocessing, because if going e.g. with -save-temps the distinction would
be lost, so it feels undesirable to diagnose that during parsing, plus
whether some token comes from a macro or not is currently only testable in
the FE when not -fno-track-macro-expansion and see above testcase that
the : or . actually might not come from a macro, yet there could be
a macro expanding to nothing in between.

I've kept the GARPLY case above as valid, I don't see the paper changing
anything in that regard (sure, the : has to not come from macro so that
the scanners can figure it out) and I think it isn't needed.

> > --- gcc/testsuite/g++.dg/modules/dir-only-4.C.jj	2020-12-22 23:50:17.057972516 +0100
> > +++ gcc/testsuite/g++.dg/modules/dir-only-4.C	2024-08-08 09:33:09.454522024 +0200
> > @@ -1,8 +1,8 @@
> >   // { dg-additional-options {-fmodules-ts -fpreprocessed -fdirectives-only} }
> > -// { dg-module-cmi !foo }
> > +// { dg-module-cmi !baz }
> >   module;
> >   #define foo baz
> > -export module foo;
> > +export module baz;
> >   class import {};
> > --- gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C.jj	2020-12-22 23:50:17.055972539 +0100
> > +++ gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C	2024-08-08 09:35:56.093364042 +0200
> > @@ -1,6 +1,6 @@
> >   // { dg-additional-options "-fmodules-ts" }
> >   #define malcolm kevin
> > -export module malcolm;
> > +export module kevin;
> >   // { dg-module-cmi kevin }
> >   export class X;
> > --- gcc/testsuite/g++.dg/modules/atom-preamble-4.C.jj	2020-12-22 23:50:17.055972539 +0100
> > +++ gcc/testsuite/g++.dg/modules/atom-preamble-4.C	2024-08-08 09:35:32.463670046 +0200
> > @@ -1,5 +1,5 @@
> >   // { dg-additional-options "-fmodules-ts" }
> > -#define NAME(X) X;
> > +#define NAME(X) ;
> > -export module NAME(bob)
> > +export module bob NAME(bob)
> 
> It looks like these tests were specifically testing patterns that this paper
> makes ill-formed, and so we should check for errors instead of changing them
> to be well-formed.

Ok, changed dir-only-4.C and atom-preamble-4.C back to previous content
+ dg-error and added new tests with the changes in and not expecting
error.
I had to keep the atom-preamble-2_a.C change as is, because otherwise
all of atom-preamble-2*.C tests FAIL.

Here is a so far lightly tested patch, ok for trunk if it passes full
bootstrap/regtest or do you want some further changes?

2024-10-29  Jakub Jelinek  <jakub@redhat.com>

	PR c++/114461
libcpp/
	* include/cpplib.h: Implement C++26 P3034R1
	- Module Declarations Shouldn’t be Macros (or more precisely
	its expected intent).
	(NO_DOT_COLON): Define.
	* internal.h (struct cpp_reader): Add diagnose_dot_colon_from_macro_p
	member.
	* lex.cc (cpp_maybe_module_directive): For pp-module, if
	module keyword is followed by CPP_NAME, ensure all CPP_NAME
	tokens possibly matching module name and module partition
	syntax aren't expanded and aren't defined as object-like macros.
	Verify first token after that doesn't start with open paren.
	If the next token after module name/partition is CPP_NAME defined
	as macro, set NO_DOT_COLON flag on it.
	* macro.cc (cpp_get_token_1): Set
	pfile->diagnose_dot_colon_from_macro_p if token to be expanded has
	NO_DOT_COLON bit set in flags.  Before returning, if
	pfile->diagnose_dot_colon_from_macro_p is true and not returning
	CPP_PADDING or CPP_COMMENT and not during macro expansion preparation,
	set pfile->diagnose_dot_colon_from_macro_p to false and diagnose
	if returning CPP_DOT or CPP_COLON.
gcc/testsuite/
	* g++.dg/modules/cpp-7.C: New test.
	* g++.dg/modules/cpp-8.C: New test.
	* g++.dg/modules/cpp-9.C: New test.
	* g++.dg/modules/cpp-10.C: New test.
	* g++.dg/modules/cpp-11.C: New test.
	* g++.dg/modules/cpp-12.C: New test.
	* g++.dg/modules/cpp-13.C: New test.
	* g++.dg/modules/cpp-14.C: New test.
	* g++.dg/modules/cpp-15.C: New test.
	* g++.dg/modules/cpp-16.C: New test.
	* g++.dg/modules/cpp-17.C: New test.
	* g++.dg/modules/cpp-18.C: New test.
	* g++.dg/modules/cpp-19.C: New test.
	* g++.dg/modules/cpp-20.C: New test.
	* g++.dg/modules/pmp-4.C: New test.
	* g++.dg/modules/pmp-5.C: New test.
	* g++.dg/modules/pmp-6.C: New test.
	* g++.dg/modules/token-6.C: New test.
	* g++.dg/modules/token-7.C: New test.
	* g++.dg/modules/token-8.C: New test.
	* g++.dg/modules/token-9.C: New test.
	* g++.dg/modules/token-10.C: New test.
	* g++.dg/modules/token-11.C: New test.
	* g++.dg/modules/token-12.C: New test.
	* g++.dg/modules/token-13.C: New test.
	* g++.dg/modules/token-14.C: New test.
	* g++.dg/modules/token-15.C: New test.
	* g++.dg/modules/token-16.C: New test.
	* g++.dg/modules/dir-only-3.C: Expect an error.
	* g++.dg/modules/dir-only-4.C: Expect an error.
	* g++.dg/modules/dir-only-5.C: New test.
	* g++.dg/modules/atom-preamble-2_a.C: In export module malcolm;
	replace malcolm with kevin.
	* g++.dg/modules/atom-preamble-4.C: Expect an error.
	* g++.dg/modules/atom-preamble-5.C: New test.



	Jakub

Comments

Jakub Jelinek Oct. 30, 2024, 6:55 a.m. UTC | #1
On Tue, Oct 29, 2024 at 04:31:00PM +0100, Jakub Jelinek wrote:
> Here is a so far lightly tested patch, ok for trunk if it passes full
> bootstrap/regtest or do you want some further changes?

Bootstrapped/regtested successfully on x86_64-linux and i686-linux.

	Jakub
Jason Merrill Nov. 1, 2024, 3:22 p.m. UTC | #2
On 10/29/24 11:31 AM, Jakub Jelinek wrote:
> On Fri, Oct 25, 2024 at 12:52:41PM -0400, Jason Merrill wrote:
>> This does seem like a hole in the wording.  I think the clear intent is that
>> the name/partition must neither be macro-expanded nor come from macro
>> expansion.
> 
> I'll defer filing the DR and figuring out the right wording for the standard
> to you/WG21.

I've reported it to CWG, and people agree with both my understanding and 
the need for wording improvement.

>>> The patch below implements what is above described as the first variant
>>> of the first issue resolution, i.e. disables expansion of as many tokens
>>> as could be in the valid module name and module partition syntax, but
>>> as soon as it e.g. sees two adjacent identifiers, the second one can be
>>> macro expanded.  So, effectively:
>>> #define SEMI ;
>>> export module SEMI
>>> used to be valid and isn't anymore,
>>> #define FOO bar
>>> export module FOO;
>>> isn't valid,
>>> #define COLON :
>>> export module COLON private;
>>> isn't valid,
>>> #define BAR baz
>>> export module foo.bar:baz.qux.BAR;
>>> isn't valid,
>>
>> Agreed.
> 
> Ok.
> 
>>> but
>>> #define BAZ .qux
>>> export module foo BAZ;
>>> or
>>> #define QUX [[]]
>>> export module foo QUX;
>>> or
>>> #define FREDDY :garply
>>> export module foo FREDDY;
>>> or
>>> #define GARPLY private
>>> module : GARPLY;
>>> etc. is.
>>
>> I think QUX is valid, but the others are intended to be invalid.
> 
> I've changed the patch so that the BAZ and FREDDY cases above
> are also diagnosed as invalid, so cases where a module name or module
> partition name is followed by an identifier defined as object-like or
> function-like macro and where after macro expansion the first
> non-padding/comment token is dot or colon (so, either an object-like
> or function-like macro whose expansion starts with . or : or one
> which expands to nothing and . or : following that macro either directly
> or from some other macro expansion.
> E.g. one could have
> #define A [[vendor::attr(1 ? 2
> #define B 3)]]
> export module foo.bar A : B;
> which I think ought to be valid and expanded as
> export module foo.bar [[vendor::attr(1 ? 2 : 3)]];
> or
> #define A
> #define B baz
> export module foo.bar A : B;
> which would preprocess into
> export module foo.bar:baz;
> and I think that is against the intent of the paper where the quick
> scanners couldn't figure this out without actually performing preprocessing.
> Unfortunately, trying to enter macro context inside of
> cpp_maybe_module_directive doesn't seem to work well, so I've instead added
> a flag and let cpp_get_token_1 to diagnose it later (but still during
> preprocessing, because if going e.g. with -save-temps the distinction would
> be lost, so it feels undesirable to diagnose that during parsing, plus
> whether some token comes from a macro or not is currently only testable in
> the FE when not -fno-track-macro-expansion and see above testcase that
> the : or . actually might not come from a macro, yet there could be
> a macro expanding to nothing in between.

Sounds good.

> I've kept the GARPLY case above as valid, I don't see the paper changing
> anything in that regard (sure, the : has to not come from macro so that
> the scanners can figure it out) and I think it isn't needed.

It seems to me that no macro expansion should be done for a directive 
starting "module :" as that cannot be a pp-module.

>>> --- gcc/testsuite/g++.dg/modules/dir-only-4.C.jj	2020-12-22 23:50:17.057972516 +0100
>>> +++ gcc/testsuite/g++.dg/modules/dir-only-4.C	2024-08-08 09:33:09.454522024 +0200
>>> @@ -1,8 +1,8 @@
>>>    // { dg-additional-options {-fmodules-ts -fpreprocessed -fdirectives-only} }
>>> -// { dg-module-cmi !foo }
>>> +// { dg-module-cmi !baz }
>>>    module;
>>>    #define foo baz
>>> -export module foo;
>>> +export module baz;
>>>    class import {};
>>> --- gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C.jj	2020-12-22 23:50:17.055972539 +0100
>>> +++ gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C	2024-08-08 09:35:56.093364042 +0200
>>> @@ -1,6 +1,6 @@
>>>    // { dg-additional-options "-fmodules-ts" }
>>>    #define malcolm kevin
>>> -export module malcolm;
>>> +export module kevin;
>>>    // { dg-module-cmi kevin }
>>>    export class X;
>>> --- gcc/testsuite/g++.dg/modules/atom-preamble-4.C.jj	2020-12-22 23:50:17.055972539 +0100
>>> +++ gcc/testsuite/g++.dg/modules/atom-preamble-4.C	2024-08-08 09:35:32.463670046 +0200
>>> @@ -1,5 +1,5 @@
>>>    // { dg-additional-options "-fmodules-ts" }
>>> -#define NAME(X) X;
>>> +#define NAME(X) ;
>>> -export module NAME(bob)
>>> +export module bob NAME(bob)
>>
>> It looks like these tests were specifically testing patterns that this paper
>> makes ill-formed, and so we should check for errors instead of changing them
>> to be well-formed.
> 
> Ok, changed dir-only-4.C and atom-preamble-4.C back to previous content
> + dg-error and added new tests with the changes in and not expecting
> error.
> I had to keep the atom-preamble-2_a.C change as is, because otherwise
> all of atom-preamble-2*.C tests FAIL.

Fair enough.  But then let's also remove the #define that is no longer used.

Incidentally, I think 2_b.C should fail because the pp-module is in the 
midle of an if-section, which is forbidden by the [cpp] grammar since P1857.

> Here is a so far lightly tested patch, ok for trunk if it passes full
> bootstrap/regtest or do you want some further changes?

OK with removing the #define from atom-preamble-2_a.C.

Jason
diff mbox series

Patch

--- libcpp/include/cpplib.h.jj	2024-10-25 10:03:16.785374003 +0200
+++ libcpp/include/cpplib.h	2024-10-29 14:27:03.530521515 +0100
@@ -204,6 +204,9 @@  struct GTY(()) cpp_string {
 #define PURE_ZERO	(1 << 7) /* Single 0 digit, used by the C++ frontend,
 				    set in c-lex.cc.  */
 #define COLON_SCOPE	PURE_ZERO /* Adjacent colons in C < 23.  */
+#define NO_DOT_COLON	PURE_ZERO /* Set on CPP_NAME tokens whose expansion
+				     shouldn't start with CPP_DOT or CPP_COLON
+				     after optional CPP_PADDING.  */
 #define SP_DIGRAPH	(1 << 8) /* # or ## token was a digraph.  */
 #define SP_PREV_WHITE	(1 << 9) /* If whitespace before a ##
 				    operator, or before this token
--- libcpp/internal.h.jj	2024-10-25 10:03:16.786373988 +0200
+++ libcpp/internal.h	2024-10-29 14:34:42.125244956 +0100
@@ -468,6 +468,10 @@  struct cpp_reader
      one.  */
   bool about_to_expand_macro_p;
 
+  /* True if the preprocessor should diagnose CPP_DOT or CPP_COLON
+     tokens as the first ones coming from macro expansion.  */
+  bool diagnose_dot_colon_from_macro_p;
+
   /* Search paths for include files.  */
   struct cpp_dir *quote_include;	/* "" */
   struct cpp_dir *bracket_include;	/* <> */
--- libcpp/lex.cc.jj	2024-10-25 10:03:16.786373988 +0200
+++ libcpp/lex.cc	2024-10-29 15:11:08.023890616 +0100
@@ -3604,6 +3604,78 @@  cpp_maybe_module_directive (cpp_reader *
       /* Maybe tell the tokenizer we expect a header-name down the
 	 road.  */
       pfile->state.directive_file_token = header_count;
+
+      /* According to P3034R1, pp-module-name and pp-module-partition tokens
+	 if any shouldn't be macro expanded and identifiers shouldn't be
+	 defined as object-like macro.  */
+      if (!header_count && peek->type == CPP_NAME)
+	{
+	  int state = 0;
+	  do
+	    {
+	      cpp_token *tok = peek;
+	      if (tok->type == CPP_NAME)
+		{
+		  cpp_hashnode *node = tok->val.node.node;
+		  /* Don't attempt to expand the token.  */
+		  tok->flags |= NO_EXPAND;
+		  if (_cpp_defined_macro_p (node)
+		      && _cpp_maybe_notify_macro_use (pfile, node,
+						      tok->src_loc)
+		      && !cpp_fun_like_macro_p (node))
+		    {
+		      if (state == 0)
+			cpp_error_with_line (pfile, CPP_DL_ERROR,
+					     tok->src_loc, 0,
+					     "module name %qs cannot "
+					     "be an object-like macro",
+					     NODE_NAME (node));
+		      else
+			cpp_error_with_line (pfile, CPP_DL_ERROR,
+					     tok->src_loc, 0,
+					     "module partition %qs cannot "
+					     "be an object-like macro",
+					     NODE_NAME (node));
+		    }
+		}
+	      peek = _cpp_lex_direct (pfile);
+	      backup++;
+	      if (tok->type == CPP_NAME)
+		{
+		  if (peek->type == CPP_DOT)
+		    continue;
+		  else if (peek->type == CPP_COLON && state == 0)
+		    {
+		      ++state;
+		      continue;
+		    }
+		  else if (peek->type == CPP_OPEN_PAREN)
+		    {
+		      if (state == 0)
+			cpp_error_with_line (pfile, CPP_DL_ERROR,
+					     peek->src_loc, 0,
+					     "module name followed by %<(%>");
+		      else
+			cpp_error_with_line (pfile, CPP_DL_ERROR,
+					     peek->src_loc, 0,
+					     "module partition followed by "
+					     "%<(%>");
+		      break;
+		    }
+		  else if (peek->type == CPP_NAME
+			   && _cpp_defined_macro_p (peek->val.node.node))
+		    {
+		      peek->flags |= NO_DOT_COLON;
+		      break;
+		    }
+		  else
+		    break;
+		}
+	      else if (peek->type != CPP_NAME)
+		break;
+	    }
+	  while (true);
+	}
     }
   else
     {
--- libcpp/macro.cc.jj	2024-10-25 10:03:16.788373960 +0200
+++ libcpp/macro.cc	2024-10-29 16:03:10.225312607 +0100
@@ -3076,6 +3076,9 @@  cpp_get_token_1 (cpp_reader *pfile, loca
 	  if (pfile->state.prevent_expansion)
 	    break;
 
+	  if ((result->flags & NO_DOT_COLON) != 0)
+	    pfile->diagnose_dot_colon_from_macro_p = true;
+
 	  /* Conditional macros require that a predicate be evaluated
 	     first.  */
 	  if ((node->flags & NODE_CONDITIONAL) != 0)
@@ -3226,6 +3229,20 @@  cpp_get_token_1 (cpp_reader *pfile, loca
 	}
     }
 
+  if (pfile->diagnose_dot_colon_from_macro_p
+      && !pfile->about_to_expand_macro_p
+      && result->type != CPP_PADDING
+      && result->type != CPP_COMMENT)
+    {
+      if (result->type == CPP_DOT || result->type == CPP_COLON)
+	cpp_error_with_line (pfile, CPP_DL_ERROR,
+			     result->src_loc, 0,
+			     "%qc in module name or partition "
+			     "comes from or after macro expansion",
+			     result->type == CPP_DOT ? '.' : ':');
+      pfile->diagnose_dot_colon_from_macro_p = false;
+    }
+
   return result;
 }
 
--- gcc/testsuite/g++.dg/modules/cpp-7.C.jj	2024-10-29 13:53:15.228616120 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-7.C	2024-10-29 14:45:11.319542826 +0100
@@ -0,0 +1,8 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define NAME(X) X;
+
+export module NAME(bob)		// { dg-error "module name followed by '\\\('" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-8.C.jj	2024-10-29 13:53:15.250615813 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-8.C	2024-10-29 14:44:02.278497703 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred;
+export module bob;		// { dg-error "module name 'bob' cannot be an object-like macro" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-9.C.jj	2024-10-29 13:53:15.250615813 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-9.C	2024-10-29 14:45:53.512959265 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred;
+export module foo.bob;		// { dg-error "module name 'bob' cannot be an object-like macro" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-10.C.jj	2024-10-29 13:53:15.250615813 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-10.C	2024-10-29 14:45:45.528069701 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred;
+export module foo:bob;		// { dg-error "module partition 'bob' cannot be an object-like macro" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-11.C.jj	2024-10-29 13:53:15.250615813 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-11.C	2024-10-29 14:45:20.844411089 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred;
+export module foo:bar.bob;		// { dg-error "module partition 'bob' cannot be an object-like macro" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-12.C.jj	2024-10-29 15:37:32.043702670 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-12.C	2024-10-29 15:50:59.545468046 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz .qux		// { dg-error "'\\\.' in module name or partition comes from or after macro expansion" }
+export module foo:bar baz;
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-13.C.jj	2024-10-29 15:38:21.892008287 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-13.C	2024-10-29 15:51:08.306346264 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz :qux.garply		// { dg-error "':' in module name or partition comes from or after macro expansion" }
+export module foo.bar baz;
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-14.C.jj	2024-10-29 15:40:29.607229223 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-14.C	2024-10-29 15:40:43.249039193 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz [[]]
+export module foo.bar baz;
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-15.C.jj	2024-10-29 15:40:50.729934981 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-15.C	2024-10-29 15:47:52.610066467 +0100
@@ -0,0 +1,8 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz
+#define qux
+export module foo.bar baz . garply qux;	// { dg-error "'\\\.' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-16.C.jj	2024-10-29 15:41:44.169190577 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-16.C	2024-10-29 15:48:21.870659744 +0100
@@ -0,0 +1,8 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz
+#define qux
+export module foo.bar baz : garply qux;	// { dg-error "':' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-17.C.jj	2024-10-29 15:48:49.977269054 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-17.C	2024-10-29 15:49:21.493830972 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz(x) x qux
+export module foo:bar baz(.);	// { dg-error "'\\\.' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-18.C.jj	2024-10-29 15:48:52.688231371 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-18.C	2024-10-29 15:49:33.722660988 +0100
@@ -0,0 +1,7 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz(x) x qux.garply
+export module foo.bar baz(:);	// { dg-error "':' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-19.C.jj	2024-10-29 15:48:55.852187394 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-19.C	2024-10-29 15:49:50.529427372 +0100
@@ -0,0 +1,8 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz(x)
+#define qux(x)
+export module foo.bar baz(.) . garply qux(:);	// { dg-error "'\\\.' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/cpp-20.C.jj	2024-10-29 15:48:58.555149822 +0100
+++ gcc/testsuite/g++.dg/modules/cpp-20.C	2024-10-29 15:50:04.487233357 +0100
@@ -0,0 +1,8 @@ 
+// { dg-do preprocess }
+// { dg-additional-options "-fmodules-ts" }
+
+#define baz(x)
+#define qux(x)
+export module foo.bar baz(:) : garply qux(.);	// { dg-error "':' in module name or partition comes from or after macro expansion" }
+
+int i;
--- gcc/testsuite/g++.dg/modules/pmp-4.C.jj	2024-10-29 13:53:15.251615799 +0100
+++ gcc/testsuite/g++.dg/modules/pmp-4.C	2024-10-29 13:53:15.251615799 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options -fmodules-ts }
+
+export module bob;
+// { dg-module-cmi bob }
+
+#define PRIVATE private
+
+module :PRIVATE; // { dg-message "sorry, unimplemented: private module fragment" }
+int i;
--- gcc/testsuite/g++.dg/modules/pmp-5.C.jj	2024-10-29 13:53:15.251615799 +0100
+++ gcc/testsuite/g++.dg/modules/pmp-5.C	2024-10-29 13:53:15.251615799 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options -fmodules-ts }
+
+export module bob;
+// { dg-module-cmi bob }
+
+#define PRIVATE_SEMI private ;
+
+module :PRIVATE_SEMI // { dg-message "sorry, unimplemented: private module fragment" }
+int i;
--- gcc/testsuite/g++.dg/modules/pmp-6.C.jj	2024-10-29 13:53:15.251615799 +0100
+++ gcc/testsuite/g++.dg/modules/pmp-6.C	2024-10-29 13:53:15.251615799 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options -fmodules-ts }
+
+export module bob;
+// { dg-module-cmi bob }
+
+#define SEMI ;
+
+module :private SEMI // { dg-message "sorry, unimplemented: private module fragment" }
+int i;
--- gcc/testsuite/g++.dg/modules/token-6.C.jj	2024-10-29 13:53:15.251615799 +0100
+++ gcc/testsuite/g++.dg/modules/token-6.C	2024-10-29 14:44:23.202208319 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred
+export module bob;
+// { dg-error "module name 'bob' cannot be an object-like macro" "" { target *-*-* } .-1 }
+
+// { dg-module-cmi !bob }
+// { dg-module-cmi !fred }
+// { dg-prune-output "not writing module" }
--- gcc/testsuite/g++.dg/modules/token-7.C.jj	2024-10-29 13:53:15.251615799 +0100
+++ gcc/testsuite/g++.dg/modules/token-7.C	2024-10-29 14:45:00.891687054 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred
+export module foo.bar.bob;
+// { dg-error "module name 'bob' cannot be an object-like macro" "" { target *-*-* } .-1 }
+
+// { dg-module-cmi !foo.bar.bob }
+// { dg-module-cmi !foo.bar.fred }
+// { dg-prune-output "not writing module" }
--- gcc/testsuite/g++.dg/modules/token-8.C.jj	2024-10-29 13:53:15.252615786 +0100
+++ gcc/testsuite/g++.dg/modules/token-8.C	2024-10-29 14:45:31.273266856 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob fred
+export module foo.bar:bob;
+// { dg-error "module partition 'bob' cannot be an object-like macro" "" { target *-*-* } .-1 }
+
+// { dg-module-cmi !foo.bar:bob }
+// { dg-module-cmi !foo.bar:fred }
+// { dg-prune-output "not writing module" }
--- gcc/testsuite/g++.dg/modules/token-9.C.jj	2024-10-29 13:53:15.252615786 +0100
+++ gcc/testsuite/g++.dg/modules/token-9.C	2024-10-29 14:44:11.577369096 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define garply fred
+export module foo.bar:baz.garply;
+// { dg-error "module partition 'garply' cannot be an object-like macro" "" { target *-*-* } .-1 }
+
+// { dg-module-cmi !foo.bar:baz.garply }
+// { dg-module-cmi !foo.bar:baz.fred }
+// { dg-prune-output "not writing module" }
--- gcc/testsuite/g++.dg/modules/token-10.C.jj	2024-10-29 13:53:15.252615786 +0100
+++ gcc/testsuite/g++.dg/modules/token-10.C	2024-10-29 13:53:15.252615786 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define semi ;
+export module foo.bar:baz.bob semi
+
+// { dg-module-cmi foo.bar:baz.bob }
--- gcc/testsuite/g++.dg/modules/token-11.C.jj	2024-10-29 13:53:15.252615786 +0100
+++ gcc/testsuite/g++.dg/modules/token-11.C	2024-10-29 13:53:15.252615786 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define attr [[]]
+export module foo.bar:baz.bob attr ;
+
+// { dg-module-cmi foo.bar:baz.bob }
--- gcc/testsuite/g++.dg/modules/token-12.C.jj	2024-10-29 13:53:15.252615786 +0100
+++ gcc/testsuite/g++.dg/modules/token-12.C	2024-10-29 13:53:15.252615786 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob() fred
+export module bob;
+
+// { dg-module-cmi bob }
--- gcc/testsuite/g++.dg/modules/token-13.C.jj	2024-10-29 13:53:15.253615772 +0100
+++ gcc/testsuite/g++.dg/modules/token-13.C	2024-10-29 13:53:15.253615772 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob() fred
+export module foo.bar.bob;
+
+// { dg-module-cmi foo.bar.bob }
--- gcc/testsuite/g++.dg/modules/token-14.C.jj	2024-10-29 13:53:15.253615772 +0100
+++ gcc/testsuite/g++.dg/modules/token-14.C	2024-10-29 13:53:15.253615772 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob(n) fred
+export module foo.bar:bob;
+
+// { dg-module-cmi foo.bar:bob }
--- gcc/testsuite/g++.dg/modules/token-15.C.jj	2024-10-29 13:53:15.253615772 +0100
+++ gcc/testsuite/g++.dg/modules/token-15.C	2024-10-29 13:53:15.253615772 +0100
@@ -0,0 +1,6 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob() fred
+export module foo.bar:baz.bob;
+
+// { dg-module-cmi foo.bar:baz.bob }
--- gcc/testsuite/g++.dg/modules/token-16.C.jj	2024-10-29 13:53:15.253615772 +0100
+++ gcc/testsuite/g++.dg/modules/token-16.C	2024-10-29 14:44:38.975990154 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options "-fmodules-ts" }
+
+#define bob() fred
+export module foo.bar:baz.bob ();
+// { dg-error "module partition followed by '\\\('" "" { target *-*-* } .-1 }
+// { dg-error "expected" "" { target *-*-* } .-2 }
+
+// { dg-module-cmi !foo.bar:baz.bob }
+// { dg-module-cmi !foo.bar:baz.fred }
--- gcc/testsuite/g++.dg/modules/dir-only-3.C.jj	2020-12-23 14:09:58.847107398 +0100
+++ gcc/testsuite/g++.dg/modules/dir-only-3.C	2024-10-29 14:44:48.230862156 +0100
@@ -7,10 +7,12 @@ 
 # 32 "<command-line>" 2
 # 1 "dir-only-3.C"
 // { dg-additional-options {-fmodules-ts -fpreprocessed -fdirectives-only} }
-// { dg-module-cmi foo }
+// { dg-module-cmi !foo }
 module;
 #define foo baz
 export module foo;
+// { dg-error "module name 'foo' cannot be an object-like macro" "" { target *-*-* } 5 }
+// { dg-prune-output "not writing module" }
 
 class import {};
 
--- gcc/testsuite/g++.dg/modules/dir-only-4.C.jj	2024-08-30 09:09:46.603609137 +0200
+++ gcc/testsuite/g++.dg/modules/dir-only-4.C	2024-10-29 15:33:01.032477847 +0100
@@ -2,7 +2,7 @@ 
 // { dg-module-cmi !foo }
 module;
 #define foo baz
-export module foo;
+export module foo; // { dg-error "module name 'foo' cannot be an object-like macro" }
 
 class import {};
 
--- gcc/testsuite/g++.dg/modules/dir-only-5.C.jj	2024-10-29 15:18:50.438386316 +0100
+++ gcc/testsuite/g++.dg/modules/dir-only-5.C	2024-10-29 13:53:15.282615367 +0100
@@ -0,0 +1,9 @@ 
+// { dg-additional-options {-fmodules-ts -fpreprocessed -fdirectives-only} }
+// { dg-module-cmi !baz }
+module;
+#define foo baz
+export module baz;
+
+class import {};
+
+import x; // { dg-error "post-module-declaration" }
--- gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C.jj	2020-12-23 14:09:58.845107420 +0100
+++ gcc/testsuite/g++.dg/modules/atom-preamble-2_a.C	2024-10-29 13:53:15.298615144 +0100
@@ -1,6 +1,6 @@ 
 // { dg-additional-options "-fmodules-ts" }
 #define malcolm kevin
-export module malcolm;
+export module kevin;
 // { dg-module-cmi kevin }
 
 export class X;
--- gcc/testsuite/g++.dg/modules/atom-preamble-4.C.jj	2020-12-23 14:09:58.845107420 +0100
+++ gcc/testsuite/g++.dg/modules/atom-preamble-4.C	2024-10-29 15:31:55.292393604 +0100
@@ -3,3 +3,5 @@ 
 
 export module NAME(bob)
 
+// { dg-error "module name followed by '\\\('" "" { target *-*-* } .-2 }
+// { dg-error "expected ';' before '\\\(' token" "" { target *-*-* } .-3 }
--- gcc/testsuite/g++.dg/modules/atom-preamble-5.C.jj	2024-10-29 15:17:29.641522799 +0100
+++ gcc/testsuite/g++.dg/modules/atom-preamble-5.C	2024-10-29 13:53:15.299615130 +0100
@@ -0,0 +1,5 @@ 
+// { dg-additional-options "-fmodules-ts" }
+#define NAME(X) ;
+
+export module bob NAME(bob)
+