diff --git a/clang/include/clang/AST/EvaluatedExprVisitor.h b/clang/include/clang/AST/EvaluatedExprVisitor.h --- a/clang/include/clang/AST/EvaluatedExprVisitor.h +++ b/clang/include/clang/AST/EvaluatedExprVisitor.h @@ -32,6 +32,9 @@ const ASTContext &Context; public: + // Return whether this visitor should recurse into discarded statements for a + // 'constexpr-if'. + bool shouldVisitDiscardedStmt() const { return true; } #define PTR(CLASS) typename Ptr::type explicit EvaluatedExprVisitorBase(const ASTContext &Context) : Context(Context) { } @@ -83,7 +86,7 @@ void VisitCallExpr(PTR(CallExpr) CE) { if (!CE->isUnevaluatedBuiltinCall(Context)) - return static_cast(this)->VisitExpr(CE); + return getDerived().VisitExpr(CE); } void VisitLambdaExpr(PTR(LambdaExpr) LE) { @@ -103,6 +106,20 @@ this->Visit(SubStmt); } + void VisitIfStmt(PTR(IfStmt) If) { + if (!getDerived().shouldVisitDiscardedStmt()) { + if (auto SubStmt = If->getNondiscardedCase(Context)) { + if (*SubStmt) + this->Visit(*SubStmt); + return; + } + } + + getDerived().VisitStmt(If); + } + + ImplClass &getDerived() { return *static_cast(this); } + #undef PTR }; diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/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; diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp --- a/clang/lib/AST/Stmt.cpp +++ b/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) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1569,6 +1569,8 @@ DeferredDiagnosticsEmitter(Sema &S) : Inherited(S), ShouldEmitRootNode(false), InOMPDeviceContext(0) {} + bool shouldVisitDiscardedStmt() const { return false; } + void VisitOMPTargetDirective(OMPTargetDirective *Node) { ++InOMPDeviceContext; Inherited::VisitOMPTargetDirective(Node); diff --git a/clang/test/SemaCUDA/deferred-diags.cu b/clang/test/SemaCUDA/deferred-diags.cu --- a/clang/test/SemaCUDA/deferred-diags.cu +++ b/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(); }