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
 |