https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/73376
>From a53b7e23c8e37227cda1cea8d87800ca7ac432d1 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser <nikolasklau...@berlin.de> Date: Sat, 25 Nov 2023 04:00:57 +0100 Subject: [PATCH] [clang] Accept lambdas in C++03 as an extensions This is a fairly simple extension, but makes the life for people who have to support C++03 a lot easier. As a nice bonus, this also improves diagnostics, since lambdas are now properly recognized when parsing C++03 code. --- .../clang/Basic/DiagnosticParseKinds.td | 1 + clang/lib/Parse/ParseExpr.cpp | 2 +- clang/lib/Parse/ParseExprCXX.cpp | 9 +- clang/lib/Parse/ParseInit.cpp | 2 +- .../OpenMP/declare_reduction_messages.cpp | 14 +-- clang/test/OpenMP/openmp_check.cpp | 2 +- clang/test/Parser/cxx03-lambda-extension.cpp | 5 + .../test/Parser/cxx0x-lambda-expressions.cpp | 116 +++++++----------- .../Parser/objcxx-lambda-expressions-neg.mm | 5 - clang/test/ParserHLSL/group_shared.hlsl | 4 +- clang/test/SemaCXX/new-delete.cpp | 7 +- 11 files changed, 68 insertions(+), 99 deletions(-) create mode 100644 clang/test/Parser/cxx03-lambda-extension.cpp diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index b66ecf0724b1c77..89bacb0a302d10a 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1017,6 +1017,7 @@ def err_expected_lambda_body : Error<"expected body of lambda expression">; def warn_cxx98_compat_lambda : Warning< "lambda expressions are incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; +def ext_lambda : ExtWarn<"lambdas are a C++11 extension">, InGroup<CXX11>; def err_lambda_decl_specifier_repeated : Error< "%select{'mutable'|'static'|'constexpr'|'consteval'}0 cannot " "appear multiple times in a lambda declarator">; diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 897810557976151..c453d77329111f6 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1799,7 +1799,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, } goto ExpectedExpression; case tok::l_square: - if (getLangOpts().CPlusPlus11) { + if (getLangOpts().CPlusPlus) { if (getLangOpts().ObjC) { // C++11 lambda expressions and Objective-C message sends both start with a // square bracket. There are three possibilities here: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 79db094e098f8e6..2b6044f29fdd8e5 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -751,9 +751,8 @@ ExprResult Parser::ParseLambdaExpression() { /// /// If we are not looking at a lambda expression, returns ExprError(). ExprResult Parser::TryParseLambdaExpression() { - assert(getLangOpts().CPlusPlus11 - && Tok.is(tok::l_square) - && "Not at the start of a possible lambda expression."); + assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) && + "Not at the start of a possible lambda expression."); const Token Next = NextToken(); if (Next.is(tok::eof)) // Nothing else to lookup here... @@ -1271,7 +1270,9 @@ static void DiagnoseStaticSpecifierRestrictions(Parser &P, ExprResult Parser::ParseLambdaExpressionAfterIntroducer( LambdaIntroducer &Intro) { SourceLocation LambdaBeginLoc = Intro.Range.getBegin(); - Diag(LambdaBeginLoc, diag::warn_cxx98_compat_lambda); + Diag(LambdaBeginLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_lambda + : diag::ext_lambda); PrettyStackTraceLoc CrashInfo(PP.getSourceManager(), LambdaBeginLoc, "lambda expression parsing"); diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index 637f21176792b6b..423497bfcb6621a 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -35,7 +35,7 @@ bool Parser::MayBeDesignationStart() { return true; case tok::l_square: { // designator: array-designator - if (!PP.getLangOpts().CPlusPlus11) + if (!PP.getLangOpts().CPlusPlus) return true; // C++11 lambda expressions and C99 designators can be ambiguous all the diff --git a/clang/test/OpenMP/declare_reduction_messages.cpp b/clang/test/OpenMP/declare_reduction_messages.cpp index 38a5d766eeadf7d..752cc4fb05a1239 100644 --- a/clang/test/OpenMP/declare_reduction_messages.cpp +++ b/clang/test/OpenMP/declare_reduction_messages.cpp @@ -58,16 +58,10 @@ class Class2 : public Class1<T> { #pragma omp declare reduction(fun1 : long : omp_out += omp_in) initializer // expected-error {{expected '(' after 'initializer'}} #pragma omp declare reduction(fun2 : long : omp_out += omp_in) initializer { // expected-error {{expected '(' after 'initializer'}} expected-error {{expected expression}} expected-warning {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} #pragma omp declare reduction(fun3 : long : omp_out += omp_in) initializer[ -#if __cplusplus <= 199711L -// expected-error@-2 {{expected '(' after 'initializer'}} -// expected-error@-3 {{expected expression}} -// expected-warning@-4 {{extra tokens at the end of '#pragma omp declare reduction' are ignored}} -#else -// expected-error@-6 {{expected '(' after 'initializer'}} -// expected-error@-7 {{expected variable name or 'this' in lambda capture list}} -// expected-error@-8 {{expected ')'}} -// expected-note@-9 {{to match this '('}} -#endif +// expected-error@-1 {{expected '(' after 'initializer'}} +// expected-error@-2 {{expected variable name or 'this' in lambda capture list}} +// expected-error@-3 {{expected ')'}} +// expected-note@-4 {{to match this '('}} #pragma omp declare reduction(fun4 : long : omp_out += omp_in) initializer() // expected-error {{expected expression}} #pragma omp declare reduction(fun5 : long : omp_out += omp_in) initializer(temp) // expected-error {{only 'omp_priv' or 'omp_orig' variables are allowed in initializer expression}} #pragma omp declare reduction(fun6 : long : omp_out += omp_in) initializer(omp_orig // expected-error {{expected ')'}} expected-note {{to match this '('}} diff --git a/clang/test/OpenMP/openmp_check.cpp b/clang/test/OpenMP/openmp_check.cpp index 6a8dd17fc8367fe..b52ce0c066922aa 100644 --- a/clang/test/OpenMP/openmp_check.cpp +++ b/clang/test/OpenMP/openmp_check.cpp @@ -18,7 +18,7 @@ int nested(int a) { auto F = [&]() { #if __cplusplus <= 199711L // expected-warning@-2 {{'auto' type specifier is a C++11 extension}} - // expected-error@-3 {{expected expression}} + // expected-warning@-3 {{lambdas are a C++11 extension}} #endif #pragma omp parallel diff --git a/clang/test/Parser/cxx03-lambda-extension.cpp b/clang/test/Parser/cxx03-lambda-extension.cpp new file mode 100644 index 000000000000000..0118036350326bb --- /dev/null +++ b/clang/test/Parser/cxx03-lambda-extension.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++03 %s + +void func() { + []() { return 0; }; // expected-warning {{lambdas are a C++11 extension}} +} diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp index 72b315a497c0679..a786a964163e4c4 100644 --- a/clang/test/Parser/cxx0x-lambda-expressions.cpp +++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp @@ -1,10 +1,15 @@ -// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++11 -Wno-c99-designator %s -// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++20 -Wno-c99-designator %s -// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++23 -Wno-c99-designator %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected,cxx14ext,cxx17ext,cxx20ext,cxx23ext -std=c++03 -Wno-c99-designator %s -Wno-c++11-extensions +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected,cxx14ext,cxx17ext,cxx20ext,cxx23ext -std=c++11 -Wno-c99-designator %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected,cxx17ext,cxx20ext,cxx23ext -std=c++14 -Wno-c99-designator %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected,cxx20ext,cxx23ext -std=c++17 -Wno-c99-designator %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected,cxx23ext -std=c++20 -Wno-c99-designator %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify=expected -std=c++23 -Wno-c99-designator %s enum E { e }; +#if __cplusplus >= 201103L constexpr int id(int n) { return n; } +#endif class C { @@ -19,28 +24,25 @@ class C { [&,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} [=,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} [] {}; - [=] (int i) {}; - [&] (int) mutable -> void {}; - [foo,bar] () { return 3; }; - [=,&foo] () {}; - [&,foo] () {}; - [this] () {}; + [=] (int i) {}; + [&] (int) mutable -> void {}; + [foo,bar] () { return 3; }; + [=,&foo] () {}; + [&,foo] () {}; + [this] () {}; [] () -> class C { return C(); }; [] () -> enum E { return e; }; - [] -> int { return 0; }; - [] mutable -> int { return 0; }; -#if __cplusplus <= 202002L - // expected-warning@-3 {{lambda without a parameter clause is a C++23 extension}} - // expected-warning@-3 {{is a C++23 extension}} -#endif + [] -> int { return 0; }; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}} + [] mutable -> int { return 0; }; // cxx23ext-warning {{is a C++23 extension}} + [](int) -> {}; // PR13652 expected-error {{expected a type}} return 1; } void designator_or_lambda() { - typedef int T; - const int b = 0; + typedef int T; + const int b = 0; const int c = 1; int d; int a1[1] = {[b] (T()) {}}; // expected-error{{no viable conversion from '(lambda}} @@ -49,19 +51,18 @@ class C { int a4[1] = {[&b] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'const int *'}} int a5[3] = { []{return 0;}() }; int a6[1] = {[this] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'C *'}} - int a7[1] = {[d(0)] { return d; } ()}; - int a8[1] = {[d = 0] { return d; } ()}; - int a10[1] = {[id(0)] { return id; } ()}; -#if __cplusplus <= 201103L - // expected-warning@-4{{extension}} - // expected-warning@-4{{extension}} - // expected-warning@-4{{extension}} + int a7[1] = {[d(0)] { return d; } ()}; // cxx14ext-warning {{initialized lambda captures are a C++14 extension}} + int a8[1] = {[d = 0] { return d; } ()}; // cxx14ext-warning {{initialized lambda captures are a C++14 extension}} +#if __cplusplus >= 201103L + int a10[1] = {[id(0)] { return id; } ()}; // cxx14ext-warning {{initialized lambda captures are a C++14 extension}} #endif int a9[1] = {[d = 0] = 1}; // expected-error{{is not an integral constant expression}} #if __cplusplus >= 201402L // expected-note@-2{{constant expression cannot modify an object that is visible outside that expression}} #endif +#if __cplusplus >= 201103L int a11[1] = {[id(0)] = 1}; +#endif } void delete_lambda(int *p) { @@ -80,43 +81,33 @@ class C { // We support init-captures in C++11 as an extension. int z; void init_capture() { - [n(0)] () mutable -> int { return ++n; }; - [n{0}] { return; }; - [a([&b = z]{})](){}; - [n = 0] { return ++n; }; // expected-error {{captured by copy in a non-mutable}} - [n = {0}] { return; }; // expected-error {{<initializer_list>}} -#if __cplusplus <= 201103L - // expected-warning@-6{{extension}} - // expected-warning@-6{{extension}} - // expected-warning@-6{{extension}} - // expected-warning@-7{{extension}} - // expected-warning@-7{{extension}} - // expected-warning@-7{{extension}} -#endif + [n(0)] () mutable -> int { return ++n; }; // cxx14ext-warning {{initialized lambda captures are a C++14 extension}} + [n{0}] { return; }; // cxx14ext-warning {{initialized lambda captures are a C++14 extension}} + [a([&b = z]{})](){}; // cxx14ext-warning 2 {{initialized lambda captures are a C++14 extension}} + [n = 0] { return ++n; }; // expected-error {{captured by copy in a non-mutable}} + // cxx14ext-warning@-1 {{initialized lambda captures are a C++14 extension}} + [n = {0}] { return; }; // expected-error {{<initializer_list>}} + // cxx14ext-warning@-1 {{initialized lambda captures are a C++14 extension}} int x = 4; - auto y = [&r = x, x = x + 1]() -> int { -#if __cplusplus <= 201103L - // expected-warning@-2{{extension}} - // expected-warning@-3{{extension}} -#endif + auto y = [&r = x, x = x + 1]() -> int { // cxx14ext-warning 2 {{initialized lambda captures are a C++14 extension}} r += 2; return x + 2; } (); } void attributes() { - [] __attribute__((noreturn)){}; -#if __cplusplus <= 202002L - // expected-warning@-2 {{is a C++23 extension}} -#endif + [] __attribute__((noreturn)){}; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}} + []() [[]] mutable {}; // expected-error {{expected body of lambda expression}} []() [[]] {}; []() [[]] -> void {}; []() mutable [[]] -> void {}; +#if __cplusplus >= 201103L []() mutable noexcept [[]] -> void {}; +#endif // Testing GNU-style attributes on lambdas -- the attribute is specified // before the mutable specifier instead of after (unlike C++11). @@ -126,28 +117,18 @@ class C { // Testing support for P2173 on adding attributes to the declaration // rather than the type. - [][[]](){}; -#if __cplusplus <= 202002L - // expected-warning@-2 {{an attribute specifier sequence in this position is a C++23 extension}} -#endif -#if __cplusplus > 201703L - []<typename>[[]](){}; -#if __cplusplus <= 202002L - // expected-warning@-2 {{an attribute specifier sequence in this position is a C++23 extension}} -#endif -#endif - [][[]]{}; -#if __cplusplus <= 202002L - // expected-warning@-2 {{an attribute specifier sequence in this position is a C++23 extension}} -#endif + [][[]](){}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}} + + []<typename>[[]](){}; // cxx20ext-warning {{explicit template parameter list for lambdas is a C++20 extension}} + // cxx23ext-warning@-1 {{an attribute specifier sequence in this position is a C++23 extension}} + + [][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}} } void missing_parens() { - [] mutable {}; - [] noexcept {}; -#if __cplusplus <= 202002L - // expected-warning@-3 {{is a C++23 extension}} - // expected-warning@-3 {{is a C++23 extension}} + [] mutable {}; // cxx23ext-warning {{is a C++23 extension}} +#if __cplusplus >= 201103L + [] noexcept {}; // cxx23ext-warning {{is a C++23 extension}} #endif } }; @@ -165,10 +146,7 @@ struct A { }; struct S { - void mf() { A{[*this]{}}; } -#if __cplusplus < 201703L - // expected-warning@-2 {{C++17 extension}} -#endif + void mf() { A(([*this]{})); } // cxx17ext-warning {{'*this' by copy is a C++17 extension}} }; } diff --git a/clang/test/Parser/objcxx-lambda-expressions-neg.mm b/clang/test/Parser/objcxx-lambda-expressions-neg.mm index b2fe39dfbf70827..a775f52d2bf70e6 100644 --- a/clang/test/Parser/objcxx-lambda-expressions-neg.mm +++ b/clang/test/Parser/objcxx-lambda-expressions-neg.mm @@ -4,10 +4,5 @@ int main() { []{}; -#if __cplusplus <= 199711L - // expected-error@-2 {{expected expression}} -#else // expected-no-diagnostics -#endif - } diff --git a/clang/test/ParserHLSL/group_shared.hlsl b/clang/test/ParserHLSL/group_shared.hlsl index 0b9f28395ee486f..44f3a2e5b4505d0 100644 --- a/clang/test/ParserHLSL/group_shared.hlsl +++ b/clang/test/ParserHLSL/group_shared.hlsl @@ -3,8 +3,8 @@ extern groupshared float f; extern float groupshared f; // Ok, redeclaration? -// NOTE:lambda is not enabled except for hlsl202x. -// expected-error@+2 {{expected expression}} +// expected-warning@+3 {{lambdas are a C++11 extension}} +// expected-error@+2 {{expected body of lambda expression}} // expected-warning@+1 {{'auto' type specifier is a C++11 extension}} auto l = []() groupshared {}; diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 0270e42b7389fec..96122cfe87b4f71 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -165,12 +165,7 @@ void good_deletes() void bad_deletes() { delete 0; // expected-error {{cannot delete expression of type 'int'}} - delete [0] (int*)0; -#if __cplusplus <= 199711L - // expected-error@-2 {{expected expression}} -#else - // expected-error@-4 {{expected variable name or 'this' in lambda capture list}} -#endif + delete [0] (int*)0; // expected-error {{expected variable name or 'this' in lambda capture list}} delete (void*)0; // expected-warning {{cannot delete expression with pointer-to-'void' type 'void *'}} delete (T*)0; // expected-warning {{deleting pointer to incomplete type}} ::S::delete (int*)0; // expected-error {{expected unqualified-id}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits