From: Richard Smith Date: Fri, 1 Dec 2017 01:07:10 +0000 (+0000) Subject: [c++2a] P0515R3: lexer support for new <=> token. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=daf599b1b133881d3adb23304e08588dbd3f143a;p=clang [c++2a] P0515R3: lexer support for new <=> token. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@319509 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 8bc6d77c92..89874b50f6 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -31,6 +31,14 @@ def warn_cxx98_compat_less_colon_colon : Warning< "'<::' is treated as digraph '<:' (aka '[') followed by ':' in C++98">, InGroup, DefaultIgnore; +def warn_cxx17_compat_spaceship : Warning< + "'<=>' operator is incompatible with C++ standards before C++2a">, + InGroup, DefaultIgnore; +def warn_cxx2a_compat_spaceship : Warning< + "'<=>' is a single token in C++2a; " + "add a space to avoid a change in behavior">, + InGroup; + // Trigraphs. def trigraph_ignored : Warning<"trigraph ignored">, InGroup; def trigraph_ignored_block_comment : Warning< diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 9bb7b6a7d6..91c13244f9 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -194,6 +194,7 @@ PUNCTUATOR(less, "<") PUNCTUATOR(lessless, "<<") PUNCTUATOR(lessequal, "<=") PUNCTUATOR(lesslessequal, "<<=") +PUNCTUATOR(spaceship, "<=>") PUNCTUATOR(greater, ">") PUNCTUATOR(greatergreater, ">>") PUNCTUATOR(greaterequal, ">=") diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 67dcff6b71..b06971968f 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -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 == ':') { // '<:' -> '[' diff --git a/lib/Lex/TokenConcatenation.cpp b/lib/Lex/TokenConcatenation.cpp index d1facd9c68..c1ee28dcc1 100644 --- a/lib/Lex/TokenConcatenation.cpp +++ b/lib/Lex/TokenConcatenation.cpp @@ -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 index 0000000000..f407937c4e --- /dev/null +++ b/test/Lexer/cxx2a-spaceship.cpp @@ -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 struct X {}; +X +#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{{'<=>'}} +)]];