October 21, 2009
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
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
== 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
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
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
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
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
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
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
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