www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Problem with taking inout, const references

reply "Uranuz" <neuranuz gmail.com> writes:
I have problem with understanding of work of modifiers const, 
inout, immutable modifiers. As I understand inout keyword is 
intended to consume const, immutable and data without modifiers. 
It's how I expect it to work otherwise I don't understand what is 
the purpose of inout. In current implementation of language it 
looks like (it's just my feeling) that it's completly other 
modifier that connects badly with const and immutable. In this 
case I don't understand sense of it. Also I have strange feelings 
about shared, because it's not widely used in code that I'm 
experienced to see and lacks of good usage examples.

May main question is that I don't understand sense of this error:

Error: cannot implicitly convert expression (&c) of type 
const(Cookie)* to inout(Cookie)*

It occured in the following code. Can someone understand what I'm 
doing wrong? Or is it a bug or something?

struct Cookie
{	string name;
	string value;
	string domain;
	string path;
	string expires;
	bool isHTTPOnly;
	bool isSecure;

	void opAssign(string rhs)
	{	value = rhs; }

	string toString()
	{	string result = `Set-Cookie: ` ~ name  ~ `=` ~ value;
		if( domain.length > 0 )
			result ~= `; Domain=` ~ domain;
		if( path.length > 0 )
			result ~= `; Path=` ~ path;
		if( expires.length > 0 )
			result ~= `; Expires=` ~ expires;
		if( isHTTPOnly )
			result ~= `; HttpOnly`;
		if( isSecure )
			result ~= `; Secure`;
		return result;
	}
}

class ResponseCookies
{
	Cookie[] _cookies;

	//.....

	inout(Cookie)* opBinaryRight(string op)(string name) inout if(op 
== "in")
	{	foreach( ref inout(Cookie) c; _cookies )
			if( c.name == name )
				return &c; //Error is here
		return null;
	}
}
Mar 25 2014
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/25/2014 12:20 PM, Uranuz wrote:

 I have problem with understanding of work of modifiers const, inout,
 immutable modifiers.
I am sure you know these as well, but here is my quick list: const: "I shall not modify data" immutable: "I demand immutable data" inout: "Whatever the actual type qualifier is"
 As I understand inout keyword is intended to consume const,
 immutable and data without modifiers.
Yes. Further, the code is compiled as 'const' (because const is compatible with mutable, immutable, and const).
 I don't understand what is the purpose of inout.
So that the following function works with any kind of int array: inout(int)[] first_two(inout(int)[] arr) { // (input validation omitted) return arr[0..2]; } We don't have to write three overloads just for these qualifiers. (Yes, templates with constraints can be used as well.)
 Error: cannot implicitly convert expression (&c) of type const(Cookie)*
 to inout(Cookie)*
Your code compiles with the development branch of dmd. What version are you using? Ali
Mar 25 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
 Your code compiles with the development branch of dmd. What 
 version are you using?
Now I'm using 2.064. May be this bug is already fixed. In 2.065 I experience problem that std.json library module is not compatible with code written for previous versions. This is why I returned to 2.064. As far as I understand these breaking changes were a mistake. Now I don't know how to deal with it. I'm not sure that using old Phobos verion with new compiler could be good solution. If were a mistake It doesn't sound reasonable to change existing code to compile with this version.
Mar 25 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/25/2014 02:01 PM, Uranuz wrote:

 Your code compiles with the development branch of dmd. What version
 are you using?
Now I'm using 2.064.
Still compiles for me. Could you please show us a minimal main() as well. Thank you, Ali
Mar 25 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
 Still compiles for me. Could you please show us a minimal 
 main() as well.

 Thank you,
 Ali
Yes. This exact piece of code compiles in 2.064, but it doesn't compile in my project. I don't know how to localize the problem yet. It's very strange. I have tried to create class instance with different modifiers (const, inout, immutable), because it could be because of type of array that I try to access. Very strange. I'll try to repeat it.
Mar 26 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
Source of error is that I have also the following methods inside 
ResponseCookise class:

	void opIndexAssign(string value, string name)
	{	auto cookie = name in this;
		if( cookie is null )
			_cookies ~= Cookie(name, value);
		else
			cookie.value = value;
	}

	void opIndexAssign( ref Cookie value, string name )
	{	auto cookie = name in this;
		if( cookie is null )
			_cookies ~= value;
		else
			*cookie = value;
	}

In 2.065 I get other error message than in 2.064, but it still 
not very informative. Why I get message inside *in* operator but 
not in *opIndexAssign*?. And how could I solve this?

/d800/f144.d(49): Error: cannot implicitly convert expression 
(&c) of type inout(const(Cookie))* to inout(Cookie)*
/d800/f144.d(38): Error: template instance 
f144.ResponseCookies.opBinaryRight!"in" error instantiating
/d800/f144.d(38): Error: rvalue of in expression must be an 
associative array, not f144.ResponseCookies
Mar 26 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
If inout is treated as const does it mean that in this operator I 
can't assign new value to cookie object. In this case it means 
that I still should make separate class method with the same body 
to woraround this. I think that it's a bug. Am I right?

 	void opIndexAssign(string value, string name)
 	{	auto cookie = name in this;
 		if( cookie is null )
 			_cookies ~= Cookie(name, value);
 		else
 			cookie.value = value;
 	}
Mar 26 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/26/2014 01:21 AM, Uranuz wrote:

 If inout is treated as const does it mean that in this operator I can't
 assign new value to cookie object.
I have not investigated the compiler code but the following is very logical to me. inout is not a template mechanism. The code gets compiled once. What happens is, the inout from some variable is transferred to some other variable(s). Since inout must work with mutable, const, and immutable, and since const binds to all three, it is easy for the compiler to assume inout is const inside the code and reject code that tries to modify an inout variable (even if inout is 'mutable' for some calls to the function). After compiling the code, the compiler also ensures that the inout that is returned from the function is compatible with the calling code. For example, if the source inout array were immutable, a slice of it should not be assigned to a mutable slice at the caller site: inout(int)[] foo(inout(int)[] arr) { return arr; } immutable(int)[] imm = // ...; int[] result = foo(imm); // ERROR
 In this case it means that I still
 should make separate class method with the same body to woraround this.
 I think that it's a bug. Am I right?
It is not a bug. If the member function makes modifications to the object, it cannot be inout, because it can work only with mutable objects. Ali
Mar 26 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
I modified this with casting pointer to pointer to *inout* and it 
compiles then.

inout(Cookie)* opBinaryRight(string op)(string name) inout if(op 
== "in")
{	foreach( ref inout(Cookie) c; _cookies )
		if( c.name == name )
			return cast(inout(Cookie)*) &c; //Error is here
	return null;
}

As I think compiler may be should create const version and 
mutable version of this function. In case when it's mutable it 
should return pointer to mutable pointer to *const* data when 
function is const. In first case it will be able to modify 
variable from outside using this pointer to mutable. Otherwise if 
pointer to *const* produced modification is not allowed. I don't 
understand why compiler produce pointer to const, when inout 
"converts" to mutable. I think that compiler should check that 
object field is not modified inside it's const method, but I 
don't understand why it produces pointer to nonconst in case when 
object field shouldn't be const.

In case when all is treated like const  I don't understand how 
*inout* could be useful to get mutable references and pointers as 
result of *inout* method when object (and it's fields) are 
mutable. Is this behaviour forbidden for some reason that  I 
don't know?
Mar 26 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/26/2014 12:17 PM, Uranuz wrote:

 I modified this with casting pointer to pointer to *inout* and it
 compiles then.

 inout(Cookie)* opBinaryRight(string op)(string name) inout if(op == "in")
 {    foreach( ref inout(Cookie) c; _cookies )
          if( c.name == name )
              return cast(inout(Cookie)*) &c; //Error is here
      return null;
 }
I think it is a bug then. I only now understand the problem. Here is a reduced case: class C { int[1] arr; inout(int)* foo() inout { foreach (ref e; arr) { static assert(is (typeof(e) == inout(const(int)))); // WAT? return &e; } } } void main() { (new C).foo(); } The problem is, when the foreach variable is ref, the type gains a const as well. Unless there is a reason for that we should file a bug. Another workaround for you is using the foreach index: { foreach(i, ref inout(Cookie) c; _cookies ) // NOTE i if( c.name == name ) return &_cookies[i]; // NOTE i return null; }
 As I think compiler may be should create const version and mutable
 version of this function.
Type is a compile-time concept; there is no difference between the compiled mutable versus const versions of your inout function. So, there is no need for more than one compilation.
 In case when it's mutable it should return pointer to mutable
 pointer to *const* data when function is const.
Makes sense and it should be checked at compile time.
 I don't understand why compiler produce pointer to const,
I think that's a bug.
 In case when all is treated like const
The body of an inout function must not mutate the object because the same body supports the non-mutable cases as well. So, it is acceptable for the compiler to compile as if it's const. However, as you say, the interface of the function still obeys the actual types used in the program. You are right. I think it is just a bug. Ali
Mar 26 2014
parent reply "Uranuz" <neuranuz gmail.com> writes:
 Type is a compile-time concept; there is no difference between 
 the compiled mutable versus const versions of your inout 
 function. So, there is no need for more than one compilation.
Yes. It's my mistake. For CPU there is only data (that are sequence of bits) and comands (that are sequence of bits too). Type is human interpretation of meaning of these bits.
 The problem is, when the foreach variable is ref, the type 
 gains a const as well. Unless there is a reason for that we 
 should file a bug.
So problem is in foreach operator but not in *inout* itself. I haven't send a bug so I'm not familiar with it. How could I do it?
Mar 26 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/26/2014 10:29 PM, Uranuz wrote:

 I haven't
 send a bug so I'm not familiar with it. How could I do it?
Go to https://d.puremagic.com/issues/ Click "File an Issue" (but you may need to "Open a New Account" first). Product: D Component: DMD Version: D2 Importance: P3 normal A descriptive title... Additional Comments: the reduced code etc. Then click "Commit". Thank you, :) Ali
Mar 26 2014
parent "Uranuz" <neuranuz gmail.com> writes:
Posted bugreport.

https://d.puremagic.com/issues/show_bug.cgi?id=12478
Mar 27 2014
prev sibling parent "Kagamin" <spam here.lot> writes:
On Tuesday, 25 March 2014 at 19:20:10 UTC, Uranuz wrote:
 In this case I don't understand sense of it. Also I have 
 strange feelings about shared, because it's not widely used in 
 code that I'm experienced to see and lacks of good usage 
 examples.
shared is used for low-level multithreading, if you're not experienced in it, use safer methods from std.concurrency.
Mar 26 2014