diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5074,6 +5074,16 @@ /// conversion. ExprResult tryConvertExprToType(Expr *E, QualType Ty); + /// Conditionally issue a diagnostic based on the statement's reachability + /// analysis evaluation context. + /// + /// \param Statement If Statement is non-null, delay reporting the + /// diagnostic until the function body is parsed, and then do a basic + /// reachability analysis to determine if the statement is reachable. + /// If it is unreachable, the diagnostic will not be emitted. + bool DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD); + /// Conditionally issue a diagnostic based on the current /// evaluation context. /// diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18785,6 +18785,37 @@ EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E); } +/// Emit a diagnostic when statements are reachable. +/// FIXME: check for reachability even in expressions for which we don't build a +/// CFG (eg, in the initializer of a global or in a constant expression). +/// For example, +/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } +bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD) { + if (!Stmts.empty() && !FunctionScopes.empty()) { + FunctionScopes.back()->PossiblyUnreachableDiags.push_back( + sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; + } + + // The initializer of a constexpr variable or of the first declaration of a + // static data member is not syntactically a constant evaluated constant, + // but nonetheless is always required to be a constant expression, so we + // can skip diagnosing. + // FIXME: Using the mangling context here is a hack. + if (auto *VD = dyn_cast_or_null( + ExprEvalContexts.back().ManglingContextDecl)) { + if (VD->isConstexpr() || + (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) + return false; + // FIXME: For any other kind of variable, we should build a CFG for its + // initializer and check whether the context in question is reachable. + } + + Diag(Loc, PD); + return true; +} + /// Emit a diagnostic that describes an effect on the run-time behavior /// of the program being compiled. /// @@ -18817,28 +18848,7 @@ case ExpressionEvaluationContext::PotentiallyEvaluated: case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { - FunctionScopes.back()->PossiblyUnreachableDiags. - push_back(sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); - return true; - } - - // The initializer of a constexpr variable or of the first declaration of a - // static data member is not syntactically a constant evaluated constant, - // but nonetheless is always required to be a constant expression, so we - // can skip diagnosing. - // FIXME: Using the mangling context here is a hack. - if (auto *VD = dyn_cast_or_null( - ExprEvalContexts.back().ManglingContextDecl)) { - if (VD->isConstexpr() || - (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) - break; - // FIXME: For any other kind of variable, we should build a CFG for its - // initializer and check whether the context in question is reachable. - } - - Diag(Loc, PD); - return true; + return DiagIfReachable(Loc, Stmts, PD); } return false; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -379,7 +379,8 @@ return; } - DiagRuntimeBehavior(Loc, nullptr, PDiag(DiagID) << R1 << R2); + DiagIfReachable(Loc, S ? llvm::makeArrayRef(S) : llvm::None, + PDiag(DiagID) << R1 << R2); } void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) { diff --git a/clang/test/CXX/drs/dr14xx.cpp b/clang/test/CXX/drs/dr14xx.cpp --- a/clang/test/CXX/drs/dr14xx.cpp +++ b/clang/test/CXX/drs/dr14xx.cpp @@ -18,7 +18,7 @@ Check::type *var1; // expected-error {{undeclared identifier 'var1'}} Check::type *var2; // ok, variable declaration expected-note 0+{{here}} Check::type *var3; // expected-error {{undeclared identifier 'var3'}} - Check::type *var4; // expected-error {{undeclared identifier 'var4'}} + Check::type *var4; // expected-error {{undeclared identifier 'var4'}} // value-dependent because of the implied type-dependent 'this->', not because of 'd' Check::type *var5; // expected-error {{undeclared identifier 'var5'}} // value-dependent because of the value-dependent '&' operator, not because of 'A::d' diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp @@ -26,7 +26,7 @@ template requires (T{}) // expected-error{{atomic constraint must be of type 'bool' (found 'int')}} struct B {}; - static_assert((B{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B' required here}} + static_assert(((void)B{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B' required here}} // expected-note@-1{{while checking constraint satisfaction for class template partial specialization 'B' required here}} // expected-note@-2{{during template argument deduction for class template partial specialization 'B' [with T = int *]}} // expected-note@-3{{during template argument deduction for class template partial specialization 'B' [with T = int]}} diff --git a/clang/test/CodeCompletion/pragma-macro-token-caching.c b/clang/test/CodeCompletion/pragma-macro-token-caching.c --- a/clang/test/CodeCompletion/pragma-macro-token-caching.c +++ b/clang/test/CodeCompletion/pragma-macro-token-caching.c @@ -12,7 +12,7 @@ void completeParamPragmaError(int param) { Outer(__extension__({ _Pragma(2) })); // expected-error {{_Pragma takes a parenthesized string literal}} - param; // expected-warning {{expression result unused}} + param; } // RUN: %clang_cc1 -fsyntax-only -verify -code-completion-at=%s:16:1 %s | FileCheck %s diff --git a/clang/test/PCH/cxx-explicit-specifier.cpp b/clang/test/PCH/cxx-explicit-specifier.cpp --- a/clang/test/PCH/cxx-explicit-specifier.cpp +++ b/clang/test/PCH/cxx-explicit-specifier.cpp @@ -12,7 +12,7 @@ template struct T { template - explicit((Y{}, true)) T(A &&a) {} + explicit(((void)Y{}, true)) T(A &&a) {} }; template struct U : T { @@ -28,7 +28,7 @@ U a = foo('0'); } -//CHECK: explicit((char{} , true)) +//CHECK: explicit(((void)char{} , true)) #endif diff --git a/clang/test/Parser/cxx-ambig-decl-expr.cpp b/clang/test/Parser/cxx-ambig-decl-expr.cpp --- a/clang/test/Parser/cxx-ambig-decl-expr.cpp +++ b/clang/test/Parser/cxx-ambig-decl-expr.cpp @@ -36,8 +36,8 @@ int(a[{0}]); // expected-warning {{unused}} // These are array declarations. - int(x[(1,1)]); // expected-error {{redefinition}} - int(x[true ? 1,1 : 1]); // expected-error {{redefinition}} + int(x[((void)1,1)]); // expected-error {{redefinition}} + int(x[true ? 1 : (1,1)]); // expected-error {{redefinition}} // expected-warning {{unused}} int (*_Atomic atomic_ptr_to_int); *atomic_ptr_to_int = 42; diff --git a/clang/test/Parser/cxx0x-ambig.cpp b/clang/test/Parser/cxx0x-ambig.cpp --- a/clang/test/Parser/cxx0x-ambig.cpp +++ b/clang/test/Parser/cxx0x-ambig.cpp @@ -163,7 +163,7 @@ (void)p1; UnsignedTmplArgSink *t0; // ok - UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0; + UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0; // expected-warning 2{{expression result unused}} } template void foo(int, int, int); // expected-note {{in instantiation of function template specialization 'ellipsis::foo' requested here}} diff --git a/clang/test/Sema/const-eval.c b/clang/test/Sema/const-eval.c --- a/clang/test/Sema/const-eval.c +++ b/clang/test/Sema/const-eval.c @@ -74,7 +74,7 @@ EVAL_EXPR(35, constbool) EVAL_EXPR(36, constbool) -EVAL_EXPR(37, (1,2.0) == 2.0 ? 1 : -1) +EVAL_EXPR(37, ((void)1,2.0) == 2.0 ? 1 : -1) EVAL_EXPR(38, __builtin_expect(1,1) == 1 ? 1 : -1) // PR7884 diff --git a/clang/test/Sema/exprs.c b/clang/test/Sema/exprs.c --- a/clang/test/Sema/exprs.c +++ b/clang/test/Sema/exprs.c @@ -13,10 +13,9 @@ // Test that we don't report divide-by-zero errors in unreachable code. -// This test should be left as is, as it also tests CFG functionality. void radar9171946() { if (0) { - 0 / (0 ? 1 : 0); // expected-warning {{expression result unused}} + 0 / (0 ? 1 : 0); // no-warning } } diff --git a/clang/test/Sema/i-c-e.c b/clang/test/Sema/i-c-e.c --- a/clang/test/Sema/i-c-e.c +++ b/clang/test/Sema/i-c-e.c @@ -70,10 +70,12 @@ char z[__builtin_constant_p(4) ? 1 : -1]; // Comma tests -int comma1[0?1,2:3]; -int comma2[1||(1,2)]; // expected-warning {{use of logical '||' with constant operand}} \ - // expected-note {{use '|' for a bitwise operation}} -int comma3[(1,2)]; // expected-warning {{variable length array folded to constant array as an extension}} +int comma1[0?1,2:3]; // expected-warning {{expression result unused}} +int comma2[1 || (1, 2)]; // expected-warning {{use of logical '||' with constant operand}} \ + // expected-note {{use '|' for a bitwise operation}} \ + // expected-warning {{expression result unused}} +int comma3[(1, 2)]; // expected-warning {{variable length array folded to constant array as an extension}} \ + // expected-warning {{expression result unused}} // Pointer + __builtin_constant_p char pbcp[__builtin_constant_p(4) ? (intptr_t)&expr : 0]; // expected-error {{variable length array declaration not allowed at file scope}} diff --git a/clang/test/Sema/vla-2.c b/clang/test/Sema/vla-2.c --- a/clang/test/Sema/vla-2.c +++ b/clang/test/Sema/vla-2.c @@ -13,5 +13,5 @@ } void PotentiallyEvaluatedArrayBoundWarn(int n) { - (void)*(int(*)[(0 << 32,n)])0; // FIXME: We should warn here. + (void)*(int(*)[(0 << 32,n)])0; // expected-warning {{expression result unused}} } diff --git a/clang/test/SemaCXX/attr-annotate.cpp b/clang/test/SemaCXX/attr-annotate.cpp --- a/clang/test/SemaCXX/attr-annotate.cpp +++ b/clang/test/SemaCXX/attr-annotate.cpp @@ -42,7 +42,7 @@ template struct B { - [[clang::annotate("test", (T{}, 9))]] void t() {} + [[clang::annotate("test", ((void)T{}, 9))]] void t() {} // expected-error@-1 {{illegal initializer type 'void'}} }; B b; @@ -73,7 +73,7 @@ [[clang::annotate("jui", b, cf)]] void t2() {} // expected-error@-1 {{'annotate' attribute requires parameter 1 to be a constant expression}} // expected-note@-2 {{is not allowed in a constant expression}} - [[clang::annotate("jui", (b, 0), cf)]] [[clang::annotate("jui", &b, cf, &foo::t2, str())]] void t3() {} + [[clang::annotate("jui", ((void)b, 0), cf)]] [[clang::annotate("jui", &b, cf, &foo::t2, str())]] void t3() {} }; }; diff --git a/clang/test/SemaCXX/builtin-constant-p.cpp b/clang/test/SemaCXX/builtin-constant-p.cpp --- a/clang/test/SemaCXX/builtin-constant-p.cpp +++ b/clang/test/SemaCXX/builtin-constant-p.cpp @@ -157,12 +157,12 @@ constexpr ~A() { *p = 0; } }; struct Q { int n; constexpr int *get() { return &n; } }; - static_assert(!__builtin_constant_p((A{}, 123))); + static_assert(!__builtin_constant_p(((void)A{}, 123))); // FIXME: We should probably accept this. GCC does. // However, GCC appears to do so by running the destructors at the end of the // enclosing full-expression, which seems broken; running them at the end of // the evaluation of the __builtin_constant_p argument would be more // defensible. - static_assert(!__builtin_constant_p((A{Q().get()}, 123))); + static_assert(!__builtin_constant_p(((void)A{Q().get()}, 123))); } #endif diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -745,7 +745,7 @@ // Ensure that we can handle temporary cleanups for array temporaries. struct ArrElem { constexpr ~ArrElem() {} }; using Arr = ArrElem[3]; - static_assert((Arr{}, true)); + static_assert(((void)Arr{}, true)); } namespace dynamic_alloc { diff --git a/clang/test/SemaCXX/constant-expression.cpp b/clang/test/SemaCXX/constant-expression.cpp --- a/clang/test/SemaCXX/constant-expression.cpp +++ b/clang/test/SemaCXX/constant-expression.cpp @@ -88,8 +88,8 @@ void diags(int n) { switch (n) { - case (1/0, 1): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} - case (int)(1/0, 2.0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} + case (1/0, 1): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} expected-warning {{expression result unused}} + case (int)(1/0, 2.0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} expected-warning {{expression result unused}} case __imag(1/0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} case (int)__imag((double)(1/0)): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} ; diff --git a/clang/test/SemaCXX/expression-traits.cpp b/clang/test/SemaCXX/expression-traits.cpp --- a/clang/test/SemaCXX/expression-traits.cpp +++ b/clang/test/SemaCXX/expression-traits.cpp @@ -583,10 +583,10 @@ // Can't use the ASSERT_XXXX macros without adding parens around // the comma expression. - static_assert(__is_lvalue_expr(x,x), "expected an lvalue"); - static_assert(__is_rvalue_expr(x,1), "expected an rvalue"); - static_assert(__is_lvalue_expr(1,x), "expected an lvalue"); - static_assert(__is_rvalue_expr(1,1), "expected an rvalue"); + static_assert(__is_lvalue_expr((void)x,x), "expected an lvalue"); + static_assert(__is_rvalue_expr((void)x,1), "expected an rvalue"); + static_assert(__is_lvalue_expr((void)1,x), "expected an lvalue"); + static_assert(__is_rvalue_expr((void)1,1), "expected an rvalue"); } #if 0 diff --git a/clang/test/SemaCXX/warn-comma-operator.cpp b/clang/test/SemaCXX/warn-comma-operator.cpp --- a/clang/test/SemaCXX/warn-comma-operator.cpp +++ b/clang/test/SemaCXX/warn-comma-operator.cpp @@ -242,8 +242,8 @@ template class Foo { - typedef bool_seq<(xs::value, true)...> all_true; - typedef bool_seq<(xs::value, false)...> all_false; + typedef bool_seq<((void)xs::value, true)...> all_true; + typedef bool_seq<((void)xs::value, false)...> all_false; typedef bool_seq seq; }; diff --git a/clang/test/SemaCXX/warn-unused-value.cpp b/clang/test/SemaCXX/warn-unused-value.cpp --- a/clang/test/SemaCXX/warn-unused-value.cpp +++ b/clang/test/SemaCXX/warn-unused-value.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value %s // RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++98 %s // RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++17 %s // PR4806 namespace test0 { @@ -138,3 +139,26 @@ (void)arr3; (void)arr4; } + +#if __cplusplus >= 201103L // C++11 or later +namespace test5 { +int v[(5, 6)]; // expected-warning {{expression result unused}} +void foo() { + new double[false ? (1, 2) : 3] + // FIXME: We shouldn't diagnose the unreachable constant expression + // here. + [false ? (1, 2) : 3]; // expected-warning {{expression result unused}} +} +} // namespace test5 +#endif + +#if __cplusplus >= 201703L // C++11 or later +namespace test6 { +auto b() { + if constexpr (false) + return (1,0); + else + return (1.0,0.0); // expected-warning {{expression result unused}} +} +} // namespace test6 +#endif