summaryrefslogtreecommitdiffstats
path: root/clang/test/SemaCXX/builtin-operator-new-delete.cpp
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2018-03-21 19:19:48 +0000
committerEric Fiselier <eric@efcs.ca>2018-03-21 19:19:48 +0000
commitfa752f23cc20d38259a84a1c44e508df4503f284 (patch)
tree3e70a071037e71411ba137b80b5c14dca0850e81 /clang/test/SemaCXX/builtin-operator-new-delete.cpp
parentb17fff79f0e933b4b6955d4b308f2fa66f3d169f (diff)
downloadbcm5719-llvm-fa752f23cc20d38259a84a1c44e508df4503f284.tar.gz
bcm5719-llvm-fa752f23cc20d38259a84a1c44e508df4503f284.zip
[Builtins] Overload __builtin_operator_new/delete to allow forwarding to usual allocation/deallocation functions.
Summary: Libc++'s default allocator uses `__builtin_operator_new` and `__builtin_operator_delete` in order to allow the calls to new/delete to be ellided. However, libc++ now needs to support over-aligned types in the default allocator. In order to support this without disabling the existing optimization Clang needs to support calling the aligned new overloads from the builtins. See llvm.org/PR22634 for more information about the libc++ bug. This patch changes `__builtin_operator_new`/`__builtin_operator_delete` to call any usual `operator new`/`operator delete` function. It does this by performing overload resolution with the arguments passed to the builtin to determine which allocation function to call. If the selected function is not a usual allocation function a diagnostic is issued. One open issue is if the `align_val_t` overloads should be considered "usual" when `LangOpts::AlignedAllocation` is disabled. In order to allow libc++ to detect this new behavior the value for `__has_builtin(__builtin_operator_new)` has been updated to `201802`. Reviewers: rsmith, majnemer, aaron.ballman, erik.pilkington, bogner, ahatanak Reviewed By: rsmith Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D43047 llvm-svn: 328134
Diffstat (limited to 'clang/test/SemaCXX/builtin-operator-new-delete.cpp')
-rw-r--r--clang/test/SemaCXX/builtin-operator-new-delete.cpp153
1 files changed, 153 insertions, 0 deletions
diff --git a/clang/test/SemaCXX/builtin-operator-new-delete.cpp b/clang/test/SemaCXX/builtin-operator-new-delete.cpp
new file mode 100644
index 00000000000..fb5e9a48574
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-operator-new-delete.cpp
@@ -0,0 +1,153 @@
+// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++03 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fsized-deallocation %s
+
+#if !__has_builtin(__builtin_operator_new) || !__has_builtin(__builtin_operator_delete)
+#error builtins should always be available
+#endif
+
+#if __has_builtin(__builtin_operator_new) != 201802L || \
+ __has_builtin(__builtin_operator_delete) != 201802L
+#error builtin should report updated value
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+namespace std {
+ struct nothrow_t {};
+#if __cplusplus >= 201103L
+enum class align_val_t : size_t {};
+#else
+ enum align_val_t { __zero = 0,
+ __max = (size_t)-1 };
+#endif
+}
+std::nothrow_t nothrow;
+
+void *operator new(size_t); // expected-note 1+ {{candidate function}}
+void operator delete(void *); // expected-note 1+ {{candidate function}}
+
+// Declare the reserved placement operators.
+void *operator new(size_t, void*) throw(); // expected-note 1+ {{candidate function}}
+void operator delete(void *, void *)throw(); // expected-note 1+ {{candidate function}}
+void *operator new[](size_t, void*) throw();
+void operator delete[](void*, void*) throw();
+
+// Declare the replaceable global allocation operators.
+void *operator new(size_t, const std::nothrow_t &) throw(); // expected-note 1+ {{candidate function}}
+void *operator new[](size_t, const std::nothrow_t &) throw();
+void operator delete(void *, const std::nothrow_t &)throw(); // expected-note 1+ {{candidate function}}
+void operator delete[](void *, const std::nothrow_t &) throw();
+
+// aligned allocation and deallocation functions.
+void* operator new ( size_t count, std::align_val_t al); // expected-note 1+ {{candidate function}}
+void operator delete(void *, std::align_val_t); // expected-note 1+ {{candidate}}
+#ifndef __cpp_aligned_new
+// expected-note@-3 1+ {{non-usual 'operator new' declared here}}
+// expected-note@-3 1+ {{non-usual 'operator delete' declared here}}
+#endif
+void *operator new[](size_t count, std::align_val_t al);
+void operator delete[](void*, std::align_val_t);
+
+void operator delete(void *, size_t); // expected-note 1+ {{candidate}}
+#ifndef __cpp_sized_deallocation
+// expected-note@-2 1+ {{non-usual 'operator delete' declared here}}
+#endif
+void operator delete[](void*, size_t);
+
+// Declare some other placemenet operators.
+void *operator new(size_t, void*, bool) throw(); // expected-note 1+ {{candidate function}}
+void *operator new[](size_t, void*, bool) throw();
+
+void *NP = 0;
+
+void test_typo_in_args() {
+ __builtin_operator_new(DNE); // expected-error {{undeclared identifier 'DNE'}}
+ __builtin_operator_new(DNE, DNE2); // expected-error {{undeclared identifier 'DNE'}} expected-error {{'DNE2'}}
+ __builtin_operator_delete(DNE); // expected-error {{'DNE'}}
+ __builtin_operator_delete(DNE, DNE2); // expected-error {{'DNE'}} expected-error {{'DNE2'}}
+}
+
+void test_arg_types() {
+ __builtin_operator_new(NP); // expected-error {{no matching function for call to 'operator new'}}
+ __builtin_operator_new(NP, std::align_val_t(0)); // expected-error {{no matching function for call to 'operator new'}}}
+}
+void test_return_type() {
+ int w = __builtin_operator_new(42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}}
+ int y = __builtin_operator_delete(NP); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}}
+}
+
+void test_aligned_new() {
+#ifdef __cpp_aligned_new
+ void *p = __builtin_operator_new(42, std::align_val_t(2));
+ __builtin_operator_delete(p, std::align_val_t(2));
+#else
+ // FIXME: We've manually declared the aligned new/delete overloads,
+ // but LangOpts::AlignedAllocation is false. Should our overloads be considered
+ // usual allocation/deallocation functions?
+ void *p = __builtin_operator_new(42, std::align_val_t(2)); // expected-error {{call to '__builtin_operator_new' selects non-usual allocation function}}
+ __builtin_operator_delete(p, std::align_val_t(2)); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
+#endif
+}
+
+void test_sized_delete() {
+#ifdef __cpp_sized_deallocation
+ __builtin_operator_delete(NP, 4);
+#else
+ __builtin_operator_delete(NP, 4); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
+#endif
+}
+
+void *operator new(size_t, bool); // expected-note 1+ {{candidate}}
+// expected-note@-1 {{non-usual 'operator new' declared here}}
+void operator delete(void *, bool); // expected-note 1+ {{candidate}}
+// expected-note@-1 {{non-usual 'operator delete' declared here}}
+
+void test_non_usual() {
+ __builtin_operator_new(42, true); // expected-error {{call to '__builtin_operator_new' selects non-usual allocation function}}
+ __builtin_operator_delete(NP, false); // expected-error {{call to '__builtin_operator_delete' selects non-usual deallocation function}}
+}
+
+template <int ID>
+struct Tag {};
+struct ConvertsToTypes {
+ operator std::align_val_t() const;
+ operator Tag<0>() const;
+};
+
+void *operator new(size_t, Tag<0>); // expected-note 0+ {{candidate}}
+void operator delete(void *, Tag<0>); // expected-note 0+ {{candidate}}
+
+void test_ambiguous() {
+#ifdef __cpp_aligned_new
+ ConvertsToTypes cvt;
+ __builtin_operator_new(42, cvt); // expected-error {{call to 'operator new' is ambiguous}}
+ __builtin_operator_delete(NP, cvt); // expected-error {{call to 'operator delete' is ambiguous}}
+#endif
+}
+
+void test_no_args() {
+ __builtin_operator_new(); // expected-error {{no matching function for call to 'operator new'}}
+ __builtin_operator_delete(); // expected-error {{no matching function for call to 'operator delete'}}
+}
+
+void test_no_matching_fn() {
+ Tag<1> tag;
+ __builtin_operator_new(42, tag); // expected-error {{no matching function for call to 'operator new'}}
+ __builtin_operator_delete(NP, tag); // expected-error {{no matching function for call to 'operator delete'}}
+}
+
+template <class Tp, class Up, class RetT>
+void test_dependent_call(Tp new_arg, Up delete_arg, RetT) {
+ RetT ret = __builtin_operator_new(new_arg);
+ __builtin_operator_delete(delete_arg);
+}
+template void test_dependent_call(int, int*, void*);
+
+void test_const_attribute() {
+ __builtin_operator_new(42); // expected-warning {{ignoring return value of function declared with const attribute}}
+#ifdef __cpp_aligned_new
+ __builtin_operator_new(42, std::align_val_t(8)); // expected-warning {{ignoring return value of function declared with const attribute}}
+#endif
+}
OpenPOWER on IntegriCloud