llvm-project/clang/test/Analysis/more-dtors-cfg-output.cpp

318 lines
9.7 KiB
C++

// RUN: rm -f %t.14 %t.2a
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1
// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s
// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s
int puts(const char *);
struct Foo {
Foo() = delete;
#if CXX2A
// Guarantee that the elided examples are actually elided by deleting the
// copy constructor.
Foo(const Foo &) = delete;
#else
// No elision support, so we need a copy constructor.
Foo(const Foo &);
#endif
~Foo();
};
struct TwoFoos {
Foo foo1, foo2;
~TwoFoos();
};
Foo get_foo();
struct Bar {
Bar();
Bar(const Bar &);
~Bar();
Bar &operator=(const Bar &);
};
Bar get_bar();
struct TwoBars {
Bar foo1, foo2;
~TwoBars();
};
// Start of tests:
void elided_assign() {
Foo x = get_foo();
}
// CHECK: void elided_assign()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_assign() {
Bar x = (const Bar &)get_bar();
}
// CHECK: void nonelided_assign()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_paren_init() {
Foo x(get_foo());
}
// CHECK: void elided_paren_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_paren_init() {
Bar x((const Bar &)get_bar());
}
// CHECK: void nonelided_paren_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_brace_init() {
Foo x{get_foo()};
}
// CHECK: void elided_brace_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_brace_init() {
Bar x{(const Bar &)get_bar()};
}
// CHECK: void nonelided_brace_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_lambda_capture_init() {
// The copy from get_foo() into the lambda should be elided. Should call
// the lambda's destructor, but not ~Foo() separately.
// (This syntax is C++14 'generalized lambda captures'.)
auto z = [x=get_foo()]() {};
}
// CHECK: void elided_lambda_capture_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
void nonelided_lambda_capture_init() {
// Should call the lambda's destructor as well as ~Bar() for the temporary.
auto z = [x((const Bar &)get_bar())]() {};
}
// CHECK: void nonelided_lambda_capture_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
Foo elided_return_stmt_expr() {
// Two copies, both elided in C++17.
return ({ get_foo(); });
}
// CHECK: Foo elided_return_stmt_expr()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
void elided_stmt_expr() {
// One copy, elided in C++17.
({ get_foo(); });
}
// CHECK: void elided_stmt_expr()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
void elided_stmt_expr_multiple_stmts() {
// Make sure that only the value returned out of a statement expression is
// elided.
({ get_bar(); get_foo(); });
}
// CHECK: void elided_stmt_expr_multiple_stmts()
// CHECK: ~Bar() (Temporary object destructor)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
void unelided_stmt_expr() {
({ (const Bar &)get_bar(); });
}
// CHECK: void unelided_stmt_expr()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
void elided_aggregate_init() {
TwoFoos x{get_foo(), get_foo()};
}
// CHECK: void elided_aggregate_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~TwoFoos() (Implicit destructor)
void nonelided_aggregate_init() {
TwoBars x{(const Bar &)get_bar(), (const Bar &)get_bar()};
}
// CHECK: void nonelided_aggregate_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~TwoBars() (Implicit destructor)
TwoFoos return_aggregate_init() {
return TwoFoos{get_foo(), get_foo()};
}
// CHECK: TwoFoos return_aggregate_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~TwoFoos() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
void lifetime_extended() {
const Foo &x = (get_foo(), get_foo());
puts("one destroyed before, one destroyed after");
}
// CHECK: void lifetime_extended()
// CHECK: ~Foo() (Temporary object destructor)
// CHECK: one destroyed before, one destroyed after
// CHECK: ~Foo() (Implicit destructor)
void not_lifetime_extended() {
Foo x = (get_foo(), get_foo());
puts("one destroyed before, one destroyed after");
}
// CHECK: void not_lifetime_extended()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CHECK: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: one destroyed before, one destroyed after
// CHECK: ~Foo() (Implicit destructor)
void compound_literal() {
(void)(Bar[]){{}, {}};
}
// CHECK: void compound_literal()
// CHECK: (CXXConstructExpr, struct Bar)
// CHECK: (CXXConstructExpr, struct Bar)
// CHECK: ~Bar[2]() (Temporary object destructor)
Foo elided_return() {
return get_foo();
}
// CHECK: Foo elided_return()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
auto elided_return_lambda() {
return [x=get_foo()]() {};
}
// CHECK: (lambda at {{.*}}) elided_return_lambda()
// CXX14: (CXXConstructExpr{{.*}}, class (lambda at {{.*}}))
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
void const_auto_obj() {
const Bar bar;
}
// CHECK: void const_auto_obj()
// CHECK: .~Bar() (Implicit destructor)
void has_default_arg(Foo foo = get_foo());
void test_default_arg() {
// FIXME: This emits a destructor but no constructor. Search CFG.cpp for
// 'PR13385' for details.
has_default_arg();
}
// CHECK: void test_default_arg()
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
struct DefaultArgInCtor {
DefaultArgInCtor(Foo foo = get_foo());
~DefaultArgInCtor();
};
void default_ctor_with_default_arg() {
// FIXME: Default arguments are mishandled in two ways:
// - The constructor is not emitted at all (not specific to arrays; see fixme
// in CFG.cpp that mentions PR13385).
// - The destructor is emitted once, even though the default argument will be
// constructed and destructed once per array element.
// Ideally, the CFG would expand array constructions into a loop that
// constructs each array element, allowing default argument
// constructor/destructor calls to be correctly placed inside the loop.
DefaultArgInCtor qux[3];
}
// CHECK: void default_ctor_with_default_arg()
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor[3]
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
// CHECK: .~DefaultArgInCtor[3]() (Implicit destructor)
void new_default_ctor_with_default_arg(long count) {
// Same problems as above.
new DefaultArgInCtor[count];
}
// CHECK: void new_default_ctor_with_default_arg(long count)
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor[]
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
#if CXX2A
// Boilerplate needed to test co_return:
namespace std {
template <typename Promise>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
} // namespace std
struct TestPromise {
TestPromise initial_suspend();
TestPromise final_suspend() noexcept;
bool await_ready() noexcept;
void await_suspend(const std::coroutine_handle<TestPromise> &) noexcept;
void await_resume() noexcept;
Foo return_value(const Bar &);
Bar get_return_object();
void unhandled_exception();
};
namespace std {
template <typename Ret, typename... Args>
struct coroutine_traits;
template <>
struct coroutine_traits<Bar> {
using promise_type = TestPromise;
};
} // namespace std
Bar coreturn() {
co_return get_bar();
// This expands to something like:
// promise.return_value(get_bar());
// get_bar() is passed by reference to return_value() and is then destroyed;
// there is no equivalent of RVO. TestPromise::return_value also returns a
// Foo, which should be immediately destroyed.
// FIXME: The generated CFG completely ignores get_return_object().
}
// CXX2A: Bar coreturn()
// CXX2A: ~Foo() (Temporary object destructor)
// CXX2A: ~Bar() (Temporary object destructor)
#endif