[clang] 2669e41 - PR45083: Mark statement expressions as being dependent if they contain

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 9 17:03:39 PDT 2020


Author: Richard Smith
Date: 2020-03-09T16:57:07-07:00
New Revision: 2669e41b7b9c1561a01048d5ed0aba3c62432dfc

URL: https://github.com/llvm/llvm-project/commit/2669e41b7b9c1561a01048d5ed0aba3c62432dfc
DIFF: https://github.com/llvm/llvm-project/commit/2669e41b7b9c1561a01048d5ed0aba3c62432dfc.diff

LOG: PR45083: Mark statement expressions as being dependent if they contain
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.

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 7271dbb830a2..75b7a5f6ecd3 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -3959,14 +3959,8 @@ class StmtExpr : public Expr {
   Stmt *SubStmt;
   SourceLocation LParenLoc, RParenLoc;
 public:
-  // FIXME: Does type-dependence need to be computed 
diff erently?
-  // 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) { }

diff  --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 4eb7a177de00..feaa9e01f8eb 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -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)

diff  --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp
index bb1e239c3490..12a99acc21cd 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -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 {{
diff erent 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}}
+}


        


More information about the cfe-commits mailing list