https://github.com/epiqc/ScaffCC
Raw File
Tip revision: 4d7bfa034cfaea4e8346396c6198cdd3e271d272 authored by Andrew Litteken on 23 April 2020, 16:55:47 UTC
Version 5 Upgrade! (#40)
Tip revision: 4d7bfa0
more-dtors-cfg-output.cpp
// 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
back to top