diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -3840,6 +3840,8 @@ /// DecompositionDecl of type 'int (&)[3]'. class BindingDecl : public ValueDecl { /// The declaration that this binding binds to part of. + // FIXME: Currently not set during deserialization of the BindingDecl; + // only set when the corresponding DecompositionDecl is visited. LazyDeclPtr Decomp; /// The binding represented by this declaration. References to this /// declaration are effectively equivalent to this expression (except diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3988,7 +3988,8 @@ def err_param_default_argument_references_param : Error< "default argument references parameter %0">; def err_param_default_argument_references_local : Error< - "default argument references local variable %0 of enclosing function">; + "default argument references local %select{variable|binding}0 %1 " + "of enclosing function">; def err_param_default_argument_references_this : Error< "default argument references 'this'">; def err_param_default_argument_nonfunc : Error< diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -86,6 +86,22 @@ /// argument expression. bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) { const NamedDecl *Decl = DRE->getDecl(); + + auto CheckAndDiagnoseLocalEntity = [&](const VarDecl *VD, unsigned DiagID, + const auto &... DiagArgs) -> bool { + if (VD->isLocalVarDecl() && !DRE->isNonOdrUse()) { + auto DB = S.Diag(DRE->getBeginLoc(), DiagID); + // FIXME: Replace with a fold once we can use C++17. + int dummy[] = {(DB << DiagArgs, 0)...}; + (void)dummy; + + DB << DefaultArg->getSourceRange(); + return true; + } + + return false; + }; + if (const auto *Param = dyn_cast(Decl)) { // C++ [dcl.fct.default]p9: // [...] parameters of a function shall not be used in default @@ -111,10 +127,28 @@ // 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 << DefaultArg->getSourceRange(); + if (CheckAndDiagnoseLocalEntity( + VDecl, diag::err_param_default_argument_references_local, + /*variable*/ 0, VDecl)) + return true; + + } else if (const auto *Binding = dyn_cast(Decl)) { + // C++20 [basic.pre]p7: + // A local entity is [...] a structured binding whose corresponding + // variable is such an entity [...] + // + // C++20 [basic.def.odr]p9: + // A local entity (6.1) is odr-usable in a declarative region if [...] + // + // Note that this was not entirely clear in C++17 since [dcl.fct.default]p7 + // only prohibited local variables (a structured binding declaration + // introduces identifiers as names). + // + const auto *VD = cast_or_null(Binding->getDecomposedDecl()); + if (VD && CheckAndDiagnoseLocalEntity( + VD, diag::err_param_default_argument_references_local, + /*binding*/ 1, Binding)) + return true; } return false; diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp @@ -1,5 +1,20 @@ // RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +namespace std { +using size_t = decltype(sizeof(int)); +template struct tuple_size; +template struct tuple_element; +} // namespace std +using size_t = std::size_t; + +struct E {}; +template int get(E); + +namespace std { +template <> struct tuple_size { static constexpr size_t value = 2; }; +template struct tuple_element { using type = int; }; +}; // namespace std + void h() { int i1 = 0; extern void h1(int x = i1); @@ -22,10 +37,21 @@ }; extern void h6(int = i5); - // expected-error-re@-1 {{default argument references local variable '(unnamed variable of type (anonymous union at {{.*}}:20:3))' of enclosing function}} + // expected-error-re@-1 {{default argument references local variable '(unnamed variable of type (anonymous union at {{.*}}:35:3))' of enclosing function}} + + struct S { + int i, j; + }; + auto [x, y] = S(); + + extern void h7(int = x); // expected-error {{default argument references local binding 'x'}} - struct S { int i; }; - auto [x] = S(); + auto [z, w] = E(); + extern void h8a(int = sizeof(z)); // ok + extern void h8b(int = w); // expected-error {{default argument references local binding 'w'}} - extern void h7(int = x); // FIXME: reject + extern auto get_array()->int(&)[2]; + auto [a0, a1] = get_array(); + extern void h9a(int = sizeof(a0)); + extern void h9b(int = a1); // expected-error {{default argument references local binding 'a1'}} }