From 5951ff4512332fff9d191da8661143a883d3b8aa Mon Sep 17 00:00:00 2001 From: Bruno Ricci Date: Thu, 11 Jun 2020 12:41:08 +0100 Subject: [PATCH] [clang] CWG 2082 and 2346: loosen the restrictions on parameters and local variables in default arguments. This patch implements the resolution of CWG 2082 and CWG 2346. The resolution of CWG 2082 changed [dcl.fct.default]p7 and p9 to allow a parameter or local variable to appear in a default argument if not in a potentially-evaluated expression. The resolution of CWG 2346 changed [dcl.fct.default]p7 to allow a local variable to appear in a default argument if not odr-used. An issue remains after this patch (see the FIXME in test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp). This is addressed by the next patch. Differential Revision: https://reviews.llvm.org/D81615 Reviewed By: rsmith, erichkeane --- clang/lib/Sema/SemaDeclCXX.cpp | 35 ++++++++++++------- .../dcl.meaning/dcl.fct.default/p7.cpp | 21 ++++++++--- .../dcl.meaning/dcl.fct.default/p8.cpp | 6 ++++ .../dcl.meaning/dcl.fct.default/p9.cpp | 12 +++++++ clang/test/CXX/drs/dr20xx.cpp | 7 ++++ clang/test/CXX/drs/dr23xx.cpp | 7 ++++ 6 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p9.cpp diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e33df2527d2c3..28a84d27c0387 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -87,22 +87,31 @@ bool CheckDefaultArgumentVisitor::VisitExpr(const Expr *Node) { bool CheckDefaultArgumentVisitor::VisitDeclRefExpr(const DeclRefExpr *DRE) { const NamedDecl *Decl = DRE->getDecl(); if (const auto *Param = dyn_cast(Decl)) { - // C++ [dcl.fct.default]p9 - // Default arguments are evaluated each time the function is - // called. The order of evaluation of function arguments is - // unspecified. Consequently, parameters of a function shall not - // be used in default argument expressions, even if they are not - // evaluated. Parameters of a function declared before a default - // argument expression are in scope and can hide namespace and - // class member names. - return S.Diag(DRE->getBeginLoc(), - diag::err_param_default_argument_references_param) - << Param->getDeclName() << DefaultArg->getSourceRange(); + // C++ [dcl.fct.default]p9: + // [...] parameters of a function shall not be used in default + // argument expressions, even if they are not evaluated. [...] + // + // C++17 [dcl.fct.default]p9 (by CWG 2082): + // [...] A parameter shall not appear as a potentially-evaluated + // expression in a default argument. [...] + // + if (DRE->isNonOdrUse() != NOUR_Unevaluated) + return S.Diag(DRE->getBeginLoc(), + diag::err_param_default_argument_references_param) + << Param->getDeclName() << DefaultArg->getSourceRange(); } else if (const auto *VDecl = dyn_cast(Decl)) { - // C++ [dcl.fct.default]p7 + // C++ [dcl.fct.default]p7: // Local variables shall not be used in default argument // expressions. - if (VDecl->isLocalVarDecl()) + // + // 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(); 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 index 164eb3682f31a..07b527bbdc203 100644 --- 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,7 +1,20 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -void h() -{ - int i; - extern void h2(int x = sizeof(i)); // expected-error {{default argument references local variable 'i' of enclosing function}} +void h() { + int i1 = 0; + extern void h1(int x = i1); + // expected-error@-1 {{default argument references local variable 'i1' of enclosing function}} + + const int i2 = 0; + extern void h2a(int x = i2); // FIXME: ok, not odr-use + // expected-error@-1 {{default argument references local variable 'i2' of enclosing function}} + extern void h2b(int x = i2 + 0); // ok, not odr-use + + const int i3 = 0; + extern void h3(const int *x = &i3); + // expected-error@-1 {{default argument references local variable 'i3' of enclosing function}} + + const int i4 = 0; + extern void h4(int x = sizeof(i4)); // ok, not odr-use + extern void h5(int x = decltype(i4 + 4)()); // ok, not odr-use } diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p8.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p8.cpp index 1a08ab7332877..ce2c9cb9fb9da 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p8.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p8.cpp @@ -1,4 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s class A { void f(A* p = this) { } // expected-error{{invalid use of 'this'}} + + void test(); }; + +void A::test() { + void g(int = this); // expected-error {{default argument references 'this'}} +} diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p9.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p9.cpp new file mode 100644 index 0000000000000..0971b1458f57e --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p9.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void h() { + void f1(int x, int y = sizeof(x)); // ok + void f2(int x, int y = decltype(x)()); // ok + void f3(int x, int y = x); + // expected-error@-1 {{default argument references parameter 'x'}} + void f4(int x, int y = x + 0); + // expected-error@-1 {{default argument references parameter 'x'}} + void f5(int x, int y = ((void)x, 0)); + // expected-error@-1 {{default argument references parameter 'x'}} +} diff --git a/clang/test/CXX/drs/dr20xx.cpp b/clang/test/CXX/drs/dr20xx.cpp index c1429492a51ae..56cc1161a00c8 100644 --- a/clang/test/CXX/drs/dr20xx.cpp +++ b/clang/test/CXX/drs/dr20xx.cpp @@ -49,6 +49,13 @@ namespace dr2026 { // dr2026: 11 } } +namespace dr2082 { // dr2082: 11 + void test1(int x, int = sizeof(x)); // ok +#if __cplusplus >= 201103L + void test2(int x, int = decltype(x){}); // ok +#endif +} + namespace dr2083 { // dr2083: partial #if __cplusplus >= 201103L void non_const_mem_ptr() { diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp index 85f5920746079..3268838ac6c85 100644 --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -4,6 +4,13 @@ // RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +namespace dr2346 { // dr2346: 11 + void test() { + const int i2 = 0; + extern void h2b(int x = i2 + 0); // ok, not odr-use + } +} + namespace dr2352 { // dr2352: 10 int **p; const int *const *const &f1() { return p; }