diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 67e0c7c63909e..fb2250d1c98af 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 +struct Polymorphic { + Polymorphic() noexcept(NoexceptConstructor) {} + virtual ~Polymorphic() noexcept(NoexceptDestructor) {} +}; + +static_assert(noexcept(typeid(Polymorphic{}))); // Not evaluated (not glvalue) +static_assert(noexcept(typeid((Polymorphic&&) Polymorphic{}))); +static_assert(!noexcept(typeid((Polymorphic&&) Polymorphic{}))); +static_assert(!noexcept(typeid((Polymorphic&&) Polymorphic{}))); +static_assert(!noexcept(typeid(*&(const Polymorphic&) Polymorphic{}))); +static_assert(!noexcept(typeid(*&(const Polymorphic&) Polymorphic{}))); +static_assert(!noexcept(typeid(*&(const Polymorphic&) Polymorphic{}))); + +template +struct X { + template void f(); +}; +template +void f1() { + X 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* for example) + dependent.f(); // 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 +void f2() { + X*>(nullptr) && ... && T{}))))> dependent; + // X when T...[0] is a type with some operator&& which returns int* + // X when sizeof...(T) == 0 + dependent.f(); + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} +} +template +void f3() { + X(nullptr)))> dependent; + // X when T is int, X when T is Polymorphic + dependent.f(); + // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} +} +template +void f4() { + X not_dependent; + not_dependent.non_existent(); + // expected-error@-1 {{no member named 'non_existent' in 'typeid_::X'}} +} +template +void f5() { + X not_dependent; + not_dependent.non_existent(); + // expected-error@-1 {{no member named 'non_existent' in 'typeid_::X'}} +} +} // namespace typeid_