std.traits.ParameterDefaults implementation

Adam D. Ruppe destructionator at gmail.com
Mon Apr 8 18:57:59 UTC 2019


I'm looking further into some pains with parameter default 
reflection and I feel Phobos' overcomplicated implementation is 
to blame for the troubles and am trying to figure why it is that 
way.

First, let me paste some code:

----
class C {
	override @safe string toString() const { return "C"; }
}

class A {
	void foo(lazy scope inout C a = new inout(C)) {}
}
@safe void main() {
	import std.stdio;
	foreach(overload; __traits(getOverloads, A, "foo")) {
		static if(is(typeof(overload) Params == __parameters))
			static foreach(idx, _; Params) {{
				alias param = Params[idx .. idx + 1];
				writeln("\t", __traits(identifier, param),
					" = ",
					function(param p) { return p[0]; }().toString
				);
			}}
	}
}
---


This is my testbed for default argument without Phobos. And this 
line is all there really is to it:


function(param p) { return p[0]; }()


I'm building with dmd -dip25 -dip1000 in an attempt to break it 
with attributes. (the explicit toString on the outside is because 
writeln didn't like the const class being passed to it)


On the other hand, this is Phobos' implementation:

---
         template Get(size_t i)
         {
             // `PT[i .. i+1]` declares a parameter with an 
arbitrary name.
             // To avoid a name clash, generate local names that 
are distinct
             // from the parameter name, and mix them in.
             enum name = param_names[i];
             enum args = "args" ~ (name == "args" ? "_" : "");
             enum val = "val" ~ (name == "val" ? "_" : "");
             enum ptr = "ptr" ~ (name == "ptr" ? "_" : "");
             mixin("
                 // workaround scope escape check, see
                 // https://issues.dlang.org/show_bug.cgi?id=16582
                 // should use return scope once available
                 enum get = (PT[i .. i+1] " ~ args ~ ") @trusted
                 {
                     // If the parameter is lazy, we force it to 
be evaluated
                     // like this.
                     auto " ~ val ~ " = " ~ args ~ "[0];
                     auto " ~ ptr ~ " = &" ~ val ~ ";
                         // workaround Bugzilla 16582
                     return *" ~ ptr ~ ";
                 };
             ");
             static if (is(typeof(get())))
                 enum Get = get();
---


It handles a missing default too, but that's trivial, just the 
is(typeof()) check.

What gets me here is the function body. In mine, I just defined 
an inline function and returned the parameter.

Phobos uses what appears to be a gratuitous mixin and defines two 
local variables inside the function. These cause trouble with 
inout

         writeln(ParameterDefaults!(A.foo)[0].toString);

/home/me/d/dmd2/linux/bin32/../../src/phobos/std/traits.d(1492): 
Error: variable `std.traits.ParameterDefaults!(foo).Get!0u.Get` 
only parameters or stack based variables can be inout
/home/me/d/dmd2/linux/bin32/../../src/phobos/std/traits.d(1512): 
Error: template instance 
`std.traits.ParameterDefaults!(foo).Get!0u` error instantiating


I've seen people blame inout for this before... but it seems to 
me to be that Phobos has an overcomplicated implementation.

Looking at bug 16582 which introduced the new code

https://issues.dlang.org/show_bug.cgi?id=16582

the test case is similar to what I just wrote, and my simple code 
works. The other stuff in there talks about lazy. Mine worked 
with that too.


Is the Phobos implementation just working around compiler bugs 
that have since been fixed? Or am I missing something else in 
there?


More information about the Digitalmars-d mailing list