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@-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@-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@-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@-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@-1 {{no member named 'non_existent' in 'typeid_::X<true>'}} +} +} // namespace typeid_ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits