From 310bd9b19310af76496ca3fb388ad77e9cffc230 Mon Sep 17 00:00:00 2001 From: Inkrementator <70717315+Inkrementator@users.noreply.github.com> Date: Fri, 21 Feb 2025 00:18:51 +0100 Subject: [PATCH] Tuple toString (#10645) * Use formattedWrite instead of format * Remove workaround for shared objects in formattedWrite The tests are still working. Is it safe to assume that the workaround would've only been merged if there is a test-case checking it? * Remove sharedToString workaround For shared classes, it prints the mangled name, for struct pointers it will print the adress. This version of Tuple.toString won't work anymore in @safe code in general if the tuple contains anything descended from Object. I think this is correct even if a breaking change, since dealing with this issue should be the responsibility of the caller. * Comment to existing code * Reference the bug that a unit test exemplifies --- std/typecons.d | 67 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/std/typecons.d b/std/typecons.d index 989ccba04..51d3e0b06 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -447,19 +447,6 @@ private: assert(ptr.bar.val == 7); } -// Used in Tuple.toString -private template sharedToString(alias field) -if (is(typeof(field) == shared)) -{ - static immutable sharedToString = typeof(field).stringof; -} - -private template sharedToString(alias field) -if (!is(typeof(field) == shared)) -{ - alias sharedToString = field; -} - private enum bool distinctFieldNames(names...) = __traits(compiles, { static foreach (__name; names) @@ -1355,20 +1342,12 @@ if (distinctFieldNames!(Specs)) { sink(fmt.sep); } - // TODO: Change this once formattedWrite() works for shared objects. - static if (is(Type == class) && is(Type == shared)) - { - sink(Type.stringof); - } - else - { - formattedWrite(sink, fmt.nested, this.field[i]); - } + formattedWrite(sink, fmt.nested, this.field[i]); } } else { - formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand)); + formattedWrite(sink, fmt.nested, this.expand); } } else if (fmt.spec == 's') @@ -1383,15 +1362,8 @@ if (distinctFieldNames!(Specs)) { sink(separator); } - // TODO: Change this once format() works for shared objects. - static if (is(Type == class) && is(Type == shared)) - { - sink(Type.stringof); - } - else - { - sink(format!("%(%s%)")(only(field[i]))); - } + // Among other things, using "only" causes string-fields to be inside quotes in the result + sink.formattedWrite!("%(%s%)")(only(field[i])); } sink(footer); } @@ -1812,7 +1784,36 @@ private template ReverseTupleSpecs(T...) Tuple!(int, shared A) nosh; nosh[0] = 5; assert(nosh[0] == 5 && nosh[1] is null); - assert(nosh.to!string == "Tuple!(int, shared(A))(5, shared(A))"); + + assert(nosh.to!string == "Tuple!(int, shared(A))(5, null)"); + } + { + // Shared, without fmt.sep + import std.format; + import std.algorithm.searching; + static class A {int i = 1;} + Tuple!(int, shared A) nosh; + nosh[0] = 5; + assert(nosh[0] == 5 && nosh[1] is null); + + // needs trusted, because Object.toString() isn't @safe + auto f = ()@trusted => format!("%(%s, %s%)")(nosh); + assert(f() == "5, null"); + nosh[1] = new shared A(); + // Currently contains the mangled type name + // 5, const(std.typecons.__unittest_L1750_C7.A) + // This assert is not necessarily to prescribe this behaviour, only to signal if there is a breaking change. + // See https://github.com/dlang/phobos/issues/9811 + auto s = f(); + assert(s.canFind("__unittest_L")); + assert(s.endsWith(".A)")); + } + { + static struct A {} + Tuple!(int, shared A*) nosh; + nosh[0] = 5; + assert(nosh[0] == 5 && nosh[1] is null); + assert(nosh.to!string == "Tuple!(int, shared(A*))(5, null)"); } { Tuple!(int, string) t;