1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00
llvm-mirror/include/llvm/ADT/AllocatorList.h
Barry Revzin 2fc9f32ca3 Make LLVM build in C++20 mode
Part of the <=> changes in C++20 make certain patterns of writing equality
operators ambiguous with themselves (sorry!).
This patch goes through and adjusts all the comparison operators such that
they should work in both C++17 and C++20 modes. It also makes two other small
C++20-specific changes (adding a constructor to a type that cases to be an
aggregate, and adding casts from u8 literals which no longer have type
const char*).

There were four categories of errors that this review fixes.
Here are canonical examples of them, ordered from most to least common:

// 1) Missing const
namespace missing_const {
    struct A {
    #ifndef FIXED
        bool operator==(A const&);
    #else
        bool operator==(A const&) const;
    #endif
    };

    bool a = A{} == A{}; // error
}

// 2) Type mismatch on CRTP
namespace crtp_mismatch {
    template <typename Derived>
    struct Base {
    #ifndef FIXED
        bool operator==(Derived const&) const;
    #else
        // in one case changed to taking Base const&
        friend bool operator==(Derived const&, Derived const&);
    #endif
    };

    struct D : Base<D> { };

    bool b = D{} == D{}; // error
}

// 3) iterator/const_iterator with only mixed comparison
namespace iter_const_iter {
    template <bool Const>
    struct iterator {
        using const_iterator = iterator<true>;

        iterator();

        template <bool B, std::enable_if_t<(Const && !B), int> = 0>
        iterator(iterator<B> const&);

    #ifndef FIXED
        bool operator==(const_iterator const&) const;
    #else
        friend bool operator==(iterator const&, iterator const&);
    #endif
    };

    bool c = iterator<false>{} == iterator<false>{} // error
          || iterator<false>{} == iterator<true>{}
          || iterator<true>{} == iterator<false>{}
          || iterator<true>{} == iterator<true>{};
}

// 4) Same-type comparison but only have mixed-type operator
namespace ambiguous_choice {
    enum Color { Red };

    struct C {
        C();
        C(Color);
        operator Color() const;
        bool operator==(Color) const;
        friend bool operator==(C, C);
    };

    bool c = C{} == C{}; // error
    bool d = C{} == Red;
}

Differential revision: https://reviews.llvm.org/D78938
2020-12-17 10:44:10 +00:00

234 lines
7.3 KiB
C++

//===- llvm/ADT/AllocatorList.h - Custom allocator list ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ADT_ALLOCATORLIST_H
#define LLVM_ADT_ALLOCATORLIST_H
#include "llvm/ADT/ilist_node.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/simple_ilist.h"
#include "llvm/Support/Allocator.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
namespace llvm {
/// A linked-list with a custom, local allocator.
///
/// Expose a std::list-like interface that owns and uses a custom LLVM-style
/// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the
/// implementation details.
///
/// Because this list owns the allocator, calling \a splice() with a different
/// list isn't generally safe. As such, \a splice has been left out of the
/// interface entirely.
template <class T, class AllocatorT> class AllocatorList : AllocatorT {
struct Node : ilist_node<Node> {
Node(Node &&) = delete;
Node(const Node &) = delete;
Node &operator=(Node &&) = delete;
Node &operator=(const Node &) = delete;
Node(T &&V) : V(std::move(V)) {}
Node(const T &V) : V(V) {}
template <class... Ts> Node(Ts &&... Vs) : V(std::forward<Ts>(Vs)...) {}
T V;
};
using list_type = simple_ilist<Node>;
list_type List;
AllocatorT &getAlloc() { return *this; }
const AllocatorT &getAlloc() const { return *this; }
template <class... ArgTs> Node *create(ArgTs &&... Args) {
return new (getAlloc()) Node(std::forward<ArgTs>(Args)...);
}
struct Cloner {
AllocatorList &AL;
Cloner(AllocatorList &AL) : AL(AL) {}
Node *operator()(const Node &N) const { return AL.create(N.V); }
};
struct Disposer {
AllocatorList &AL;
Disposer(AllocatorList &AL) : AL(AL) {}
void operator()(Node *N) const {
N->~Node();
AL.getAlloc().Deallocate(N);
}
};
public:
using value_type = T;
using pointer = T *;
using reference = T &;
using const_pointer = const T *;
using const_reference = const T &;
using size_type = typename list_type::size_type;
using difference_type = typename list_type::difference_type;
private:
template <class ValueT, class IteratorBase>
class IteratorImpl
: public iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>,
IteratorBase,
std::bidirectional_iterator_tag, ValueT> {
template <class OtherValueT, class OtherIteratorBase>
friend class IteratorImpl;
friend AllocatorList;
using base_type =
iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>, IteratorBase,
std::bidirectional_iterator_tag, ValueT>;
public:
using value_type = ValueT;
using pointer = ValueT *;
using reference = ValueT &;
IteratorImpl() = default;
IteratorImpl(const IteratorImpl &) = default;
IteratorImpl &operator=(const IteratorImpl &) = default;
explicit IteratorImpl(const IteratorBase &I) : base_type(I) {}
template <class OtherValueT, class OtherIteratorBase>
IteratorImpl(const IteratorImpl<OtherValueT, OtherIteratorBase> &X,
std::enable_if_t<std::is_convertible<
OtherIteratorBase, IteratorBase>::value> * = nullptr)
: base_type(X.wrapped()) {}
~IteratorImpl() = default;
reference operator*() const { return base_type::wrapped()->V; }
pointer operator->() const { return &operator*(); }
};
public:
using iterator = IteratorImpl<T, typename list_type::iterator>;
using reverse_iterator =
IteratorImpl<T, typename list_type::reverse_iterator>;
using const_iterator =
IteratorImpl<const T, typename list_type::const_iterator>;
using const_reverse_iterator =
IteratorImpl<const T, typename list_type::const_reverse_iterator>;
AllocatorList() = default;
AllocatorList(AllocatorList &&X)
: AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {}
AllocatorList(const AllocatorList &X) {
List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
}
AllocatorList &operator=(AllocatorList &&X) {
clear(); // Dispose of current nodes explicitly.
List = std::move(X.List);
getAlloc() = std::move(X.getAlloc());
return *this;
}
AllocatorList &operator=(const AllocatorList &X) {
List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
return *this;
}
~AllocatorList() { clear(); }
void swap(AllocatorList &RHS) {
List.swap(RHS.List);
std::swap(getAlloc(), RHS.getAlloc());
}
bool empty() { return List.empty(); }
size_t size() { return List.size(); }
iterator begin() { return iterator(List.begin()); }
iterator end() { return iterator(List.end()); }
const_iterator begin() const { return const_iterator(List.begin()); }
const_iterator end() const { return const_iterator(List.end()); }
reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); }
reverse_iterator rend() { return reverse_iterator(List.rend()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(List.rbegin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(List.rend());
}
T &back() { return List.back().V; }
T &front() { return List.front().V; }
const T &back() const { return List.back().V; }
const T &front() const { return List.front().V; }
template <class... Ts> iterator emplace(iterator I, Ts &&... Vs) {
return iterator(List.insert(I.wrapped(), *create(std::forward<Ts>(Vs)...)));
}
iterator insert(iterator I, T &&V) {
return iterator(List.insert(I.wrapped(), *create(std::move(V))));
}
iterator insert(iterator I, const T &V) {
return iterator(List.insert(I.wrapped(), *create(V)));
}
template <class Iterator>
void insert(iterator I, Iterator First, Iterator Last) {
for (; First != Last; ++First)
List.insert(I.wrapped(), *create(*First));
}
iterator erase(iterator I) {
return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this)));
}
iterator erase(iterator First, iterator Last) {
return iterator(
List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this)));
}
void clear() { List.clearAndDispose(Disposer(*this)); }
void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); }
void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); }
void push_back(T &&V) { insert(end(), std::move(V)); }
void push_front(T &&V) { insert(begin(), std::move(V)); }
void push_back(const T &V) { insert(end(), V); }
void push_front(const T &V) { insert(begin(), V); }
template <class... Ts> void emplace_back(Ts &&... Vs) {
emplace(end(), std::forward<Ts>(Vs)...);
}
template <class... Ts> void emplace_front(Ts &&... Vs) {
emplace(begin(), std::forward<Ts>(Vs)...);
}
/// Reset the underlying allocator.
///
/// \pre \c empty()
void resetAlloc() {
assert(empty() && "Cannot reset allocator if not empty");
getAlloc().Reset();
}
};
template <class T> using BumpPtrList = AllocatorList<T, BumpPtrAllocator>;
} // end namespace llvm
#endif // LLVM_ADT_ALLOCATORLIST_H