www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opAssign() still accepted for classes???

reply Alexander <aldem+dmars nk7.net> writes:
Doing some experiments, and after conversion of struct into a class I've found
that opAssign(), contrary to what documentation says, is silently accepted for
classes (in D 2.052)

What is worse, it segfaults when object is not constructed first:

---snip---
class X {
	int	v;

	typeof(this) opAssign(int i)
	{
		v = i;
		return this;
	}
}

void main()
{
	X x0 = new X();
	x0 = 0;			// ok
	X x1 = 0;		// segfault
}
---snip--

Well, I understand why it segfaults, but shouldn't opAssign() be disallowed? If
documentation is wrong, and it is still valid for classes, this should be
handled somehow else.

/Alexander
Apr 29 2011
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 10:43:56 -0400, Alexander <aldem+dmars nk7.net> wrote:

 Doing some experiments, and after conversion of struct into a class I've  
 found that opAssign(), contrary to what documentation says, is silently  
 accepted for classes (in D 2.052)

opAssign is only invalid if you are assigning something that implicitly converts to that type. So this will fail: typeof(this) opAssign(typeof(this) otherthing) {...}
 What is worse, it segfaults when object is not constructed first:

x = y is equivalent to x.opAssign(y). So x is null, you are dereferencing a null pointer. -Steve
Apr 29 2011
next sibling parent reply Alexander <aldem+dmars nk7.net> writes:
On 29.04.2011 17:05, Steven Schveighoffer wrote:

 opAssign is only invalid if you are assigning something that implicitly
converts to that type.

Well, the documentation says: "The assignment operator = can be overloaded if the lvalue is a struct aggregate, and opAssign is a member function of that aggregate." Thus, I assume that for classes it shouldn't be possible, or did I get it wrong?
 x = y is equivalent to x.opAssign(y).  So x is null, you are dereferencing a
null pointer.

This I understand, of course - the main point is why it is accepted for objects at all. BTW, it seems that overloads can be static - what is the semantic of static overload? (static opAssign() also fails). /Alexander
Apr 29 2011
parent Alexander <aldem+dmars nk7.net> writes:
On 29.04.2011 18:13, Steven Schveighoffer wrote:

 Static opCall is possible, but I wasn't aware of the other operator overloads
being possible.

I wasn't too - it is not mentioned anywhere, just tried it.
 Note that opAssign is a valid symbol name, so it can be used in places even
where it doesn't overload assignment, such as a static or global function.  It
just won't map to any operator usage.

Well, the fact is - it maps. If I've static opAssign() defined, it is called when I assign something to an object. Even more fun - static opAdd() maps too, and - wow! - if it returns new object, i.e. construction like: X x; x = x + 3; then it will allocate new instance of X, where: static typeof(this) opAdd(int i) { return new X(i); } I am impressed... :) /Alexander
Apr 29 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 11:20:16 -0400, Alexander <aldem+dmars nk7.net> wrote:

 On 29.04.2011 17:05, Steven Schveighoffer wrote:

 opAssign is only invalid if you are assigning something that implicitly  
 converts to that type.

Well, the documentation says: "The assignment operator = can be overloaded if the lvalue is a struct aggregate, and opAssign is a member function of that aggregate."

That's puzzling. The D1 docs say "struct or class aggregate". So it was obviously removed in some version of the docs, but I'm not sure why. Walter?
 x = y is equivalent to x.opAssign(y).  So x is null, you are  
 dereferencing a null pointer.

This I understand, of course - the main point is why it is accepted for objects at all.

OK, some don't initially grasp that classes need to be initialized, I thought you were asking why the segfault occurs.
   BTW, it seems that overloads can be static - what is the semantic of  
 static overload? (static opAssign() also fails).

Static opCall is possible, but I wasn't aware of the other operator overloads being possible. Note that opAssign is a valid symbol name, so it can be used in places even where it doesn't overload assignment, such as a static or global function. It just won't map to any operator usage. -Steve
Apr 29 2011
prev sibling next sibling parent reply Alexander <aldem+dmars nk7.net> writes:
On 29.04.2011 17:05, Steven Schveighoffer wrote:

 x = y is equivalent to x.opAssign(y).  So x is null, you are dereferencing a
null pointer.

Second thought... Since compiler *knows* that the pointer is null at this point, shouldn't it produce a warning at least? /Alexander
Apr 29 2011
parent reply KennyTM~ <kennytm gmail.com> writes:
On Apr 30, 11 00:14, Alexander wrote:
 On 29.04.2011 17:05, Steven Schveighoffer wrote:

 x = y is equivalent to x.opAssign(y).  So x is null, you are dereferencing a
null pointer.

Second thought... Since compiler *knows* that the pointer is null at this point, shouldn't it produce a warning at least? /Alexander

That requires data flow analysis. For example, how could the compiler knows the 'x' below is 'null'? K x; if (veryComplexCondition(y)) x = new K; x = 4.0; // <---- See http://www.digitalmars.com/d/2.0/faq.html#nan. (That said, I see no problem treating this as a warning (which is outside of the D spec) if a variable is provable 'null'.)
Apr 29 2011
next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
On Apr 30, 11 01:19, Steven Schveighoffer wrote:
 I think he was referring to the line:

 X x = 0;

 Where x could not possibly be anything other than null.

 -Steve

That's definitely a bug. Why should a declaration call opAssign?
Apr 29 2011
parent reply KennyTM~ <kennytm gmail.com> writes:
On Apr 30, 11 01:36, Steven Schveighoffer wrote:
 On Fri, 29 Apr 2011 13:29:56 -0400, KennyTM~ <kennytm gmail.com> wrote:

 On Apr 30, 11 01:19, Steven Schveighoffer wrote:
 I think he was referring to the line:

 X x = 0;

 Where x could not possibly be anything other than null.

 -Steve

That's definitely a bug. Why should a declaration call opAssign?

X x = new X; is equivalent to X x; x = new X; new X is an expression, which doesn't have to be the rvalue of an assignment, I suppose you can substitute any valid expression for it: X x; x = 0; => X x = 0; Also, if X is a struct, and the struct defines opAssign(int), this would be valid. I wouldn't mind if it was a bug, because clearly you never want to call opAssign on an uninitialized class. But it definitely would be a special case. -Steve

I see. Though this isn't valid with a 'struct'. ------------------------ import std.stdio; struct K { K opAssign(int x) { writeln(x); return this; } } void main() { // K k = 8; // Error: cannot implicitly convert expression (8) of type int to K K k; k = 8; // OK. } ------------------------
Apr 29 2011
next sibling parent KennyTM~ <kennytm gmail.com> writes:
On Apr 30, 11 01:54, Steven Schveighoffer wrote:
 On Fri, 29 Apr 2011 13:49:19 -0400, KennyTM~ <kennytm gmail.com> wrote:

 Though this isn't valid with a 'struct'.

 ------------------------
 import std.stdio;
 struct K {
 K opAssign(int x) {
 writeln(x);
 return this;
 }
 }
 void main() {
 // K k = 8; // Error: cannot implicitly convert expression (8) of type
 int to K
 K k;
 k = 8; // OK.
 }
 ------------------------

Interesting! So this is definitely an inconsistency that indicates the class version is a bug. -Steve

That said, for a 'struct' the 'Type var = expr;' declaration is expected to call the constructor: --------------------- import std.stdio; struct K { this(int x) { writeln(x); } } void main() { K k = 8; } ---------------------
Apr 29 2011
prev sibling parent KennyTM~ <kennytm gmail.com> writes:
On Apr 30, 11 01:54, Steven Schveighoffer wrote:
 On Fri, 29 Apr 2011 13:49:19 -0400, KennyTM~ <kennytm gmail.com> wrote:

 Though this isn't valid with a 'struct'.

 ------------------------
 import std.stdio;
 struct K {
 K opAssign(int x) {
 writeln(x);
 return this;
 }
 }
 void main() {
 // K k = 8; // Error: cannot implicitly convert expression (8) of type
 int to K
 K k;
 k = 8; // OK.
 }
 ------------------------

Interesting! So this is definitely an inconsistency that indicates the class version is a bug. -Steve

Turns out it is an old bug. http://d.puremagic.com/issues/show_bug.cgi?id=671
Apr 29 2011
prev sibling parent Alexander <aldem+dmars nk7.net> writes:
On 29.04.2011 19:19, Steven Schveighoffer wrote:

 I think he was referring to the line:
 
 X x = 0;
 
 Where x could not possibly be anything other than null.

Exactly. And, BTW, code X x; x = 0; is correctly detected as a null dereference at compile time. /Alexander
Apr 29 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 12:25:40 -0400, Alexander <aldem+dmars nk7.net> wrote:

 On 29.04.2011 18:13, Steven Schveighoffer wrote:

 Static opCall is possible, but I wasn't aware of the other operator  
 overloads being possible.

I wasn't too - it is not mentioned anywhere, just tried it.
 Note that opAssign is a valid symbol name, so it can be used in places  
 even where it doesn't overload assignment, such as a static or global  
 function.  It just won't map to any operator usage.

Well, the fact is - it maps. If I've static opAssign() defined, it is called when I assign something to an object. Even more fun - static opAdd() maps too, and - wow! - if it returns new object, i.e. construction like: X x; x = x + 3; then it will allocate new instance of X, where: static typeof(this) opAdd(int i) { return new X(i); } I am impressed... :)

That most certainly is a bug. What I think is happening is you can call static functions with an instance of that type, but the instance isn't passed to it, it's just used as a namespace. This is a "feature" that I continue to feel is a bug. So for example, you might expect: x = new X(4); x = x + 3; assert(x.value == 7) but it will fail, because: x.opAdd(3) doesn't actually pass x into the function! So there is no way you could possibly know what the left hand side of the operator is! -Steve
Apr 29 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 13:03:05 -0400, KennyTM~ <kennytm gmail.com> wrote:

 On Apr 30, 11 00:14, Alexander wrote:
 On 29.04.2011 17:05, Steven Schveighoffer wrote:

 x = y is equivalent to x.opAssign(y).  So x is null, you are  
 dereferencing a null pointer.

Second thought... Since compiler *knows* that the pointer is null at this point, shouldn't it produce a warning at least? /Alexander

That requires data flow analysis. For example, how could the compiler knows the 'x' below is 'null'? K x; if (veryComplexCondition(y)) x = new K; x = 4.0; // <---- See http://www.digitalmars.com/d/2.0/faq.html#nan. (That said, I see no problem treating this as a warning (which is outside of the D spec) if a variable is provable 'null'.)

I think he was referring to the line: X x = 0; Where x could not possibly be anything other than null. -Steve
Apr 29 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 13:29:56 -0400, KennyTM~ <kennytm gmail.com> wrote:

 On Apr 30, 11 01:19, Steven Schveighoffer wrote:
 I think he was referring to the line:

 X x = 0;

 Where x could not possibly be anything other than null.

 -Steve

That's definitely a bug. Why should a declaration call opAssign?

X x = new X; is equivalent to X x; x = new X; new X is an expression, which doesn't have to be the rvalue of an assignment, I suppose you can substitute any valid expression for it: X x; x = 0; => X x = 0; Also, if X is a struct, and the struct defines opAssign(int), this would be valid. I wouldn't mind if it was a bug, because clearly you never want to call opAssign on an uninitialized class. But it definitely would be a special case. -Steve
Apr 29 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 13:36:20 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:


 I wouldn't mind if it was a bug, because clearly you never want to call  
 opAssign on an uninitialized class.  But it definitely would be a  
 special case.

Meant to say, I wouldn't mind if it was an error. I don't think it's a bug exactly... -Steve
Apr 29 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 29 Apr 2011 13:49:19 -0400, KennyTM~ <kennytm gmail.com> wrote:

 Though this isn't valid with a 'struct'.

 ------------------------
 import std.stdio;
 struct K {
      K opAssign(int x) {
          writeln(x);
          return this;
      }
 }
 void main() {
      // K k = 8;   // Error: cannot implicitly convert expression (8) of  
 type int to K
      K k;
      k = 8;        // OK.
 }
 ------------------------

Interesting! So this is definitely an inconsistency that indicates the class version is a bug. -Steve
Apr 29 2011