PR45083: Mark statement expressions as being dependent if they contain
authorRichard Smith <richard@metafoo.co.uk>
Thu, 5 Mar 2020 20:20:02 +0000 (12:20 -0800)
committerRichard Smith <richard@metafoo.co.uk>
Mon, 9 Mar 2020 23:57:07 +0000 (16:57 -0700)
dependent constructs.

We previously assumed they were neither value- nor
instantiation-dependent under any circumstances, which would lead to
crashes and other misbehavior.

This doesn't match GCC's behavior (where statement expressions appear to
be treated as value-dependent if they appear in a dependent context),
but seems to be the best thing we can do in the short term: it turns out
to be remarkably difficult for us to correctly determine whether we are
in a dependent context (and it's not even possible in some cases, such
as in a generic lambda where we might not have seen the 'auto' yet).

This was previously reverted in 8e4a867 for rejecting some code, but that
code was invalid and Clang was previously incorrectly accepting it.

clang/include/clang/AST/Expr.h
clang/lib/AST/Expr.cpp
clang/test/SemaTemplate/dependent-expr.cpp

index 7271dbb..75b7a5f 100644 (file)
@@ -3959,14 +3959,8 @@ class StmtExpr : public Expr {
   Stmt *SubStmt;
   SourceLocation LParenLoc, RParenLoc;
 public:
-  // FIXME: Does type-dependence need to be computed differently?
-  // FIXME: Do we need to compute instantiation instantiation-dependence for
-  // statements? (ugh!)
-  StmtExpr(CompoundStmt *substmt, QualType T,
-           SourceLocation lp, SourceLocation rp) :
-    Expr(StmtExprClass, T, VK_RValue, OK_Ordinary,
-         T->isDependentType(), false, false, false),
-    SubStmt(substmt), LParenLoc(lp), RParenLoc(rp) { }
+  StmtExpr(CompoundStmt *SubStmt, QualType T,
+           SourceLocation LParen, SourceLocation RParen);
 
   /// Build an empty statement expression.
   explicit StmtExpr(EmptyShell Empty) : Expr(StmtExprClass, Empty) { }
index 4eb7a17..feaa9e0 100644 (file)
@@ -4101,6 +4101,53 @@ void ExtVectorElementExpr::getEncodedElementAccess(
   }
 }
 
+StmtExpr::StmtExpr(CompoundStmt *SubStmt, QualType T, SourceLocation LParen,
+                   SourceLocation RParen)
+    : Expr(StmtExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(),
+           false, false, false),
+      SubStmt(SubStmt), LParenLoc(LParen), RParenLoc(RParen) {
+  llvm::SmallVector<Stmt*, 16> Queue(1, SubStmt);
+  while (!Queue.empty()) {
+    Stmt *S = Queue.pop_back_val();
+    if (!S)
+      continue;
+
+    // If any subexpression is dependent, the statement expression is dependent
+    // in the same way.
+    if (Expr *E = dyn_cast<Expr>(S)) {
+      addDependence(E->getDependence());
+      continue;
+    }
+
+    // FIXME: Need to properly compute whether DeclStmts contain unexpanded
+    // parameter packs.
+    if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+      for (Decl *D : DS->decls()) {
+        // If any contained declaration is in a dependent context, then it
+        // needs to be instantiated, so the statement expression itself is
+        // instantiation-dependent.
+        //
+        // Note that we don't need to worry about the case where the context is
+        // non-dependent but contains dependent entities here (eg, inside a
+        // variable template or alias template): that can only happen at file
+        // scope, where statement expressions are prohibited.
+        if (D->getLexicalDeclContext()->isDependentContext())
+          addDependence(ExprDependence::Instantiation);
+
+        // If any contained variable declaration has a dependent type, we can't
+        // evaluate that declaration.
+        if (auto *VD = dyn_cast<VarDecl>(D))
+          if (VD->getType()->isDependentType())
+            addDependence(ExprDependence::Value);
+      }
+    }
+
+    // Recurse to substatements.
+    // FIXME: Should we skip the unchosen side of 'if constexpr' if known?
+    Queue.insert(Queue.end(), S->child_begin(), S->child_end());
+  }
+}
+
 ShuffleVectorExpr::ShuffleVectorExpr(const ASTContext &C, ArrayRef<Expr*> args,
                                      QualType Type, SourceLocation BLoc,
                                      SourceLocation RP)
index bb1e239..12a99ac 100644 (file)
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s
 
 // PR5908
 template <typename Iterator>
@@ -108,3 +107,42 @@ namespace PR18152 {
   };
   template struct A<0>;
 }
+
+template<typename T> void stmt_expr_1() {
+  // GCC doesn't check this: it appears to treat statement-expressions as being
+  // value-dependent if they appear in a dependent context, regardless of their
+  // contents.
+  static_assert( ({ false; }), "" ); // expected-error {{failed}}
+}
+void stmt_expr_2() {
+  static_assert( ({ false; }), "" ); // expected-error {{failed}}
+}
+
+namespace PR45083 {
+  struct A { bool x; };
+
+  template<typename> struct B : A {
+    void f() {
+      const int n = ({ if (x) {} 0; });
+    }
+  };
+
+  template void B<int>::f();
+
+  template<typename> void f() {
+    decltype(({})) x; // expected-error {{incomplete type}}
+  }
+  template void f<int>();
+
+  template<typename> auto g() {
+    auto c = [](auto, int) -> decltype(({})) {};
+    using T = decltype(c(0.0, 0));
+    using T = void;
+    return c(0, 0);
+  }
+  using U = decltype(g<int>()); // expected-note {{previous}}
+  using U = float; // expected-error {{different types ('float' vs 'decltype(g<int>())' (aka 'void'))}}
+
+  void h(auto a, decltype(g<char>())*) {} // expected-note {{previous}}
+  void h(auto a, void*) {} // expected-error {{redefinition}}
+}