diff mbox series

Handle :: tokens in C for C2x

Message ID alpine.DEB.2.21.1910020008410.24964@digraph.polyomino.org.uk
State New
Headers show
Series Handle :: tokens in C for C2x | expand

Commit Message

Joseph Myers Oct. 2, 2019, 12:10 a.m. UTC
As part of adding [[]]-style attributes, C2x adds the token :: for use
in scoped attribute names.

This patch adds corresponding support for that token in C to GCC.  The
token is supported both for C2x and for older gnu* standards (on the
basis that extensions are normally supported in older gnu* versions;
people will expect to be able to use [[]] attributes, before C2x is
the default, without needing to use -std=gnu2x).

There are no cases in older C standards where the token : can be
followed by a token starting with : in syntactically valid sources;
the only cases the :: token could break in older standard C thus are
ones involving concatenation of pp-tokens where the result does not
end up as tokens (e.g., gets stringized).  In GNU C extensions, the
main case where :: might appear in existing sources is in asm
statements, and the C parser is thus made to handle it like two
consecutive : tokens, which the C++ parser already does.  A limited
test of various positionings of :: in asm statements is added to the
testsuite (in particular, to cover the syntax error when :: means too
many colons but a single : would be OK), but existing tests cover a
variety of styles there anyway.

Technically there are cases in Objective-C and OpenMP for which this
also changes how previously valid code is lexed: the objc-selector-arg
syntax allows multiple consecutive : tokens (although I don't think
they are particularly useful there), while OpenMP syntax includes
array section syntax such as [:] which, before :: was a token, could
also be written as [::> (there might be other OpenMP cases potentially
affected, I didn't check all the OpenMP syntax in detail).  I don't
think either of those cases affects the basis for supporting the ::
token in all -std=gnu* modes, or that there is any obvious need to
special-case handling of CPP_SCOPE tokens for those constructs the way
there is for asm statements.

cpp_avoid_paste, which determines when spaces need adding between
tokens in preprocessed output where there wouldn't otherwise be
whitespace between them (e.g. if stringized), already inserts space
between : and : unconditionally, rather than only for C++, so no
change is needed there (but a C2x test is added that such space is
indeed inserted).

Bootstrapped with no regressions on x86-64-pc-linux-gnu.  Applied to
mainline.

gcc/c:
2019-10-02  Joseph Myers  <joseph@codesourcery.com>

	* c-parser.c (c_parser_asm_statement): Handle CPP_SCOPE like two
	CPP_COLON tokens.

gcc/testsuite:
2019-10-02  Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/asm-scope-1.c, gcc.dg/cpp/c11-scope-1.c,
	gcc.dg/cpp/c17-scope-1.c, gcc.dg/cpp/c2x-scope-1.c,
	gcc.dg/cpp/c2x-scope-2.c, gcc.dg/cpp/c90-scope-1.c,
	gcc.dg/cpp/c94-scope-1.c, gcc.dg/cpp/c99-scope-1.c,
	gcc.dg/cpp/gnu11-scope-1.c, gcc.dg/cpp/gnu17-scope-1.c,
	gcc.dg/cpp/gnu89-scope-1.c, gcc.dg/cpp/gnu99-scope-1.c: New tests.

libcpp:
2019-10-02  Joseph Myers  <joseph@codesourcery.com>

	* include/cpplib.h (struct cpp_options): Add member scope.
	* init.c (struct lang_flags, lang_defaults): Likewise.
	(cpp_set_lang): Set scope member of pfile.
	* lex.c (_cpp_lex_direct): Test CPP_OPTION (pfile, scope) not
	CPP_OPTION (pfile, cplusplus) for creating CPP_SCOPE tokens.
diff mbox series

Patch

Index: gcc/c/c-parser.c
===================================================================
--- gcc/c/c-parser.c	(revision 276414)
+++ gcc/c/c-parser.c	(working copy)
@@ -6411,8 +6411,10 @@ 
 
    The form with asm-goto-operands is valid if and only if the
    asm-qualifier-list contains goto, and is the only allowed form in that case.
-   Duplicate asm-qualifiers are not allowed.  */
+   Duplicate asm-qualifiers are not allowed.
 
+   The :: token is considered equivalent to two consecutive : tokens.  */
+
 static tree
 c_parser_asm_statement (c_parser *parser)
 {
@@ -6509,11 +6511,21 @@ 
   nsections = 3 + is_goto;
   for (section = 0; section < nsections; ++section)
     {
-      if (!c_parser_require (parser, CPP_COLON,
-			     is_goto
-			     ? G_("expected %<:%>")
-			     : G_("expected %<:%> or %<)%>"),
-			     UNKNOWN_LOCATION, is_goto))
+      if (c_parser_next_token_is (parser, CPP_SCOPE))
+	{
+	  ++section;
+	  if (section == nsections)
+	    {
+	      c_parser_error (parser, "expected %<)%>");
+	      goto error_close_paren;
+	    }
+	  c_parser_consume_token (parser);
+	}
+      else if (!c_parser_require (parser, CPP_COLON,
+				  is_goto
+				  ? G_("expected %<:%>")
+				  : G_("expected %<:%> or %<)%>"),
+				  UNKNOWN_LOCATION, is_goto))
 	goto error_close_paren;
 
       /* Once past any colon, we're no longer a simple asm.  */
@@ -6520,6 +6532,7 @@ 
       simple = false;
 
       if ((!c_parser_next_token_is (parser, CPP_COLON)
+	   && !c_parser_next_token_is (parser, CPP_SCOPE)
 	   && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	  || section == 3)
 	switch (section)
Index: gcc/testsuite/gcc.dg/asm-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/asm-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/asm-scope-1.c	(working copy)
@@ -0,0 +1,27 @@ 
+/* Test :: token handling in asm.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x" } */
+
+void
+f (void)
+{
+  asm ("");
+  asm ("" : );
+  asm ("" : :);
+  asm ("" ::);
+  asm ("" : : :);
+  asm ("" :: :);
+  asm ("" : ::);
+  asm goto ("" : : : : lab);
+  asm goto ("" :: : : lab);
+  asm goto ("" : :: : lab);
+  asm goto ("" : : :: lab);
+  asm goto ("" :: :: lab);
+ lab: ;
+  /* Test errors when :: is at the end of asm and only one : allowed.  */
+  asm ("" : : ::); /* { dg-error "expected" } */
+  asm ("" :: ::); /* { dg-error "expected" } */
+  asm goto ("" : : : :: lab); /* { dg-error "expected" } */
+  asm goto ("" :: : :: lab); /* { dg-error "expected" } */
+  asm goto ("" : :: :: lab); /* { dg-error "expected" } */
+}
Index: gcc/testsuite/gcc.dg/cpp/c11-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c11-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c11-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token not in C11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :) /* { dg-error "does not give a valid preprocessing token" } */
+CONCAT (::, >)
Index: gcc/testsuite/gcc.dg/cpp/c17-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c17-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c17-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token not in C17.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c17 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :) /* { dg-error "does not give a valid preprocessing token" } */
+CONCAT (::, >)
Index: gcc/testsuite/gcc.dg/cpp/c2x-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c2x-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c2x-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token in C2x.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :)
+CONCAT (::, >) /* { dg-error "does not give a valid preprocessing token" } */
Index: gcc/testsuite/gcc.dg/cpp/c2x-scope-2.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c2x-scope-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c2x-scope-2.c	(working copy)
@@ -0,0 +1,11 @@ 
+/* Test :: token in C2x: preprocessed output.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c2x -pedantic-errors -P" } */
+
+#define COLON() :
+#define TEST() ABC
+
+/* This must have a space inserted between the two ':' tokens in
+   preprocessed output.  */
+TEST()COLON()COLON()TEST()
+/* { dg-final { scan-file c2x-scope-2.i "ABC: :ABC" } } */
Index: gcc/testsuite/gcc.dg/cpp/c90-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c90-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c90-scope-1.c	(working copy)
@@ -0,0 +1,7 @@ 
+/* Test :: token not in C90.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c90 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :) /* { dg-error "does not give a valid preprocessing token" } */
Index: gcc/testsuite/gcc.dg/cpp/c94-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c94-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c94-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token not in C94.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=iso9899:199409 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :) /* { dg-error "does not give a valid preprocessing token" } */
+CONCAT (::, >)
Index: gcc/testsuite/gcc.dg/cpp/c99-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/c99-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/c99-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token not in C99.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=c99 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :) /* { dg-error "does not give a valid preprocessing token" } */
+CONCAT (::, >)
Index: gcc/testsuite/gcc.dg/cpp/gnu11-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/gnu11-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/gnu11-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token in gnu11.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu11 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :)
+CONCAT (::, >) /* { dg-error "does not give a valid preprocessing token" } */
Index: gcc/testsuite/gcc.dg/cpp/gnu17-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/gnu17-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/gnu17-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token in gnu17.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu17 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :)
+CONCAT (::, >) /* { dg-error "does not give a valid preprocessing token" } */
Index: gcc/testsuite/gcc.dg/cpp/gnu89-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/gnu89-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/gnu89-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token in gnu89.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu89 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :)
+CONCAT (::, >) /* { dg-error "does not give a valid preprocessing token" } */
Index: gcc/testsuite/gcc.dg/cpp/gnu99-scope-1.c
===================================================================
--- gcc/testsuite/gcc.dg/cpp/gnu99-scope-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/cpp/gnu99-scope-1.c	(working copy)
@@ -0,0 +1,8 @@ 
+/* Test :: token in gnu99.  */
+/* { dg-do preprocess } */
+/* { dg-options "-std=gnu99 -pedantic-errors" } */
+
+#define CONCAT(x, y) x ## y
+
+CONCAT (:, :)
+CONCAT (::, >) /* { dg-error "does not give a valid preprocessing token" } */
Index: libcpp/include/cpplib.h
===================================================================
--- libcpp/include/cpplib.h	(revision 276414)
+++ libcpp/include/cpplib.h	(working copy)
@@ -483,6 +483,9 @@ 
   /* Nonzero for C++2a __VA_OPT__ feature.  */
   unsigned char va_opt;
 
+  /* Nonzero for the '::' token.  */
+  unsigned char scope;
+
   /* Holds the name of the target (execution) character set.  */
   const char *narrow_charset;
 
Index: libcpp/init.c
===================================================================
--- libcpp/init.c	(revision 276414)
+++ libcpp/init.c	(working copy)
@@ -92,32 +92,33 @@ 
   char trigraphs;
   char utf8_char_literals;
   char va_opt;
+  char scope;
 };
 
 static const struct lang_flags lang_defaults[] =
-{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt */
-  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1 },
-  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
-  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
-  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
-  /* GNUC2X   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1 },
-  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0,      0 },
-  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
-  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
-  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0 },
-  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0 },
-  /* STDC2X   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0 },
-  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1 },
-  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0 },
-  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0,      1 },
-  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0,      0 },
-  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0,      1 },
-  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0 },
-  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
-  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      0 },
-  /* GNUCXX2A */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
-  /* CXX2A    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1 },
-  /* ASM      */  { 0,  0,  1,  0,  0,  0,  0,   0,   0,   0,    0,     0,     0,   0,      0 }
+{ /*              c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope*/
+  /* GNUC89   */  { 0,  0,  1,  0,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1 },
+  /* GNUC99   */  { 1,  0,  1,  1,  0,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1 },
+  /* GNUC11   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1 },
+  /* GNUC17   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1 },
+  /* GNUC2X   */  { 1,  0,  1,  1,  1,  0,  1,   1,   1,   0,    0,     0,     0,   0,      1,   1 },
+  /* STDC89   */  { 0,  0,  0,  0,  0,  1,  0,   0,   0,   0,    0,     0,     1,   0,      0,   0 },
+  /* STDC94   */  { 0,  0,  0,  0,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0 },
+  /* STDC99   */  { 1,  0,  1,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   0 },
+  /* STDC11   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0 },
+  /* STDC17   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   0 },
+  /* STDC2X   */  { 1,  0,  1,  1,  1,  1,  1,   1,   0,   0,    0,     0,     1,   0,      0,   1 },
+  /* GNUCXX   */  { 0,  1,  1,  1,  0,  0,  1,   0,   0,   0,    0,     0,     0,   0,      1,   1 },
+  /* CXX98    */  { 0,  1,  0,  1,  0,  1,  1,   0,   0,   0,    0,     0,     1,   0,      0,   1 },
+  /* GNUCXX11 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    0,     0,     0,   0,      1,   1 },
+  /* CXX11    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    0,     0,     1,   0,      0,   1 },
+  /* GNUCXX14 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   0,      1,   1 },
+  /* CXX14    */  { 1,  1,  0,  1,  1,  1,  1,   1,   1,   1,    1,     1,     1,   0,      0,   1 },
+  /* GNUCXX17 */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1 },
+  /* CXX17    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      0,   1 },
+  /* GNUCXX2A */  { 1,  1,  1,  1,  1,  0,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1 },
+  /* CXX2A    */  { 1,  1,  1,  1,  1,  1,  1,   1,   1,   1,    1,     1,     0,   1,      1,   1 },
+  /* ASM      */  { 0,  0,  1,  0,  0,  0,  0,   0,   0,   0,    0,     0,     0,   0,      0,   0 }
 };
 
 /* Sets internal flags correctly for a given language.  */
@@ -143,6 +144,7 @@ 
   CPP_OPTION (pfile, trigraphs)			 = l->trigraphs;
   CPP_OPTION (pfile, utf8_char_literals)	 = l->utf8_char_literals;
   CPP_OPTION (pfile, va_opt)			 = l->va_opt;
+  CPP_OPTION (pfile, scope)			 = l->scope;
 }
 
 /* Initialize library global state.  */
Index: libcpp/lex.c
===================================================================
--- libcpp/lex.c	(revision 276414)
+++ libcpp/lex.c	(working copy)
@@ -3104,7 +3104,7 @@ 
 
     case ':':
       result->type = CPP_COLON;
-      if (*buffer->cur == ':' && CPP_OPTION (pfile, cplusplus))
+      if (*buffer->cur == ':' && CPP_OPTION (pfile, scope))
 	buffer->cur++, result->type = CPP_SCOPE;
       else if (*buffer->cur == '>' && CPP_OPTION (pfile, digraphs))
 	{