<map> and <unordered_map> define __value_type as a union between pair<K, V> and pair<const K, V> so that various operations can move into/from these pairs [1]. This is a pretty blatant strict aliasing violation, and has the potential break code that was optimized by TBAA. We can't remove this, not only would that be a significant pessimization, but we are also required to provide a non-const reference to the key_type to implement splicing maps & sets. This patch fixes __value_type by downgrading it from "accidentally launch the missiles"-level UB to theoretical UB, namely, instead of type-punning between pairs we just const_cast the key. It's still undefined to mutate a const object, but clang doesn't actually optimize on this rule. Just to be paranoid this patch also launder()s to "pick-up" the new value of key whenever we read from it, though this isn't launder's actual use-case. Thanks to @rsmith for the suggestions!
To do this, this patch:
- removes the __nc_value_type data member of the __value_type union
- provides access to the pair with pair<key_type&, mapped_type&>, which is used to assign through to the pair
- changes __value_type.__cc to __value_type.__get_value(), the latter calls std::launder in C++1z mode.
https://reviews.llvm.org/D46845 depends on this patch.
[1]: https://stackoverflow.com/a/31667355
Thanks for taking a look!
Erik
This doesn't need to be a union any more; change to class?