Skip to content

Commit b608b22

Browse files
authored
[Clang] [Sema] Ensure noexcept(typeid(E)) checks if E throws when needed (#95846)
3ad31e1 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.
1 parent 33c9331 commit b608b22

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

clang/lib/Sema/SemaExceptionSpec.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,13 +1111,22 @@ static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) {
11111111
}
11121112

11131113
static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) {
1114+
// A typeid of a type is a constant and does not throw.
11141115
if (DC->isTypeOperand())
11151116
return CT_Cannot;
11161117

11171118
if (DC->isValueDependent())
11181119
return CT_Dependent;
11191120

1120-
return DC->hasNullCheck() ? CT_Can : CT_Cannot;
1121+
// If this operand is not evaluated it cannot possibly throw.
1122+
if (!DC->isPotentiallyEvaluated())
1123+
return CT_Cannot;
1124+
1125+
// Can throw std::bad_typeid if a nullptr is dereferenced.
1126+
if (DC->hasNullCheck())
1127+
return CT_Can;
1128+
1129+
return S.canThrow(DC->getExprOperand());
11211130
}
11221131

11231132
CanThrowResult Sema::canThrow(const Stmt *S) {

clang/test/SemaCXX/cxx0x-noexcept-expression.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s -fexceptions -fcxx-exceptions -Wno-unevaluated-expression
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s -fexceptions -fcxx-exceptions -Wno-unevaluated-expression -fexperimental-new-constant-interpreter
33

4+
namespace std {
5+
struct type_info;
6+
}
7+
48
void f(); // expected-note {{possible target for call}}
59
void f(int); // expected-note {{possible target for call}}
610

@@ -97,3 +101,59 @@ void j() noexcept(0);
97101
void k() noexcept(1);
98102
void l() noexcept(2); // expected-error {{noexcept specifier argument evaluates to 2, which cannot be narrowed to type 'bool'}}
99103
} // namespace P1401
104+
105+
namespace typeid_ {
106+
template<bool NoexceptConstructor, bool NoexceptDestructor>
107+
struct Polymorphic {
108+
Polymorphic() noexcept(NoexceptConstructor) {}
109+
virtual ~Polymorphic() noexcept(NoexceptDestructor) {}
110+
};
111+
112+
static_assert(noexcept(typeid(Polymorphic<false, false>{}))); // Not evaluated (not glvalue)
113+
static_assert(noexcept(typeid((Polymorphic<true, true>&&) Polymorphic<true, true>{})));
114+
static_assert(!noexcept(typeid((Polymorphic<false, true>&&) Polymorphic<false, true>{})));
115+
static_assert(!noexcept(typeid((Polymorphic<true, false>&&) Polymorphic<true, false>{})));
116+
static_assert(!noexcept(typeid(*&(const Polymorphic<true, true>&) Polymorphic<true, true>{})));
117+
static_assert(!noexcept(typeid(*&(const Polymorphic<false, true>&) Polymorphic<false, true>{})));
118+
static_assert(!noexcept(typeid(*&(const Polymorphic<true, false>&) Polymorphic<true, false>{})));
119+
120+
template<bool B>
121+
struct X {
122+
template<typename T> void f();
123+
};
124+
template<typename T>
125+
void f1() {
126+
X<noexcept(typeid(*T{}))> dependent;
127+
// `dependent` should be type-dependent because the noexcept-expression should be value-dependent
128+
// (it is true if T is int*, false if T is Polymorphic<false, false>* for example)
129+
dependent.f<void>(); // This should need to be `.template f` to parse as a template
130+
// expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}}
131+
}
132+
template<typename... T>
133+
void f2() {
134+
X<noexcept(typeid(*((static_cast<Polymorphic<false, false>*>(nullptr) && ... && T{}))))> dependent;
135+
// X<true> when T...[0] is a type with some operator&& which returns int*
136+
// X<false> when sizeof...(T) == 0
137+
dependent.f<void>();
138+
// expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}}
139+
}
140+
template<typename T>
141+
void f3() {
142+
X<noexcept(typeid(*static_cast<T*>(nullptr)))> dependent;
143+
// X<true> when T is int, X<false> when T is Polymorphic<false, false>
144+
dependent.f<void>();
145+
// expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}}
146+
}
147+
template<typename T>
148+
void f4() {
149+
X<noexcept(typeid(T))> not_dependent;
150+
not_dependent.non_existent();
151+
// expected-error@-1 {{no member named 'non_existent' in 'typeid_::X<true>'}}
152+
}
153+
template<typename T>
154+
void f5() {
155+
X<noexcept(typeid(sizeof(sizeof(T))))> not_dependent;
156+
not_dependent.non_existent();
157+
// expected-error@-1 {{no member named 'non_existent' in 'typeid_::X<true>'}}
158+
}
159+
} // namespace typeid_

0 commit comments

Comments
 (0)