From 2669e41b7b9c1561a01048d5ed0aba3c62432dfc Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 5 Mar 2020 12:20:02 -0800 Subject: [PATCH] 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. --- clang/include/clang/AST/Expr.h | 10 ++----- clang/lib/AST/Expr.cpp | 47 ++++++++++++++++++++++++++++++ clang/test/SemaTemplate/dependent-expr.cpp | 42 ++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 7271dbb..75b7a5f 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 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) { } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 4eb7a17..feaa9e0 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 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(S)) { + addDependence(E->getDependence()); + continue; + } + + // FIXME: Need to properly compute whether DeclStmts contain unexpanded + // parameter packs. + if (DeclStmt *DS = dyn_cast(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(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 args, QualType Type, SourceLocation BLoc, SourceLocation RP) diff --git a/clang/test/SemaTemplate/dependent-expr.cpp b/clang/test/SemaTemplate/dependent-expr.cpp index bb1e239..12a99ac 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 @@ -108,3 +107,42 @@ namespace PR18152 { }; template struct A<0>; } + +template 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 struct B : A { + void f() { + const int n = ({ if (x) {} 0; }); + } + }; + + template void B::f(); + + template void f() { + decltype(({})) x; // expected-error {{incomplete type}} + } + template void f(); + + template 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()); // expected-note {{previous}} + using U = float; // expected-error {{different types ('float' vs 'decltype(g())' (aka 'void'))}} + + void h(auto a, decltype(g())*) {} // expected-note {{previous}} + void h(auto a, void*) {} // expected-error {{redefinition}} +} -- 2.7.4