View mode: basic / threaded / horizontal-split · Log in · Help
August 29, 2012
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
> *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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
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
Re: Why can't we make reference variables?
...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;
}
1 2 3 4 5
Top | Discussion index | About this forum | D home