"'<::' 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<
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 == ':') { // '<:' -> '['
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; // +=
return FirstChar == '#' || FirstChar == '@' || FirstChar == '%';
case tok::arrow: // ->*
return PP.getLangOpts().CPlusPlus && FirstChar == '*';
+ case tok::lessequal: // <=> (C++2a)
+ return PP.getLangOpts().CPlusPlus2a && FirstChar == '>';
}
}
--- /dev/null
+// 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{{'<=>'}}
+)]];