October 18, 2013
On Thursday, 17 October 2013 at 22:56:04 UTC, DDD wrote:
> Hi I heard that you can pass a command line argument to make D safe. Like 0 chance of memory corruption and such. I tried looking here http://dlang.org/dmd-linux.html but I couldn't figure it out. If it matters I'm on windows using the latest until a new version came out ~3weeks ago

Safe code in D is defective feature and claims that D's code can be memory safe is the biggest misconception!

First of all, @safe is broken, which means there are many cases when code should be rejected, but actually isn't, secondly it is misdesigned. Root of the issue is that in D static type system and memory safity are separated from each other. Key idea is that having information about static type of object is not sufficient to know whether using it is memory safe or not.

@safe foo(int[] arr) {}

void main @safe()
{
   int [3] x;
   // auto dg = { return x; } ;
   foo(x[]);
}

This a nice case to show @safe problems. First of all, passing static array to dynamic is of relative safety: if slice doesn't escape, this is fine, if is does, this can be still fine if it later for example is appended (or any operation is performed which does not access any elements). Otherwise it is a memory bug. In a presence of separate compilation this is impossible to know. In such cases memory safety is at best undefined. Other issue here is that if delegate declaration is uncommented, then code is always memory safe because array will be allocated on heap. However, by @safe definition code will be still rejected, because @safe is defined in notion of static type rather than memory type. Last issue here is that implementation is buggy and does not reject the code in accordance with its buggy spec.

And at last, there are cases in the language which are not covered by @safe spec, but which still produce memory errors.

So, what actually D has, is a feature, which does not really prevents memory errors, but bans some language features and criteria for banning features is wrong: some legal cases are banned, some cases which should be blocked, are not. If you are really interested in safety you can put our attention to languages like C#. D doesn't provide such facilities.
October 18, 2013
On Thursday, 17 October 2013 at 23:18:21 UTC, DDD wrote:
>
>
> I tried this code and the compiler allowed it (runtime I get object.Error: Access Violation). What am I doing wrong?
>
> Thanks I didn't notice
>
> @safe
> import std.stdio;
> class A {
> 	int x  = 1;
> }
> @safe void main() {
> 	A a;
> 	a.x=9;
> }

Yes, compiler allows it. But this is basic case.

Here is D' Bugs Hall Of Fame (collection of memory errors, type system breakages and other cases to shoot your foot provided by bugzilla issues, me and other people) :

// --- Case 1. Breaking all of type system through delegates --- //

extern(C) @system int printf(const char*, ...);

auto frame1() pure nothrow @safe
{
    immutable void delegate() pure nothrow @safe x;
    auto tmp = { return x; } ;
    return tmp;
}

auto frame2(T)(T t) pure nothrow @safe
{
    void delegate() @system x;
    x.funcptr = t;
    return { return x; } ;
}

void bar() { printf("pure nothrow @safe loophole\n"); } @system

void main() @safe pure nothrow
{
    //bar(); Error: ...
    auto fm1 = frame1();
    auto fm2 = frame2(&bar);
    fm1.ptr = fm2.ptr;
    fm1()();

    fm2 = frame2({ printf("pure nothrow @safe loophole\n"); });
    fm1.ptr = fm2.ptr;
    fm1()();
}


// ---- Case 2. Breaking immutability ---- //

import std.stdio;

class A
{
    int[] c = [3,3];
}

void main()
{
    int[] a = [2,2];
    int[] b = [2,2];
    a[0] = 33;
    assert(b[0] == 2);   // success

    A ca = new A; // assume that one of them is immutable
    A cb = new A;
    ca.c[0] = 44;
    assert(cb.c[0] == 3); // failure: value is 44
}

// ---- Case 3. Breaking type system via delegates ---- //

class A
{
   int i;
   void foo() { ++i; }
}

void main()
{
   immutable a = new immutable A;
   //a.foo();
   (&a.foo)();
}

// --- Case 4. Mutating immutable -- //
immutable int i;

void f(ref int n = i)
{
    ++n; // hello to those who thinks immutable never changes
}

import std.stdio;

void main()
{
   f();
   writeln(i);
}

// -- Case 5. Memory corruption via lazy --- //

extern(C) int printf(const char*,...) @safe;

alias int T;

auto foo(lazy T i) @safe
{
   return { return i; } ;
}

auto bar() @safe
{
   T i = 4;
   return foo(i);
}

void baz() @safe
{
   double[2] i = 3.14;
}

void main() @safe
{
   auto x = bar();
   baz();
   printf("%d\n", x());
}
October 18, 2013
On Friday, 18 October 2013 at 07:04:39 UTC, Maxim Fomin wrote:
> On Thursday, 17 October 2013 at 23:18:21 UTC, DDD wrote:
>>
>>
>> I tried this code and the compiler allowed it (runtime I get object.Error: Access Violation). What am I doing wrong?
>>
>> Thanks I didn't notice
>>
>> @safe
>> import std.stdio;
>> class A {
>> 	int x  = 1;
>> }
>> @safe void main() {
>> 	A a;
>> 	a.x=9;
>> }
>
> Yes, compiler allows it. But this is basic case.
>
> Here is D' Bugs Hall Of Fame (collection of memory errors, type system breakages and other cases to shoot your foot provided by bugzilla issues, me and other people) :
>

Moar :)

// ---- Case 6. UDA bugs - purity loophole ---- //
// discovered by Timon Gehr
import std.stdio;
int x;
@(write(x++),writeln()) void foo(){}

@safe pure void main(){
    __traits(getAttributes, foo);
    __traits(getAttributes, foo)[0];
    __traits(getAttributes, foo)[0];
    //write(x++), writeln(); // Error: pure function cannot ...
}

// ---- Case 7. Writing to typeinfo in purity code ---- //

import core.stdc.string, std.stdio;

pure void rtSetDefaultHeapInitializer(T)(T value) if (is(T == class))
{
	byte[] init_mem = new byte[T.classinfo.init.length];
	memcpy(init_mem.ptr, cast(byte*)value, T.classinfo.init.length);
	T.classinfo.init = init_mem;
}

class A
{
   int a;
}

int foo() pure
{
   A a = new A;
   a.a++;
   rtSetDefaultHeapInitializer(a);
   return a.a;
}

void main()
{
   writeln(foo, foo, foo);
}

// ---- Case 8. Escaping references and memory bugs ---- //
import std.stdio;

alias long[100] T;

T delegate() dg;

//ref T foo(ref T i) @safe
void foo(ref T i) @safe
{
   dg = { return i; } ;
   //return i;
}

//ref T bar()
void bar() @safe
{
   T i = 1;
   //return foo(i);
   foo(i);
}

void rewrite_stack() @safe
{
   T tmp = -1;
}

void main()
{
   //T i = bar();
   bar();
   rewrite_stack();
   writeln(dg());
}

This is not so obvious as previous.

// ---- Case 9. Escaping static arrays. ---- //

import std.stdio;

class A
{
   int[] data;
   ~this()
   {
      writeln(data);
   }
}

void foo(int[] a) @safe
{
   A x = new A;
   x.data = a;
}

void main() @safe
{
   int[4] y;
   foo(y);
}

// ---- Case 10. Mutating immutable ---- //
import std.stdio;

immutable int i;
immutable int *ptr;

static this()
{
   ++i, ptr = &i;
}

void main()
{
   foreach (i; 0 .. 10)
   {
      _staticCtor1();
      writeln(i);
   }
}

(Basically problem here is that nether dmd nor linker cares about symbol protection)

// --- Case 11. Writing to global though pure functions ---- //

int a;

pure foo(ref int a = a) { ++a; }

void main() pure
{
   foo();
}

// ---- Case 12. Breaking immutable via pure ---- //

import std.stdio;
import core.memory;

class A
{
   int *ptr;
   ~this()
   {
      (*ptr)++;
   }
}

pure foo()
{
   A a = new A;
   int* ptr = new int;
   a.ptr = ptr;
   a = null;
   return ptr;
}

void main()
{
   immutable int* ptr = foo();
   writeln(*ptr); // 0
   GC.collect();
   writeln(*ptr); // 1
}

The latest bug is nice, because after 2.063 Kenji has fixed the language, improving consistency of calling immutable/const qualified constructors, but allowed to cast return value of pure function to immutable because there was belief that inside purity function value cannot escape. Such ideom was advertised several times in this newsgroups. It appears that idea is not reliable.

Probably there are other cases.

tl;dr do not take into account D' type system and safety seriously.
October 18, 2013
On Friday, 18 October 2013 at 06:26:51 UTC, Maxim Fomin wrote:
> On Thursday, 17 October 2013 at 23:25:52 UTC, Meta wrote:
>> On Thursday, 17 October 2013 at 23:18:21 UTC, DDD wrote:
>>> I tried this code and the compiler allowed it (runtime I get object.Error: Access Violation). What am I doing wrong?
>>>
>>> Thanks I didn't notice
>>>
>>> @safe
>>> import std.stdio;
>>> class A {
>>> 	int x  = 1;
>>> }
>>> @safe void main() {
>>> 	A a;
>>> 	a.x=9;
>>> }
>>
>> This is more or less a different thing. SafeD doesn't guarantee that your class references will not be null. Trying to call a method on a null reference is perfectly valid in SafeD. There's a pull request sitting in GitHub for a NotNull type that should be reasonable good for ensuring that your references are not null, but it hasn't been pulled yet.
>
> Actually on linux this will segfault so in general this is not safe across all platforms.

It's still memory safe in the sense that it's guaranteed to not stomp on anything -> no silent corruption.

I don't really think the distinction between an Error or a segfault is the dividing line between safe and not safe. Both are supposed to be (under 99.9% of circumstances) unrecoverable errors.
October 18, 2013
On Friday, 18 October 2013 at 09:10:30 UTC, John Colvin wrote:
> On Friday, 18 October 2013 at 06:26:51 UTC, Maxim Fomin wrote:
>> On Thursday, 17 October 2013 at 23:25:52 UTC, Meta wrote:
>>> On Thursday, 17 October 2013 at 23:18:21 UTC, DDD wrote:
>>>> I tried this code and the compiler allowed it (runtime I get object.Error: Access Violation). What am I doing wrong?
>>>>
>>>> Thanks I didn't notice
>>>>
>>>> @safe
>>>> import std.stdio;
>>>> class A {
>>>> 	int x  = 1;
>>>> }
>>>> @safe void main() {
>>>> 	A a;
>>>> 	a.x=9;
>>>> }
>>>
>>> This is more or less a different thing. SafeD doesn't guarantee that your class references will not be null. Trying to call a method on a null reference is perfectly valid in SafeD. There's a pull request sitting in GitHub for a NotNull type that should be reasonable good for ensuring that your references are not null, but it hasn't been pulled yet.
>>
>> Actually on linux this will segfault so in general this is not safe across all platforms.
>
> It's still memory safe in the sense that it's guaranteed to not stomp on anything -> no silent corruption.

Well, in some sence yes. The problem with dereferencing nulls is that behavior is different in linux and windows (i.e. non portable), but you can enable treating nulls as exception in linux if you use etc.linux.memoryerrors.

> I don't really think the distinction between an Error or a segfault is the dividing line between safe and not safe. Both are supposed to be (under 99.9% of circumstances) unrecoverable errors.

Some errors are propagated as exceptions, some errors are handled like abort, so situation depends on type of the error. Users are not supposed to catch errors, but they still can, which makes situation is compilcated in general.

However, dereferencing null is tiny problem comparing to other issues in D's safity.
October 18, 2013
On Friday, 18 October 2013 at 10:03:03 UTC, Maxim Fomin wrote:
>
> portable), but you can enable treating nulls as exception in linux if you use etc.linux.memoryerrors.
>
Oh.  This exists.  Don't suppose there were any plans to document it?

-Wyatt
October 18, 2013
On 10/18/2013 09:30 AM, Maxim Fomin wrote:
> ...

Nice collection.

> // ---- Case 12. Breaking immutable via pure ---- //
>
> import std.stdio;
> import core.memory;
>
> class A
> {
>     int *ptr;
>     ~this()
>     {
>        (*ptr)++;
>     }
> }
>
> pure foo()
> {
>     A a = new A;
>     int* ptr = new int;
>     a.ptr = ptr;
>     a = null;
>     return ptr;
> }
>
> void main()
> {
>     immutable int* ptr = foo();
>     writeln(*ptr); // 0
>     GC.collect();
>     writeln(*ptr); // 1
> }
>
> The latest bug is nice, because after 2.063 Kenji has fixed the
> language, improving consistency of calling immutable/const qualified
> constructors, but allowed to cast return value of pure function to
> immutable because there was belief that inside purity function value
> cannot escape. Such ideom was advertised several times in this
> newsgroups. It appears that idea is not reliable.
>

It's no less of a design issue, but the main problem is actually that class destructors are unsafe in general.

import core.memory;
class A{
    int *ptr;
    this(immutable int* ptr)immutable{ this.ptr = ptr; }
    ~this(){
        (*ptr)++;
    }
}

immutable y = 2;
void main(){
    auto x = new immutable(A)(&y);
    GC.collect();
    assert(*&y==y); // fails
}

Of course, @safe implicit conversions to immutable do not simplify any attempt to solve this, but the feature is not essential to the problem.


> Probably there are other cases.
>
> tl;dr do not take into account D' type system and safety seriously.

Your other examples are just DMD bugs afaics. (With the possible exception of delegate context pointer memory safety, but that is a simple fix.)
October 18, 2013
On 10/17/13 11:53 PM, Maxim Fomin wrote:
> Root of the issue is that in D static type system and memory safity are
> separated from each other. Key idea is that having information about
> static type of object is not sufficient to know whether using it is
> memory safe or not.

This is a common approach in many languages (actually all that I know about). Clearly stuff on the stack can be attached an invisible attribute "it's on the stack!" but that doesn't go well with separate compilation.

Thanks for collecting together the situations where safety is broken. It's a bummer your posts are perpetually gloomy, but there's hope we may be able to do something about that. I'd like to make improving @safe the focus of 2.065, by fixing the implementation bugs and by looking at the more complicated cases, too.

> So, what actually D has, is a feature, which does not really prevents
> memory errors, but bans some language features and criteria for banning
> features is wrong: some legal cases are banned, some cases which should
> be blocked, are not. If you are really interested in safety you can put
> our attention to languages like C#. D doesn't provide such facilities.

It's a given that safety will disallow constructs that are safe upon inspection but the type system is unable to prove correct. This is the case for all languages, C# included.


Andrei

October 18, 2013
On Friday, 18 October 2013 at 15:39:35 UTC, Andrei Alexandrescu wrote:
> This is a common approach in many languages (actually all that I know about). Clearly stuff on the stack can be attached an invisible attribute "it's on the stack!" but that doesn't go well with separate compilation.

As we don't provide ABI stability guarantees yet, it is not too late to make all important type system properties part of symbol mangling.
October 18, 2013
Am 18.10.2013 17:40, schrieb Andrei Alexandrescu:
> On 10/17/13 11:53 PM, Maxim Fomin wrote:
>> ...
>
> It's a given that safety will disallow constructs that are safe upon
> inspection but the type system is unable to prove correct. This is the
> case for all languages, C# included.
>
>
> Andrei
>


Wouldn't be easier, if D followed a model similar to C# and Modula-3 where the code is by default safe and system/trusted code is only allowed inside explicitly unsafe code blocks?

Just an idea, maybe it wouldn't be much different from what it is now.

--
Paulo