// RUN: %clang_cc1 -std=c++17 %s -verify // RUN: %clang_cc1 -std=c++20 %s -verify // RUN: %clang_cc1 -std=c++20 %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++20 %s -E -o - | FileCheck %s --check-prefix=CXX20 namespace N { struct A {}; void operator<=(A, A); #if __cplusplus > 201703L void operator<=>(A, A); #ifdef COMPAT // expected-warning@-2 {{'<=>' operator is incompatible with C++ standards before C++20}} #endif #endif template struct X {}; X #if __cplusplus <= 201703L // expected-warning@-2 {{'<=>' is a single token in C++20; add a space to avoid a change in behavior}} #else > #endif #ifdef COMPAT // expected-warning@-7 {{'<=>' operator is incompatible with C++ standards before C++20}} #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{{'<=>'}} )]];