August 29, 2012
Am Tue, 28 Aug 2012 22:08:11 -0700
schrieb Jonathan M Davis <jmdavisProg@gmx.com>:

> On Wednesday, August 29, 2012 06:46:25 Tommi wrote:
> > The weird thing is that you can use a member access operator with a pointer (without explicitly dereferencing the pointer first).
> 
> Well, you clearly haven't done much pointers to structs in D, or that wouldn't be surprising at all. . always implicitly dereferences the pointer, which pretty much makes the -> operator completely unnecessary.
> 
> - Jonathan M Davis

P.S.: Also not much Delphi, which did the same before when innovating over Pascal. (There never was a '->' though, just dereference then access fields.)

-- 
Marco

August 29, 2012
On Wednesday, 29 August 2012 at 04:54:31 UTC, Tommi wrote:
> On Wednesday, 29 August 2012 at 03:17:39 UTC, Era Scarecrow
>> I think that's right; Otherwise ref wouldn't be allowed in @safe code (at all).
>
> But couldn't the compiler disallow all unsafe ref use in @safe code, and allow all use of ref in @system and @trusted code?

 So.... Ref can only then be used if it's heap allocated... That would be the only @safe way, but even that is ridden with timebombs.

 int[10] xyz;

 foreach(i; xyz) {
   //all's good, they are copies!
 }

 foreach(ref i; xyz) {
   // cannot use ref, (not even const ref) as it may be unsafe
 }

 //a ref function
 void func(ref int i);

 int *z = new int;
 func(z); //or is it *z? 'Might' be good
 func(xyz[0]); //Compile error, may be unsafe.

 class C {
   int cc;
 }

 struct S {
   int sc;
 }

 C cSomething = new C();
 S sSomething = S();
 S* sSomething2 = new S();

 func(cSomething.cc); //good, heap allocated enforced
 func(sSomething.sc); //error, may be unsafe!
 func(sSomething2.sc); //pointer dereference; It bypasses protections since it probably is heap (but not guaranteed), so maybe this should fail too.

 sSomething2 = &sSomething;
 func(sSomething2.sc); //back to a timebomb situation but the compiler can't verify it! only something from a class could be considered safe (maybe)



 Why is it unsafe? Let's assume we did this:

 ref int unsafe; //global ref. Hey it's legal if ref is allowed as you want it!

 void func(ref int i) {
   unsafe = i;
 }

 Now I ask you. If any of the 'unsafe' ones were used and then the scope ended that held that information and we use unsafe now, it can only be a time bomb: All of them stem from them being on the stack.

 void someFunc() {
   int stacked = 42;
   func(stacked);
 }

 someFunc();
 writeln(unsafe); //what will this print? Will it crash?

 //some scope. FOR loop? IF statement? who cares?
 {
   int local = 42;
   func(&local); //assuming the pointer bypasses and gets dereferenced to get past the heap only compiler problems (pointers and classes only) limitations
   writeln(unsafe); //prints 42 as expected
 }

 writeln(unsafe); //????? Scope ended. Now what? can't ensure local exists anymore as we reffed a stack


 class Reffed {
   ref int i;
 }

//some scope
 Reffed r = new Reffed();
 Reffed r2 = new Reffed();

 @trusted {
   int local;
   r.i = local; //may or may not work normally, @trusted should force it
 }

 //Why not copy a reference from a class?
 //it should be good (heap only), right?
 r2.i = r.i;

 writeln(r.i); //?????
 writeln(r2.i); //?!?!


 To make it 'safe' the only way to do it is ref can only be used on heap allocated data (or global fixed data). The current implementation as I see it anything referenced is safe because you can only reference something that currently exists (via function calls).

 The other way to make it safe is to silently include a flag that specifies if it was stack allocated or not, but even that becomes more complicated and more of an issue.
August 29, 2012
On Wednesday, 29 August 2012 at 02:57:27 UTC, Era Scarecrow wrote:
>  Assuming 'ref' works:
>
>  struct S {
>    ref int r;
>  }
>
>  //ref local variable/stack, Ticking timebomb
>  //compiler may refuse
>  void useRef(ref S input, int r) {
>    input.r = r;
>  }

I think we might be talking about somewhat different things. What I mean by reference variable is what the term means in C++. From wikipedia: http://en.wikipedia.org/wiki/Reference_%28C%2B%2B%29

C++ references differ from pointers in several essential ways:

* It is not possible to refer directly to a reference object after it is defined; any occurrence of its name refers directly to the object it references.

* Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.

*References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid. Note that for this reason, containers of references are not allowed.

* References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor. For example:
int& k; // compiler will complain: error: `k' declared as reference but not initialized

August 29, 2012
On Wednesday, 29 August 2012 at 12:41:26 UTC, Tommi wrote:
> I think we might be talking about somewhat different things. What I mean by reference variable is what the term means in C++. From wikipedia: http://en.wikipedia.org/wiki/Reference_%28C%2B%2B%29
>
> C++ references differ from pointers in several essential ways:
>
> * It is not possible to refer directly to a reference object after it is defined; any occurrence of its name refers directly to the object it references.
>
> * Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.
>
> *References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid. Note that for this reason, containers of references are not allowed.
>
> * References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor. For example: int& k; // compiler will complain: error: `k' declared as reference but not initialized

 Then most of my examples change to going into the constructor rather than out, and the global one goes away. But it still doesn't help with the problem of anything stack allocated.

 struct S {
   //default construtor otherwise then fails to work because of ref? Since
   //x must be known at compile time so it would be null to start with, or no
   //default onstructor
   ref int x;
   this(ref int r) { x = r;}
 }

 S func() {
   int local = 42;
   S s = S(local);
   return s;
 }

 S func(ref int reffed) {
   S s = S(reffed);
   return s;
 }

 S s = func();

 writeln(s.x); //mystery value still!

 {
   int local = 42;
   s = func(local);
   writeln(s.x); //42
 }

 writeln(s.x); //!?!?


 Unless I'm understanding this wrong (And I'm tired right now maybe I missed something), once again the only safe 'reference variable' is from actively being called from a live accessible reference; And anything else is still a ticking timebomb and cannot ever be @safe. Stuff referencing within heaped/class related stuff may suffer fewer problems, but only as long as you rely on the GC and not do any magic involving malloc/free, or maybe a dynamic range.

 Reminds me. The example for the foreach are missing parts.

 int[10] fixed;
 int[] dynamic;
 dynamic.length = 10;

 foreach(i; fixed) {
    //fine, i is a copy
 }

 foreach(ref i; fixed) {
     //compile-time error for @safe checking
     //cannot ensure this is safe otherwise
 }

 foreach(i; dynamic) {
    //fine, i is a copy
 }

 foreach(ref i; dynamic) {
    //Dynamic/heap allocated, so fine.
 }

 //from function/ref
 void func(int[] huh) {
   foreach(ref i; huh) {
     //????? safe?
   }
 }

 //both legal last I checked.
 func(fixed);
 func(dynamic);



 My god, as I look at this more, reference variables would cripple D so badly C++ wouldn't look half bad afterwards.
August 29, 2012
> *References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid. Note that for this reason, containers of references are not allowed.
>
> * References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor. For example:
> int& k; // compiler will complain: error: `k' declared as reference but not initialized

That would be a dream: not null references. I'm still think that D needs something like that. And I'm not talking about of some struct constructs like NotNullable, which will be added in std.typecons later. I'm talking about built-in support.
August 29, 2012
On Wednesday, 29 August 2012 at 15:49:26 UTC, Namespace wrote:
>> *References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid. Note that for this reason, containers of references are not allowed.
>>
>> * References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor. For example:
>> int& k; // compiler will complain: error: `k' declared as reference but not initialized
>
> That would be a dream: not null references. I'm still think that D needs something like that. And I'm not talking about of some struct constructs like NotNullable, which will be added in std.typecons later. I'm talking about built-in support.

+1
August 29, 2012
On Wednesday, 29 August 2012 at 14:17:21 UTC, Era Scarecrow wrote:
>  Then most of my examples change to going into the constructor rather than out, and the global one goes away. But it still doesn't help with the problem of anything stack allocated.
>
>  struct S {
>    //default construtor otherwise then fails to work because of ref? Since
>    //x must be known at compile time so it would be null to start with, or no
>    //default onstructor
>    ref int x;
>    this(ref int r) { x = r;}
>  }
>
>  S func() {
>    int local = 42;
>    S s = S(local);
>    return s;
>  }
>
>  S func(ref int reffed) {
>    S s = S(reffed);
>    return s;
>  }
>
>  S s = func();
>
>  writeln(s.x); //mystery value still!
>
>  {
>    int local = 42;
>    s = func(local);
>    writeln(s.x); //42
>  }
>
>  writeln(s.x); //!?!?
>
>
>  Unless I'm understanding this wrong (And I'm tired right now maybe I missed something), once again the only safe 'reference variable' is from actively being called from a live accessible reference; And anything else is still a ticking timebomb and cannot ever be @safe. Stuff referencing within heaped/class related stuff may suffer fewer problems, but only as long as you rely on the GC and not do any magic involving malloc/free, or maybe a dynamic range.
>
>  Reminds me. The example for the foreach are missing parts.
>
>  int[10] fixed;
>  int[] dynamic;
>  dynamic.length = 10;
>
>  foreach(i; fixed) {
>     //fine, i is a copy
>  }
>
>  foreach(ref i; fixed) {
>      //compile-time error for @safe checking
>      //cannot ensure this is safe otherwise
>  }
>
>  foreach(i; dynamic) {
>     //fine, i is a copy
>  }
>
>  foreach(ref i; dynamic) {
>     //Dynamic/heap allocated, so fine.
>  }
>
>  //from function/ref
>  void func(int[] huh) {
>    foreach(ref i; huh) {
>      //????? safe?
>    }
>  }
>
>  //both legal last I checked.
>  func(fixed);
>  func(dynamic);
>
>
>
>  My god, as I look at this more, reference variables would cripple D so badly C++ wouldn't look half bad afterwards.

I honestly don't know anything about memory safety and what it entails. So, I can't comment about any of that stuff. But if you say general ref variables can't be implemented in @safe mode, then I can just take your word for it. But what I'm saying is that ref variables would be a nice feature to have in @system code.

My logic is very simple: since we can use pointers in @system code, and references are no more unsafe than pointers, then we should be able to use references in @system code. You might argue that references are little more than syntactic sugar (and a bit safer) compared to pointers, but you shouldn't underestimate the importance of syntactic sugar especially when you're trying to lure in users of C++.
August 29, 2012
On Wednesday, 29 August 2012 at 19:55:08 UTC, Tommi wrote:
> I honestly don't know anything about memory safety and what it entails. So, I can't comment about any of that stuff. But if you say general ref variables can't be implemented in @safe mode, then I can just take your word for it. But what I'm saying is that ref variables would be a nice feature to have in @system code.

 They can be used safely if they are heap allocated (or global).

> My logic is very simple: since we can use pointers in @system code, and references are no more unsafe than pointers, then we should be able to use references in @system code. You might argue that references are little more than syntactic sugar (and a bit safer) compared to pointers, but you shouldn't underestimate the importance of syntactic sugar especially when you're trying to lure in users of C++.

 Maybe. But the scope of referencing variables should likely be left alone. I am not sure of all the details of C++ references either. Ref hides the fact it's a pointer/reference. This means unless you really know what you're doing that it is not a good thing to use.

 Let's assume we compare a pointer and a ref in a struct and copying.

 struct P {
   int* ptr;
 }

 struct R {
   ref int r;
   this(ref int i) {r = i;}
 }

 int value;
 P p1, p2;

 p1.ptr = value; //Compile error, not a pointer, need to use &value
 p2 = p1;
 p2.ptr = 100; //compile error, need to use *p2.ptr = 100

 value = 42;
 R r1 = R(value)
 R r2 = r1;      //both reference value now
 r2.r = 100;     //r must be a variable if this succeeds

 assert(r1.r == 42, "FAIL!");
 assert(r2.r == 100, "Would succeed if it got this far");


 If I have this right, as a pointer you know it's a pointer/reference and don't make more obvious mistakes as you're told by the compiler what's wrong. Also since it hides the fact it's actually a pointer hidden bugs are bound to crop up more often where reference variables were used.

 If we return now r1 or r2, it silently fails since it can't guarantee that the reference wasn't a local stack variable, or of anything at all really. As a pointer you can blame the programmer (It's their fault afterall), as a reference you blame the language designer.

 What would you do for the postblitz if you can't reset the reference? Or if you could, would you do...? It would be so easy to forget.

 this(this) {
   r = new int(r);
 }


 *Prepares for flames from Walter and Andrei*
August 29, 2012
On Wednesday, 29 August 2012 at 21:37:33 UTC, Era Scarecrow wrote:
>  struct R {
>    ref int r;
>    this(ref int i) {r = i;}
>  }

I had totally forgotten what it says in "The book" about struct and class construction. It's basically that all fields are first initialized to either T.init or by using the field's initializer. That means the use of ref inside class or struct would be quite restricted:

int globalVal;

struct MyStruct
{
    // ref int defaultInitRef; // Illegal: reference variables
                               // can't be default initialized

    ref int explicitRef = globalVal; // Fine

    this(int val)
    {
        explicitRef = val; // Assigns val to globalVal (because
                           // explicitRef references globalVal)
    }
}
August 29, 2012
...but I guess you could do this:

int globalVal1;
int globalVal2;

struct MyStruct(alias valToRef)
{
    ref int refVal = valToRef;
}

void main()
{
    MyStruct!globalVal1 ms1;
    MyStruct!globalVal2 ms2;
}