DMD 0.177 release

Kevin Bealer kevinbealer at gmail.com
Fri Dec 15 15:09:46 PST 2006


== Quote from Andrei Alexandrescu (See Website For Email)
(SeeWebsiteForEmail at erdani.org)'s article
> Kevin Bealer wrote:
> > == Quote from Andrei Alexandrescu (See Website For Email)
> > (SeeWebsiteForEmail at erdani.org)'s article
> >> ++s[a]; // possible through a compiler hack
> >> ++s(a, b); // impossible
> >
> > I missed this possibility.  In order to solve just this next step { ++s(a, b); },
> > it seems like D would need something that copies like a pointer to either a struct
> > or class reference, but automatically turns into (or acts like) a regular
> > reference or LValue when something like ++ is applied.  This means something in
> > between a pointer and an LValue.
>
> I think it's an lvalue all right. Inside the compiler it's very clear
> what's an lvalue and what's not. Think of this:
>
> int x;
>
> Wherever you use x, even if you pass it to functions etc., the compiler
> entirely knows it's an lvalue. If, on the other hand, you use (x + 1),
> all of a sudden the compiler understands that's an rvalue, and
> accordingly won't let you e.g. pass it as an out or inout parameter.
> I was quite worried last night about this issue, but now I think I
> realize that returning from a function is about the only (I hope!) case
> in which the lvalue yes/no information about a value is lost. That is,
> you can't today write a function ident() that is totally transparent -
> i.e., can be added around any expression with no effect whatsoever:
>
> template ident(T) {
>    T ident(T rvalue) { // works
>      return rvalue;
>    }
>    T ident(inout T lvalue) { // loss of information
>      return lvalue;
>    }
> }
> By the way, ident() is a good function to check a language's quality. If
> it can't be implemented at all or efficiently, then the language is not
> ideal. Most languages can implement ident() but not efficiently. C++
> came close to implementing it properly, but all the confusion between
> rvalues and const references made implementation exceedingly difficult
> (to add insult to injury, built-in rvalues are treated differently than
> user-defined rvalues). Current D cannot implement ident at all. I also
> wonder whether overloading on inout is possible. I think not, and if
> not, that is a good urgent item to put on the list. The code that should
> compile and execute correctly is:
>
> template ident(T) {
>    T ident(T rvalue) { // works
>      return rvalue;
>    }
>    inout T ident(inout T lvalue) { // works
>      return lvalue;
>    }
> }
>
> > I kind of hate to say it, but given all this, how far am I from describing a C++
> > "&" type?   Is there anything the C++ type does that isn't in the above list?
> > (Other than the new proposal for '& &' references I guess, which seems unnecessary
> > for D.)
>
> There are a few differences that make all the difference :o). One is
> that C++ is too eager to convert rvalues to const &, which has caused an
> unbounded amount of harm. Second, in C++, T& is a type indeed, but it's
> a half-life type, a pariah. So it would be probably wise to do the
> entire lvalue/rvalue distinction without making references into types.
> For example, outside function declarations, there should be no other
> place where inout can be used.
> Andrei

This is really interesting stuff.  You've probably seen these already but lately D
has gotten some compile time introspection stuff via tuples, in particular I'm
thinking of (http://www.digitalmars.com/d/tuple.html) and
(http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite
useful.

So I'm imagining a case of "meta-ident", which is to say, a template cousin of the
ident test, applying the 'wrapper' concept to metaprogramming instead of
programming.  Knowing only the name of a method of a parameter class method
(T::run), I want to duplicate it's signature in a template class method (A::meta).

template A(T) {
    class A {
        T wrapped;

        this(T w) {
            wrapped = w;
        }

        ReturnType!(T.run) meta1(ParameterType!(T.run) i)
        {
            return wrapped.run(i);
        }

        inout ReturnType!(T.run) meta2(inout ParameterType!(T.run) i)
        {
            return wrapped.run(i);
        }
    }
}

Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout
qualification) through the Tuple.  I'm thinking that it can't.  If the LValue
return type concept only affects function signatures as you say, then it can't be
stored in a tuple, right?  Instead it would get the type minus the inout qualifier?

Now consider these two definitions of T::run():

struct S {...}

      S T::run1(in    S x);
inout S T::run2(inout S x);

If I want to write a perfect-forwarding wrapper for run2, I need to use something
like meta2, so that the LValue-ness is preserved.  But meta2 can't work with run1,
because the local S returned by run1 can't be returned via an inout return value.
 It would be returning a ref to a local var.  So I need to know if the function
takes and returns out vs. inout, in order to wrap it up like meta() is trying to do.

This looks a bit like the C++ problem of having to define const and non-const
versions of all the operators, except that meta1 or meta2 will do the wrong thing
*silently* in these cases, either returning references to local vars (meta2 +
run1), or discarding LValue-ness (meta1 + run2).

Maybe I'm solving a non-problem here: in most cases the user should know if
T::run() returns an LValue or RValue; for example, opIndex() could be understood
to always return an LValue by convention.

It seems like this local case could be solved by making ReturnType!() and
ParameterTypeTuple!() into language attributes, so that they could see the fully
qualified function-signature grade types with passing conventions etc.

  T::run.return_type_of  meta_all(T::run.parameter_type_of[0..$] x)
  {
      // pass all the parameters via some kind of compiler-aware tuple
      return wrapped.run(x[0..$]);
  }

Which could presumably, provide perfect forwarding of T::run() when used in a
function signature context.

[Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can
only be pushed a finite distance toward obscurity with each new language feature.
 I hope not, but...]

Kevin



More information about the Digitalmars-d-announce mailing list