Skip to content

Commit

Permalink
[Clang] Implement CWG2358 Explicit capture of value
Browse files Browse the repository at this point in the history
Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D137172
  • Loading branch information
cor3ntin committed Nov 3, 2022
1 parent 44d8f80 commit 7429709
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 27 deletions.
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -574,6 +574,7 @@ C++ Language Changes in Clang
This means Clang will by default accept code using features from C++17 and
conforming GNU extensions. Projects incompatible with C++17 can add
``-std=gnu++14`` to their build settings to restore the previous behaviour.
- Implemented DR2358 allowing init captures in lambdas in default arguments.

C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
62 changes: 38 additions & 24 deletions clang/lib/Sema/SemaDeclCXX.cpp
Expand Up @@ -102,24 +102,31 @@ bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) {
return S.Diag(DRE->getBeginLoc(),
diag::err_param_default_argument_references_param)
<< Param->getDeclName() << DefaultArg->getSourceRange();
} else if (const auto *VDecl = dyn_cast<VarDecl>(Decl)) {
// C++ [dcl.fct.default]p7:
// Local variables shall not be used in default argument
// expressions.
//
// C++17 [dcl.fct.default]p7 (by CWG 2082):
// A local variable shall not appear as a potentially-evaluated
// expression in a default argument.
//
// C++20 [dcl.fct.default]p7 (DR as part of P0588R1, see also CWG 2346):
// Note: A local variable cannot be odr-used (6.3) in a default argument.
//
if (VDecl->isLocalVarDecl() && !DRE->isNonOdrUse())
return S.Diag(DRE->getBeginLoc(),
diag::err_param_default_argument_references_local)
<< VDecl->getDeclName() << DefaultArg->getSourceRange();
} else {
const VarDecl *VD = nullptr;
if (const auto *BD = dyn_cast<BindingDecl>(Decl))
VD = dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
else
VD = dyn_cast<VarDecl>(Decl);
if (VD) {
// C++ [dcl.fct.default]p7:
// Local variables shall not be used in default argument
// expressions.
//
// C++17 [dcl.fct.default]p7 (by CWG 2082):
// A local variable shall not appear as a potentially-evaluated
// expression in a default argument.
//
// C++20 [dcl.fct.default]p7 (DR as part of P0588R1, see also CWG 2346):
// Note: A local variable cannot be odr-used (6.3) in a default
// argument.
//
if (VD->isLocalVarDecl() && !DRE->isNonOdrUse())
return S.Diag(DRE->getBeginLoc(),
diag::err_param_default_argument_references_local)
<< Decl->getDeclName() << DefaultArg->getSourceRange();
}
}

return false;
}

Expand Down Expand Up @@ -149,13 +156,20 @@ bool CheckDefaultArgumentVisitor::VisitPseudoObjectExpr(
}

bool CheckDefaultArgumentVisitor::VisitLambdaExpr(const LambdaExpr *Lambda) {
// C++11 [expr.lambda.prim]p13:
// A lambda-expression appearing in a default argument shall not
// implicitly or explicitly capture any entity.
if (Lambda->capture_begin() == Lambda->capture_end())
return false;

return S.Diag(Lambda->getBeginLoc(), diag::err_lambda_capture_default_arg);
// [expr.prim.lambda.capture]p9
// a lambda-expression appearing in a default argument cannot implicitly or
// explicitly capture any local entity. Such a lambda-expression can still
// have an init-capture if any full-expression in its initializer satisfies
// the constraints of an expression appearing in a default argument.
bool Invalid = false;
for (const LambdaCapture &LC : Lambda->captures()) {
if (!Lambda->isInitCapture(&LC))
return S.Diag(LC.getLocation(), diag::err_lambda_capture_default_arg);
// Init captures are always VarDecl.
auto *D = cast<VarDecl>(LC.getCapturedVar());
Invalid |= Visit(D->getInit());
}
return Invalid;
}
} // namespace

Expand Down
4 changes: 3 additions & 1 deletion clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp
Expand Up @@ -27,5 +27,7 @@ void h() {
struct S { int i; };
auto [x] = S();

extern void h7(int = x); // FIXME: reject
extern void h7(int = x);
// expected-error@-1 {{default argument references local variable 'x' of enclosing function}}

}
10 changes: 10 additions & 0 deletions clang/test/CXX/drs/dr23xx.cpp
Expand Up @@ -89,6 +89,16 @@ namespace dr2353 { // dr2353: 9
#pragma clang __debug dump not_use_2
}

#if __cplusplus >= 201402L
namespace dr2358 { // dr2358: 16
void f2() {
int i = 1;
void g1(int = [xxx=1] { return xxx; }()); // OK
void g2(int = [xxx=i] { return xxx; }()); // expected-error {{default argument references local variable 'i' of enclosing function}}
}
}
#endif

#if __cplusplus >= 201707L
// Otherwise, if the qualified-id std::tuple_size<E> names a complete class
// type **with a member value**, the expression std::tuple_size<E>::value shall
Expand Down
19 changes: 18 additions & 1 deletion clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
@@ -1,4 +1,8 @@
// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify
// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -Wno-c++14-extensions -verify
// RUN: %clang_cc1 -std=c++17 %s -Wunused -Wno-unused-lambda-capture -Wno-c++14-extensions -verify


const int global = 0;

void f2() {
int i = 1;
Expand All @@ -7,7 +11,20 @@ void f2() {
void g3(int = ([=]{ return i; })()); // expected-error{{lambda expression in default argument cannot capture any entity}}
void g4(int = ([=]{ return 0; })());
void g5(int = ([]{ return sizeof i; })());
void g6(int = ([x=1, y = global, &z = global]{ return x; })());
void g7(int = ([x=i, &y=i]{ return x; })()); // expected-error 2{{default argument references local variable 'i' of enclosing function}}
}

#if __cplusplus >= 201703L
int global_array[] = { 1, 2 };
auto [ga, gb] = global_array;

void structured_bindings() {
int array[] = { 1, 2 };
auto [a, b] = array;
void func(int c = [x = a, &xref = a, y = ga, &yref = ga] { return x; }()); // expected-error 2{{default argument references local variable 'a' of enclosing function}}
}
#endif

namespace lambda_in_default_args {
int f(int = [] () -> int { int n; return ++n; } ());
Expand Down
2 changes: 1 addition & 1 deletion clang/www/cxx_dr_status.html
Expand Up @@ -13956,7 +13956,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://wg21.link/cwg2358">2358</a></td>
<td>CD5</td>
<td>Explicit capture of value</td>
<td class="none" align="center">Unknown</td>
<td class="unreleased" align="center">Clang 16</td>
</tr>
<tr id="2359">
<td><a href="https://wg21.link/cwg2359">2359</a></td>
Expand Down

0 comments on commit 7429709

Please sign in to comment.