Index: clang/include/clang/AST/Stmt.h =================================================================== --- clang/include/clang/AST/Stmt.h +++ clang/include/clang/AST/Stmt.h @@ -2080,6 +2080,7 @@ /// If this is an 'if constexpr', determine which substatement will be taken. /// Otherwise, or if the condition is value-dependent, returns None. Optional getNondiscardedCase(const ASTContext &Ctx) const; + Optional getNondiscardedCase(const ASTContext &Ctx); bool isObjCAvailabilityCheck() const; Index: clang/lib/AST/Stmt.cpp =================================================================== --- clang/lib/AST/Stmt.cpp +++ clang/lib/AST/Stmt.cpp @@ -989,12 +989,20 @@ return isa(getCond()); } -Optional IfStmt::getNondiscardedCase(const ASTContext &Ctx) const { +Optional IfStmt::getNondiscardedCase(const ASTContext &Ctx) { if (!isConstexpr() || getCond()->isValueDependent()) return None; return !getCond()->EvaluateKnownConstInt(Ctx) ? getElse() : getThen(); } +Optional +IfStmt::getNondiscardedCase(const ASTContext &Ctx) const { + if (Optional Result = + const_cast(this)->getNondiscardedCase(Ctx)) + return *Result; + return None; +} + ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP) Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -1666,6 +1666,18 @@ } } } + + void VisitIfStmt(IfStmt *If) { + // Only evaluate the non-discarded side of a constexpr-if. + if (Optional SubStmt = If->getNondiscardedCase(S.Context)) { + // The non-discarded statement could can be a non-existent 'else', so make + // sure we don't try to visit it. + if (*SubStmt) + Inherited::Visit(*SubStmt); + } else { + Inherited::VisitStmt(If); + } + } }; } // namespace Index: clang/test/SemaCUDA/deferred-diags.cu =================================================================== --- clang/test/SemaCUDA/deferred-diags.cu +++ clang/test/SemaCUDA/deferred-diags.cu @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fcuda-is-device -fsyntax-only -verify %s +// RUN: %clang_cc1 -fcxx-exceptions -fcuda-is-device -fsyntax-only -std=c++17 -verify %s #include "Inputs/cuda.h" @@ -8,29 +8,55 @@ // expected-error@-1 2{{cannot use 'throw' in __host__ __device__ function}} } +inline __host__ __device__ void hasInvalid2() { + throw NULL; + // expected-error@-1 2{{cannot use 'throw' in __host__ __device__ function}} +} + +inline __host__ __device__ void hasInvalidDiscarded() { + // This is only used in the discarded statements below, so this should not diagnose. + throw NULL; +} + static __device__ void use0() { hasInvalid(); // expected-note {{called by 'use0'}} hasInvalid(); // expected-note {{called by 'use0'}} + + if constexpr (true) { + hasInvalid2(); // expected-note {{called by 'use0'}} + } else { + hasInvalidDiscarded(); + } + + if constexpr (false) { + hasInvalidDiscarded(); + } else { + hasInvalid2(); // expected-note {{called by 'use0'}} + } + + if constexpr (false) { + hasInvalidDiscarded(); + } } // To avoid excessive diagnostic messages, deferred diagnostics are only // emitted the first time a function is called. static __device__ void use1() { - use0(); // expected-note 2{{called by 'use1'}} + use0(); // expected-note 4{{called by 'use1'}} use0(); } static __device__ void use2() { - use1(); // expected-note 2{{called by 'use2'}} + use1(); // expected-note 4{{called by 'use2'}} use1(); } static __device__ void use3() { - use2(); // expected-note 2{{called by 'use3'}} + use2(); // expected-note 4{{called by 'use3'}} use2(); } __global__ void use4() { - use3(); // expected-note 2{{called by 'use4'}} + use3(); // expected-note 4{{called by 'use4'}} use3(); }