From 6d57c68df1f72e9416548ad450e56a16bb1fdb90 Mon Sep 17 00:00:00 2001 From: k-hara Date: Thu, 16 Jul 2015 23:13:54 +0900 Subject: [PATCH] fix Issue 14626 - byValue doesn't work with inout AA Until 2.065, compiler had substituted all `inout` qualifiers in the `Key` and `Value` types to `const`, then those had passed to the template struct `AssociativeArray`. https://github.com/D-Programming-Language/dmd/blob/v2.065.0/src/mtype.c#L4897 This change emulates that. --- src/core/internal/traits.d | 32 +++++++++++++++++++++++ src/object.d | 52 ++++++++++++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 85be039336e..8142f90c512 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -53,6 +53,38 @@ template Unqual(T) } } +// Substitute all `inout` qualifiers that appears in T to `const` +template substInout(T) +{ + static if (is(T == immutable)) + { + alias substInout = T; + } + else static if (is(T : shared const U, U) || is(T : const U, U)) + { + // U is top-unqualified + mixin("alias substInout = " + ~ (is(T == shared) ? "shared " : "") + ~ (is(T == const) || is(T == inout) ? "const " : "") // substitute inout to const + ~ "substInoutForm!U;"); + } + else + static assert(0); +} + +private template substInoutForm(T) +{ + static if (is(T == struct) || is(T == class) || is(T == union) || is(T == interface)) + { + alias substInoutForm = T; // prevent matching to the form of alias-this-ed type + } + else static if (is(T : V[K], K, V)) alias substInoutForm = substInout!V[substInout!K]; + else static if (is(T : U[n], U, size_t n)) alias substInoutForm = substInout!U[n]; + else static if (is(T : U[], U)) alias substInoutForm = substInout!U[]; + else static if (is(T : U*, U)) alias substInoutForm = substInout!U*; + else alias substInoutForm = T; +} + /// used to declare an extern(D) function that is defined in a different module template externDFunc(string fqn, T:FT*, FT) if(is(FT == function)) { diff --git a/src/object.d b/src/object.d index f8834f01493..b253c30ea46 100644 --- a/src/object.d +++ b/src/object.d @@ -1825,13 +1825,15 @@ V[K] dup(T : V[K], K, V)(T* aa) auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc { + import core.internal.traits : substInout; + static struct Result { AARange r; pure nothrow @nogc: @property bool empty() { return _aaRangeEmpty(r); } - @property ref K front() { return *cast(K*)_aaRangeFrontKey(r); } + @property ref front() { return *cast(substInout!K*)_aaRangeFrontKey(r); } void popFront() { _aaRangePopFront(r); } @property Result save() { return this; } } @@ -1846,13 +1848,15 @@ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc { + import core.internal.traits : substInout; + static struct Result { AARange r; pure nothrow @nogc: @property bool empty() { return _aaRangeEmpty(r); } - @property ref V front() { return *cast(V*)_aaRangeFrontValue(r); } + @property ref front() { return *cast(substInout!V*)_aaRangeFrontValue(r); } void popFront() { _aaRangePopFront(r); } @property Result save() { return this; } } @@ -1867,6 +1871,8 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc { + import core.internal.traits : substInout; + static struct Result { AARange r; @@ -1879,14 +1885,14 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc { // We save the pointers here so that the Pair we return // won't mutate when Result.popFront is called afterwards. - private K* keyp; - private V* valp; + private void* keyp; + private void* valp; - @property ref inout(K) key() inout { return *keyp; } - @property ref inout(V) value() inout { return *valp; } + @property ref key() inout { return *cast(substInout!K*)keyp; } + @property ref value() inout { return *cast(substInout!V*)valp; } } - return Pair(cast(K*)_aaRangeFrontKey(r), - cast(V*)_aaRangeFrontValue(r)); + return Pair(_aaRangeFrontKey(r), + _aaRangeFrontValue(r)); } void popFront() { _aaRangePopFront(r); } @property Result save() { return this; } @@ -2228,6 +2234,36 @@ unittest } } +unittest +{ + // test for bug 14626 + static struct S + { + string[string] aa; + inout(string) key() inout { return aa.byKey().front; } + inout(string) val() inout { return aa.byValue().front; } + auto keyval() inout { return aa.byKeyValue().front; } + } + + S s = S(["a":"b"]); + assert(s.key() == "a"); + assert(s.val() == "b"); + assert(s.keyval().key == "a"); + assert(s.keyval().value == "b"); + + void testInoutKeyVal(inout(string) key) + { + inout(string)[typeof(key)] aa; + + foreach (i; aa.byKey()) {} + foreach (i; aa.byValue()) {} + foreach (i; aa.byKeyValue()) {} + } + + const int[int] caa; + static assert(is(typeof(caa.byValue().front) == const int)); +} + private void _destructRecurse(S)(ref S s) if (is(S == struct)) {