Jump to page: 1 2
Thread overview
IFTI, value types, and top-level const
Jun 07, 2013
Peter Alexander
Jun 07, 2013
monarch_dodra
Jun 07, 2013
monarch_dodra
Jun 07, 2013
Peter Alexander
Jun 08, 2013
Jonathan M Davis
Jun 09, 2013
Peter Alexander
Jun 09, 2013
Jonathan M Davis
Jun 09, 2013
monarch_dodra
Jun 09, 2013
Jonathan M Davis
Jun 09, 2013
monarch_dodra
Jun 09, 2013
Jonathan M Davis
Jun 09, 2013
monarch_dodra
Jun 09, 2013
Jonathan M Davis
Jun 09, 2013
Peter Alexander
Jun 09, 2013
Diggory
Jun 09, 2013
Timon Gehr
Jun 09, 2013
Jonathan M Davis
Jun 09, 2013
Timon Gehr
Jun 07, 2013
Peter Alexander
June 07, 2013
This code fails to compile because T is deduced to be `const int`:

void foo(T)(T x)
{
    x++;
}

void main()
{
    const int y = 0;
    foo(y);
}


Analogous code in C++ works because C++ strips the top-level const.

Is there any reason this isn't done in D with non-ref value type parameters? Of course, I can use Unqual, but it is a hassle to always remember. It also doesn't help with bloat because these will all call different functions:

const int a;
immutable int b;
int c;
foo(a);
foo(b);
foo(c);

What is the value in things being the way they are?
June 07, 2013
On Friday, 7 June 2013 at 13:58:33 UTC, Peter Alexander wrote:
> This code fails to compile because T is deduced to be `const int`:
>
> void foo(T)(T x)
> {
>     x++;
> }
>
> void main()
> {
>     const int y = 0;
>     foo(y);
> }
>
>
> Analogous code in C++ works because C++ strips the top-level const.
>
> Is there any reason this isn't done in D with non-ref value type parameters? Of course, I can use Unqual, but it is a hassle to always remember. It also doesn't help with bloat because these will all call different functions:
>
> const int a;
> immutable int b;
> int c;
> foo(a);
> foo(b);
> foo(c);
>
> What is the value in things being the way they are?

I seem to specifically remember that it *didn't* work this way. Isn't this a regression?
June 07, 2013
On Friday, 7 June 2013 at 16:24:16 UTC, monarch_dodra wrote:
> I seem to specifically remember that it *didn't* work this way. Isn't this a regression?

...and I seem to have been wrong. This has never worked.

And I can also confirm that C++ does strip the top level const in this case.

I agree, it would be nice for D to do this too.
June 07, 2013
On Friday, 7 June 2013 at 16:24:16 UTC, monarch_dodra wrote:
> I seem to specifically remember that it *didn't* work this way. Isn't this a regression?

I checked the last 5 versions, and this code has never compiled.

(If it were a regression, I would have hoped a unit test would have caught it!)
June 07, 2013
On Friday, 7 June 2013 at 16:33:50 UTC, monarch_dodra wrote:
> On Friday, 7 June 2013 at 16:24:16 UTC, monarch_dodra wrote:
>> I seem to specifically remember that it *didn't* work this way. Isn't this a regression?
>
> ...and I seem to have been wrong. This has never worked.
>
> And I can also confirm that C++ does strip the top level const in this case.
>
> I agree, it would be nice for D to do this too.

Ack, it appears it is only done for arrays and pointers :-S

string foo(T)(T x)
{
	return T.stringof;
}

void main()
{
	class C {}
	const int[] a;
	const int* b;
	const C c;
	const int d;

	import std.stdio;
	writeln(foo(a)); // const(int)[]
	writeln(foo(b)); // const(int)*
	writeln(foo(c)); // const(C)
	writeln(foo(d)); // const(int)
}

Obviously you can't strip anything from (c), but I have no idea why (d) isn't stripped. This is bizarre.

... or is this because of the const postblit problem?
June 08, 2013
On Friday, June 07, 2013 18:43:11 Peter Alexander wrote:
> Ack, it appears it is only done for arrays and pointers :-S

And it pretty much only does that because of all of the issues that const and immutable strings were giving us with range-based functions. IFTI now automatically slices arrays, and slices are now tail-const.

Now, we certainly can't strip const in the general case, but I'm inclined to agree that when the mutable version of the type has no mutable indirections, stripping const would make sense. However, one problem that I can think of is that if you're dealing with an rvalue and you don't strip the const, under some circumstances, you can move the object and avoid a copy altogether, but if you strip the const, you can't do that or you'd violate the constness of the object (though maybe you could still get away with it if the type had no postblit) - and that's problem that doesn't exist in C++, since C++ would always do the copy. So, I don't know if we can quite get away with stripping const even if the type has no mutable indirections. It's certainly something that we should consider though.

- Jonathan M Davis
June 09, 2013
On Saturday, 8 June 2013 at 02:18:06 UTC, Jonathan M Davis wrote:
> On Friday, June 07, 2013 18:43:11 Peter Alexander wrote:
>> Ack, it appears it is only done for arrays and pointers :-S
>
> And it pretty much only does that because of all of the issues that const and
> immutable strings were giving us with range-based functions. IFTI now
> automatically slices arrays, and slices are now tail-const.
>
> Now, we certainly can't strip const in the general case, but I'm inclined to
> agree that when the mutable version of the type has no mutable indirections,
> stripping const would make sense. However, one problem that I can think of is
> that if you're dealing with an rvalue and you don't strip the const, under
> some circumstances, you can move the object and avoid a copy altogether, but
> if you strip the const, you can't do that or you'd violate the constness of
> the object (though maybe you could still get away with it if the type had no
> postblit) - and that's problem that doesn't exist in C++, since C++ would
> always do the copy. So, I don't know if we can quite get away with stripping
> const even if the type has no mutable indirections. It's certainly something
> that we should consider though.

It looks like the core issue here is that it's simply not possible to create a mutable copy of a const object with indirection:

struct S { int* p; }
const(S) a;
S b = a;

That's fair, because the current way of copying the object (via memcpy+postblit) could lead to type system violations.

I believe this is a hole in the language. It means that it's impossible to create slices or pointer-like types in a library since they require this compiler magic to strip the top-level const. This is just because of how struct copy with postblit works, it's certainly possible and type safe in theory with some sort of copy constructor:

struct S
{
    int* p;
    this(const(S) s) { p = new int; *p = *s.p; }
}
const(S) a;
S b = S(a);

And a templated slice-like type could be made with tail-const semantics.

struct Slice(T)
{
    T* ptr;
    size_t length;
    static if (is(T==const))
    {
        this(const Slice!T s) { ptr = s.ptr; length = s.length; }
        this(const Slice!(Unqual!T) s) { ptr = s.ptr; length = s.length; }
    }
}

The question is, can a copy constructor be introduced into the language without breaking lots of code, and preserving D's approach to move operations, i.e. not requiring explicit C++ style rvalue references. I'm not sure.
June 09, 2013
On Sunday, June 09, 2013 12:33:39 Peter Alexander wrote:
> It looks like the core issue here is that it's simply not possible to create a mutable copy of a const object with indirection:

Part of my point here (that you seem to have missed) is that there is a cost to making it so that const is stripped from the type even if you can do so. It _forces_ a copy if the value being passed in isn't mutable, whereas if the constness were the same, then the value being passed in could potentially be moved rather than copied. As such, I don't know if we even want to strip the constness even if we can.

> The question is, can a copy constructor be introduced into the language without breaking lots of code, and preserving D's approach to move operations, i.e. not requiring explicit C++ style rvalue references. I'm not sure.

I fully expect that copy constructors could be added, and AFAIK, we're going to have to at some point, because postlbit constructors cannot copy const or immutable objects - even to create another const or immutable object. Postblit constructors simply do not work with const or immutable, because what they're trying to do is fundamentally broken with const or immutable. You must create objects as const or immutable, not copy them and then alter them as postblits do. It's come up before, and Andrei and Walter have discussed it and supposedly have some sort of solution, but they've never explained it publicly, and AFIK, nothing has been done to fix the problem.

The other big problem which is related is how to deal with tail-const and ranges. It's currently very difficult right now (if it's even possible), which makes it so that you should pretty much never mix const and ranges. I know that this is one of Steven Schveighoffer's pet peeves, and he's working on some sort of proposal to fix it, but he hasn't finished it yet.

Copying const or immutable objects in general tends to be a bit iffy unless they're value types.

- Jonathan M Davis
June 09, 2013
On Sunday, 9 June 2013 at 10:49:17 UTC, Jonathan M Davis wrote:
> On Sunday, June 09, 2013 12:33:39 Peter Alexander wrote:
>> It looks like the core issue here is that it's simply not
>> possible to create a mutable copy of a const object with
>> indirection:
>
> Part of my point here (that you seem to have missed) is that there is a cost
> to making it so that const is stripped from the type even if you can do so. It
> _forces_ a copy if the value being passed in isn't mutable, whereas if the
> constness were the same, then the value being passed in could potentially be
> moved rather than copied. As such, I don't know if we even want to strip the
> constness even if we can.
>
> - Jonathan M Davis

Well, these are primitives we are talking about. I'm not sure passing an int by value instead of by ref is much more expansive. We already do the same for slices/pointers, so really, all we are doing is making things consistent.

I agree with your point for structs though. Given any struct, even if POD without indirections, const means const.

But for built-in primitives, I don't think it is a problem.
June 09, 2013
On Sunday, 9 June 2013 at 10:49:17 UTC, Jonathan M Davis wrote:
> I fully expect that copy constructors could be added, and AFAIK, we're going
> to have to at some point, because postlbit constructors cannot copy const or
> immutable objects - even to create another const or immutable object. Postblit
> constructors simply do not work with const or immutable, because what they're
> trying to do is fundamentally broken with const or immutable. You must create
> objects as const or immutable, not copy them and then alter them as postblits
> do. It's come up before, and Andrei and Walter have discussed it and
> supposedly have some sort of solution, but they've never explained it
> publicly, and AFIK, nothing has been done to fix the problem.
>
> - Jonathan M Davis

I don't really see what sets apart a "copy constructor" from a "postblit contructor": They have a different sequence of operations, but it's not like the copy constructor will magically have the object ready for use without ever mutating anything?

So if your CC looks like this:
struct S
{
    int i = 0;
    this(typeof(this) other)
    {
        i = other.i; //oops! mutable operation!
    }
}

Back to square one, no?

Just because postblit means "copy the bits over, and then construct", doesn't mean the compiler has to place the actual bits in the immutable area before the object is completly constructed. Which is how a CC would work around the problem, unless I'm mistaken.

postblit should just be able to modify its own members, as if they were tail const (though there might be a problem for class references...). The problem right now (AFAIK), is that for the compiler, postblit is "just" a function, but it should be more than that.
« First   ‹ Prev
1 2