<html>
    <head>
      <base href="https://bugs.llvm.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - std::any uses mismatched allocation and deallocation"
   href="https://bugs.llvm.org/show_bug.cgi?id=45099">45099</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>std::any uses mismatched allocation and deallocation
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>libc++
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>All Bugs
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedclangbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>tkoeppe@google.com
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org, mclow.lists@gmail.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>The constructors of std::any that create an object of type T use
std::allocator<T> to allocate memory and "::new ((T*)p)" placement new to
construct the object. (That is, memory allocation subject only to
specializations of std::allocator, and construction only to overloads of global
operator new for user pointer types.)

By contrast, destruction uses "delete (T*)p". This means that overloaded
operators T::operator delete will be used.

(In the above, the cast notation "(T*)" doesn't appear literally in the code,
but is just my way of indicating the argument type. Specifically, note that
there is no cast to void* in the placement new.)

This mismatch results in undefined behaviour whenever a suitable amount of user
customization is present for T. Examples can be constructed to exercise all
aspects of this (specializing std::allocator, adding a ::operator
new(std::size_t, T*) overload, or adding overloaded operators T::operator
delete).

One example:

================

#include <any>
#include <cstddef>
#include <cstdlib>

struct U {
  int big[128];
  static void* operator new(std::size_t n) { return std::malloc(n); }
  static void operator delete(void* p) { std::free(p); }
};

int main() {
    std::any a(std::in_place_type<U>);
    return std::any_cast<U*>(a) != nullptr;  // avoid optimizing everything
away
}

================

The generated code clearly shows one call to "operator new" and one to "free".


One solution would be to make the construction use an unqualified new
expression "new _Tp(...)"; this means that standard library code will call
user-defined functions for internal bookkeeping. An alternative solution would
be to also use std::allocator to deallocate the allocation.</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are on the CC list for the bug.</li>
      </ul>
    </body>
</html>