riccibruno created this revision. riccibruno added reviewers: rsmith, erichkeane, rjmccall. riccibruno added a project: clang. Herald added a subscriber: cfe-commits. riccibruno requested review of this revision.
Currently clang accepts a default argument containing a structured binding which is a local entity: struct S { int i, j; }; auto [x, y] = S(); extern void h(int = x); // Should be rejected. It was not entirely clear in C++17 that this was forbidden since `C++17 [dcl.fct.default]p7` only used the term "local variable". However this is clearly forbidden in C++20 with the new wording in terms of odr-usable local entities. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D85613 Files: clang/lib/Sema/SemaDeclCXX.cpp clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp Index: clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp =================================================================== --- clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp +++ 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 <typename> struct tuple_size; +template <size_t, typename> struct tuple_element; +} // namespace std +using size_t = std::size_t; + +struct E {}; +template <size_t> int get(E); + +namespace std { +template <> struct tuple_size<E> { static constexpr size_t value = 2; }; +template <size_t I> struct tuple_element<I, E> { 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 variable '[x, y]'}} - 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 variable '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 variable '[a0, a1]'}} } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -86,6 +86,34 @@ /// argument expression. bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) { const NamedDecl *Decl = DRE->getDecl(); + + // 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). + if (const auto *Binding = dyn_cast<BindingDecl>(Decl)) { + if (const VarDecl *HoldingVar = Binding->getHoldingVar()) { + // C++20 [dcl.struct.bind]p4: + // Each vi is the name [...] that refers to the object bound to ri [...] + Decl = HoldingVar; + } else { + // C++20 [dcl.struct.bind]p3: + // Each vi is the name of an lvalue that refers to the element i of + // the array [...] + // + // C++20 [dcl.struct.bind]p5: + // [...] each vi is the name of an lvalue that refers to the member mi + // of e [...] + Decl = Binding->getDecomposedDecl(); + } + } + if (const auto *Param = dyn_cast<ParmVarDecl>(Decl)) { // C++ [dcl.fct.default]p9: // [...] parameters of a function shall not be used in default
Index: clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp =================================================================== --- clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp +++ 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 <typename> struct tuple_size; +template <size_t, typename> struct tuple_element; +} // namespace std +using size_t = std::size_t; + +struct E {}; +template <size_t> int get(E); + +namespace std { +template <> struct tuple_size<E> { static constexpr size_t value = 2; }; +template <size_t I> struct tuple_element<I, E> { 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 variable '[x, y]'}} - 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 variable '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 variable '[a0, a1]'}} } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -86,6 +86,34 @@ /// argument expression. bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) { const NamedDecl *Decl = DRE->getDecl(); + + // 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). + if (const auto *Binding = dyn_cast<BindingDecl>(Decl)) { + if (const VarDecl *HoldingVar = Binding->getHoldingVar()) { + // C++20 [dcl.struct.bind]p4: + // Each vi is the name [...] that refers to the object bound to ri [...] + Decl = HoldingVar; + } else { + // C++20 [dcl.struct.bind]p3: + // Each vi is the name of an lvalue that refers to the element i of + // the array [...] + // + // C++20 [dcl.struct.bind]p5: + // [...] each vi is the name of an lvalue that refers to the member mi + // of e [...] + Decl = Binding->getDecomposedDecl(); + } + } + if (const auto *Param = dyn_cast<ParmVarDecl>(Decl)) { // C++ [dcl.fct.default]p9: // [...] parameters of a function shall not be used in default
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits