forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			318 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			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::experimental {
 | |
|   template <typename Promise>
 | |
|   struct coroutine_handle {
 | |
|     static coroutine_handle from_address(void *);
 | |
|   };
 | |
| }
 | |
| 
 | |
| struct TestPromise {
 | |
|   TestPromise initial_suspend();
 | |
|   TestPromise final_suspend();
 | |
|   bool await_ready();
 | |
|   void await_suspend(const std::experimental::coroutine_handle<TestPromise> &);
 | |
|   void await_resume();
 | |
|   Foo return_value(const Bar &);
 | |
|   Bar get_return_object();
 | |
|   void unhandled_exception();
 | |
| };
 | |
| 
 | |
| namespace std::experimental {
 | |
|   template <typename Ret, typename... Args>
 | |
|   struct coroutine_traits;
 | |
|   template <>
 | |
|   struct coroutine_traits<Bar> {
 | |
|       using promise_type = TestPromise;
 | |
|   };
 | |
| }
 | |
| 
 | |
| 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
 |