Skip to content

Commit

Permalink
[Clang][Sema] Prohibit statement expression in the default argument
Browse files Browse the repository at this point in the history
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 #53488

Differential Revision: https://reviews.llvm.org/D119609
  • Loading branch information
junaire committed Apr 6, 2022
1 parent c32f8f3 commit 8a4d388
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 21 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/llvm/llvm-project/issues/54462>`_.
- Statement expressions are now disabled in default arguments in general.
This fixes Issue `Issue 53488 <https://github.com/llvm/llvm-project/issues/53488>`_.

Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 17 additions & 11 deletions clang/lib/Parse/ParseTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down
29 changes: 29 additions & 0 deletions clang/test/Sema/stmt-expr-in-default-arg.cpp
Original file line number Diff line number Diff line change
@@ -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 = []<int = ({ 1; })>(){}; // 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 <int foo = ({ 1; })> // expected-error {{default non-type template argument may not use a GNU statement expression}}
void f() {}

template <int bar = ({ 1; })> // expected-error {{default non-type template argument may not use a GNU statement expression}}
class S {};

template <typename Callable>
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);
}
10 changes: 1 addition & 9 deletions clang/test/SemaTemplate/dependent-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,7 @@ namespace PR45083 {
using U = float; // expected-error {{different types ('float' vs 'decltype(g<int>())' (aka 'void'))}}

void h(auto a, decltype(g<char>())*) {} // 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 {
Expand Down

0 comments on commit 8a4d388

Please sign in to comment.