[clang] b608b22 - [Clang] [Sema] Ensure noexcept(typeid(E)) checks if E throws when needed (#95846)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 20 06:43:18 PDT 2024


Author: Mital Ashok
Date: 2024-06-20T15:43:14+02:00
New Revision: b608b223ab152bb84c8f28a4a1184f9033c99560

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

LOG: [Clang] [Sema] Ensure noexcept(typeid(E)) checks if E throws when needed (#95846)

3ad31e12ccfc7db25f3cbedc4ee966e7099ac78f changed it so that not all
potentially-evaluated `typeid`s were marked as potentially-throwing, but
I forgot to check the subexpression if the null check of the `typeid`
didn't potentially-throw. This adds that check.

Added: 
    

Modified: 
    clang/lib/Sema/SemaExceptionSpec.cpp
    clang/test/SemaCXX/cxx0x-noexcept-expression.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index c914448885b5e..d226e3bb13072 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1111,13 +1111,22 @@ static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) {
 }
 
 static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) {
+  // A typeid of a type is a constant and does not throw.
   if (DC->isTypeOperand())
     return CT_Cannot;
 
   if (DC->isValueDependent())
     return CT_Dependent;
 
-  return DC->hasNullCheck() ? CT_Can : CT_Cannot;
+  // If this operand is not evaluated it cannot possibly throw.
+  if (!DC->isPotentiallyEvaluated())
+    return CT_Cannot;
+
+  // Can throw std::bad_typeid if a nullptr is dereferenced.
+  if (DC->hasNullCheck())
+    return CT_Can;
+
+  return S.canThrow(DC->getExprOperand());
 }
 
 CanThrowResult Sema::canThrow(const Stmt *S) {

diff  --git a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
index c2b2244c117a0..c616a77f36619 100644
--- a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
+++ b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
@@ -1,6 +1,10 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s -fexceptions -fcxx-exceptions -Wno-unevaluated-expression
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s -fexceptions -fcxx-exceptions -Wno-unevaluated-expression -fexperimental-new-constant-interpreter
 
+namespace std {
+struct type_info;
+}
+
 void f(); // expected-note {{possible target for call}}
 void f(int); // expected-note {{possible target for call}}
 
@@ -97,3 +101,59 @@ void j() noexcept(0);
 void k() noexcept(1);
 void l() noexcept(2); // expected-error {{noexcept specifier argument evaluates to 2, which cannot be narrowed to type 'bool'}}
 } // namespace P1401
+
+namespace typeid_ {
+template<bool NoexceptConstructor, bool NoexceptDestructor>
+struct Polymorphic {
+  Polymorphic() noexcept(NoexceptConstructor) {}
+  virtual ~Polymorphic() noexcept(NoexceptDestructor) {}
+};
+
+static_assert(noexcept(typeid(Polymorphic<false, false>{})));  // Not evaluated (not glvalue)
+static_assert(noexcept(typeid((Polymorphic<true, true>&&) Polymorphic<true, true>{})));
+static_assert(!noexcept(typeid((Polymorphic<false, true>&&) Polymorphic<false, true>{})));
+static_assert(!noexcept(typeid((Polymorphic<true, false>&&) Polymorphic<true, false>{})));
+static_assert(!noexcept(typeid(*&(const Polymorphic<true, true>&) Polymorphic<true, true>{})));
+static_assert(!noexcept(typeid(*&(const Polymorphic<false, true>&) Polymorphic<false, true>{})));
+static_assert(!noexcept(typeid(*&(const Polymorphic<true, false>&) Polymorphic<true, false>{})));
+
+template<bool B>
+struct X {
+  template<typename T> void f();
+};
+template<typename T>
+void f1() {
+  X<noexcept(typeid(*T{}))> dependent;
+  // `dependent` should be type-dependent because the noexcept-expression should be value-dependent
+  // (it is true if T is int*, false if T is Polymorphic<false, false>* for example)
+  dependent.f<void>();  // This should need to be `.template f` to parse as a template
+  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+}
+template<typename... T>
+void f2() {
+  X<noexcept(typeid(*((static_cast<Polymorphic<false, false>*>(nullptr) && ... && T{}))))> dependent;
+  // X<true> when T...[0] is a type with some operator&& which returns int*
+  // X<false> when sizeof...(T) == 0
+  dependent.f<void>();
+  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+}
+template<typename T>
+void f3() {
+  X<noexcept(typeid(*static_cast<T*>(nullptr)))> dependent;
+  // X<true> when T is int, X<false> when T is Polymorphic<false, false>
+  dependent.f<void>();
+  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+}
+template<typename T>
+void f4() {
+  X<noexcept(typeid(T))> not_dependent;
+  not_dependent.non_existent();
+  // expected-error at -1 {{no member named 'non_existent' in 'typeid_::X<true>'}}
+}
+template<typename T>
+void f5() {
+  X<noexcept(typeid(sizeof(sizeof(T))))> not_dependent;
+  not_dependent.non_existent();
+  // expected-error at -1 {{no member named 'non_existent' in 'typeid_::X<true>'}}
+}
+} // namespace typeid_


        


More information about the cfe-commits mailing list