Skip to content

Commit 555e118

Browse files
ronliebbcahoon
authored andcommitted
[ADT] Restore llvm::enumerate reverted patches
Restores: 92416b6 [ADT] Work around `enumerate` compilation error with modules enabled Also restores: a0a7680 [ADT] Allow `llvm::enumerate` to enumerate over multiple ranges b337735 Fix build failures with MSVC 14.x Change-Id: Id48ed483b60cc193a43f2e1a48edf44a36eed572
1 parent cc942d4 commit 555e118

File tree

16 files changed

+349
-146
lines changed

16 files changed

+349
-146
lines changed

clang/test/Analysis/self-assign.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// RUN: -analyzer-checker=debug.ExprInspection \
66
// RUN: -analyzer-config eagerly-assume=false
77

8-
// XFAIL: *
98
extern "C" char *strdup(const char* s);
109
extern "C" void free(void* ptr);
1110

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 190 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -691,26 +691,25 @@ template<typename... Iters> struct ZipTupleType {
691691
using type = std::tuple<decltype(*declval<Iters>())...>;
692692
};
693693

694-
template <typename ZipType, typename... Iters>
694+
template <typename ZipType, typename ReferenceTupleType, typename... Iters>
695695
using zip_traits = iterator_facade_base<
696696
ZipType,
697697
std::common_type_t<
698698
std::bidirectional_iterator_tag,
699699
typename std::iterator_traits<Iters>::iterator_category...>,
700700
// ^ TODO: Implement random access methods.
701-
typename ZipTupleType<Iters...>::type,
701+
ReferenceTupleType,
702702
typename std::iterator_traits<
703703
std::tuple_element_t<0, std::tuple<Iters...>>>::difference_type,
704704
// ^ FIXME: This follows boost::make_zip_iterator's assumption that all
705705
// inner iterators have the same difference_type. It would fail if, for
706706
// instance, the second field's difference_type were non-numeric while the
707707
// first is.
708-
typename ZipTupleType<Iters...>::type *,
709-
typename ZipTupleType<Iters...>::type>;
708+
ReferenceTupleType *, ReferenceTupleType>;
710709

711-
template <typename ZipType, typename... Iters>
712-
struct zip_common : public zip_traits<ZipType, Iters...> {
713-
using Base = zip_traits<ZipType, Iters...>;
710+
template <typename ZipType, typename ReferenceTupleType, typename... Iters>
711+
struct zip_common : public zip_traits<ZipType, ReferenceTupleType, Iters...> {
712+
using Base = zip_traits<ZipType, ReferenceTupleType, Iters...>;
714713
using IndexSequence = std::index_sequence_for<Iters...>;
715714
using value_type = typename Base::value_type;
716715

@@ -760,17 +759,22 @@ struct zip_common : public zip_traits<ZipType, Iters...> {
760759
};
761760

762761
template <typename... Iters>
763-
struct zip_first : zip_common<zip_first<Iters...>, Iters...> {
764-
using zip_common<zip_first, Iters...>::zip_common;
762+
struct zip_first : zip_common<zip_first<Iters...>,
763+
typename ZipTupleType<Iters...>::type, Iters...> {
764+
using zip_common<zip_first, typename ZipTupleType<Iters...>::type,
765+
Iters...>::zip_common;
765766

766767
bool operator==(const zip_first &other) const {
767768
return std::get<0>(this->iterators) == std::get<0>(other.iterators);
768769
}
769770
};
770771

771772
template <typename... Iters>
772-
struct zip_shortest : zip_common<zip_shortest<Iters...>, Iters...> {
773-
using zip_common<zip_shortest, Iters...>::zip_common;
773+
struct zip_shortest
774+
: zip_common<zip_shortest<Iters...>, typename ZipTupleType<Iters...>::type,
775+
Iters...> {
776+
using zip_common<zip_shortest, typename ZipTupleType<Iters...>::type,
777+
Iters...>::zip_common;
774778

775779
bool operator==(const zip_shortest &other) const {
776780
return any_iterator_equals(other, std::index_sequence_for<Iters...>{});
@@ -2168,113 +2172,195 @@ template <typename T> struct deref {
21682172

21692173
namespace detail {
21702174

2171-
template <typename R> class enumerator_iter;
2175+
/// Tuple-like type for `zip_enumerator` dereference.
2176+
template <typename... Refs> struct enumerator_result;
21722177

2173-
template <typename R> struct result_pair {
2174-
using value_reference =
2175-
typename std::iterator_traits<IterOfRange<R>>::reference;
2176-
2177-
friend class enumerator_iter<R>;
2178-
2179-
result_pair(std::size_t Index, IterOfRange<R> Iter)
2180-
: Index(Index), Iter(Iter) {}
2181-
2182-
std::size_t index() const { return Index; }
2183-
value_reference value() const { return *Iter; }
2184-
2185-
private:
2186-
std::size_t Index = std::numeric_limits<std::size_t>::max();
2187-
IterOfRange<R> Iter;
2188-
};
2189-
2190-
template <std::size_t i, typename R>
2191-
decltype(auto) get(const result_pair<R> &Pair) {
2192-
static_assert(i < 2);
2193-
if constexpr (i == 0) {
2194-
return Pair.index();
2195-
} else {
2196-
return Pair.value();
2197-
}
2198-
}
2199-
2200-
template <typename R>
2201-
class enumerator_iter
2202-
: public iterator_facade_base<enumerator_iter<R>, std::forward_iterator_tag,
2203-
const result_pair<R>> {
2204-
using result_type = result_pair<R>;
2205-
2206-
public:
2207-
explicit enumerator_iter(IterOfRange<R> EndIter)
2208-
: Result(std::numeric_limits<size_t>::max(), EndIter) {}
2209-
2210-
enumerator_iter(std::size_t Index, IterOfRange<R> Iter)
2211-
: Result(Index, Iter) {}
2212-
2213-
const result_type &operator*() const { return Result; }
2178+
template <typename... Iters>
2179+
using EnumeratorTupleType = enumerator_result<decltype(*declval<Iters>())...>;
2180+
2181+
/// Zippy iterator that uses the second iterator for comparisons. For the
2182+
/// increment to be safe, the second range has to be the shortest.
2183+
/// Returns `enumerator_result` on dereference to provide `.index()` and
2184+
/// `.value()` member functions.
2185+
/// Note: Because the dereference operator returns `enumerator_result` as a
2186+
/// value instead of a reference and does not strictly conform to the C++17's
2187+
/// definition of forward iterator. However, it satisfies all the
2188+
/// forward_iterator requirements that the `zip_common` and `zippy` depend on
2189+
/// and fully conforms to the C++20 definition of forward iterator.
2190+
/// This is similar to `std::vector<bool>::iterator` that returns bit reference
2191+
/// wrappers on dereference.
2192+
template <typename... Iters>
2193+
struct zip_enumerator : zip_common<zip_enumerator<Iters...>,
2194+
EnumeratorTupleType<Iters...>, Iters...> {
2195+
static_assert(sizeof...(Iters) >= 2, "Expected at least two iteratees");
2196+
using zip_common<zip_enumerator<Iters...>, EnumeratorTupleType<Iters...>,
2197+
Iters...>::zip_common;
22142198

2215-
enumerator_iter &operator++() {
2216-
assert(Result.Index != std::numeric_limits<size_t>::max());
2217-
++Result.Iter;
2218-
++Result.Index;
2219-
return *this;
2199+
bool operator==(const zip_enumerator &Other) const {
2200+
return std::get<1>(this->iterators) == std::get<1>(Other.iterators);
22202201
}
2202+
};
22212203

2222-
bool operator==(const enumerator_iter &RHS) const {
2223-
// Don't compare indices here, only iterators. It's possible for an end
2224-
// iterator to have different indices depending on whether it was created
2225-
// by calling std::end() versus incrementing a valid iterator.
2226-
return Result.Iter == RHS.Result.Iter;
2204+
template <typename... Refs> struct enumerator_result<std::size_t, Refs...> {
2205+
static constexpr std::size_t NumRefs = sizeof...(Refs);
2206+
static_assert(NumRefs != 0);
2207+
// `NumValues` includes the index.
2208+
static constexpr std::size_t NumValues = NumRefs + 1;
2209+
2210+
// Tuple type whose element types are references for each `Ref`.
2211+
using range_reference_tuple = std::tuple<Refs...>;
2212+
// Tuple type who elements are references to all values, including both
2213+
// the index and `Refs` reference types.
2214+
using value_reference_tuple = std::tuple<std::size_t, Refs...>;
2215+
2216+
enumerator_result(std::size_t Index, Refs &&...Rs)
2217+
: Idx(Index), Storage(std::forward<Refs>(Rs)...) {}
2218+
2219+
/// Returns the 0-based index of the current position within the original
2220+
/// input range(s).
2221+
std::size_t index() const { return Idx; }
2222+
2223+
/// Returns the value(s) for the current iterator. This does not include the
2224+
/// index.
2225+
decltype(auto) value() const {
2226+
if constexpr (NumRefs == 1)
2227+
return std::get<0>(Storage);
2228+
else
2229+
return Storage;
2230+
}
2231+
2232+
/// Returns the value at index `I`. This case covers the index.
2233+
template <std::size_t I, typename = std::enable_if_t<I == 0>>
2234+
friend std::size_t get(const enumerator_result &Result) {
2235+
return Result.Idx;
2236+
}
2237+
2238+
/// Returns the value at index `I`. This case covers references to the
2239+
/// iteratees.
2240+
template <std::size_t I, typename = std::enable_if_t<I != 0>>
2241+
friend decltype(auto)
2242+
get(const enumerator_result &Result) {
2243+
// Note: This is a separate function from the other `get`, instead of an
2244+
// `if constexpr` case, to work around an MSVC 19.31.31XXX compiler
2245+
// (Visual Studio 2022 17.1) return type deduction bug.
2246+
return std::get<I - 1>(Result.Storage);
2247+
}
2248+
2249+
template <typename... Ts>
2250+
friend bool operator==(const enumerator_result &Result,
2251+
const std::tuple<std::size_t, Ts...> &Other) {
2252+
static_assert(NumRefs == sizeof...(Ts), "Size mismatch");
2253+
if (Result.Idx != std::get<0>(Other))
2254+
return false;
2255+
return Result.is_value_equal(Other, std::make_index_sequence<NumRefs>{});
22272256
}
22282257

22292258
private:
2230-
result_type Result;
2259+
template <typename Tuple, std::size_t... Idx>
2260+
bool is_value_equal(const Tuple &Other, std::index_sequence<Idx...>) const {
2261+
return ((std::get<Idx>(Storage) == std::get<Idx + 1>(Other)) && ...);
2262+
}
2263+
2264+
std::size_t Idx;
2265+
// Make this tuple mutable to avoid casts that obfuscate const-correctness
2266+
// issues. Const-correctness of references is taken care of by `zippy` that
2267+
// defines const-non and const iterator types that will propagate down to
2268+
// `enumerator_result`'s `Refs`.
2269+
// Note that unlike the results of `zip*` functions, `enumerate`'s result are
2270+
// supposed to be modifiable even when defined as
2271+
// `const`.
2272+
mutable range_reference_tuple Storage;
22312273
};
22322274

2233-
template <typename R> class enumerator {
2234-
public:
2235-
explicit enumerator(R &&Range) : TheRange(std::forward<R>(Range)) {}
2275+
/// Infinite stream of increasing 0-based `size_t` indices.
2276+
struct index_stream {
2277+
struct iterator : iterator_facade_base<iterator, std::forward_iterator_tag,
2278+
const iterator> {
2279+
iterator &operator++() {
2280+
assert(Index != std::numeric_limits<std::size_t>::max() &&
2281+
"Attempting to increment end iterator");
2282+
++Index;
2283+
return *this;
2284+
}
22362285

2237-
enumerator_iter<R> begin() {
2238-
return enumerator_iter<R>(0, adl_begin(TheRange));
2239-
}
2240-
enumerator_iter<R> begin() const {
2241-
return enumerator_iter<R>(0, adl_begin(TheRange));
2242-
}
2286+
// Note: This dereference operator returns a value instead of a reference
2287+
// and does not strictly conform to the C++17's definition of forward
2288+
// iterator. However, it satisfies all the forward_iterator requirements
2289+
// that the `zip_common` depends on and fully conforms to the C++20
2290+
// definition of forward iterator.
2291+
std::size_t operator*() const { return Index; }
22432292

2244-
enumerator_iter<R> end() { return enumerator_iter<R>(adl_end(TheRange)); }
2245-
enumerator_iter<R> end() const {
2246-
return enumerator_iter<R>(adl_end(TheRange));
2247-
}
2293+
friend bool operator==(const iterator &Lhs, const iterator &Rhs) {
2294+
return Lhs.Index == Rhs.Index;
2295+
}
22482296

2249-
private:
2250-
R TheRange;
2297+
std::size_t Index = 0;
2298+
};
2299+
2300+
iterator begin() const { return {}; }
2301+
iterator end() const {
2302+
// We approximate 'infinity' with the max size_t value, which should be good
2303+
// enough to index over any container.
2304+
iterator It;
2305+
It.Index = std::numeric_limits<std::size_t>::max();
2306+
return It;
2307+
}
22512308
};
22522309

22532310
} // end namespace detail
22542311

2255-
/// Given an input range, returns a new range whose values are are pair (A,B)
2256-
/// such that A is the 0-based index of the item in the sequence, and B is
2257-
/// the value from the original sequence. Example:
2312+
/// Given two or more input ranges, returns a new range whose values are are
2313+
/// tuples (A, B, C, ...), such that A is the 0-based index of the item in the
2314+
/// sequence, and B, C, ..., are the values from the original input ranges. All
2315+
/// input ranges are required to have equal lengths. Note that the returned
2316+
/// iterator allows for the values (B, C, ...) to be modified. Example:
2317+
///
2318+
/// ```c++
2319+
/// std::vector<char> Letters = {'A', 'B', 'C', 'D'};
2320+
/// std::vector<int> Vals = {10, 11, 12, 13};
22582321
///
2259-
/// std::vector<char> Items = {'A', 'B', 'C', 'D'};
2260-
/// for (auto X : enumerate(Items)) {
2261-
/// printf("Item %zu - %c\n", X.index(), X.value());
2322+
/// for (auto [Index, Letter, Value] : enumerate(Letters, Vals)) {
2323+
/// printf("Item %zu - %c: %d\n", Index, Letter, Value);
2324+
/// Value -= 10;
22622325
/// }
2326+
/// ```
22632327
///
2264-
/// or using structured bindings:
2328+
/// Output:
2329+
/// Item 0 - A: 10
2330+
/// Item 1 - B: 11
2331+
/// Item 2 - C: 12
2332+
/// Item 3 - D: 13
22652333
///
2266-
/// for (auto [Index, Value] : enumerate(Items)) {
2267-
/// printf("Item %zu - %c\n", Index, Value);
2334+
/// or using an iterator:
2335+
/// ```c++
2336+
/// for (auto it : enumerate(Vals)) {
2337+
/// it.value() += 10;
2338+
/// printf("Item %zu: %d\n", it.index(), it.value());
22682339
/// }
2340+
/// ```
22692341
///
22702342
/// Output:
2271-
/// Item 0 - A
2272-
/// Item 1 - B
2273-
/// Item 2 - C
2274-
/// Item 3 - D
2343+
/// Item 0: 20
2344+
/// Item 1: 21
2345+
/// Item 2: 22
2346+
/// Item 3: 23
22752347
///
2276-
template <typename R> detail::enumerator<R> enumerate(R &&TheRange) {
2277-
return detail::enumerator<R>(std::forward<R>(TheRange));
2348+
template <typename FirstRange, typename... RestRanges>
2349+
auto enumerate(FirstRange &&First, RestRanges &&...Rest) {
2350+
if constexpr (sizeof...(Rest) != 0) {
2351+
#ifndef NDEBUG
2352+
// Note: Create an array instead of an initializer list to work around an
2353+
// Apple clang 14 compiler bug.
2354+
size_t sizes[] = {
2355+
static_cast<size_t>(std::distance(adl_begin(First), adl_end(First))),
2356+
static_cast<size_t>(std::distance(adl_begin(Rest), adl_end(Rest)))...};
2357+
assert(all_equal(sizes) && "Ranges have different length");
2358+
#endif
2359+
}
2360+
using enumerator = detail::zippy<detail::zip_enumerator, detail::index_stream,
2361+
FirstRange, RestRanges...>;
2362+
return enumerator(detail::index_stream{}, std::forward<FirstRange>(First),
2363+
std::forward<RestRanges>(Rest)...);
22782364
}
22792365

22802366
namespace detail {
@@ -2406,15 +2492,17 @@ template <class T> constexpr T *to_address(T *P) { return P; }
24062492
} // end namespace llvm
24072493

24082494
namespace std {
2409-
template <typename R>
2410-
struct tuple_size<llvm::detail::result_pair<R>>
2411-
: std::integral_constant<std::size_t, 2> {};
2495+
template <typename... Refs>
2496+
struct tuple_size<llvm::detail::enumerator_result<Refs...>>
2497+
: std::integral_constant<std::size_t, sizeof...(Refs)> {};
24122498

2413-
template <std::size_t i, typename R>
2414-
struct tuple_element<i, llvm::detail::result_pair<R>>
2415-
: std::conditional<i == 0, std::size_t,
2416-
typename llvm::detail::result_pair<R>::value_reference> {
2417-
};
2499+
template <std::size_t I, typename... Refs>
2500+
struct tuple_element<I, llvm::detail::enumerator_result<Refs...>>
2501+
: std::tuple_element<I, std::tuple<Refs...>> {};
2502+
2503+
template <std::size_t I, typename... Refs>
2504+
struct tuple_element<I, const llvm::detail::enumerator_result<Refs...>>
2505+
: std::tuple_element<I, std::tuple<Refs...>> {};
24182506

24192507
} // namespace std
24202508

llvm/lib/Target/AArch64/AArch64PerfectShuffle.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6590,11 +6590,11 @@ static unsigned getPerfectShuffleCost(llvm::ArrayRef<int> M) {
65906590
assert(M.size() == 4 && "Expected a 4 entry perfect shuffle");
65916591

65926592
// Special case zero-cost nop copies, from either LHS or RHS.
6593-
if (llvm::all_of(llvm::enumerate(M), [](auto &E) {
6593+
if (llvm::all_of(llvm::enumerate(M), [](const auto &E) {
65946594
return E.value() < 0 || E.value() == (int)E.index();
65956595
}))
65966596
return 0;
6597-
if (llvm::all_of(llvm::enumerate(M), [](auto &E) {
6597+
if (llvm::all_of(llvm::enumerate(M), [](const auto &E) {
65986598
return E.value() < 0 || E.value() == (int)E.index() + 4;
65996599
}))
66006600
return 0;

llvm/lib/Target/ARM/ARMLowOverheadLoops.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ bool LowOverheadLoop::ValidateMVEInst(MachineInstr *MI) {
12441244
const MCInstrDesc &MCID = MI->getDesc();
12451245
bool IsUse = false;
12461246
unsigned LastOpIdx = MI->getNumOperands() - 1;
1247-
for (auto &Op : enumerate(reverse(MCID.operands()))) {
1247+
for (const auto &Op : enumerate(reverse(MCID.operands()))) {
12481248
const MachineOperand &MO = MI->getOperand(LastOpIdx - Op.index());
12491249
if (!MO.isReg() || !MO.isUse() || MO.getReg() != ARM::VPR)
12501250
continue;

0 commit comments

Comments
 (0)