ahatanak created this revision. This patch fixes an assertion failure that occurs when compiling the following invalid code:
struct S { template<class T> S(T &&) {} }; template<class T> void foo1(int a0, S a1 = [](){ (void)&a0; } ) { // a0 cannot be used in the default argument for a1 } void foo2() { foo1<int>(1); } $ clang++ -std=c++14 -c -o /dev/null test.cpp Assertion failed: (isa<LabelDecl>(D) && "declaration not instantiated in this scope"), function findInstantiationOf, file llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp, line 2911. The assertion fails when findInstantiationOf is called to find the instantiated decl of a0 when instantiating the lambda expression that is the default argument for a1. To prevent the assertion failure, this patch makes CheckDefaultArgumentVisitor visit all subexpressions belonging to a default argument expression and detect local variables and parameters (that are external to the default argument) referenced in the default argument expression after the template definition is parsed. This patch also removes the diagnostic that is printed in test p7.cpp when a local variable is referenced inside a unevaluated default argument expression, which I think conforms to c++14 or later. Also, with this patch, clang prints diagnostics when local variables or parameters are referenced inside a block expression that is used as a default argument. I wasn't 100% sure it is legal to use blocks for default arguments (I found that compiling the code below causes clang to segfault), but it seems to me that we want to handle blocks in default arguments the same way we handle lambdas. void logRange(id i = [](){}) { } void foo1() { logRange(); } $ clang++ -std=c++14 -c -o /dev/null -fobjc-arc test.mm rdar://problem/33239958 https://reviews.llvm.org/D36915 Files: lib/Sema/SemaDeclCXX.cpp test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp test/SemaCXX/default1.cpp test/SemaObjCXX/blocks.mm
Index: test/SemaObjCXX/blocks.mm =================================================================== --- test/SemaObjCXX/blocks.mm +++ test/SemaObjCXX/blocks.mm @@ -169,3 +169,17 @@ return b; // expected-error {{no viable conversion from returned value of type 'MoveBlockVariable::B0' to function return type 'MoveBlockVariable::B1'}} } } + +namespace DefaultArg { +void test() { + id x; + void func0(id a0, id a1 = ^{ (void)&a0; }); // expected-error {{default argument references parameter 'a0'}} + void func1(id a0, id a1 = ^{ (void)&x; }); // expected-error {{default argument references local variable 'x' of enclosing function}} + void func2(id a0, id a1 = ^{ (void)sizeof(a0); }); + void func3(id a0 = ^{ (void)sizeof(x); }); + void func4(id a0, id a1 = ^{ + ^{ (void)&a0; }(); // expected-error {{default argument references parameter 'a0'}} + [=](){ (void)&a0; }(); // expected-error {{default argument references parameter 'a0'}} + }); +} +} Index: test/SemaCXX/default1.cpp =================================================================== --- test/SemaCXX/default1.cpp +++ test/SemaCXX/default1.cpp @@ -78,3 +78,60 @@ void PR20769_b(int = 1); void PR20769_b() { void PR20769_b(int = 2); } + +#if __cplusplus >= 201103L // C++11 or later +struct S2 { + template<class T> + S2(T&&) {} +}; + +template<class T> +void func0(int a0, S2 a1 = [](){ (void)&a0; }); // expected-error {{default argument references parameter 'a0'}} + +template<class T> +void func1(T a0, int a1, S2 a2 = _Generic((a0), default: [](){ (void)&a1; }, int: 0)); // expected-error {{default argument references parameter 'a1'}} + +template<class T> +void func2(S2 a0 = [](){ + int t; [&t](){ (void)&t;}(); +}); + +template<class T> +void func3(int a0, S2 a1 = [](){ + [=](){ (void)&a0;}(); // expected-error {{default argument references parameter 'a0'}} +}); + +double d; + +void test1() { + int i; + float f; + void foo0(int a0 = _Generic((f), double: d, float: f)); // expected-error {{default argument references local variable 'f' of enclosing function}} + void foo1(int a0 = _Generic((d), double: d, float: f)); + void foo2(int a0 = _Generic((i), int: d, float: f)); + void foo3(int a0 = _Generic((i), default: d, float: f)); + + void foo4(S2 a0 = [&](){ (void)&i; }); // expected-error {{lambda expression in default argument cannot capture any entity}} + void foo5(S2 a0 = [](){ + // No warning about capture list of a lambda expression defined in a block scope. + int t; [&t](){ (void)&t;}(); + }); + void foo6(int a0, S2 a1 = [](){ + // No warning about local variables or parameters referenced by an + // unevaluated expression. + int t = sizeof({i, a0;}); + }); + void foo6(S2 a0 = [](){ + int i; // expected-note {{'i' declared here}} + void foo7(int a0, // expected-note {{'a0' declared here}} + S2 a1 = [](){ (void)&a0; }); // expected-error {{variable 'a0' cannot be implicitly captured in a lambda with no capture-default specified}} expected-error {{default argument references parameter 'a0'}} expected-note {{lambda expression begins here}} + void foo8(S2 a0 = [](){ (void)&i; }); // expected-error {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}} expected-error {{default argument references local variable 'i' of enclosing function}} expected-note {{lambda expression begins here}} + }); + + func0<int>(1); // expected-error {{no matching function for call to 'func0'}} + func1<int>(1); // expected-error {{no matching function for call to 'func1'}} + func2<int>(); + func3<int>(1); // expected-error {{no matching function for call to 'func3'}} +} + +#endif Index: test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp =================================================================== --- test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp +++ test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p7.cpp @@ -1,7 +1,10 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics void h() { int i; - extern void h2(int x = sizeof(i)); // expected-error {{default argument references local variable 'i' of enclosing function}} + // Do not warn about local variables referenced in default arguments if it is + // in an unevaluated expression. + extern void h2(int x = sizeof(i)); } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -59,31 +59,75 @@ : public StmtVisitor<CheckDefaultArgumentVisitor, bool> { Expr *DefaultArg; Sema *S; + bool CompoundStmtSeen; + + // The set of variables declared in this default argument expression. + llvm::SmallPtrSet<const VarDecl *, 4> Decls; + + template<class FuncDeclTy> + bool VisitFunctionDecl(FuncDeclTy *Func) { + for (ParmVarDecl *PD : Func->parameters()) + Decls.insert(PD); + return Visit(Func->getBody()); + } public: CheckDefaultArgumentVisitor(Expr *defarg, Sema *s) - : DefaultArg(defarg), S(s) {} + : DefaultArg(defarg), S(s), CompoundStmtSeen(false) {} bool VisitExpr(Expr *Node); + bool VisitStmt(Stmt *Node); + bool VisitCompoundStmt(CompoundStmt *Node); + bool VisitDeclStmt(DeclStmt *Node); bool VisitDeclRefExpr(DeclRefExpr *DRE); bool VisitCXXThisExpr(CXXThisExpr *ThisE); + bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node); + bool VisitGenericSelectionExpr(GenericSelectionExpr *Node); bool VisitLambdaExpr(LambdaExpr *Lambda); + bool VisitBlockExpr(BlockExpr *Block); bool VisitPseudoObjectExpr(PseudoObjectExpr *POE); }; /// VisitExpr - Visit all of the children of this expression. bool CheckDefaultArgumentVisitor::VisitExpr(Expr *Node) { + return VisitStmt(Node); + } + + bool CheckDefaultArgumentVisitor::VisitStmt(Stmt *Node) { + bool IsInvalid = false; + for (Stmt *SubStmt : Node->children()) + IsInvalid |= Visit(SubStmt); + return IsInvalid; + } + + bool CheckDefaultArgumentVisitor::VisitCompoundStmt(CompoundStmt *Node) { + CompoundStmtSeen = true; + return VisitStmt(Node); + } + + bool CheckDefaultArgumentVisitor::VisitDeclStmt(DeclStmt *Node) { bool IsInvalid = false; + + for (const auto *D : Node->decls()) + if (const auto *VD = dyn_cast<VarDecl>(D)) + Decls.insert(VD); + 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 CheckDefaultArgumentVisitor::VisitDeclRefExpr(DeclRefExpr *DRE) { NamedDecl *Decl = DRE->getDecl(); + + if (auto *VD = dyn_cast<VarDecl>(Decl)) + if (Decls.count(VD)) + return false; + if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(Decl)) { // C++ [dcl.fct.default]p9 // Default arguments are evaluated each time the function is @@ -119,6 +163,26 @@ << ThisE->getSourceRange(); } + bool CheckDefaultArgumentVisitor::VisitUnaryExprOrTypeTraitExpr( + UnaryExprOrTypeTraitExpr *Node) { + // Ignore unevaluated expressions such as sizeof. + return false; + } + + bool CheckDefaultArgumentVisitor::VisitGenericSelectionExpr( + GenericSelectionExpr *Node) { + // Ignore the controlling expression and all the association expressions + // except the result expression. Those expressions are unevaluated. + if (!Node->isResultDependent()) + return Visit(Node->getResultExpr()); + + bool IsInvalid = false; + for (Expr *AE : Node->getAssocExprs()) + IsInvalid |= Visit(AE); + + return IsInvalid; + } + bool CheckDefaultArgumentVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *POE) { bool Invalid = false; for (PseudoObjectExpr::semantics_iterator @@ -140,11 +204,22 @@ // 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; + // Lambda expressions in a block scope can have captures. + if (!CompoundStmtSeen && Lambda->capture_begin() != Lambda->capture_end()) + return S->Diag(Lambda->getLocStart(), + diag::err_lambda_capture_default_arg); + + if (CXXMethodDecl *CO = Lambda->getCallOperator()) + return VisitFunctionDecl(CO); + + return false; + } - return S->Diag(Lambda->getLocStart(), - diag::err_lambda_capture_default_arg); + bool CheckDefaultArgumentVisitor::VisitBlockExpr(BlockExpr *Block) { + if (BlockDecl *BD = Block->getBlockDecl()) + return VisitFunctionDecl(BD); + + return false; } }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits