]> granicus.if.org Git - clang/commitdiff
[c++2a] P0515R3: lexer support for new <=> token.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 1 Dec 2017 01:07:10 +0000 (01:07 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 1 Dec 2017 01:07:10 +0000 (01:07 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@319509 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticLexKinds.td
include/clang/Basic/TokenKinds.def
lib/Lex/Lexer.cpp
lib/Lex/TokenConcatenation.cpp
test/Lexer/cxx2a-spaceship.cpp [new file with mode: 0644]

index 8bc6d77c9213a86ba870bd601434fba437e13a31..89874b50f68d5db80059da2af546bb95b7b92872 100644 (file)
@@ -31,6 +31,14 @@ def warn_cxx98_compat_less_colon_colon : Warning<
   "'<::' is treated as digraph '<:' (aka '[') followed by ':' in C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
 
+def warn_cxx17_compat_spaceship : Warning<
+  "'<=>' operator is incompatible with C++ standards before C++2a">,
+  InGroup<CXXPre2aCompat>, DefaultIgnore;
+def warn_cxx2a_compat_spaceship : Warning<
+  "'<=>' is a single token in C++2a; "
+  "add a space to avoid a change in behavior">,
+  InGroup<CXX2aCompat>;
+
 // Trigraphs.
 def trigraph_ignored : Warning<"trigraph ignored">, InGroup<Trigraphs>;
 def trigraph_ignored_block_comment : Warning<
index 9bb7b6a7d69974b4994cb56c3eacf3017f82d106..91c13244f9a00268adb70466b5da1c202757792d 100644 (file)
@@ -194,6 +194,7 @@ PUNCTUATOR(less,                "<")
 PUNCTUATOR(lessless,            "<<")
 PUNCTUATOR(lessequal,           "<=")
 PUNCTUATOR(lesslessequal,       "<<=")
+PUNCTUATOR(spaceship,           "<=>")
 PUNCTUATOR(greater,             ">")
 PUNCTUATOR(greatergreater,      ">>")
 PUNCTUATOR(greaterequal,        ">=")
index 67dcff6b71534412c674ca32f945b45c4e5517d1..b06971968fcf48a0969d492f2c66af2690897ce6 100644 (file)
@@ -3522,6 +3522,24 @@ LexNextToken:
         Kind = tok::lessless;
       }
     } else if (Char == '=') {
+      char After = getCharAndSize(CurPtr+SizeTmp, SizeTmp2);
+      if (After == '>') {
+        if (getLangOpts().CPlusPlus2a) {
+          if (!isLexingRawMode())
+            Diag(BufferPtr, diag::warn_cxx17_compat_spaceship);
+          CurPtr = ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result),
+                               SizeTmp2, Result);
+          Kind = tok::spaceship;
+          break;
+        }
+        // Suggest adding a space between the '<=' and the '>' to avoid a
+        // change in semantics if this turns up in C++ <=17 mode.
+        if (getLangOpts().CPlusPlus && !isLexingRawMode()) {
+          Diag(BufferPtr, diag::warn_cxx2a_compat_spaceship)
+            << FixItHint::CreateInsertion(
+                   getSourceLocation(CurPtr + SizeTmp, SizeTmp2), " ");
+        }
+      }
       CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
       Kind = tok::lessequal;
     } else if (LangOpts.Digraphs && Char == ':') {     // '<:' -> '['
index d1facd9c6879615e8408e3c47d4fcd32826b4e76..c1ee28dcc18554ec09e37613655989756736aa8b 100644 (file)
@@ -103,6 +103,10 @@ TokenConcatenation::TokenConcatenation(Preprocessor &pp) : PP(pp) {
   if (PP.getLangOpts().CPlusPlus1z)
     TokenInfo[tok::utf8_char_constant] |= aci_custom;
 
+  // These tokens have custom code in C++2a mode.
+  if (PP.getLangOpts().CPlusPlus2a)
+    TokenInfo[tok::lessequal ] |= aci_custom_firstchar;
+
   // These tokens change behavior if followed by an '='.
   TokenInfo[tok::amp         ] |= aci_avoid_equal;           // &=
   TokenInfo[tok::plus        ] |= aci_avoid_equal;           // +=
@@ -283,5 +287,7 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
     return FirstChar == '#' || FirstChar == '@' || FirstChar == '%';
   case tok::arrow:           // ->*
     return PP.getLangOpts().CPlusPlus && FirstChar == '*';
+  case tok::lessequal:       // <=> (C++2a)
+    return PP.getLangOpts().CPlusPlus2a && FirstChar == '>';
   }
 }
diff --git a/test/Lexer/cxx2a-spaceship.cpp b/test/Lexer/cxx2a-spaceship.cpp
new file mode 100644 (file)
index 0000000..f407937
--- /dev/null
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++17 %s -verify
+// RUN: %clang_cc1 -std=c++2a %s -verify
+// RUN: %clang_cc1 -std=c++2a %s -verify -Wc++17-compat -DCOMPAT
+//
+// RUN: %clang_cc1 -std=c++17 %s -E -o - | FileCheck %s --check-prefix=CXX17
+// RUN: %clang_cc1 -std=c++2a %s -E -o - | FileCheck %s --check-prefix=CXX20
+
+namespace N {
+
+struct A {};
+void operator<=(A, A);
+#if __cplusplus > 201703L
+void operator<=>(A, A); // expected-error {{}}
+#ifdef COMPAT
+// expected-warning@-2 {{'<=>' operator is incompatible with C++ standards before C++2a}}
+#endif
+#endif
+
+template<auto> struct X {};
+X<operator<=>
+#if __cplusplus <= 201703L
+  // expected-warning@-2 {{'<=>' is a single token in C++2a; add a space to avoid a change in behavior}}
+#else
+  > // expected-error@-4 {{}}
+#endif
+#ifdef COMPAT
+// expected-warning@-7 {{'<=>' operator is incompatible with C++ standards before C++2a}}
+#endif
+  x;
+}
+
+// <=> can be formed by pasting other comparison operators.
+#if __cplusplus > 201703L
+#define STR(x) #x
+#define STR_EXPANDED(x) STR(x)
+#define PASTE(x, y) x ## y
+constexpr char a[] = STR_EXPANDED(PASTE(<, =>));
+constexpr char b[] = STR_EXPANDED(PASTE(<=, >));
+static_assert(__builtin_strcmp(a, "<=>") == 0);
+static_assert(__builtin_strcmp(b, "<=>") == 0);
+#endif
+
+// -E must not accidentally form a <=> token.
+
+// CXX17: preprocess1: < =>
+// CXX17: preprocess2: <=>
+// CXX17: preprocess3: < =>
+// CXX17: preprocess4: <=>=
+// CXX17: preprocess5: <=>>
+// CXX17: preprocess6: <=>>=
+// CXX17: preprocess7: <=>
+// CXX17: preprocess8: <=>=
+//
+// CXX20: preprocess1: < =>
+// CXX20: preprocess2: <= >
+// CXX20: preprocess3: < =>
+// CXX20: preprocess4: <= >=
+// CXX20: preprocess5: <= >>
+// CXX20: preprocess6: <= >>=
+// CXX20: preprocess7: <=>
+// CXX20: preprocess8: <=>=
+
+#define ID(x) x
+[[some_vendor::some_attribute( // expected-warning {{unknown attribute}}
+preprocess1: ID(<)ID(=>),
+preprocess2: ID(<=)ID(>),
+preprocess3: ID(<)ID(=)ID(>),
+preprocess4: ID(<=)ID(>=),
+preprocess5: ID(<=)ID(>>),
+preprocess6: ID(<=)ID(>>=),
+preprocess7: ID(<=>) // expected-warning 0-1{{'<=>'}}
+preprocess8: ID(<=>=) // expected-warning 0-1{{'<=>'}}
+)]];