Re: Destructors, const structs, and opEquals

2010-12-21 Thread Bruno Medeiros

On 10/12/2010 20:58, Andrei Alexandrescu wrote:

On 12/10/10 12:46 PM, Steven Schveighoffer wrote:

On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


Sorry, indeed I haven't seen it.

The problem with binding rvalues to const ref is that once that is in
place you have no way to distinguish an rvalue from a const ref on the
callee site. If you do want to distinguish, you must rely on complicated
conversion priorities. For example, consider:

void foo(ref const Widget);
void foo(Widget);

You'd sometimes want to do that because you want to exploit an rvalue by
e.g. moving its state instead of copying it. However, if rvalues become
convertible to ref const, then they are motivated to go either way. A
rule could be put in place that gives priority to the second
declaration. However, things quickly get complicated in the presence of
other applicable rules, multiple parameters etc. Essentially it was
impossible for C++ to go this way and that's how rvalue references were
born.

For D I want to avoid all that aggravation and have a simple rule:
rvalues don't bind to references to const. If you don't care, use auto
ref. This is a simple rule that works promisingly well in various
forwarding scenarios.


Andrei



For some cases, one could just use a differently-named function, instead 
of overloading it (like 'foo'). That wouldn't solve the issue for 
generic code (or operator overloading), yes.


But I wonder if this is common and important enough (versus the 
alternative) to merit a new type qualifier or storage class.



If this is really necessary, I think it might be better to have rvalues 
convertible to ref const, and instead of auto ref have a new qualifier 
that binds *only* to rvalues, like ref(auto) or ref(in) or whatever. 
This way the more common case is simpler (just const Widget instead of 
auto ref const Widget, in the case of opEquals for example). And as 
for distinguishing rvalues, the example above would be:

  void foo(ref(in) Widget); // exploit rvalue, move state
  void foo(Widget);
which also has the advantage of being able to distinguish the rvalue 
without making the whole parameter const (which is unnecessarily 
transitive, unlike C++)


--
Bruno Medeiros - Software Engineer


Re: Destructors, const structs, and opEquals

2010-12-21 Thread Bruno Medeiros

On 10/12/2010 21:17, Andrei Alexandrescu wrote:

We all need to think about this a bit more because it's related to
another issue that I'm still losing sleep over: should we promote cheap
copy construction throughout D or not?


I was reminded of another comment that could be said in favor of that: 
If you look back at your own article and thoughts about ranges and 
iteration, you made the case for the benefits of the iteration 
primitives having complexity guarantees (just as is the case with STL). 
It seems to me that the very same reasoning could be applied to these 
fundamental type primitives, like the copy constructor at least. If the 
copy constructor guarantees constant complexity, then other algorithms 
and operations can be built on top of that, and also provide useful 
complexity guarantees. Like the sort example you mentioned.


(Hum, and if we go this way, it will probably be best not to call it 
copy constructor then)


--
Bruno Medeiros - Software Engineer


Re: Destructors, const structs, and opEquals

2010-12-14 Thread Don

Brad Roberts wrote:

On 12/13/2010 2:54 PM, Don wrote:


I can't really escape the feeling that 'const' guarantees too little.
It makes guarantees to the caller, but tells the callee *nothing*.


As far as I'm concerned, that's exactly what I want const for.  The caller can
rely on the object not being modified.

Later,
Brad


Yes. But the callee wants some guarantees as well. How can we provide them?


Re: Destructors, const structs, and opEquals

2010-12-14 Thread Don

Jesse Phillips wrote:

Don Wrote:


I can't really escape the feeling that 'const' guarantees too little.
It makes guarantees to the caller, but tells the callee *nothing*.


But it tells the callee exactly what it does, (assuming you unintuitive associate that const objects can be modified). 


It says, you are not allowed to modify this. But it doesn't tell you 
what it is. Is it immutable? Is it an rvalue? If I write to another 
variable, will this one change? Will the caller call the destructor for 
this parameter?
You don't know any of these things. Yet, the point of the thread is that 
  sometimes you want to know.


But interestingly, if the function is (weakly) pure and has no mutable 
ref parameters, any const parameter could safely be passed by reference.


OTOH one problem I see with 'auto ref' is that it encourages the callee 
to think that a const parameter won't change during the function.


foo(auto ref const X x, ref Y y)
// makes it explicit that modifying y might change x.

foo(const ref X x, ref Y y)
// Modifying y might change x.

foo(const X x, ref Y y)
// If I modify y, will x change?
It can, if x contains a reference type.

AFICT, allowing rvalues to implicitly convert to 'const ref' 
exaccerbates an existing problem, rather than actually creating the problem.





To me const is nothing but a middle man. It allows you to call functions with 
both immutable and mutable object types. Which is only similar to @trusted and 
similar in goals as templates or even 'auto ref'


It would be nice if const meant this value is immutable for the 
duration of this function call. But I don't think that's possible 
without expensive deep copying.




BTW the really big problem I have with 'auto ref' is that it isn't 
'auto', and it isn't 'ref'. I wouldn't have the same objection to 
something like 'autoref'.


I agree here. Makes it seem like you should also have 'auto immutable' and the 
likes. Maybe there would be reason to look at how we can consolidate all of 
this. But personally I am not familiar with the problems.


Re: Destructors, const structs, and opEquals

2010-12-14 Thread Steven Schveighoffer

On Mon, 13 Dec 2010 17:54:57 -0500, Don nos...@nospam.com wrote:


I can't really escape the feeling that 'const' guarantees too little.
It makes guarantees to the caller, but tells the callee *nothing*.


This is the basis of my argument that adding logical const would not  
compromise the guarantee of const, because it has no guarantees to begin  
with.


But what const *does* do well is give you a good guard-rail to prevent you  
from making dumb mistakes.  Most people are not going to write code that  
exploits the lack of guarantees, so it's a reasonable constraint.


The huge value of const is to unify both mutable and immutable parameters  
into one function.


-Steve


Re: Destructors, const structs, and opEquals

2010-12-13 Thread Don

Andrei Alexandrescu wrote:

On 12/10/10 4:10 PM, foobar wrote:

Don Wrote:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can 
have instead:

void foo(const Widget);
and have an optimization inside the compiler for value types to pass 
by ref.


Everyone - please stop suggesting that. It causes severe undue aliasing 
issues.


Andrei


I don't understand this.

For sure,
const Widget foo(const Widget  x) { return x; }

is inefficient.

But I don't see how problems can ever arise with a function which 
returns a built-in type (eg, opEquals() ).


It seems to me, that the issue relates to deterministic destruction.

As I see it, there can be two forms of const parameters:
* caller manages lifetime. Caller must call destructor. It must 
duplicate anything it wants to return.

* callee manages lifetime. Callee must destroy the variable, or return it.

Interestingly, any parameter marked as 'inout' is the second form.
Seems pretty clear to me that opEquals needs the first form.
And I think it's a pretty common case: I'm only going to look at this 
variable, I'm not going to take ownership of it or modify it any way.


We have
ref const in inout scope 'auto ref' (which does not mean auto + ref).

And yet, even with this zoo of modifiers, the best syntax we have for 
that simple situation is 'auto ref const' ???


We've got to do better than that.












Re: Destructors, const structs, and opEquals

2010-12-13 Thread Andrei Alexandrescu

On 12/13/10 9:28 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/10/10 4:10 PM, foobar wrote:

Don Wrote:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can
have instead:
void foo(const Widget);
and have an optimization inside the compiler for value types to pass
by ref.


Everyone - please stop suggesting that. It causes severe undue
aliasing issues.

Andrei


I don't understand this.

For sure,
const Widget foo(const Widget x) { return x; }

is inefficient.

But I don't see how problems can ever arise with a function which
returns a built-in type (eg, opEquals() ).

It seems to me, that the issue relates to deterministic destruction.

As I see it, there can be two forms of const parameters:
* caller manages lifetime. Caller must call destructor. It must
duplicate anything it wants to return.
* callee manages lifetime. Callee must destroy the variable, or return it.

Interestingly, any parameter marked as 'inout' is the second form.
Seems pretty clear to me that opEquals needs the first form.
And I think it's a pretty common case: I'm only going to look at this
variable, I'm not going to take ownership of it or modify it any way.

We have
ref const in inout scope 'auto ref' (which does not mean auto + ref).

And yet, even with this zoo of modifiers, the best syntax we have for
that simple situation is 'auto ref const' ???

We've got to do better than that.


I agree we should ideally do better than that. The problem with the 
compiler taking initiative in the ref vs. value decision is undue aliasing:


void fun(const Widget a, Widget b) { ... }

In the call fun(x, x) the compiler may or may not alias a with b - a 
very difficult to detect bug. The two objects don't have to be 
parameters - one could be e.g. a global.


Andrei


Re: Destructors, const structs, and opEquals

2010-12-13 Thread Don

Andrei Alexandrescu wrote:

On 12/13/10 9:28 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/10/10 4:10 PM, foobar wrote:

Don Wrote:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can
have instead:
void foo(const Widget);
and have an optimization inside the compiler for value types to pass
by ref.


Everyone - please stop suggesting that. It causes severe undue
aliasing issues.

Andrei


I don't understand this.

For sure,
const Widget foo(const Widget x) { return x; }

is inefficient.

But I don't see how problems can ever arise with a function which
returns a built-in type (eg, opEquals() ).

It seems to me, that the issue relates to deterministic destruction.

As I see it, there can be two forms of const parameters:
* caller manages lifetime. Caller must call destructor. It must
duplicate anything it wants to return.
* callee manages lifetime. Callee must destroy the variable, or return 
it.


Interestingly, any parameter marked as 'inout' is the second form.
Seems pretty clear to me that opEquals needs the first form.
And I think it's a pretty common case: I'm only going to look at this
variable, I'm not going to take ownership of it or modify it any way.

We have
ref const in inout scope 'auto ref' (which does not mean auto + ref).

And yet, even with this zoo of modifiers, the best syntax we have for
that simple situation is 'auto ref const' ???

We've got to do better than that.


I agree we should ideally do better than that. The problem with the 
compiler taking initiative in the ref vs. value decision is undue aliasing:


void fun(const Widget a, Widget b) { ... }

In the call fun(x, x) the compiler may or may not alias a with b - a 
very difficult to detect bug. The two objects don't have to be 
parameters - one could be e.g. a global.


Andrei


I can't really escape the feeling that 'const' guarantees too little.
It makes guarantees to the caller, but tells the callee *nothing*.
(Except for the (important) special case where *all* the parameters are 
const, none have destructors, and the function is pure).
I think everything we're actually doing here is trying to tie the 
semantics down, for the benefit of the callee.
So I would think that we need to be very clear about what semantics we 
can realistically guarantee, and tie the syntax to that.



BTW the really big problem I have with 'auto ref' is that it isn't 
'auto', and it isn't 'ref'. I wouldn't have the same objection to 
something like 'autoref'.





Re: Destructors, const structs, and opEquals

2010-12-13 Thread Michel Fortin

On 2010-12-13 17:54:57 -0500, Don nos...@nospam.com said:

BTW the really big problem I have with 'auto ref' is that it isn't 
'auto', and it isn't 'ref'. I wouldn't have the same objection to 
something like 'autoref'.


I don't like auto ref as a syntax either, but I also dislike the 
general direction this solution is leading us to (irrespective of the 
syntax). One shouldn't have to specify for every function whether the 
argument should be passed by ref or by copy under the hood. That's just 
repeating C++ mistake where for certain type you almost always have to 
use the easy the idiom const T  for function parameters. Efficiency 
should be the default way to pass function parameters around.


I made a proposal earlier that instead of having auto ref for this we 
could have a way to define a struct as being automatically passed by 
ref in function calls. This way you don't have to remember to pass them 
by auto ref to be efficient, it's done automatically. I said earlier 
that the default way to pass parameters should be efficient, and this 
is what it allows.


Earlier proposal:
http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.comgroup=digitalmars.Dartnum=123991


--


Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: Destructors, const structs, and opEquals

2010-12-13 Thread Brad Roberts
On 12/13/2010 2:54 PM, Don wrote:

 I can't really escape the feeling that 'const' guarantees too little.
 It makes guarantees to the caller, but tells the callee *nothing*.

As far as I'm concerned, that's exactly what I want const for.  The caller can
rely on the object not being modified.

Later,
Brad


For whom is (was: D2? Re: Destructors, const structs, and opEquals)

2010-12-11 Thread spir
On Fri, 10 Dec 2010 21:25:49 -0500
Michel Fortin michel.for...@michelf.com wrote:

 On 2010-12-10 17:12:16 -0500, Don nos...@nospam.com said:
 
  Steven Schveighoffer wrote:
  To summarize for those looking for the C++ behavior, the equivalent would 
  be:
  
  void foo(auto ref const Widget)
  
  That use of 'auto' is an abomination.
 
 One problem I'm starting to realize is that we now have so many 
 available qualifiers for function parameters than it's really easy to 
 get lost.
 
 In D1 it was simple: in for regular arguments (the default), 
 inout/ref for passing arguments by refrence, and out for output 
 arguments. They all had clear semantics and not too much overlap.
 
 In D2, we've lost this simplicity. Add const/immutable/shared, add 
 scope, change in as an alias for const scope, give inout a 
 totally new meaning, keep ref and out the same except that now 
 ref can be prefixed with auto to give it a double meaning...  
 choosing the right modifiers for function parameters is getting extra 
 complicated.
 
 Have we lost track of one of D's principles, that doing the right thing 
 should be the easiest way to do things? To me it looks like we're 
 adding more and more ways to pass arguments because the defaults are 
 failing us. Perhaps it's time to revisit how arguments are passed by 
 default.
 
 As for auto ref, if we're to keep it I think it'd be much better if 
 it was a keyword of its own, such as autoref. Having modifiers is one 
 thing, but having modifiers that apply to modifiers is getting a little 
 hard to parse in my head. This is not unprecedented, in English when 
 one qualifier apply to another and it becomes hard to read we group 
 them by adding a hyphen between the two.

I totally agree. This extends to all sorts of D qualifiers:
abstract alias const extern final immutable in inout lazy nothrow out override 
private protected public pure ref scope shared static.

I'm afraid D2 in on the track of becoming a language for the elite. What do you 
think?
(I'm certain it is possible to make most languages simpler and as powerful, if 
we use clever designer brains with this target in mind. The issue I see with 
all those features is: what do they mean? Note What is absent from D docs is 
the purpose and meaning of most elements of the language. Probably obvious for 
their designers, but who else is supposed to use them?)


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Destructors, const structs, and opEquals

2010-12-11 Thread spir
On Fri, 10 Dec 2010 18:28:08 -0800
Andrei Alexandrescu seewebsiteforem...@erdani.org wrote:

 It's sort of ironic. Tu viens d'implementing yet another type 
 constructor yourself!

The need for yet another one signifie sûrement (probably means) their semantics 
(in the human sense) are wrongly defined. D2 needs un regard neuf et lucide (a 
fresh external look) at its whole set of qualifiers.

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Destructors, const structs, and opEquals

2010-12-11 Thread Don

Michel Fortin wrote:

On 2010-12-10 17:12:16 -0500, Don nos...@nospam.com said:


Steven Schveighoffer wrote:
To summarize for those looking for the C++ behavior, the equivalent 
would be:


void foo(auto ref const Widget)


That use of 'auto' is an abomination.


One problem I'm starting to realize is that we now have so many 
available qualifiers for function parameters than it's really easy to 
get lost.


In D1 it was simple: in for regular arguments (the default), 
inout/ref for passing arguments by refrence, and out for output 
arguments. They all had clear semantics and not too much overlap.


In D2, we've lost this simplicity. Add const/immutable/shared, add 
scope, change in as an alias for const scope, give inout a 
totally new meaning, keep ref and out the same except that now ref 
can be prefixed with auto to give it a double meaning...  choosing the 
right modifiers for function parameters is getting extra complicated.


Have we lost track of one of D's principles, that doing the right thing 
should be the easiest way to do things? To me it looks like we're adding 
more and more ways to pass arguments because the defaults are failing 
us. Perhaps it's time to revisit how arguments are passed by default.


As for auto ref, if we're to keep it I think it'd be much better if it 
was a keyword of its own, such as autoref. Having modifiers is one 
thing, but having modifiers that apply to modifiers is getting a little 
hard to parse in my head.


 This is not unprecedented, in English when one
qualifier apply to another and it becomes hard to read we group them by 
adding a hyphen between the two.


The problem is that 'auto' in 'auto ref' has *a contradictory meaning* 
to every other usage of 'auto' in the language.


If we need another keyword, we have to create another keyword.
Almost any other syntax would be better.

And as far as I can tell, 'auto ref', 'scope' and 'in' as function 
parameters aren't explained at all in the spec.




Re: Destructors, const structs, and opEquals

2010-12-11 Thread Michel Fortin
On 2010-12-10 19:32:30 -0500, Andrei Alexandrescu 
seewebsiteforem...@erdani.org said:



On 12/10/10 4:10 PM, foobar wrote:

Don Wrote:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can 
have instead:

void foo(const Widget);
and have an optimization inside the compiler for value types to pass by ref.


Everyone - please stop suggesting that. It causes severe undue aliasing issues.


If I understand you well Andrei, what you want auto ref to be the 
same thing as ref except that it would also accept rvalues. I think 
the reason you want this is because for some types it is more efficient 
to pass them as ref than by value, so you want to pass them as ref 
for efficiency and and not necessarily for its semantics. And from 
there goes the need for a ref that also accepts rvalues.


I think this is a bad usage of ref. Efficient should be the way 
arguments are passed by default, and modifiers should be used to alter 
semantics and not required for efficiency (in most situations). Is 
there a way to pass arguments more efficiently without introducing a 
bazillion options the programmer then has to choose from?


Perhaps we're just trying to address the problem from the wrong end. 
Instead of having to say for each function parameter how you want it to 
be passed, what if the type itself knew how it should be passed as a 
parameter?


@passbyref struct ArrayOf50 {
float[50] content;
}

string test1(ArrayOf50 a); // accepts rvalues
string test2(ref ArrayOf50 a); // rejects rvalues

void main() {
test1(ArrayOf50());
test2(ArrayOf50()); // error, first argument requires a 
reference
}

Now, obviously we've given different semantics to the type itself, but 
those semantics are going to be consistent and predictable everywhere. 
But mostly, you don't have to remember how to pass this struct every 
time now. That's a really big gain.


Also note how you could use this feature to design containers which 
don't need to be reference counted but which are still passed 
efficiently across function calls.


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: Destructors, const structs, and opEquals

2010-12-11 Thread Andrei Alexandrescu

On 12/11/10 2:49 CST, spir wrote:

On Fri, 10 Dec 2010 18:28:08 -0800
Andrei Alexandrescuseewebsiteforem...@erdani.org  wrote:


It's sort of ironic. Tu viens d'implementing yet another type
constructor yourself!


The need for yet another one signifie sûrement (probably means) their semantics 
(in the human sense) are wrongly defined. D2 needs un regard neuf et lucide (a 
fresh external look) at its whole set of qualifiers.


I'm all for it. Even if we can't change the language, I'd like to know 
the truth. Because if there's a simple way to go about it that has 
comparable power, we couldn't find it.


Andrei



Re: Destructors, const structs, and opEquals

2010-12-11 Thread spir
On Sat, 11 Dec 2010 09:06:33 -0500
Michel Fortin michel.for...@michelf.com wrote:

 On 2010-12-10 19:32:30 -0500, Andrei Alexandrescu 
 seewebsiteforem...@erdani.org said:
 
  On 12/10/10 4:10 PM, foobar wrote:
  Don Wrote:
  
  Steven Schveighoffer wrote:
  To summarize for those looking for the C++ behavior, the equivalent
  would be:
  
  void foo(auto ref const Widget)
  
  That use of 'auto' is an abomination.
  
  I agree with don.
  IMHO, this is incredibly silly given Andrei's use case, since D can 
  have instead:
  void foo(const Widget);
  and have an optimization inside the compiler for value types to pass by 
  ref.
  
  Everyone - please stop suggesting that. It causes severe undue aliasing 
  issues.
 
 If I understand you well Andrei, what you want auto ref to be the 
 same thing as ref except that it would also accept rvalues. I think 
 the reason you want this is because for some types it is more efficient 
 to pass them as ref than by value, so you want to pass them as ref 
 for efficiency and and not necessarily for its semantics. And from 
 there goes the need for a ref that also accepts rvalues.
 
 I think this is a bad usage of ref. Efficient should be the way 
 arguments are passed by default, and modifiers should be used to alter 
 semantics and not required for efficiency (in most situations). Is 
 there a way to pass arguments more efficiently without introducing a 
 bazillion options the programmer then has to choose from?
 
 Perhaps we're just trying to address the problem from the wrong end. 
 Instead of having to say for each function parameter how you want it to 
 be passed, what if the type itself knew how it should be passed as a 
 parameter?
 
   @passbyref struct ArrayOf50 {
   float[50] content;
   }
 
   string test1(ArrayOf50 a); // accepts rvalues
   string test2(ref ArrayOf50 a); // rejects rvalues
 
   void main() {
   test1(ArrayOf50());
   test2(ArrayOf50()); // error, first argument requires a 
 reference
   }
 
 Now, obviously we've given different semantics to the type itself, but 
 those semantics are going to be consistent and predictable everywhere. 
 But mostly, you don't have to remember how to pass this struct every 
 time now. That's a really big gain.
 
 Also note how you could use this feature to design containers which 
 don't need to be reference counted but which are still passed 
 efficiently across function calls.
 
If parameters are 'in' or 'const' by default, then whether they are passed by 
value or by ref has no consequence, I guess. The compiler can then safely 
choose the most efficent more --what it can do as it knows sizeof-- grossly 
structs by ref, the rest by value. Is this reasoning correct?


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Destructors, const structs, and opEquals

2010-12-11 Thread Michel Fortin

On 2010-12-11 11:48:36 -0500, spir denis.s...@gmail.com said:


If parameters are 'in' or 'const' by default, then whether they are passed
by value or by ref has no consequence, I guess. The compiler can then safel
y choose the most efficent more --what it can do as it knows sizeof-- gross
ly structs by ref, the rest by value. Is this reasoning correct?


No it can't because of aliasing (the object might be referenced by 
something else). Look at this simple example:


struct A { int value; }

bool test(const A a1, ref A a2) {
++a2.value;
return a1.value == a2.value;
}

void main() {
A a;
a.value = 8;
bool result = test(a, a);
assert(result == false);
assert(a.value == 9);
}

If a1 is passed by ref under the hood, it can only be done whenever 
you know for sure there is no aliasing, or if the data is immutable, 
otherwise you'll have side effects. So while it could be done in some 
circumstances (strongly pure functions and immutable parameters), 
there's still an important need for a more general solution.



--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: Destructors, const structs, and opEquals

2010-12-11 Thread so
If parameters are 'in' or 'const' by default, then whether they are  
passed by value or by ref has no consequence, I guess. The compiler can  
then safely choose the most efficent more --what it can do as it knows  
sizeof-- grossly structs by ref, the rest by value. Is this reasoning  
correct?


This kind of design decisions, leaving this responsibility to compiler is  
very wrong.

Also, you are introducing a lock in here, no way to do the opposite.
T a ref T a are very good tools. You can't always assume compiler  
always knows the best.


If parameters are in or const what about if not? Another inconsistency  
and a quite a bad one at it.


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Steven Schveighoffer
On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer  
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue.  
Consider:


void bump(ref int x) {
   ++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't understand  
why const ref is such a mistake.  Before you explained it was because  
when you pass an rvalue by ref, it's much more expensive, so auto ref  
passes by ref if it's an lvalue and by value if it's an rvalue.  At  
least that's what I understood.


With const ref, you get the same behavior, plus you are guaranteed that  
the code isn't going to do something stupid (like modify a value that  
will be thrown away at the end).


Not sure if this got lost in the noise, I'm still puzzled about this...

-Steve


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrei Alexandrescu

On 12/10/10 12:46 PM, Steven Schveighoffer wrote:

On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue.
Consider:

void bump(ref int x) {
++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't
understand why const ref is such a mistake. Before you explained it
was because when you pass an rvalue by ref, it's much more expensive,
so auto ref passes by ref if it's an lvalue and by value if it's an
rvalue. At least that's what I understood.

With const ref, you get the same behavior, plus you are guaranteed
that the code isn't going to do something stupid (like modify a value
that will be thrown away at the end).


Not sure if this got lost in the noise, I'm still puzzled about this...


Sorry, indeed I haven't seen it.

The problem with binding rvalues to const ref is that once that is in 
place you have no way to distinguish an rvalue from a const ref on the 
callee site. If you do want to distinguish, you must rely on complicated 
conversion priorities. For example, consider:


void foo(ref const Widget);
void foo(Widget);

You'd sometimes want to do that because you want to exploit an rvalue by 
e.g. moving its state instead of copying it. However, if rvalues become 
convertible to ref const, then they are motivated to go either way. A 
rule could be put in place that gives priority to the second 
declaration. However, things quickly get complicated in the presence of 
other applicable rules, multiple parameters etc. Essentially it was 
impossible for C++ to go this way and that's how rvalue references were 
born.


For D I want to avoid all that aggravation and have a simple rule: 
rvalues don't bind to references to const. If you don't care, use auto 
ref. This is a simple rule that works promisingly well in various 
forwarding scenarios.



Andrei



Re: Destructors, const structs, and opEquals

2010-12-10 Thread Steven Schveighoffer
On Fri, 10 Dec 2010 15:58:17 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/10/10 12:46 PM, Steven Schveighoffer wrote:

On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being pestered  
by

the compiler?


Because you sometimes do care about dealing with a true lvalue.
Consider:

void bump(ref int x) {
++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't
understand why const ref is such a mistake. Before you explained it
was because when you pass an rvalue by ref, it's much more expensive,
so auto ref passes by ref if it's an lvalue and by value if it's an
rvalue. At least that's what I understood.

With const ref, you get the same behavior, plus you are guaranteed
that the code isn't going to do something stupid (like modify a value
that will be thrown away at the end).


Not sure if this got lost in the noise, I'm still puzzled about this...


Sorry, indeed I haven't seen it.

The problem with binding rvalues to const ref is that once that is in  
place you have no way to distinguish an rvalue from a const ref on the  
callee site. If you do want to distinguish, you must rely on complicated  
conversion priorities. For example, consider:


void foo(ref const Widget);
void foo(Widget);

You'd sometimes want to do that because you want to exploit an rvalue by  
e.g. moving its state instead of copying it. However, if rvalues become  
convertible to ref const, then they are motivated to go either way. A  
rule could be put in place that gives priority to the second  
declaration. However, things quickly get complicated in the presence of  
other applicable rules, multiple parameters etc. Essentially it was  
impossible for C++ to go this way and that's how rvalue references were  
born.


For D I want to avoid all that aggravation and have a simple rule:  
rvalues don't bind to references to const. If you don't care, use auto  
ref. This is a simple rule that works promisingly well in various  
forwarding scenarios.


OK, now I get it, thanks for explaining it again :)  So essentially you  
are overriding the no ref rvalues rule, this is a good thing, because  
many times the compiler is too conservative in that decision.


To summarize for those looking for the C++ behavior, the equivalent would  
be:


void foo(auto ref const Widget)

right?  Well, at least when it's implemented properly :)  BTW, is there a  
bugzilla entry on this?


-Steve


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrei Alexandrescu

On 12/10/10 1:10 PM, Steven Schveighoffer wrote:

On Fri, 10 Dec 2010 15:58:17 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/10/10 12:46 PM, Steven Schveighoffer wrote:

On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being
pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue.
Consider:

void bump(ref int x) {
++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't
understand why const ref is such a mistake. Before you explained it
was because when you pass an rvalue by ref, it's much more expensive,
so auto ref passes by ref if it's an lvalue and by value if it's an
rvalue. At least that's what I understood.

With const ref, you get the same behavior, plus you are guaranteed
that the code isn't going to do something stupid (like modify a value
that will be thrown away at the end).


Not sure if this got lost in the noise, I'm still puzzled about this...


Sorry, indeed I haven't seen it.

The problem with binding rvalues to const ref is that once that is in
place you have no way to distinguish an rvalue from a const ref on the
callee site. If you do want to distinguish, you must rely on
complicated conversion priorities. For example, consider:

void foo(ref const Widget);
void foo(Widget);

You'd sometimes want to do that because you want to exploit an rvalue
by e.g. moving its state instead of copying it. However, if rvalues
become convertible to ref const, then they are motivated to go either
way. A rule could be put in place that gives priority to the second
declaration. However, things quickly get complicated in the presence
of other applicable rules, multiple parameters etc. Essentially it was
impossible for C++ to go this way and that's how rvalue references
were born.

For D I want to avoid all that aggravation and have a simple rule:
rvalues don't bind to references to const. If you don't care, use auto
ref. This is a simple rule that works promisingly well in various
forwarding scenarios.


OK, now I get it, thanks for explaining it again :) So essentially you
are overriding the no ref rvalues rule, this is a good thing, because
many times the compiler is too conservative in that decision.

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)

right? Well, at least when it's implemented properly :) BTW, is there a
bugzilla entry on this?


That is correct. There are a couple of related bug reports 
(http://d.puremagic.com/issues/show_bug.cgi?id=4668, 
http://d.puremagic.com/issues/show_bug.cgi?id=4258) so I'm not worried 
about the problem being forgotten.


We all need to think about this a bit more because it's related to 
another issue that I'm still losing sleep over: should we promote cheap 
copy construction throughout D or not?



Andrei


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Don

Steven Schveighoffer wrote:
To summarize for those looking for the C++ behavior, the equivalent 
would be:


void foo(auto ref const Widget)


That use of 'auto' is an abomination.


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Max Samukha

On 12/10/2010 10:58 PM, Andrei Alexandrescu wrote:

On 12/10/10 12:46 PM, Steven Schveighoffer wrote:

On Mon, 06 Dec 2010 08:34:20 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being
pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue.
Consider:

void bump(ref int x) {
++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't
understand why const ref is such a mistake. Before you explained it
was because when you pass an rvalue by ref, it's much more expensive,
so auto ref passes by ref if it's an lvalue and by value if it's an
rvalue. At least that's what I understood.

With const ref, you get the same behavior, plus you are guaranteed
that the code isn't going to do something stupid (like modify a value
that will be thrown away at the end).


Not sure if this got lost in the noise, I'm still puzzled about this...


Sorry, indeed I haven't seen it.

The problem with binding rvalues to const ref is that once that is in
place you have no way to distinguish an rvalue from a const ref on the
callee site. If you do want to distinguish, you must rely on complicated
conversion priorities. For example, consider:

void foo(ref const Widget);
void foo(Widget);

You'd sometimes want to do that because you want to exploit an rvalue by
e.g. moving its state instead of copying it. However, if rvalues become
convertible to ref const, then they are motivated to go either way. A
rule could be put in place that gives priority to the second
declaration. However, things quickly get complicated in the presence of
other applicable rules, multiple parameters etc. Essentially it was
impossible for C++ to go this way and that's how rvalue references were
born.

For D I want to avoid all that aggravation and have a simple rule:
rvalues don't bind to references to const. If you don't care, use auto
ref. This is a simple rule that works promisingly well in various
forwarding scenarios.


Andrei



Thanks a lot for taking time to explain!

Anybody interested see the rationale explained in detail at 
http://thbecker.net/articles/rvalue_references/section_07.html or 
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/






Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrej Mitrovic
 Thanks a lot for taking time to explain!

 Anybody interested see the rationale explained in detail at
 http://thbecker.net/articles/rvalue_references/section_07.html or
 http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


Thanks, I had trouble understanding what this whole rvalue deal is all about.


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrej Mitrovic
On 12/11/10, Andrej Mitrovic andrej.mitrov...@gmail.com wrote:
 Thanks a lot for taking time to explain!

 Anybody interested see the rationale explained in detail at
 http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


 Thanks, I had trouble understanding what this whole rvalue deal is all
 about.


Ah, see, that article talks about RVO - Walter's cool optimization technique. :p


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrej Mitrovic
On 12/11/10, Andrej Mitrovic andrej.mitrov...@gmail.com wrote:
 On 12/11/10, Andrej Mitrovic andrej.mitrov...@gmail.com wrote:
 Thanks a lot for taking time to explain!

 Anybody interested see the rationale explained in detail at
 http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


 Thanks, I had trouble understanding what this whole rvalue deal is all
 about.


 Ah, see, that article talks about RVO - Walter's cool optimization
 technique. :p


P.S. The second link on that page is dead, but I've found a direct
link: http://www.elcamino.edu/faculty/gfry/CS2/LValues_RValues.pdf


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrej Mitrovic
On 12/11/10, Andrej Mitrovic andrej.mitrov...@gmail.com wrote:
 On 12/11/10, Andrej Mitrovic andrej.mitrov...@gmail.com wrote:
 Thanks a lot for taking time to explain!

 Anybody interested see the rationale explained in detail at
 http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/


 Thanks, I had trouble understanding what this whole rvalue deal is all
 about.


 Ah, see, that article talks about RVO - Walter's cool optimization
 technique. :p


I hate to spam this topic (last post, I swear), but I find it amusing
that Walter created this technique in 1991 and it took Microsoft 12
years to catch up
(http://blogs.msdn.com/b/slippman/archive/2004/02/03/66739.aspx).


Re: Destructors, const structs, and opEquals

2010-12-10 Thread foobar
Don Wrote:

 Steven Schveighoffer wrote:
  To summarize for those looking for the C++ behavior, the equivalent 
  would be:
  
  void foo(auto ref const Widget)
 
 That use of 'auto' is an abomination.

I agree with don. 
IMHO, this is incredibly silly given Andrei's use case, since D can have 
instead: 
void foo(const Widget);
and have an optimization inside the compiler for value types to pass by ref. 

by specifying const ref you explicitly require that only a ref to an l-value 
be provided, whereas without the ref an r-value is also allowed a-la c++. 

much KISSer.


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrei Alexandrescu

On 12/10/10 4:10 PM, foobar wrote:

Don Wrote:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can have 
instead:
void foo(const Widget);
and have an optimization inside the compiler for value types to pass by ref.


Everyone - please stop suggesting that. It causes severe undue aliasing 
issues.


Andrei



Re: Destructors, const structs, and opEquals

2010-12-10 Thread so

I agree with don.
IMHO, this is incredibly silly given Andrei's use case, since D can have  
instead:

void foo(const Widget);
and have an optimization inside the compiler for value types to pass by  
ref.


by specifying const ref you explicitly require that only a ref to an  
l-value be provided, whereas without the ref an r-value is also  
allowed a-la c++.


much KISSer.


auto ref as a syntax may be not the best choice but the way it solves  
the problem is very elegant.


your const ref:
It doesn't make it KISS.
It adds inconsistency (even though it is necessary sometimes, this one is  
bad).

You lose the ability to do the opposite.

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Michel Fortin

On 2010-12-10 17:12:16 -0500, Don nos...@nospam.com said:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


One problem I'm starting to realize is that we now have so many 
available qualifiers for function parameters than it's really easy to 
get lost.


In D1 it was simple: in for regular arguments (the default), 
inout/ref for passing arguments by refrence, and out for output 
arguments. They all had clear semantics and not too much overlap.


In D2, we've lost this simplicity. Add const/immutable/shared, add 
scope, change in as an alias for const scope, give inout a 
totally new meaning, keep ref and out the same except that now 
ref can be prefixed with auto to give it a double meaning...  
choosing the right modifiers for function parameters is getting extra 
complicated.


Have we lost track of one of D's principles, that doing the right thing 
should be the easiest way to do things? To me it looks like we're 
adding more and more ways to pass arguments because the defaults are 
failing us. Perhaps it's time to revisit how arguments are passed by 
default.


As for auto ref, if we're to keep it I think it'd be much better if 
it was a keyword of its own, such as autoref. Having modifiers is one 
thing, but having modifiers that apply to modifiers is getting a little 
hard to parse in my head. This is not unprecedented, in English when 
one qualifier apply to another and it becomes hard to read we group 
them by adding a hyphen between the two.


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrei Alexandrescu

On 12/10/10 6:25 PM, Michel Fortin wrote:

On 2010-12-10 17:12:16 -0500, Don nos...@nospam.com said:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


One problem I'm starting to realize is that we now have so many
available qualifiers for function parameters than it's really easy to
get lost.

In D1 it was simple: in for regular arguments (the default),
inout/ref for passing arguments by refrence, and out for output
arguments. They all had clear semantics and not too much overlap.

In D2, we've lost this simplicity. Add const/immutable/shared, add
scope, change in as an alias for const scope, give inout a
totally new meaning, keep ref and out the same except that now ref
can be prefixed with auto to give it a double meaning... choosing the
right modifiers for function parameters is getting extra complicated.

Have we lost track of one of D's principles, that doing the right thing
should be the easiest way to do things? To me it looks like we're adding
more and more ways to pass arguments because the defaults are failing
us. Perhaps it's time to revisit how arguments are passed by default.

As for auto ref, if we're to keep it I think it'd be much better if it
was a keyword of its own, such as autoref. Having modifiers is one
thing, but having modifiers that apply to modifiers is getting a little
hard to parse in my head. This is not unprecedented, in English when one
qualifier apply to another and it becomes hard to read we group them by
adding a hyphen between the two.


It's sort of ironic. You just argued for the utility of, and 
implemented, another type constructor yourself!


Andrei


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Andrei Alexandrescu

On 12/10/10 6:25 PM, Michel Fortin wrote:

On 2010-12-10 17:12:16 -0500, Don nos...@nospam.com said:


Steven Schveighoffer wrote:

To summarize for those looking for the C++ behavior, the equivalent
would be:

void foo(auto ref const Widget)


That use of 'auto' is an abomination.


One problem I'm starting to realize is that we now have so many
available qualifiers for function parameters than it's really easy to
get lost.

In D1 it was simple: in for regular arguments (the default),
inout/ref for passing arguments by refrence, and out for output
arguments. They all had clear semantics and not too much overlap.

In D2, we've lost this simplicity. Add const/immutable/shared, add
scope, change in as an alias for const scope, give inout a
totally new meaning, keep ref and out the same except that now ref
can be prefixed with auto to give it a double meaning... choosing the
right modifiers for function parameters is getting extra complicated.

Have we lost track of one of D's principles, that doing the right thing
should be the easiest way to do things? To me it looks like we're adding
more and more ways to pass arguments because the defaults are failing
us. Perhaps it's time to revisit how arguments are passed by default.

As for auto ref, if we're to keep it I think it'd be much better if it
was a keyword of its own, such as autoref. Having modifiers is one
thing, but having modifiers that apply to modifiers is getting a little
hard to parse in my head. This is not unprecedented, in English when one
qualifier apply to another and it becomes hard to read we group them by
adding a hyphen between the two.


It's sort of ironic. Tu viens d'implementing yet another type 
constructor yourself!


Andrei


Re: Destructors, const structs, and opEquals

2010-12-10 Thread Michel Fortin
On 2010-12-10 21:28:43 -0500, Andrei Alexandrescu 
seewebsiteforem...@erdani.org said:



On 12/10/10 6:25 PM, Michel Fortin wrote:

One problem I'm starting to realize is that we now have so many
available qualifiers for function parameters than it's really easy to
get lost.

In D1 it was simple: in for regular arguments (the default),
inout/ref for passing arguments by refrence, and out for output
arguments. They all had clear semantics and not too much overlap.

In D2, we've lost this simplicity. Add const/immutable/shared, add
scope, change in as an alias for const scope, give inout a
totally new meaning, keep ref and out the same except that now ref
can be prefixed with auto to give it a double meaning... choosing the
right modifiers for function parameters is getting extra complicated.

Have we lost track of one of D's principles, that doing the right thing
should be the easiest way to do things? To me it looks like we're adding
more and more ways to pass arguments because the defaults are failing
us. Perhaps it's time to revisit how arguments are passed by default.

As for auto ref, if we're to keep it I think it'd be much better if it
was a keyword of its own, such as autoref. Having modifiers is one
thing, but having modifiers that apply to modifiers is getting a little
hard to parse in my head. This is not unprecedented, in English when one
qualifier apply to another and it becomes hard to read we group them by
adding a hyphen between the two.


It's sort of ironic. You just argued for the utility of, and 
implemented, another type constructor yourself!


Yeah, I know it's a little ironic. There's a difference though.

The problem I'm trying to illustrate here is that you'll need to be an 
expert to choose the right one depending on the situation. How many 
times have you seen someone pass std::string by copy in C++? You need a 
lot of training to get this right all the time because it's not the 
simpler way to pass parameters. Will the compiler complain when you 
pass a parameter by value instead of passing it by 'auto ref'?


As for the optional 'ref' suffix I added in my patch for tail-const, 
it's simply the continuation of the same syntax for pointers. It's not 
a type constructor. It's only a way to make explicit the 
already-existing implicit reference that classes have so you can apply 
type constructors separately to it. I doubt people will get it wrong 
often because in most situations the compiler will complain when you 
should have made the ref mutable and you haven't. There's no 
inefficiency by default here.


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: Destructors, const structs, and opEquals

2010-12-06 Thread Steven Schveighoffer
On Sun, 05 Dec 2010 09:18:13 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue. Consider:

void bump(ref int x) {
   ++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Right.

OK, so now I understand what you are saying, but now I don't understand  
why const ref is such a mistake.  Before you explained it was because when  
you pass an rvalue by ref, it's much more expensive, so auto ref passes by  
ref if it's an lvalue and by value if it's an rvalue.  At least that's  
what I understood.


With const ref, you get the same behavior, plus you are guaranteed that  
the code isn't going to do something stupid (like modify a value that will  
be thrown away at the end).


-Steve


Re: Destructors, const structs, and opEquals

2010-12-05 Thread spir
On Sat, 04 Dec 2010 23:36:22 -0500
Steven Schveighoffer schvei...@yahoo.com wrote:

 No no no, inout does not belong here.  Use const.  inout is only used if  
 you are returning a portion of the arguments.  That should be a hard rule  
 by the compiler (error).
 
 Fixed:
 
 bool opEquals(auto ref const(Tuple) rhs) const

Why isn't the parameter simply in instead of auto ref const? (And let the 
compiler decide whther it's worth passing it as ref for efficiency).

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Destructors, const structs, and opEquals

2010-12-05 Thread Olivier Pisano

Le 04/12/2010 10:00, Franciszek Czekala a écrit :

Anyway, if struct has value semantics then perhaps the argument to
opEquals should have simply 'in' mode? In Ada95 the 'in' mode of
the arguments does not determine how the arguments are passed
internally to the function. The compiler can choose to pass them
by value or by reference as suitable. Since the 'in' mode makes
the arguments constant inside, it does not really matter how the
arguments are passed, so why burden the user with this knowledge?


Hi,
I am certainly not expert enough to estimate what the consequences such 
a change would be for the existing code base, but I really appreciate

this idea.

I suppose a potential problem would be when trying to link code written 
by different compilers together. In such a scenario, how the arguments 
are passed matters. But I don't know any bit of Ada and don't have a 
clue about they solved this issue.


Cheers,

Olivier.


Re: Destructors, const structs, and opEquals

2010-12-05 Thread so
Huh?  I don't think you understand what I mean.  inout only implicitly  
converts to const.  Example:


struct S
{
   bool opEquals(S rhs){return false;}
}

struct T
{
   S s;
   bool opEquals(auto ref inout T rhs) inout {
  return s == rhs.s; // error, cannot call S.opEquals(S rhs) with  
parameters (inout S) inout

   }
}

You gain nothing from making opEquals of Tuple inout vs. const.

IMO all opEquals should be const functions, and the parameter should be  
const if it is marked as ref, or it contains references.


You are right. Reading it again, using inout is wrong here, both as  
parameter and function qualifier.
Only thing missing here is auto ref being broken(?). Using inout in  
parameter list already giving error here as it should.


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-05 Thread Andrei Alexandrescu

On 12/5/10 12:06 AM, Steven Schveighoffer wrote:

On Sun, 05 Dec 2010 00:42:20 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


Yah, still not getting the original point there.


I responded better in another part of this thread.

Basically, inout doesn't mean what you think it means.


Oh, I see. I thought its behavior can be equivalent to producing three 
functions, each replacing inout with (a) nothing, (b) const, (c) 
immutable. This approximation works only if all methods actually end up 
having the same code.


Andrei


Re: Destructors, const structs, and opEquals

2010-12-05 Thread Andrei Alexandrescu

On 12/5/10 12:04 AM, Steven Schveighoffer wrote:

I'm totally confused. I thought the point of auto ref was to pass by
value if it's an rvalue (since the data is already on the stack). If
this is not the case, then why not just make ref work that way? Why
wouldn't I mark all my functions as auto ref to avoid being pestered by
the compiler?


Because you sometimes do care about dealing with a true lvalue. Consider:

void bump(ref int x) {
  ++x;
}

Then:

unsigned int y;
bump(y); // you don't want this to go through
short z;
bump(z); // you don't want this to go through
int w;
bump(w * 2); // you don't want this to go through


Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Franciszek Czekala
 Officially, opEquals has to have the signature:

 struct Foo {
 bool opEquals(const ref Foo x) const {...}
 }

 But this disallows comparisons with rvalues.
 eg,

 Foo bar() { Foo x = 1; return x; }
 Foo y=1;
 assert( y == bar() ); // doesn't compile

 You can get around this by declaring a non-ref opEquals.
 But this fails if Foo has a destructor.

 If a struct has a destructor, it cannot be const(this is bug
3606)
 ---
 struct S {
 ~this() {}
 }

 void main() {
 const S z;
 }
 ---
 bug.d(6): Error: destructor bug.S.~this () is not callable using
argument types ()
 ---
 Likewise, it can't be a const parameter (this is bug 4338).
 void foo(const S a) {}
 It works to have it as a const ref parameter.

 Everything will work if you declare a const ~this(), but that
seems a little nonsensical. And you cannot have both const and non-
const ~this().

 I'm a bit uncertain as to how this is all supposed to work.
 (1) Should temporaries be allowed to be passed as 'const ref'?
 (2) If a struct has a destructor, should it be passable as a
const parameter? And if so, should the destructor be called?


I was wondering. Why cannot things be done simply? In Ada95 one
has:

function = (X,Y: in T) return Boolean;

for every type T, simple or composite, records and classed
included. Ada95 has been around for 15 years and did not gain any
popularity even though it was described as better than C++. I wish
D all best, but in view of the problem signaled in this post the
prospects are dim. D seems just a bit too complicated for a
compelling replacement of existing languages.

Anyway, if struct has value semantics then perhaps the argument to
opEquals should have simply 'in' mode? In Ada95 the 'in' mode of
the arguments does not determine how the arguments are passed
internally to the function. The compiler can choose to pass them
by value or by reference as suitable. Since the 'in' mode makes
the arguments constant inside, it does not really matter how the
arguments are passed, so why burden the user with this knowledge?


in and argument passing (was Re: Destructors, const structs, and opEquals)

2010-12-04 Thread spir
On Sat, 4 Dec 2010 09:00:05 + (UTC)
Franciszek Czekala h...@valentimex.com wrote:

 Anyway, if struct has value semantics then perhaps the argument to
 opEquals should have simply 'in' mode? In Ada95 the 'in' mode of
 the arguments does not determine how the arguments are passed
 internally to the function. The compiler can choose to pass them
 by value or by reference as suitable. Since the 'in' mode makes
 the arguments constant inside, it does not really matter how the
 arguments are passed, so why burden the user with this knowledge?

I support this point of view. In general, I think properly chosen qualifiers 
should have clear semantics (meaning); then, the language internally may adopt 
what is best for simplicity, efficiency, etc... as long as semantics are 
maintained. A programmer should not have to care about it (what is wrong for 
instance about arrays). Semantics and implementation may be much more kept 
apart in D. What is the sense of in for an app designer?

For the case of in (with a value argument), as the argument does not need to 
be protected by copy since it is known not to change, the compiler may chose to 
pass it by ref when more efficient (structs, mostly). Moreover, why not have 
in be the default for values? What is the meaning of changing a value 
parameter? 
draw (shape, color, position);
What is the sense of changing color or position? Qualifiers (semantics in 
general) could make more sense.


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Destructors, const structs, and opEquals

2010-12-04 Thread Jonathan M Davis
On Friday 03 December 2010 22:42:06 Don wrote:
 (1) Should temporaries be allowed to be passed as 'const ref'?

I honestly do not understand why they can't be already. C++ definitely allows 
this. Is there something bad about it? I'd probably use const ref a lot more, 
but because it will only take lvalues, it's _highly_ limiting. If you could 
overload functions on ref (I _think_ that there's a bug on that), then you 
could 
have two versions of opEquals - with with const ref and one which would copy 
the 
value - but ideally, you'd only need the one with const ref.

- Jonathan M Davis


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so

On Friday 03 December 2010 22:42:06 Don wrote:

(1) Should temporaries be allowed to be passed as 'const ref'?


I honestly do not understand why they can't be already. C++ definitely  
allows

this.


If you don't mean new C++ standards, this is not true. It is supported by  
non-standard extensions.


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?


Rather, what keeps compiler from interpreting A fun() like const ref A  
fun() when it is necessary?

After all D's const system has no holes in this case unlike C++.

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Jonathan M Davis
On Saturday 04 December 2010 03:49:26 so wrote:
  On Friday 03 December 2010 22:42:06 Don wrote:
  (1) Should temporaries be allowed to be passed as 'const ref'?
  
  I honestly do not understand why they can't be already. C++ definitely
  allows
  this.
 
 If you don't mean new C++ standards, this is not true. It is supported by
 non-standard extensions.

I'm 99.99% certain that it's perfectly legal to pass a temporary to a function 
that takes a const T and that it's in the standard. I'm fairly certain that 
I've read it in at least one book (though I'd have to look it up to be sure), 
but regardless, both gcc and Visual Studio definitely allow it, so if it's non-
standard, it's still highly supported.

- Jonathan M Davis


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so
I'm 99.99% certain that it's perfectly legal to pass a temporary to a  
function

that takes a const T and that it's in the standard


Oh that is right, but both are different things.

Say, when you have:

T fun() {...}
void bar(const T) {...}

bar(fun()) // 1. this is perfectly legal.
const T a = fun(); // 2. not legal, but still  you can do it on some  
compilers.


What Don's example is all about as far as i can tell is that D can't do  
the first one, somehow.


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?


Oh... we are saying exact same thing but i interpreted passed as 'const  
ref' out of the context!

I guess Jonathan also saying the same thing.

Sorry about that!

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 4:35 AM, Jonathan M Davis wrote:

On Friday 03 December 2010 22:42:06 Don wrote:

(1) Should temporaries be allowed to be passed as 'const ref'?


I honestly do not understand why they can't be already. C++ definitely allows
this.


C++'s second biggest mistake.

Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 6:50 AM, so wrote:

I'm 99.99% certain that it's perfectly legal to pass a temporary to a
function
that takes a const T and that it's in the standard


Oh that is right, but both are different things.

Say, when you have:

T fun() {...}
void bar(const T) {...}

bar(fun()) // 1. this is perfectly legal.
const T a = fun(); // 2. not legal, but still you can do it on some
compilers.

What Don's example is all about as far as i can tell is that D can't do
the first one, somehow.


Second line is legal too. Petru Marginean and I use it to good effect in 
our ScopeGuard idiom (a precursor to D's scope guards).


http://www.drdobbs.com/184403758

Andrei



Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official 
implementation of opEquals, opCmp etc. All the compiler needs to worry 
about is to syntactically translate a == b to a.opEquals(b) and then let 
the usual language rules resolve the call.



But this disallows comparisons with rvalues.
eg,

Foo bar() { Foo x = 1; return x; }
Foo y=1;
assert( y == bar() ); // doesn't compile

You can get around this by declaring a non-ref opEquals.
But this fails if Foo has a destructor.

If a struct has a destructor, it cannot be const(this is bug 3606)
---
struct S {
~this() {}
}

void main() {
const S z;
}
---
bug.d(6): Error: destructor bug.S.~this () is not callable using
argument types ()
---
Likewise, it can't be a const parameter (this is bug 4338).
void foo(const S a) {}
It works to have it as a const ref parameter.

Everything will work if you declare a const ~this(), but that seems a
little nonsensical. And you cannot have both const and non-const ~this().

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?
(2) If a struct has a destructor, should it be passable as a const
parameter? And if so, should the destructor be called?


This is a delicate matter that clearly needs a solution. Pass of 
temporaries by const ref was a huge mistake of C++ that it has paid 
dearly for and required the introduction of a large complication, the 
rvalue references feature, to just undo the effects of that mistake. So 
I don't think we should allow that.


Regarding destructors, for every constructed object ever there must be a 
corresponding destructor call. One issue that has been a matter of 
debate in C++ has been the fact that any object becomes deconstified 
during destruction. The oddest consequence of that rule is that in C++ 
you can delete a pointer to a const object:


// C++ code
class A { ... };
void fun(const A* p) { delete p; /* fine */ }

There has been a lot of opposition. const is supposed to limit what you 
can do with that object, and the fact that you can't invoke certain 
methods or change members, but you can nuke the entire object, is quite 
nonintuitive (and leads to a lot of funny real-life comparisons such as 
You can go out with my daughter, but no touching. Of course, you can 
shoot her if you so wish.)


In D, the rule must be inferred from D's immutability rules, which 
pretty much dictate that the destructor must be overloaded for non-const 
and const (and possibly invariant if the struct needs that).



Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Jonathan M Davis
On Saturday 04 December 2010 06:00:58 Andrei Alexandrescu wrote:
 On 12/4/10 4:35 AM, Jonathan M Davis wrote:
  On Friday 03 December 2010 22:42:06 Don wrote:
  (1) Should temporaries be allowed to be passed as 'const ref'?
  
  I honestly do not understand why they can't be already. C++ definitely
  allows this.
 
 C++'s second biggest mistake.

Okay. Why is it a mistake? I've never heard anyone say that this was a mistake 
before. As far as I can tell, it's extremely desirable, and this problem with 
opEquals() is a prime example as to why. What is wrong with allowing 
temporaries 
to be passed as const ref? What makes it such a big mistake?

- Jonathan M Davis


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so
On Sat, 04 Dec 2010 16:05:07 +0200, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/4/10 6:50 AM, so wrote:

I'm 99.99% certain that it's perfectly legal to pass a temporary to a
function
that takes a const T and that it's in the standard


Oh that is right, but both are different things.

Say, when you have:

T fun() {...}
void bar(const T) {...}

bar(fun()) // 1. this is perfectly legal.
const T a = fun(); // 2. not legal, but still you can do it on some
compilers.


Second line is legal too. Petru Marginean and I use it to good effect in  
my ScopeGuard idiom (a precursor to D's scope guards).


http://www.drdobbs.com/184403758


Andrei


I was sure that always output C4238 on MSVC, and simply rejected on GCC.
Now I tried with 2 versions of MSVC and it didn't give any warnings.
As it looks like this is only for pointers. That is:

const T* a = fun();

I have encountered this quite a few times and i was sure reference example  
above also same since i can't think of a reason

that i would take take the address of a temporary function...

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 8:29 AM, Jonathan M Davis wrote:

On Saturday 04 December 2010 06:00:58 Andrei Alexandrescu wrote:

On 12/4/10 4:35 AM, Jonathan M Davis wrote:

On Friday 03 December 2010 22:42:06 Don wrote:

(1) Should temporaries be allowed to be passed as 'const ref'?


I honestly do not understand why they can't be already. C++ definitely
allows this.


C++'s second biggest mistake.


Okay. Why is it a mistake? I've never heard anyone say that this was a mistake
before. As far as I can tell, it's extremely desirable, and this problem with
opEquals() is a prime example as to why. What is wrong with allowing temporaries
to be passed as const ref? What makes it such a big mistake?


It makes it impossible to distinguish an rvalue from an lvalue.

Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread so

i would take take the address of a temporary function...


Should be:

i would take the address of a temporary object/value.

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Don

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official 
implementation of opEquals, opCmp etc. All the compiler needs to worry 
about is to syntactically translate a == b to a.opEquals(b) and then let 
the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals 
never gets instantiated.
How can opEquals be defined in a way that it works for structs with 
destructors, and also with rvalues?




But this disallows comparisons with rvalues.
eg,

Foo bar() { Foo x = 1; return x; }
Foo y=1;
assert( y == bar() ); // doesn't compile

You can get around this by declaring a non-ref opEquals.
But this fails if Foo has a destructor.

If a struct has a destructor, it cannot be const(this is bug 3606)
---
struct S {
~this() {}
}

void main() {
const S z;
}
---
bug.d(6): Error: destructor bug.S.~this () is not callable using
argument types ()
---
Likewise, it can't be a const parameter (this is bug 4338).
void foo(const S a) {}
It works to have it as a const ref parameter.

Everything will work if you declare a const ~this(), but that seems a
little nonsensical. And you cannot have both const and non-const ~this().

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?
(2) If a struct has a destructor, should it be passable as a const
parameter? And if so, should the destructor be called?


This is a delicate matter that clearly needs a solution. Pass of 
temporaries by const ref was a huge mistake of C++ that it has paid 
dearly for and required the introduction of a large complication, the 
rvalue references feature, to just undo the effects of that mistake. So 
I don't think we should allow that.


Regarding destructors, for every constructed object ever there must be a 
corresponding destructor call. One issue that has been a matter of 
debate in C++ has been the fact that any object becomes deconstified 
during destruction. The oddest consequence of that rule is that in C++ 
you can delete a pointer to a const object:


// C++ code
class A { ... };
void fun(const A* p) { delete p; /* fine */ }

There has been a lot of opposition. const is supposed to limit what you 
can do with that object, and the fact that you can't invoke certain 
methods or change members, but you can nuke the entire object, is quite 
nonintuitive (and leads to a lot of funny real-life comparisons such as 
You can go out with my daughter, but no touching. Of course, you can 
shoot her if you so wish.)


In D, the rule must be inferred from D's immutability rules, which 
pretty much dictate that the destructor must be overloaded for non-const 
and const (and possibly invariant if the struct needs that).


This scares me. I can see a danger of making structs with destructors 
practically unusable.


Some unprocessed thoughts are below.

Who calls postblit? I think that if you can make a clone of a const 
object, you should also be able to destroy that clone.


Seems to me that the natural rule would be, that the creator is 
responsible for destruction. This would require that, for example, given 
code like this:


const(S) foo(const(S) x) { return x; }

inside foo, a non-const S is created, blitted with x, then postblit is 
called on it, then it is returned as const. On return, the original x is 
destroyed if it was a temporary. Otherwise, it gets called at the end of 
its scope.


Eg,

const w = foo(foo( S(2) ));

would be translated into:
const w = (foo( S __tmp1 = S(2),  const(S) __tmp2 = foo(__tmp1), 
~__tmp1, __tmp2), ~__tmp2);



Has this sort of idea been explored? Is there something wrong with it?


Re: Destructors, const structs, and opEquals

2010-12-04 Thread kenji hara
Andrei, your explanation is almost the same as was my understanding. Thank you.

My shallow thought:
  const T makes automatically reference. It is convenient.
Right thinking:
  D has no semantics dividing copying/referencing, against has
dividing rvalue/lvalue.
  D should support this like T(copying)/T(referencing).

Thanks.

Kenji

2010/12/4 Andrei Alexandrescu seewebsiteforem...@erdani.org:
 On 12/4/10 12:42 AM, Don wrote:

 Officially, opEquals has to have the signature:

 struct Foo {
 bool opEquals(const ref Foo x) const {...}
 }

 This is a compiler bug. For structs there should be no official
 implementation of opEquals, opCmp etc. All the compiler needs to worry about
 is to syntactically translate a == b to a.opEquals(b) and then let the usual
 language rules resolve the call.

 But this disallows comparisons with rvalues.
 eg,

 Foo bar() { Foo x = 1; return x; }
 Foo y=1;
 assert( y == bar() ); // doesn't compile

 You can get around this by declaring a non-ref opEquals.
 But this fails if Foo has a destructor.

 If a struct has a destructor, it cannot be const(this is bug 3606)
 ---
 struct S {
 ~this() {}
 }

 void main() {
 const S z;
 }
 ---
 bug.d(6): Error: destructor bug.S.~this () is not callable using
 argument types ()
 ---
 Likewise, it can't be a const parameter (this is bug 4338).
 void foo(const S a) {}
 It works to have it as a const ref parameter.

 Everything will work if you declare a const ~this(), but that seems a
 little nonsensical. And you cannot have both const and non-const ~this().

 I'm a bit uncertain as to how this is all supposed to work.
 (1) Should temporaries be allowed to be passed as 'const ref'?
 (2) If a struct has a destructor, should it be passable as a const
 parameter? And if so, should the destructor be called?

 This is a delicate matter that clearly needs a solution. Pass of temporaries
 by const ref was a huge mistake of C++ that it has paid dearly for and
 required the introduction of a large complication, the rvalue references
 feature, to just undo the effects of that mistake. So I don't think we
 should allow that.

 Regarding destructors, for every constructed object ever there must be a
 corresponding destructor call. One issue that has been a matter of debate in
 C++ has been the fact that any object becomes deconstified during
 destruction. The oddest consequence of that rule is that in C++ you can
 delete a pointer to a const object:

 // C++ code
 class A { ... };
 void fun(const A* p) { delete p; /* fine */ }

 There has been a lot of opposition. const is supposed to limit what you can
 do with that object, and the fact that you can't invoke certain methods or
 change members, but you can nuke the entire object, is quite nonintuitive
 (and leads to a lot of funny real-life comparisons such as You can go out
 with my daughter, but no touching. Of course, you can shoot her if you so
 wish.)

 In D, the rule must be inferred from D's immutability rules, which pretty
 much dictate that the destructor must be overloaded for non-const and const
 (and possibly invariant if the struct needs that).


 Andrei



Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
  foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
  }
  return true;
}

It looks a bit alembicated but let's not forget that Tuple is supposed 
to be very flexible and to do a lot of things.



How can opEquals be defined in a way that it works for structs with
destructors, and also with rvalues?


auto ref should be used whenever you want to accept both a value and 
an rvalue. For structs with destructors see my other post in this thread.


Unfortunately, auto ref is currently implemented wrongly due to a 
misunderstanding between Walter and myself. I meant it as a relaxation 
of binding rules, i.e. I'm fine with either an rvalue or an lvalue. He 
thought it's all about generating two template instantiations. In fact 
auto ref should work when there's no template in sight.



But this disallows comparisons with rvalues.
eg,

Foo bar() { Foo x = 1; return x; }
Foo y=1;
assert( y == bar() ); // doesn't compile

You can get around this by declaring a non-ref opEquals.
But this fails if Foo has a destructor.

If a struct has a destructor, it cannot be const(this is bug 3606)
---
struct S {
~this() {}
}

void main() {
const S z;
}
---
bug.d(6): Error: destructor bug.S.~this () is not callable using
argument types ()
---
Likewise, it can't be a const parameter (this is bug 4338).
void foo(const S a) {}
It works to have it as a const ref parameter.

Everything will work if you declare a const ~this(), but that seems a
little nonsensical. And you cannot have both const and non-const
~this().

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?
(2) If a struct has a destructor, should it be passable as a const
parameter? And if so, should the destructor be called?


This is a delicate matter that clearly needs a solution. Pass of
temporaries by const ref was a huge mistake of C++ that it has paid
dearly for and required the introduction of a large complication, the
rvalue references feature, to just undo the effects of that mistake.
So I don't think we should allow that.

Regarding destructors, for every constructed object ever there must be
a corresponding destructor call. One issue that has been a matter of
debate in C++ has been the fact that any object becomes deconstified
during destruction. The oddest consequence of that rule is that in C++
you can delete a pointer to a const object:

// C++ code
class A { ... };
void fun(const A* p) { delete p; /* fine */ }

There has been a lot of opposition. const is supposed to limit what
you can do with that object, and the fact that you can't invoke
certain methods or change members, but you can nuke the entire object,
is quite nonintuitive (and leads to a lot of funny real-life
comparisons such as You can go out with my daughter, but no touching.
Of course, you can shoot her if you so wish.)

In D, the rule must be inferred from D's immutability rules, which
pretty much dictate that the destructor must be overloaded for
non-const and const (and possibly invariant if the struct needs that).


This scares me. I can see a danger of making structs with destructors
practically unusable.


Why? It makes perfect sense to qualify the destructor the same way as 
the originating constructor. It has been a serious limitation of C++ 
that you couldn't tell during either construction or destruction that a 
const object was being built/destroyed.


One issue with D's const is that people expect to use it most 
everywhere, much like C++'s const. One thing that I understood early on 
was that D's const provides much stronger guarantees than C++'s, and as 
a direct consequence it is more constrained and is usable less often.



Some unprocessed thoughts are below.

Who calls postblit? I think that if you can make a clone of a const
object, you should also be able to destroy that clone.


Yes. Any object created will also be destroyed, regardless of qualifiers.


Seems to me that the natural rule would be, that the creator is
responsible for destruction.


That rule is roughly C++'s and has an issue that D fixes (in my mind; 
the implementation is not 100% there yet). Exact issue is discussed below.



This would require that, for example, given
code like this:

const(S) 

Re: Destructors, const structs, and opEquals

2010-12-04 Thread so

bool opEquals(auto ref inout Tuple rhs) inout {
   foreach (i, T; Types) {
 if (this[i] != rhs[i]) return false;
   }
   return true;
}

It looks a bit alembicated but let's not forget that Tuple is supposed  
to be very flexible and to do a lot of things.


Const-system is a one big abomination, considering the consequences it is  
quite hard to say it is something good.
As complex as it may look, the above example addresses many problems of  
this system. I would hate to write equal C++ code.


Please lets not add any more keyword/syntax, already forgot we had auto  
ref...


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrej Mitrovic
Keyword cocktails..

2010/12/4 so s...@so.do:
 bool opEquals(auto ref inout Tuple rhs) inout {
   foreach (i, T; Types) {
     if (this[i] != rhs[i]) return false;
   }
   return true;
 }

 It looks a bit alembicated but let's not forget that Tuple is supposed to
 be very flexible and to do a lot of things.

 Const-system is a one big abomination, considering the consequences it is
 quite hard to say it is something good.
 As complex as it may look, the above example addresses many problems of this
 system. I would hate to write equal C++ code.

 Please lets not add any more keyword/syntax, already forgot we had auto
 ref...

 --
 Using Opera's revolutionary email client: http://www.opera.com/mail/



Re: Destructors, const structs, and opEquals

2010-12-04 Thread Don

Andrei Alexandrescu wrote:

On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
  foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
  }
  return true;
}

It looks a bit alembicated but let's not forget that Tuple is supposed 
to be very flexible and to do a lot of things.


Ouch.

The semantics of == are very well defined, and simple. Always, you want 
read-only access to the two objects, in the fastest possible way.
I don't see why the complexity of the object should have any influence 
on the signature of ==. If there's a method which works correctly and 
efficiently in every case, why isn't it the only way?




How can opEquals be defined in a way that it works for structs with
destructors, and also with rvalues?


auto ref should be used whenever you want to accept both a value and 
an rvalue. For structs with destructors see my other post in this thread.


Unfortunately, auto ref is currently implemented wrongly due to a 
misunderstanding between Walter and myself. I meant it as a relaxation 
of binding rules, i.e. I'm fine with either an rvalue or an lvalue. He 
thought it's all about generating two template instantiations. In fact 
auto ref should work when there's no template in sight.



But this disallows comparisons with rvalues.
eg,

Foo bar() { Foo x = 1; return x; }
Foo y=1;
assert( y == bar() ); // doesn't compile

You can get around this by declaring a non-ref opEquals.
But this fails if Foo has a destructor.

If a struct has a destructor, it cannot be const(this is bug 3606)
---
struct S {
~this() {}
}

void main() {
const S z;
}
---
bug.d(6): Error: destructor bug.S.~this () is not callable using
argument types ()
---
Likewise, it can't be a const parameter (this is bug 4338).
void foo(const S a) {}
It works to have it as a const ref parameter.

Everything will work if you declare a const ~this(), but that seems a
little nonsensical. And you cannot have both const and non-const
~this().

I'm a bit uncertain as to how this is all supposed to work.
(1) Should temporaries be allowed to be passed as 'const ref'?
(2) If a struct has a destructor, should it be passable as a const
parameter? And if so, should the destructor be called?


This is a delicate matter that clearly needs a solution. Pass of
temporaries by const ref was a huge mistake of C++ that it has paid
dearly for and required the introduction of a large complication, the
rvalue references feature, to just undo the effects of that mistake.
So I don't think we should allow that.

Regarding destructors, for every constructed object ever there must be
a corresponding destructor call. One issue that has been a matter of
debate in C++ has been the fact that any object becomes deconstified
during destruction. The oddest consequence of that rule is that in C++
you can delete a pointer to a const object:

// C++ code
class A { ... };
void fun(const A* p) { delete p; /* fine */ }

There has been a lot of opposition. const is supposed to limit what
you can do with that object, and the fact that you can't invoke
certain methods or change members, but you can nuke the entire object,
is quite nonintuitive (and leads to a lot of funny real-life
comparisons such as You can go out with my daughter, but no touching.
Of course, you can shoot her if you so wish.)

In D, the rule must be inferred from D's immutability rules, which
pretty much dictate that the destructor must be overloaded for
non-const and const (and possibly invariant if the struct needs that).


This scares me. I can see a danger of making structs with destructors
practically unusable.


Why? It makes perfect sense to qualify the destructor the same way as 
the originating constructor. It has been a serious limitation of C++ 
that you couldn't tell during either construction or destruction that a 
const object was being built/destroyed.


One issue with D's const is that people expect to use it most 
everywhere, much like C++'s const. One thing that I understood early on 
was that D's const provides much stronger guarantees than C++'s, and as 
a direct consequence it is more constrained and is usable less often.



Some unprocessed thoughts are below.

Who calls postblit? I think that if you can make a clone of a const
object, you should also be able to destroy that clone.


Yes. Any object 

Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 2:39 PM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
}
return true;
}

It looks a bit alembicated but let's not forget that Tuple is supposed
to be very flexible and to do a lot of things.


Ouch.

The semantics of == are very well defined, and simple. Always, you want
read-only access to the two objects, in the fastest possible way.
I don't see why the complexity of the object should have any influence
on the signature of ==. If there's a method which works correctly and
efficiently in every case, why isn't it the only way?


It's not the complexity of the object as much as don't pay for const if 
you don't use it. If Tuple's opEquals is implemented as above, it works 
with code that doesn't use const at all. Tack a const on to it, 
everybody must define opEquals with const.


I would agree if there were a wide agreement out there that opEquals 
must have a const signature. In that case, the required signature should be:


bool opEquals(auto ref const T) const;

auto ref, again, is NOT two templates into one, it's argument binding 
relaxation.


[snip]

Has this sort of idea been explored? Is there something wrong with it?


What's wrong with it is it consistently leads to suboptimal code,
which has created a hecatomb of problems for C++ (even the very
carefully conceived rvalue references feature, for all its size and
might, is unable to fix them all).

The caller should create the copy and pass the responsibility of
destroying to the callee. This is because oftentimes the actual object
destroyed is not the same object that was constructed. You see those
trailing calls ~__tmp1, ~__tmp2 at the end of your code? They are a
tin cat stuck to code's tail.


They need not apply to functions with 'inout' parameters. A parameter
which is passed by 'inout' will either be used in the return value, or
it will need to be destroyed.
It seems clear to me that when you declare an 'inout' parameter, you're
assuming responsibility for the lifetime of the object.


I'm not sure I understand, sorry. To recap, inout used to mean ref 
but not anymore. It just means this stands for either const, immutable, 
or nothing. I'm not sure how that affects caller's responsibility.


And to clarify: due to D's rule that all structs are moveable, the 
object against which the destructor will be called may be different than 
the one against which the constructor was called. Therefore, the rule 
that the creator code is always responsible for destruction is not 
applicable.



Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Steven Schveighoffer
On Sat, 04 Dec 2010 11:00:58 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
   foreach (i, T; Types) {
 if (this[i] != rhs[i]) return false;
   }
   return true;
}



No no no, inout does not belong here.  Use const.  inout is only used if  
you are returning a portion of the arguments.  That should be a hard rule  
by the compiler (error).


Fixed:

bool opEquals(auto ref const(Tuple) rhs) const


How can opEquals be defined in a way that it works for structs with
destructors, and also with rvalues?


auto ref should be used whenever you want to accept both a value and  
an rvalue. For structs with destructors see my other post in this thread.


Unfortunately, auto ref is currently implemented wrongly due to a  
misunderstanding between Walter and myself. I meant it as a relaxation  
of binding rules, i.e. I'm fine with either an rvalue or an lvalue. He  
thought it's all about generating two template instantiations. In fact  
auto ref should work when there's no template in sight.


But it must instantiate two functions, no?  How does one call the same  
function with by ref or by value?  And when inside the function, the code  
generation for a ref storage class is going to be drastically different,  
right?


BTW, I agree with the point, it should not require templates.  But I think  
it does result in two functions.


Actually, thinking about it more, how does this work?

T foo();
T bar();
if(foo() == bar())

both are temporaries, but opEquals passes 'this' by reference.  So there  
we have a case where a reference of a temporary is passed.  Does this make  
sense?


Indeed, I have used this 'trick' to get around the discussed limitations  
while writing dcollections.  Just always compare using 'rvalue == lvalue'.


-Steve


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 22:42 CST, Steven Schveighoffer wrote:

On Sat, 04 Dec 2010 15:58:43 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/4/10 2:39 PM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to
worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that
opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
}
return true;
}

It looks a bit alembicated but let's not forget that Tuple is supposed
to be very flexible and to do a lot of things.


Ouch.

The semantics of == are very well defined, and simple. Always, you want
read-only access to the two objects, in the fastest possible way.
I don't see why the complexity of the object should have any influence
on the signature of ==. If there's a method which works correctly and
efficiently in every case, why isn't it the only way?


It's not the complexity of the object as much as don't pay for const
if you don't use it. If Tuple's opEquals is implemented as above, it
works with code that doesn't use const at all. Tack a const on to it,
everybody must define opEquals with const.


You have not addressed that problem -- tack an inout on it, everybody
must define opEquals with inout.


I don't think so. On the contrary, declaring with inout is the most 
adaptive.



[snip]

Has this sort of idea been explored? Is there something wrong with it?


What's wrong with it is it consistently leads to suboptimal code,
which has created a hecatomb of problems for C++ (even the very
carefully conceived rvalue references feature, for all its size and
might, is unable to fix them all).

The caller should create the copy and pass the responsibility of
destroying to the callee. This is because oftentimes the actual object
destroyed is not the same object that was constructed. You see those
trailing calls ~__tmp1, ~__tmp2 at the end of your code? They are a
tin cat stuck to code's tail.


They need not apply to functions with 'inout' parameters. A parameter
which is passed by 'inout' will either be used in the return value, or
it will need to be destroyed.
It seems clear to me that when you declare an 'inout' parameter, you're
assuming responsibility for the lifetime of the object.


I'm not sure I understand, sorry. To recap, inout used to mean ref
but not anymore. It just means this stands for either const,
immutable, or nothing. I'm not sure how that affects caller's
responsibility.


It does not stand for const, immutable, or nothing exactly. It binds the
constancy of the output with the constancy of the inputs in an
enforceable way. It imposes a temporary const on everything, and then
returns things back to the way they were, even though you are returning
a portion of a parameter.


Yah, still not getting the original point there.


Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 22:36 CST, Steven Schveighoffer wrote:

On Sat, 04 Dec 2010 11:00:58 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to worry
about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that opEquals
never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
}
return true;
}



No no no, inout does not belong here. Use const. inout is only used if
you are returning a portion of the arguments. That should be a hard rule
by the compiler (error).

Fixed:

bool opEquals(auto ref const(Tuple) rhs) const


Then you handle the angry crowds for me please.


How can opEquals be defined in a way that it works for structs with
destructors, and also with rvalues?


auto ref should be used whenever you want to accept both a value and
an rvalue. For structs with destructors see my other post in this thread.

Unfortunately, auto ref is currently implemented wrongly due to a
misunderstanding between Walter and myself. I meant it as a relaxation
of binding rules, i.e. I'm fine with either an rvalue or an lvalue.
He thought it's all about generating two template instantiations. In
fact auto ref should work when there's no template in sight.


But it must instantiate two functions, no?


No.


How does one call the same
function with by ref or by value?


By always using ref.


And when inside the function, the code
generation for a ref storage class is going to be drastically different,
right?


No.


BTW, I agree with the point, it should not require templates. But I
think it does result in two functions.


No.


Actually, thinking about it more, how does this work?

T foo();
T bar();
if(foo() == bar())

both are temporaries, but opEquals passes 'this' by reference. So there
we have a case where a reference of a temporary is passed. Does this
make sense?


Yah. Method calls are already passed by reference (I'd prefer not but 
that's just me).



Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Andrei Alexandrescu

On 12/4/10 23:40 CST, Andrei Alexandrescu wrote:

Yah. Method calls are already passed by reference (I'd prefer not but
that's just me).


Sorry, I meant: method calls are already allowed for rvalues. Sleepy...

Andrei


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Steven Schveighoffer
On Sun, 05 Dec 2010 00:40:26 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 12/4/10 22:36 CST, Steven Schveighoffer wrote:

On Sat, 04 Dec 2010 11:00:58 -0500, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 12/4/10 9:23 AM, Don wrote:

Andrei Alexandrescu wrote:

On 12/4/10 12:42 AM, Don wrote:

Officially, opEquals has to have the signature:

struct Foo {
bool opEquals(const ref Foo x) const {...}
}


This is a compiler bug. For structs there should be no official
implementation of opEquals, opCmp etc. All the compiler needs to  
worry

about is to syntactically translate a == b to a.opEquals(b) and then
let the usual language rules resolve the call.


Fine, but try to implement a generic type which supports ==.
For example, Tuple in std.typecons.
The only reason that many of the Tuple unit tests pass is that  
opEquals

never gets instantiated.


I think it should be as follows:

bool opEquals(auto ref inout Tuple rhs) inout {
foreach (i, T; Types) {
if (this[i] != rhs[i]) return false;
}
return true;
}



No no no, inout does not belong here. Use const. inout is only used if
you are returning a portion of the arguments. That should be a hard rule
by the compiler (error).

Fixed:

bool opEquals(auto ref const(Tuple) rhs) const


Then you handle the angry crowds for me please.


Huh?  I don't think you understand what I mean.  inout only implicitly  
converts to const.  Example:


struct S
{
  bool opEquals(S rhs){return false;}
}

struct T
{
  S s;
  bool opEquals(auto ref inout T rhs) inout {
 return s == rhs.s; // error, cannot call S.opEquals(S rhs) with  
parameters (inout S) inout

  }
}

You gain nothing from making opEquals of Tuple inout vs. const.

IMO all opEquals should be const functions, and the parameter should be  
const if it is marked as ref, or it contains references.





How can opEquals be defined in a way that it works for structs with
destructors, and also with rvalues?


auto ref should be used whenever you want to accept both a value and
an rvalue. For structs with destructors see my other post in this  
thread.


Unfortunately, auto ref is currently implemented wrongly due to a
misunderstanding between Walter and myself. I meant it as a relaxation
of binding rules, i.e. I'm fine with either an rvalue or an lvalue.
He thought it's all about generating two template instantiations. In
fact auto ref should work when there's no template in sight.


But it must instantiate two functions, no?


No.


How does one call the same
function with by ref or by value?


By always using ref.


I'm totally confused.  I thought the point of auto ref was to pass by  
value if it's an rvalue (since the data is already on the stack).  If this  
is not the case, then why not just make ref work that way?  Why wouldn't I  
mark all my functions as auto ref to avoid being pestered by the compiler?


-Steve


Re: Destructors, const structs, and opEquals

2010-12-04 Thread Steven Schveighoffer
On Sun, 05 Dec 2010 00:42:20 -0500, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



Yah, still not getting the original point there.


I responded better in another part of this thread.

Basically, inout doesn't mean what you think it means.

-Steve