Mordante updated this revision to Diff 215448. Mordante added a comment. Updated the unit tests as requested. This required the `Sema::ActOnParamDefaultArgument` to delay a part of the ODR validation until the default argument has been 'instantiated'. As discussed on IRC; the up to date `cwg_index.html` is not public, so I only updated the unit test and removed the changes to `cxx_dr_status.html`.
CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65696/new/ https://reviews.llvm.org/D65696 Files: clang/lib/Sema/SemaDeclCXX.cpp clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p9.cpp clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp clang/test/CXX/drs/dr20xx.cpp
Index: clang/test/CXX/drs/dr20xx.cpp =================================================================== --- clang/test/CXX/drs/dr20xx.cpp +++ clang/test/CXX/drs/dr20xx.cpp @@ -8,6 +8,76 @@ #define static_assert(...) _Static_assert(__VA_ARGS__) #endif + +namespace dr2082 { // dr2082: 10 +namespace local_var { +void g() { + int k = 42; + void l(int m = k); // expected-error {{default argument references local variable 'k' of enclosing function}} +} +} // namespace local_var +namespace local_const { +void g() { + const int k = 42; + void l(int m = k); +} +} // namespace local_const +#if __cplusplus >= 201103L +namespace local_constexpr { +void g() { + constexpr int k = 42; + void l(int m = k); +} +} // namespace local_constexpr +#endif + +namespace local_const_float_to_integral { +void g() { + const double k = 42; + void l(int m = k); // expected-error {{default argument references local variable 'k' of enclosing function}} +} +} // namespace local_const_float_to_integral +#if __cplusplus >= 201103L +namespace local_constexpr_float_to_integral { +void g() { + constexpr double k = 42; + void l(int m = k); +} +} // namespace local_constexpr_float_to_integral + +namespace local_member_const { +struct a { + int b; + int c; +}; +void g() { + const a n{42, 42}; + void l(int m = n.b); // expected-error {{default argument references local variable 'n' of enclosing function}} +} +} // namespace local_member_const +namespace local_member_constexpr { +struct a { + int b; + int c; +}; +void g() { + constexpr a n{42, 42}; + void l(int m = n.b); +} +} // namespace local_member_constexpr +namespace local_member_mutable { +struct a { + int b; + mutable int c; +}; +void g() { + constexpr a n{42, 42}; + void l(int m = n.b); // expected-error {{default argument references local variable 'n' of enclosing function}} +} +} // namespace local_member_mutable +#endif +} + namespace dr2083 { // dr2083: partial #if __cplusplus >= 201103L void non_const_mem_ptr() { 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,7 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -void h() +void f() { int i; - extern void h2(int x = sizeof(i)); // expected-error {{default argument references local variable 'i' of enclosing function}} + extern void g(int x = i); // expected-error {{default argument references local variable 'i' of enclosing function}} + extern void h(int x = sizeof(i)); } Index: clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p9.cpp =================================================================== --- /dev/null +++ clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p9.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int a; +int f(int a, int b = a); // expected-error {{default argument references parameter 'a'}} +typedef int I; +int g(float I, int b = I(2)); // expected-error {{called object type 'float' is not a function or function pointer}} +int h(int a, int b = sizeof(a)); + +int b; +class X { + int a; + int mem1(int i = a); // expected-error {{invalid use of non-static data member 'a'}} + int mem2(int i = b); + static int b; +}; + +int f(int = 0); +void h() { + int j = f(1); + int k = f(); +} +int (*p1)(int) = &f; +int (*p2)() = &f; // expected-error {{cannot initialize a variable of type 'int (*)()' with an rvalue of type 'int (*)(int)': different number of parameters (0 vs 1)}} Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -86,29 +86,22 @@ NamedDecl *Decl = DRE->getDecl(); if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(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. + // A default argument is evaluated each time the function is called + // with no argument for the corresponding parameter. A parameter shall + // not appear as a potentially-evaluated expression in a default + // argument. Parameters of a function declared before a default + // argument are in scope and can hide namespace and class member + // names. + if (DRE->isNonOdrUse() == NOUR_Unevaluated) + return false; + return S->Diag(DRE->getBeginLoc(), diag::err_param_default_argument_references_param) << Param->getDeclName() << DefaultArg->getSourceRange(); - } else if (VarDecl *VDecl = dyn_cast<VarDecl>(Decl)) { - // C++ [dcl.fct.default]p7 - // Local variables shall not be used in default argument - // expressions. - if (VDecl->isLocalVarDecl()) - return S->Diag(DRE->getBeginLoc(), - diag::err_param_default_argument_references_local) - << VDecl->getDeclName() << DefaultArg->getSourceRange(); } return false; } - /// VisitCXXThisExpr - Visit a C++ "this" expression. bool CheckDefaultArgumentVisitor::VisitCXXThisExpr(CXXThisExpr *ThisE) { // C++ [dcl.fct.default]p8: @@ -147,6 +140,60 @@ } } + /// CheckDefaultArgumentVisitorODR - C++ [dcl.fct.default] Traverses + /// the default argument of a parameter to determine whether it + /// contains ODR violations. These violations cannot be checked in + /// \ref CheckDefaultArgumentVisitor since the DeclRefExp's may be changed to + /// an implicit cast from an LValue to RValue by \ref SetParamDefaultArgument. + /// When that happens the ODR usage may be allowed. + class CheckDefaultArgumentVisitorODR + : public StmtVisitor<CheckDefaultArgumentVisitorODR, bool> { + Expr *DefaultArg; + Sema *S; + + public: + CheckDefaultArgumentVisitorODR(Expr *defarg, Sema *s) + : DefaultArg(defarg), S(s) {} + + bool VisitExpr(Expr *Node); + bool VisitDeclRefExpr(DeclRefExpr *DRE); + }; + + /// VisitExpr - Visit all of the children of this expression. + bool CheckDefaultArgumentVisitorODR::VisitExpr(Expr *Node) { + bool IsInvalid = false; + for (Stmt *SubStmt : Node->children()) + IsInvalid |= Visit(SubStmt); + return IsInvalid; + } + + /// VisitDeclRefExpr - Visit a reference to a declaration, to determine + /// whether this declaration can be used in the default argument expression. + bool CheckDefaultArgumentVisitorODR::VisitDeclRefExpr(DeclRefExpr *DRE) { + NamedDecl *Decl = DRE->getDecl(); + + if (VarDecl *VDecl = dyn_cast<VarDecl>(Decl)) { + // C++ [dcl.fct.default]p7 + // A local variable cannot be odr-used (6.2) in a default argument. + // C++2a [basic.def.odr]p4: + // A variable x whose name appears as a potentially-evalauted expression + // e is odr-used by e unless [...] + // -- x is a variable of non-reference type that is usable in constant + // expressions and has no mutable subobjects, and e is an element of + // the set of potential results of an expression of + // non-volatile-qualified non-class type to which the + // lvalue-to-rvalue conversion is applied, or + if (DRE->isNonOdrUse() != NOUR_None) + return false; + + if (VDecl->isLocalVarDecl()) + return S->Diag(DRE->getBeginLoc(), + diag::err_param_default_argument_references_local) + << VDecl->getDeclName() << DefaultArg->getSourceRange(); + } + return false; + } + void Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc, const CXXMethodDecl *Method) { @@ -333,7 +380,11 @@ return; } - SetParamDefaultArgument(Param, DefaultArg, EqualLoc); + if (!SetParamDefaultArgument(Param, DefaultArg, EqualLoc)) { + CheckDefaultArgumentVisitorODR DefaultArgCheckerODR(DefaultArg, this); + if (DefaultArgCheckerODR.Visit(Param->getDefaultArg())) + Param->setInvalidDecl(); + } } /// ActOnParamUnparsedDefaultArgument - We've seen a default
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits