From b9c6e3ca4b6fec799964394ad3e080d7b347f658 Mon Sep 17 00:00:00 2001 From: Mathis Beer Date: Tue, 18 Apr 2023 14:31:00 +0200 Subject: [PATCH] Fix issue 23844: Support ranges with immutable fields (like `only` with `immutable struct`) in `chain`. `only` is a range that may be *mutable*, but not *assignable*. `chain` falls over here because it assumes it can make a struct with ranges, and reassign them with new values, which isn't necessarily the case even if the ranges are not `const`. Solved by creating a separate tuple of `Rebindable` ranges for this case. --- std/range/package.d | 51 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 7a724b0f7..80571e28b 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -232,7 +232,7 @@ module std.range; public import std.array; public import std.range.interfaces; public import std.range.primitives; -public import std.typecons : Flag, Yes, No; +public import std.typecons : Flag, Yes, No, Rebindable, rebindable; import std.internal.attributes : betterC; import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap; @@ -978,6 +978,11 @@ if (Ranges.length > 0 && static if (bidirectional) size_t backIndex; else enum backIndex = source.length; + this(typeof(this.tupleof) fields) + { + this.tupleof = fields; + } + public: this(R input) { @@ -1376,25 +1381,34 @@ if (Ranges.length > 0 && static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R)) auto opSlice(size_t begin, size_t end) return scope { - auto result = this; + // force staticMap type conversion to Rebindable + static struct ResultRanges + { + staticMap!(Rebindable, Ranges) fields; + } + auto sourceI(size_t i)() => rebindable(this.source[i]); + auto resultRanges = ResultRanges(staticMap!(sourceI, aliasSeqOf!(R.length.iota))).fields; + size_t resultFrontIndex = this.frontIndex; + static if (bidirectional) + size_t resultBackIndex = this.backIndex; sw: switch (frontIndex) { static foreach (i; 0 .. R.length) { case i: - immutable len = result.source[i].length; + immutable len = resultRanges[i].length; if (len <= begin) { - result.source[i] = result.source[i] + resultRanges[i] = resultRanges[i] [len .. len]; begin -= len; - result.frontIndex++; + resultFrontIndex++; goto case; } else { - result.source[i] = result.source[i] + resultRanges[i] = resultRanges[i] [begin .. len]; break sw; } @@ -1418,18 +1432,18 @@ if (Ranges.length > 0 && static foreach_reverse (i; 1 .. R.length + 1) { case i: - immutable len = result.source[i-1].length; + immutable len = resultRanges[i-1].length; if (len <= cut) { - result.source[i-1] = result.source[i-1] + resultRanges[i-1] = resultRanges[i-1] [0 .. 0]; cut -= len; - result.backIndex--; + resultBackIndex--; goto case; } else { - result.source[i-1] = result.source[i-1] + resultRanges[i-1] = resultRanges[i-1] [0 .. len - cut]; break sw2; } @@ -1445,7 +1459,10 @@ if (Ranges.length > 0 && assert(0, "Internal library error. Please report it."); } - return result; + static if (bidirectional) + return Result(resultRanges, resultFrontIndex, resultBackIndex); + else + return Result(resultRanges, resultFrontIndex); } } return Result(rs); @@ -1643,6 +1660,18 @@ pure @safe unittest assert(equal(r, "foobar")); } +// https://issues.dlang.org/show_bug.cgi?id=23844 +pure @safe unittest +{ + struct S + { + immutable int value; + } + + auto range = chain(only(S(5)), only(S(6))); + assert(range.array == [S(5), S(6)]); +} + pure @safe nothrow @nogc unittest { // support non-copyable items