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

Reply via email to