From 8a4d388c7fa47fa50076babecc38757009dbc987 Mon Sep 17 00:00:00 2001 From: Jun Zhang Date: Wed, 6 Apr 2022 08:45:46 +0800 Subject: [PATCH] [Clang][Sema] Prohibit statement expression in the default argument As statement expression makes no sense in the default argument, this patch tries to disable it in the all cases. Please note that the statement expression is a GNU extension, which means that Clang should be consistent with GCC. However, there's no response from GCC devs since we have raised the issue for several weeks. In this case, I think we can disallow statement expressions as a default parameter in general for now, and relax the restriction if GCC folks decide to retain the feature for functions but not lambdas in the future. Related discussion: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104765 Fixes https://github.com/llvm/llvm-project/issues/53488 Differential Revision: https://reviews.llvm.org/D119609 --- clang/docs/ReleaseNotes.rst | 2 ++ .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/lib/Parse/ParseDecl.cpp | 10 ++++++- clang/lib/Parse/ParseTemplate.cpp | 28 +++++++++++------- clang/test/Sema/stmt-expr-in-default-arg.cpp | 29 +++++++++++++++++++ clang/test/SemaTemplate/dependent-expr.cpp | 10 +------ 6 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 clang/test/Sema/stmt-expr-in-default-arg.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 86d0a3e7303f..bf592329ac02 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -97,6 +97,8 @@ Bug Fixes - The builtin function __builtin_dump_struct would crash clang when the target struct contains a bitfield. It now correctly handles bitfields. This fixes Issue `Issue 54462 `_. +- Statement expressions are now disabled in default arguments in general. + This fixes Issue `Issue 53488 `_. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c5171359e7e4..96cd00c8bb2a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4380,6 +4380,9 @@ def err_uninitialized_member_in_ctor : Error< def err_default_arg_makes_ctor_special : Error< "addition of default argument on redeclaration makes this constructor a " "%select{default|copy|move}0 constructor">; +def err_stmt_expr_in_default_arg : Error< + "default %select{argument|non-type template argument}0 may not use a GNU " + "statement expression">; def err_use_of_default_argument_to_function_declared_later : Error< "use of default argument to function %0 that is declared later in class %1">; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f07758197205..627b11fb6ac0 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7066,8 +7066,16 @@ void Parser::ParseParameterDeclarationClause( if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); DefArgResult = ParseBraceInitializer(); - } else + } else { + if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) { + Diag(Tok, diag::err_stmt_expr_in_default_arg) << 0; + Actions.ActOnParamDefaultArgumentError(Param, EqualLoc); + // Skip the statement expression and continue parsing + SkipUntil(tok::comma, StopBeforeMatch); + continue; + } DefArgResult = ParseAssignmentExpression(); + } DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult); if (DefArgResult.isInvalid()) { Actions.ActOnParamDefaultArgumentError(Param, EqualLoc); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 211d1a33542b..5687882251a0 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -19,6 +19,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -1007,18 +1008,23 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { SourceLocation EqualLoc; ExprResult DefaultArg; if (TryConsumeToken(tok::equal, EqualLoc)) { - // C++ [temp.param]p15: - // When parsing a default template-argument for a non-type - // template-parameter, the first non-nested > is taken as the - // end of the template-parameter-list rather than a greater-than - // operator. - GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); - EnterExpressionEvaluationContext ConstantEvaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - - DefaultArg = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); - if (DefaultArg.isInvalid()) + if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) { + Diag(Tok.getLocation(), diag::err_stmt_expr_in_default_arg) << 1; SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); + } else { + // C++ [temp.param]p15: + // When parsing a default template-argument for a non-type + // template-parameter, the first non-nested > is taken as the + // end of the template-parameter-list rather than a greater-than + // operator. + GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + DefaultArg = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + if (DefaultArg.isInvalid()) + SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); + } } // Create the parameter. diff --git a/clang/test/Sema/stmt-expr-in-default-arg.cpp b/clang/test/Sema/stmt-expr-in-default-arg.cpp new file mode 100644 index 000000000000..85bb8aa42899 --- /dev/null +++ b/clang/test/Sema/stmt-expr-in-default-arg.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++20 + +void foo() { + void fn(int i, int = ({ 1; })); // expected-error {{default argument may not use a GNU statement expression}} + + auto a = [](int = ({ 1; })) {}; // expected-error {{default argument may not use a GNU statement expression}} + + auto b = [](){}; // expected-error {{default non-type template argument may not use a GNU statement expression}} + + void fn(int i, int j = ({{}, {}, {,}}), int k = ""); // expected-error {{default argument may not use a GNU statement expression}} expected-error {{cannot initialize a parameter of type 'int' with an lvalue of type 'const char[1]'}} expected-note {{passing argument to parameter 'k' here}} +} + +template // expected-error {{default non-type template argument may not use a GNU statement expression}} +void f() {} + +template // expected-error {{default non-type template argument may not use a GNU statement expression}} +class S {}; + +template +int bar(Callable &&Call) { + return Call(); +} + +int baz() { + auto l = [](int a = ({ int x = 12; x; })) { // expected-error {{default argument may not use a GNU statement expression}} + return 1; + }; + return bar(l); +} diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp index abdb8e9c4a9f..51bd375d7920 100644 --- a/clang/test/SemaTemplate/dependent-expr.cpp +++ b/clang/test/SemaTemplate/dependent-expr.cpp @@ -141,15 +141,7 @@ namespace PR45083 { using U = float; // expected-error {{different types ('float' vs 'decltype(g())' (aka 'void'))}} void h(auto a, decltype(g())*) {} // expected-note {{previous}} - void h(auto a, void*) {} // expected-error {{redefinition}} - - void i(auto a) { - [](auto a, int = ({decltype(a) i; i * 2;})){}(a); // expected-error {{invalid operands to binary expression ('decltype(a)' (aka 'void *') and 'int')}} expected-note {{in instantiation of}} - } - void use_i() { - i(0); - i((void*)0); // expected-note {{instantiation of}} - } + void h(auto a, void *) {} // expected-error {{redefinition}} } namespace BindingInStmtExpr {