View mode: basic / threaded / horizontal-split · Log in · Help
October 21, 2009
Re: this() not executing code on structs
Andrei Alexandrescu wrote:
> Today, structs can't write their own this(). There aren't very solid 
> reasons for that except that it makes language implementation more 
> difficult.
> 
> I wonder how much of a problem that could be in practice. I realized 
> today that the "Counted" example - a classic C++ primer example 
> featuring a struct that counts its own instances - cannot be implemented 
> in D.
> 
> In C++ the counted example looks like this:
> 
> struct Counted {
>    static unsigned count;
>    unsigned myCount;
>    Counted() { myCount = count++; }
>    Counted(const Counted& rhs) { myCount = count++; }
>    Counted& operator=(const Counted& rhs) {
>       // no writing to myCount
>       return *this;
>    }
>    ~Counted() {
>       --count;
>    }
> }
> 
> In D there's no chance to write Counted because you can always create 
> Counted objects without executing any code.
> 
> struct Counted {
>    static uint count;
>    uint myCount;
>    this() { myCount = count++; }       // ERROR
>    this(this) { myCount = count++; }
>    ref Counted opAssign(Counted rhs) {
>       // no writing to myCount
>       return this;
>    }
>    ~this() {
>       --count;
>    }
> }
> 
> This being a toy example, I wonder whether there are much more serious 
> examples that would be impossible to implement within D.

I'd really like to know why "scope x = new X();" is "unsafe", while 
encouraging doing exactly the same with structs seems to be a perfectly 
fine idea. Allocating structs on the stack is obviously not any safer 
than with classes. I don't remember the exact reasons why you wanted to 
turn "scope" into a library feature, but I think I remember something 
about discouraging it for safety reasons; please forgive me is this is 
wrong.

Why do you want to add class functionality to structs to enable "RAII" 
like features, when you could just use scope classes?

To refresh everyone's memory: a scope class is declared like "scope 
class Foo { ... }", and references to it can "only appear as a function 
local variable".

I for one don't really like the idea of having to distinguish between 
PODs and "other stuff" (like you had in C++) just again.

At the very least, the default constructor should always be available 
for structs. (If not, have fun figuring out what S[10] should do.)

> Andrei
October 21, 2009
Re: this() not executing code on structs
On Wed, 21 Oct 2009 17:54:08 -0400, grauzone <none@example.net> wrote:

> I'd really like to know why "scope x = new X();" is "unsafe", while  
> encouraging doing exactly the same with structs seems to be a perfectly  
> fine idea. Allocating structs on the stack is obviously not any safer  
> than with classes. I don't remember the exact reasons why you wanted to  
> turn "scope" into a library feature, but I think I remember something  
> about discouraging it for safety reasons; please forgive me is this is  
> wrong.

A class is a reference type.  If you pass the reference to the stack to a  
function that then stores it for later use, it is unsafe.  If you return  
it from the function, it's unsafe.  If you do the same with a struct (not  
a struct pointer), this is not the case.  Note that you *could* have the  
same problem with struct pointers or references, but typically, you expect  
to treat struct references like they are referencing stack data, it's not  
typical for classes.

-Steve
October 21, 2009
Re: this() not executing code on structs
== Quote from grauzone (none@example.net)'s article
> Andrei Alexandrescu wrote:
> I'd really like to know why "scope x = new X();" is "unsafe", while
> encouraging doing exactly the same with structs seems to be a perfectly
> fine idea. Allocating structs on the stack is obviously not any safer
> than with classes. I don't remember the exact reasons why you wanted to
> turn "scope" into a library feature, but I think I remember something
> about discouraging it for safety reasons; please forgive me is this is
> wrong.

Because classes in D are always passed by pointer.  (Technically references, but
really they're just pointers under the hood.)  Returning a scope (stack-allocated)
class from a function is equivalent to escaping a pointer to a stack variable.
Returning a struct is done by value, just like returning an int.

Classes can't be value types because they are polymorphic, meaning their size
isn't known at compile time.  C++ tries to make them value types but really, there
is no *good* way to make a polymorphic type with size not known at compile time a
value type.

> Why do you want to add class functionality to structs to enable "RAII"
> like features, when you could just use scope classes?

To me, this makes perfect sense.  Classes are polymorphic, structs are value
types.  Except in the "here be dragons" world of C++, the two are mutually
exclusive, which is the reason for the dichotomy in the first place.  Therefore,
structs should do everything they can w/o being polymorphic and classes should do
everything they can w/o being value types.  You then decide which one you want
based on whether you need value semantics or polymorphism more.

The only place in D where this logic breaks down is monitor objects on classes.
Even here, while structs technically *could* be given monitors, this is
inefficient because they are value types, whereas the efficiency loss from storing
a few extra bytes in a reference type is minimal in most cases.

Scope is really a dangerous hack to allocate a *reference type* on the stack.
It's dangerous and kludgey, but in a performance-oriented language it's a
necessary evil.
October 22, 2009
Re: this() not executing code on structs
dsimcha wrote:
> == Quote from grauzone (none@example.net)'s article
>> Andrei Alexandrescu wrote:
>> I'd really like to know why "scope x = new X();" is "unsafe", while
>> encouraging doing exactly the same with structs seems to be a perfectly
>> fine idea. Allocating structs on the stack is obviously not any safer
>> than with classes. I don't remember the exact reasons why you wanted to
>> turn "scope" into a library feature, but I think I remember something
>> about discouraging it for safety reasons; please forgive me is this is
>> wrong.
> 
> Because classes in D are always passed by pointer.  (Technically references, but
> really they're just pointers under the hood.)  Returning a scope (stack-allocated)
> class from a function is equivalent to escaping a pointer to a stack variable.
> Returning a struct is done by value, just like returning an int.

(I'm talking about scope classes as declared in "scope class T { ... }")

But you can't return scope classes from a function. You can't pass them 
as ref parameters either. They're designed to be safe.

On the other hand, you can pass struct pointers all the way you want 
around, and it's damn unsafe.

I don't get this "structs are safe because they are value types" 
argument anyway, because the this pointer for structs is a 
pointer/reference anyway. If it's trivial to break that "safety", can 
you really call it "safety"?

> Classes can't be value types because they are polymorphic, meaning their size
> isn't known at compile time.  C++ tries to make them value types but really, there
> is no *good* way to make a polymorphic type with size not known at compile time a
> value type.
> 
>> Why do you want to add class functionality to structs to enable "RAII"
>> like features, when you could just use scope classes?
> 
> To me, this makes perfect sense.  Classes are polymorphic, structs are value
> types.  Except in the "here be dragons" world of C++, the two are mutually
> exclusive, which is the reason for the dichotomy in the first place.  Therefore,
> structs should do everything they can w/o being polymorphic and classes should do
> everything they can w/o being value types.  You then decide which one you want
> based on whether you need value semantics or polymorphism more.
> 
> The only place in D where this logic breaks down is monitor objects on classes.
> Even here, while structs technically *could* be given monitors, this is
> inefficient because they are value types, whereas the efficiency loss from storing
> a few extra bytes in a reference type is minimal in most cases.

Why do all objects have monitor pointers anyway? The idea, that every 
object can act as lock, is a really bad one. But this is off-topic...

> Scope is really a dangerous hack to allocate a *reference type* on the stack.
> It's dangerous and kludgey, but in a performance-oriented language it's a
> necessary evil.

You could say the same about structs.
October 22, 2009
Re: this() not executing code on structs
grauzone wrote:
> dsimcha wrote:
>> == Quote from grauzone (none@example.net)'s article
>>> Andrei Alexandrescu wrote:
>>> I'd really like to know why "scope x = new X();" is "unsafe", while
>>> encouraging doing exactly the same with structs seems to be a perfectly
>>> fine idea. Allocating structs on the stack is obviously not any safer
>>> than with classes. I don't remember the exact reasons why you wanted to
>>> turn "scope" into a library feature, but I think I remember something
>>> about discouraging it for safety reasons; please forgive me is this is
>>> wrong.
>>
>> Because classes in D are always passed by pointer.  (Technically 
>> references, but
>> really they're just pointers under the hood.)  Returning a scope 
>> (stack-allocated)
>> class from a function is equivalent to escaping a pointer to a stack 
>> variable.
>> Returning a struct is done by value, just like returning an int.
> 
> (I'm talking about scope classes as declared in "scope class T { ... }")
> 
> But you can't return scope classes from a function. You can't pass them 
> as ref parameters either. They're designed to be safe.

I wish it was as easy as it sounds. In fact you don't need to pass scope 
classes as ref parameters - it's enough to pass them "by value" because 
they are implicitly references.

You can't even safely call a method on a scope class object because that 
method may assign "this" to something escaping the scope of the method.

Save for using some flavor of interprocedural escape analysis and/or 
making "scope" a function attribute, I'm not seeing how scope can be 
made safe and reasonably useful.

> On the other hand, you can pass struct pointers all the way you want 
> around, and it's damn unsafe.
> 
> I don't get this "structs are safe because they are value types" 
> argument anyway, because the this pointer for structs is a 
> pointer/reference anyway. If it's trivial to break that "safety", can 
> you really call it "safety"?

The point is that you can disable address taking altogether and still 
write a great deal of good code in D. If address taking is verboten 
(e.g. in SafeD), ref parameters can never be escaped (they will be 
scoped) and therefore they become safe, too. So within SafeD, structs 
become safe, but scope class objects still couldn't be made safe without 
heroic effort.


Andrei
October 22, 2009
Re: this() not executing code on structs
On Thu, 22 Oct 2009 21:01:15 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail@erdani.org> wrote:

> grauzone wrote:
>> dsimcha wrote:
>>> == Quote from grauzone (none@example.net)'s article
>>>> Andrei Alexandrescu wrote:
>>>> I'd really like to know why "scope x = new X();" is "unsafe", while
>>>> encouraging doing exactly the same with structs seems to be a  
>>>> perfectly
>>>> fine idea. Allocating structs on the stack is obviously not any safer
>>>> than with classes. I don't remember the exact reasons why you wanted  
>>>> to
>>>> turn "scope" into a library feature, but I think I remember something
>>>> about discouraging it for safety reasons; please forgive me is this is
>>>> wrong.
>>>
>>> Because classes in D are always passed by pointer.  (Technically  
>>> references, but
>>> really they're just pointers under the hood.)  Returning a scope  
>>> (stack-allocated)
>>> class from a function is equivalent to escaping a pointer to a stack  
>>> variable.
>>> Returning a struct is done by value, just like returning an int.
>>  (I'm talking about scope classes as declared in "scope class T { ...  
>> }")
>>  But you can't return scope classes from a function. You can't pass  
>> them as ref parameters either. They're designed to be safe.
>
> I wish it was as easy as it sounds. In fact you don't need to pass scope  
> classes as ref parameters - it's enough to pass them "by value" because  
> they are implicitly references.
>
> You can't even safely call a method on a scope class object because that  
> method may assign "this" to something escaping the scope of the method.
>
> Save for using some flavor of interprocedural escape analysis and/or  
> making "scope" a function attribute, I'm not seeing how scope can be  
> made safe and reasonably useful.
>
>> On the other hand, you can pass struct pointers all the way you want  
>> around, and it's damn unsafe.
>>  I don't get this "structs are safe because they are value types"  
>> argument anyway, because the this pointer for structs is a  
>> pointer/reference anyway. If it's trivial to break that "safety", can  
>> you really call it "safety"?
>
> The point is that you can disable address taking altogether and still  
> write a great deal of good code in D. If address taking is verboten  
> (e.g. in SafeD), ref parameters can never be escaped (they will be  
> scoped) and therefore they become safe, too. So within SafeD, structs  
> become safe, but scope class objects still couldn't be made safe without  
> heroic effort.
>
>
> Andrei

Scope classes could be disallowed in SafeD, but you can't disallow  
Scope!(Object).
October 22, 2009
Re: this() not executing code on structs
Andrei Alexandrescu wrote:
> grauzone wrote:
>> dsimcha wrote:
>>> == Quote from grauzone (none@example.net)'s article
>>>> Andrei Alexandrescu wrote:
>>>> I'd really like to know why "scope x = new X();" is "unsafe", while
>>>> encouraging doing exactly the same with structs seems to be a perfectly
>>>> fine idea. Allocating structs on the stack is obviously not any safer
>>>> than with classes. I don't remember the exact reasons why you wanted to
>>>> turn "scope" into a library feature, but I think I remember something
>>>> about discouraging it for safety reasons; please forgive me is this is
>>>> wrong.
>>>
>>> Because classes in D are always passed by pointer.  (Technically 
>>> references, but
>>> really they're just pointers under the hood.)  Returning a scope 
>>> (stack-allocated)
>>> class from a function is equivalent to escaping a pointer to a stack 
>>> variable.
>>> Returning a struct is done by value, just like returning an int.
>>
>> (I'm talking about scope classes as declared in "scope class T { ... }")
>>
>> But you can't return scope classes from a function. You can't pass 
>> them as ref parameters either. They're designed to be safe.
> 
> I wish it was as easy as it sounds. In fact you don't need to pass scope 
> classes as ref parameters - it's enough to pass them "by value" because 
> they are implicitly references.
> 
> You can't even safely call a method on a scope class object because that 
> method may assign "this" to something escaping the scope of the method.
> 
> Save for using some flavor of interprocedural escape analysis and/or 
> making "scope" a function attribute, I'm not seeing how scope can be 
> made safe and reasonably useful.

OK, but the user of that class is still forced to use it safely. It's 
the implementor's responsibility to make sure he doesn't to unsafe 
things. It's the same with structs, at least in normal D (not SafeD).

>> On the other hand, you can pass struct pointers all the way you want 
>> around, and it's damn unsafe.
>>
>> I don't get this "structs are safe because they are value types" 
>> argument anyway, because the this pointer for structs is a 
>> pointer/reference anyway. If it's trivial to break that "safety", can 
>> you really call it "safety"?
> 
> The point is that you can disable address taking altogether and still 
> write a great deal of good code in D. If address taking is verboten 
> (e.g. in SafeD), ref parameters can never be escaped (they will be 
> scoped) and therefore they become safe, too. So within SafeD, structs 
> become safe, but scope class objects still couldn't be made safe without 
> heroic effort.

I wonder about that. How will the following example will be made safe? 
Right now, ref parameters _can_ escape with the help of ref returns.

import std.stdio;

ref int foo(ref int z, int something) {
    return z;
}

ref int foo2() {
    int u = 123;
    return foo(u, 0);
}

void main() {
    writefln("%s", foo(foo2(), {char[20] tmp;  return 0; }()));
}

It's probably not minimal, but it demonstrates how you can return 
references to outdated stack locations without explicitly taking 
pointers. That delegate literal overwrites the stack, so that writefln 
outputs garbage.

How to solve this? Disallow ref arguments or ref returns in SafeD, 
because they are hidden "address taking"?

Or is this all besides the point?

(Hello world doesn't compile with -safe (Phobos issue), so I couldn't 
test it with -safe on dmd v2.032.)

> 
> Andrei
October 22, 2009
Re: this() not executing code on structs
grauzone wrote:
> Andrei Alexandrescu wrote:
>> grauzone wrote:
>>> dsimcha wrote:
>>>> == Quote from grauzone (none@example.net)'s article
>>>>> Andrei Alexandrescu wrote:
>>>>> I'd really like to know why "scope x = new X();" is "unsafe", while
>>>>> encouraging doing exactly the same with structs seems to be a 
>>>>> perfectly
>>>>> fine idea. Allocating structs on the stack is obviously not any safer
>>>>> than with classes. I don't remember the exact reasons why you 
>>>>> wanted to
>>>>> turn "scope" into a library feature, but I think I remember something
>>>>> about discouraging it for safety reasons; please forgive me is this is
>>>>> wrong.
>>>>
>>>> Because classes in D are always passed by pointer.  (Technically 
>>>> references, but
>>>> really they're just pointers under the hood.)  Returning a scope 
>>>> (stack-allocated)
>>>> class from a function is equivalent to escaping a pointer to a stack 
>>>> variable.
>>>> Returning a struct is done by value, just like returning an int.
>>>
>>> (I'm talking about scope classes as declared in "scope class T { ... }")
>>>
>>> But you can't return scope classes from a function. You can't pass 
>>> them as ref parameters either. They're designed to be safe.
>>
>> I wish it was as easy as it sounds. In fact you don't need to pass 
>> scope classes as ref parameters - it's enough to pass them "by value" 
>> because they are implicitly references.
>>
>> You can't even safely call a method on a scope class object because 
>> that method may assign "this" to something escaping the scope of the 
>> method.
>>
>> Save for using some flavor of interprocedural escape analysis and/or 
>> making "scope" a function attribute, I'm not seeing how scope can be 
>> made safe and reasonably useful.
> 
> OK, but the user of that class is still forced to use it safely. It's 
> the implementor's responsibility to make sure he doesn't to unsafe 
> things. It's the same with structs, at least in normal D (not SafeD).
> 
>>> On the other hand, you can pass struct pointers all the way you want 
>>> around, and it's damn unsafe.
>>>
>>> I don't get this "structs are safe because they are value types" 
>>> argument anyway, because the this pointer for structs is a 
>>> pointer/reference anyway. If it's trivial to break that "safety", can 
>>> you really call it "safety"?
>>
>> The point is that you can disable address taking altogether and still 
>> write a great deal of good code in D. If address taking is verboten 
>> (e.g. in SafeD), ref parameters can never be escaped (they will be 
>> scoped) and therefore they become safe, too. So within SafeD, structs 
>> become safe, but scope class objects still couldn't be made safe 
>> without heroic effort.
> 
> I wonder about that. How will the following example will be made safe? 
> Right now, ref parameters _can_ escape with the help of ref returns.
> 
> import std.stdio;
> 
> ref int foo(ref int z, int something) {
>     return z;
> }
> 
> ref int foo2() {
>     int u = 123;
>     return foo(u, 0);
> }
> 
> void main() {
>     writefln("%s", foo(foo2(), {char[20] tmp;  return 0; }()));
> }
> 
> It's probably not minimal, but it demonstrates how you can return 
> references to outdated stack locations without explicitly taking 
> pointers. That delegate literal overwrites the stack, so that writefln 
> outputs garbage.
> 
> How to solve this? Disallow ref arguments or ref returns in SafeD, 
> because they are hidden "address taking"?
> 
> Or is this all besides the point?

It's right to the point. I keep on forgetting about that corner case, 
Steve pointed it to me a long time ago. In the call foo(u, 0) the 
compiler must conservatively assume that the scope of the returned value 
is the same as the scope of u, and therefore disallow compilation of foo2.


Andrei
October 23, 2009
Re: this() not executing code on structs
On Thu, 22 Oct 2009 12:50:21 -0400, grauzone <none@example.net> wrote:

> dsimcha wrote:
>> == Quote from grauzone (none@example.net)'s article
>>> Andrei Alexandrescu wrote:
>>> I'd really like to know why "scope x = new X();" is "unsafe", while
>>> encouraging doing exactly the same with structs seems to be a perfectly
>>> fine idea. Allocating structs on the stack is obviously not any safer
>>> than with classes. I don't remember the exact reasons why you wanted to
>>> turn "scope" into a library feature, but I think I remember something
>>> about discouraging it for safety reasons; please forgive me is this is
>>> wrong.
>>  Because classes in D are always passed by pointer.  (Technically  
>> references, but
>> really they're just pointers under the hood.)  Returning a scope  
>> (stack-allocated)
>> class from a function is equivalent to escaping a pointer to a stack  
>> variable.
>> Returning a struct is done by value, just like returning an int.
>
> (I'm talking about scope classes as declared in "scope class T { ... }")

Your original question was about the statement "scope x = new X()", which  
can be done on any type of class, even non-scope ones.


> But you can't return scope classes from a function. You can't pass them  
> as ref parameters either. They're designed to be safe.
>
> On the other hand, you can pass struct pointers all the way you want  
> around, and it's damn unsafe.
>
> I don't get this "structs are safe because they are value types"  
> argument anyway, because the this pointer for structs is a  
> pointer/reference anyway. If it's trivial to break that "safety", can  
> you really call it "safety"?

Passing struct pointers is not always the norm.  Passing class references  
*is* the norm (and actually the only way), even for scope classes, so  
there is much more chance for escape.

>
>> Scope is really a dangerous hack to allocate a *reference type* on the  
>> stack.
>> It's dangerous and kludgey, but in a performance-oriented language it's  
>> a
>> necessary evil.
>
> You could say the same about structs.

You have to go out of your way to pass a struct by reference.  You *can't*  
possibly pass a class by value, so they are more dangerous.

In order to make scope safer, it has to be a type modifier in addition to  
a storage class, so the compiler can make reasonable decisions (like  
disallowing implicit casting to a non-scope version).

<OT>

> Why do all objects have monitor pointers anyway? The idea, that every  
> object can act as lock, is a really bad one. But this is off-topic...

All objects have a *placeholder* for a lock, which isn't allocated until  
the object is locked for the first time.  It's a very pervasive idea in  
other successful languages like Java and C#.  Having used it extensively  
in C#, I find it very easy to use and well designed.

However, in C#, there is support for conditions on objects as well, which  
isn't "builtin" for D.  IMO, mutexes without conditions is severely  
limiting.  The library condition variables aren't as nice as C#'s builtin  
conditions.
</OT>

-Steve
October 23, 2009
Re: this() not executing code on structs
Bartosz Milewski wrote:
> Andrei Alexandrescu Wrote:
> 
>>     this() { myCount = count++; }       // ERROR
> 
> It's worse than that. Try this:
> 
> struct foo {
>        this(int dummy = 0) { writeln("Default constructor");}
> }
> 
> foo x = foo();
> 
> Nothing gets printed. If default constructors are disallowed, so should constructors with all parameters defaulted.

Ouch.
It's because it's interpreting foo() as a struct literal.
If a struct has any constructors, struct literals should be disabled.
1 2 3
Top | Discussion index | About this forum | D home