llvm-project/clang/test/SemaCXX/implicit-exception-spec.cpp

153 lines
4.9 KiB
C++
Raw Normal View History

Add -Wunused-local-typedef, a warning that finds unused local typedefs. The warning warns on TypedefNameDecls -- typedefs and C++11 using aliases -- that are !isReferenced(). Since the isReferenced() bit on TypedefNameDecls wasn't used for anything before this warning it wasn't always set correctly, so this patch also adds a few missing MarkAnyDeclReferenced() calls in various places for TypedefNameDecls. This is made a bit complicated due to local typedefs possibly being used only after their local scope has closed. Consider: template <class T> void template_fun(T t) { typename T::Foo s3foo; // YYY (void)s3foo; } void template_fun_user() { struct Local { typedef int Foo; // XXX } p; template_fun(p); } Here the typedef in XXX is only used at end-of-translation unit, when YYY in template_fun() gets instantiated. To handle this, typedefs that are unused when their scope exits are added to a set of potentially unused typedefs, and that set gets checked at end-of-TU. Typedefs that are still unused at that point then get warned on. There's also serialization code for this set, so that the warning works with precompiled headers and modules. For modules, the warning is emitted when the module is built, for precompiled headers each time the header gets used. Finally, consider a function using C++14 auto return types to return a local type defined in a header: auto f() { struct S { typedef int a; }; return S(); } Here, the typedef escapes its local scope and could be used by only some translation units including the header. To not warn on this, add a RecursiveASTVisitor that marks all delcs on local types returned from auto functions as referenced. (Except if it's a function with internal linkage, or the decls are private and the local type has no friends -- in these cases, it _is_ safe to warn.) Several of the included testcases (most of the interesting ones) were provided by Richard Smith. (gcc's spelling -Wunused-local-typedefs is supported as an alias for this warning.) llvm-svn: 217298
2014-09-06 09:25:55 +08:00
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 -Wall -Wno-unused-local-typedefs %s
template<bool b> struct ExceptionIf { static int f(); };
template<> struct ExceptionIf<false> { typedef int f; };
// The exception specification of a defaulted default constructor depends on
// the contents of in-class member initializers. However, the in-class member
// initializers can depend on the exception specification of the constructor,
// since the class is considered complete within them. We reject any such cases.
namespace InClassInitializers {
// Noexcept::Noexcept() is implicitly declared as noexcept(false), because it
// directly invokes ThrowSomething(). However...
//
// If noexcept(Noexcept()) is false, then Noexcept() is a constant expression,
// so noexcept(Noexcept()) is true. But if noexcept(Noexcept()) is true, then
// Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept())
// is false.
bool ThrowSomething() noexcept(false);
struct ConstExpr {
bool b = // expected-note {{declared here}}
noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{default member initializer for 'b' needed}}
};
// Much more obviously broken: we can't parse the initializer without already
// knowing whether it produces a noexcept expression.
struct TemplateArg {
int n = // expected-note {{declared here}}
ExceptionIf<noexcept(TemplateArg())>::f(); // expected-error {{default member initializer for 'n' needed}}
};
// And within a nested class.
struct Nested {
struct Inner {
int n = // expected-note {{declared here}}
ExceptionIf<noexcept(Nested())>::f();
} inner; // expected-error {{default member initializer for 'n' needed}}
};
Final piece of core issue 1330: delay computing the exception specification of a defaulted special member function until the exception specification is needed (using the same criteria used for the delayed instantiation of exception specifications for function temploids). EST_Delayed is now EST_Unevaluated (using 1330's terminology), and, like EST_Uninstantiated, carries a pointer to the FunctionDecl which will be used to resolve the exception specification. This is enabled for all C++ modes: it's a little faster in the case where the exception specification isn't used, allows our C++11-in-C++98 extensions to work, and is still correct for C++98, since in that mode the computation of the exception specification can't fail. The diagnostics here aren't great (in particular, we should include implicit evaluation of exception specifications for defaulted special members in the template instantiation backtraces), but they're not much worse than before. Our approach to the problem of cycles between in-class initializers and the exception specification for a defaulted default constructor is modified a little by this change -- we now reject any odr-use of a defaulted default constructor if that constructor uses an in-class initializer and the use is in an in-class initialzer which is declared lexically earlier. This is a closer approximation to the current draft solution in core issue 1351, but isn't an exact match (but the current draft wording isn't reasonable, so that's to be expected). llvm-svn: 160847
2012-07-27 12:22:15 +08:00
struct Nested2 {
Final piece of core issue 1330: delay computing the exception specification of a defaulted special member function until the exception specification is needed (using the same criteria used for the delayed instantiation of exception specifications for function temploids). EST_Delayed is now EST_Unevaluated (using 1330's terminology), and, like EST_Uninstantiated, carries a pointer to the FunctionDecl which will be used to resolve the exception specification. This is enabled for all C++ modes: it's a little faster in the case where the exception specification isn't used, allows our C++11-in-C++98 extensions to work, and is still correct for C++98, since in that mode the computation of the exception specification can't fail. The diagnostics here aren't great (in particular, we should include implicit evaluation of exception specifications for defaulted special members in the template instantiation backtraces), but they're not much worse than before. Our approach to the problem of cycles between in-class initializers and the exception specification for a defaulted default constructor is modified a little by this change -- we now reject any odr-use of a defaulted default constructor if that constructor uses an in-class initializer and the use is in an in-class initialzer which is declared lexically earlier. This is a closer approximation to the current draft solution in core issue 1351, but isn't an exact match (but the current draft wording isn't reasonable, so that's to be expected). llvm-svn: 160847
2012-07-27 12:22:15 +08:00
struct Inner;
int n = Inner().n; // expected-error {{initializer for 'n' needed}}
struct Inner {
int n = ExceptionIf<noexcept(Nested2())>::f(); // expected-note {{declared here}}
} inner;
Final piece of core issue 1330: delay computing the exception specification of a defaulted special member function until the exception specification is needed (using the same criteria used for the delayed instantiation of exception specifications for function temploids). EST_Delayed is now EST_Unevaluated (using 1330's terminology), and, like EST_Uninstantiated, carries a pointer to the FunctionDecl which will be used to resolve the exception specification. This is enabled for all C++ modes: it's a little faster in the case where the exception specification isn't used, allows our C++11-in-C++98 extensions to work, and is still correct for C++98, since in that mode the computation of the exception specification can't fail. The diagnostics here aren't great (in particular, we should include implicit evaluation of exception specifications for defaulted special members in the template instantiation backtraces), but they're not much worse than before. Our approach to the problem of cycles between in-class initializers and the exception specification for a defaulted default constructor is modified a little by this change -- we now reject any odr-use of a defaulted default constructor if that constructor uses an in-class initializer and the use is in an in-class initialzer which is declared lexically earlier. This is a closer approximation to the current draft solution in core issue 1351, but isn't an exact match (but the current draft wording isn't reasonable, so that's to be expected). llvm-svn: 160847
2012-07-27 12:22:15 +08:00
};
}
namespace ExceptionSpecification {
// FIXME: This diagnostic is quite useless; we should indicate whose
// exception specification we were looking for and why.
struct Nested {
struct T {
T() noexcept(!noexcept(Nested()));
} t; // expected-error{{exception specification is not available until end of class definition}}
};
}
namespace DefaultArgument {
struct Default {
struct T {
T(int = ExceptionIf<noexcept(Default())::f()); // expected-error {{call to implicitly-deleted default constructor}}
} t; // expected-note {{has no default constructor}}
};
}
namespace ImplicitDtorExceptionSpec {
struct A {
virtual ~A();
struct Inner {
~Inner() throw();
};
Inner inner;
};
struct B {
virtual ~B() {} // expected-note {{here}}
};
struct C : B {
virtual ~C() {}
A a;
};
struct D : B {
~D(); // expected-error {{more lax than base}}
struct E {
~E();
struct F {
~F() throw(A);
} f;
} e;
};
}
struct nothrow_t {} nothrow;
void *operator new(decltype(sizeof(0)), nothrow_t) noexcept;
namespace PotentiallyConstructed {
template<bool NE> struct A {
A() noexcept(NE);
A(const A&) noexcept(NE);
A(A&&) noexcept(NE);
A &operator=(const A&) noexcept(NE);
A &operator=(A&&) noexcept(NE);
~A() noexcept(NE);
};
template<bool NE> struct B : virtual A<NE> {};
template<bool NE> struct C : virtual A<NE> {
virtual void f() = 0; // expected-note 2{{unimplemented}}
};
template<bool NE> struct D final : C<NE> {
void f();
};
template<typename T, bool A, bool B, bool C, bool D, bool E, bool F> void check() {
T *p = nullptr;
T &a = *p;
static_assert(noexcept(a = a) == D, "");
static_assert(noexcept(a = static_cast<T&&>(a)) == E, "");
static_assert(noexcept(delete &a) == F, ""); // expected-warning 2{{abstract}}
// These are last because the first failure here causes instantiation to bail out.
static_assert(noexcept(new (nothrow) T()) == A, ""); // expected-error 2{{abstract}}
static_assert(noexcept(new (nothrow) T(a)) == B, "");
static_assert(noexcept(new (nothrow) T(static_cast<T&&>(a))) == C, "");
}
template void check<A<false>, 0, 0, 0, 0, 0, 0>();
template void check<A<true >, 1, 1, 1, 1, 1, 1>();
template void check<B<false>, 0, 0, 0, 0, 0, 0>();
template void check<B<true >, 1, 1, 1, 1, 1, 1>();
template void check<C<false>, 1, 1, 1, 0, 0, 0>(); // expected-note {{instantiation}}
template void check<C<true >, 1, 1, 1, 1, 1, 1>(); // expected-note {{instantiation}}
template void check<D<false>, 0, 0, 0, 0, 0, 0>();
template void check<D<true >, 1, 1, 1, 1, 1, 1>();
// ... the above trick doesn't work for this case...
struct Cfalse : virtual A<false> {
virtual void f() = 0;
Cfalse() noexcept;
Cfalse(const Cfalse&) noexcept;
Cfalse(Cfalse&&) noexcept;
};
Cfalse::Cfalse() noexcept = default;
Cfalse::Cfalse(const Cfalse&) noexcept = default;
Cfalse::Cfalse(Cfalse&&) noexcept = default;
}