August 09, 2010
Steven Schveighoffer:
> extern(C) int strlen(const(char)* str);

C has no const(char)* so I am now thinking about a possible D2 diagnostic enhancement request that turns that line of code into a compile time error :-)

Bye,
bearophile
August 09, 2010
On Mon, 09 Aug 2010 11:15:38 -0400, bearophile <bearophileHUGS@lycos.com> wrote:

> Steven Schveighoffer:
>> extern(C) int strlen(const(char)* str);
>
> C has no const(char)* so I am now thinking about a possible D2 diagnostic enhancement request that turns that line of code into a compile time error :-)

But an extern(C) function does not have to be written in C :)

-Steve
August 09, 2010
Steven Schveighoffer:
> But an extern(C) function does not have to be written in C :)

You are right. But that function written in an arbitrary language has to follow the C interface rules and limitations, and among those there is no way to define a variable to be const(char)*.

So in that line of code you are writing something that can't be enforced. Generally D design refuses features that the compiler is unable to verify. So I think an enhancement request to disallow that is good here. An extern(C) call has to specify only things that are understood by the C interface.

On the other hand in C you have const, but its semantics is different. Uhm...

Bye,
bearophile
August 09, 2010
Steven Schveighoffer <schveiguy@yahoo.com> wrote:

>>>  class C
>>> {
>>>    private Mutable!(int) cache = -1;
>>>    int expensiveFunction() const { return cache == -1 ? cache = _expensiveFunctionImpl() : cache; }
>>>    private int _expensiveFunctionImpl() const {...}
>>> }
>>>  If this is undefined, then something like this cannot be relied on, even when performance is critical.
>>>  -Steve
>>
>> I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.
>
> Why not?  What possible optimization can the compiler do here?  Mutable has an assign operation that is labeled as const, it should be callable by the compiler.
>
> I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.

The compiler may decide to store the value of cache in a register, and
use that value instead of fetching it after _expensiveFunctionImpl is
called. That's the simplest example I could come up with.

-- 
Simen
August 09, 2010
Hello Steven,

> On Mon, 09 Aug 2010 10:53:48 -0400, BCS <none@anon.com> wrote:
> 
>>> C's api can be modified at declaration.  It has no mangling, so you
>>> can  type it how it should be (if C had const).  For example:
>>> extern(C) int strlen(const(char)* str);
>>> I find that much more pleasant than having to cast away const.
>> OTOH that is effectively a hidden cast and has 100% of the same
>> issues  (re undefined behavior) as casting away const while being
>> slightly  harder to find.
>> 
> But you just said that casting and reading is not undefined?  Isn't
> this  the same thing?

Casting away const or tacking const into a extern(C) prototype is safe under exactly the same conditions. The gains a savings of a few keystrokes and looses some degree of findability, aside from that, there is effectively no difference between them.

> if you are sure
> a  const or immutable piece of data is on the heap/stack, it should be
> reasonable to be able to modify it for performance gains.

A conforming compiler can be implemented in such a way that you can never be sure of that without looking at the generated asm or even in such a way that it can't be know till runtime.

-- 
... <IXOYE><



August 09, 2010
On Mon, 09 Aug 2010 11:31:18 -0400, Simen kjaeraas <simen.kjaras@gmail.com> wrote:

> Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>
>>>>  class C
>>>> {
>>>>    private Mutable!(int) cache = -1;
>>>>    int expensiveFunction() const { return cache == -1 ? cache = _expensiveFunctionImpl() : cache; }
>>>>    private int _expensiveFunctionImpl() const {...}
>>>> }
>>>>  If this is undefined, then something like this cannot be relied on, even when performance is critical.
>>>>  -Steve
>>>
>>> I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.
>>
>> Why not?  What possible optimization can the compiler do here?  Mutable has an assign operation that is labeled as const, it should be callable by the compiler.
>>
>> I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.
>
> The compiler may decide to store the value of cache in a register, and
> use that value instead of fetching it after _expensiveFunctionImpl is
> called. That's the simplest example I could come up with.
>

The compiler will rewrite the code as follows:

return cache.opEquals(-1) ? cache.opAssign(_expensiveFunctionImpl()) : cache.xxx;

I'm assuming for cache.xxx that it's some sort of alias this to a getter function.  All of these function would be const.

We can define the opAssign in such a way that it cannot be inlined, forcing the compiler to assume nothing about the result of opAssign.

Note also that the optimization you stated is not possible, even without casting away const, but would be possible on an immutable class.  But the fact that the compiler cannot peek into the implementation of the opAssign means it's forced to make no assumptions about the result of opAssign.

How does one write a conforming compiler that alters the result of this?

-Steve
August 09, 2010
Am 09.08.2010 17:31, schrieb bearophile:
> Steven Schveighoffer:
>> But an extern(C) function does not have to be written in C :)
>
> You are right. But that function written in an arbitrary language has to follow the C interface rules and limitations, and among those there is no way to define a variable to be const(char)*.
>
> So in that line of code you are writing something that can't be enforced. Generally D design refuses features that the compiler is unable to verify. So I think an enhancement request to disallow that is good here. An extern(C) call has to specify only things that are understood by the C interface.
>
> On the other hand in C you have const, but its semantics is different. Uhm...
>
> Bye,
> bearophile

I think, that isn't a good idea. I mean const-ness a compile time thing so the c abi has no problem with it. What's wrong when I define a
extern(c) bool thinkeAboutMyInt(const int* x) {
  ....
}
I want to inform the D typesystem that I'm not going to change the int but I need a pointer because the adress is important. I want this function to be also callable outside D (eg C).

Mafi
August 09, 2010
On Mon, 09 Aug 2010 10:35:02 -0400, Steven Schveighoffer wrote:

> On Mon, 09 Aug 2010 10:27:09 -0400, Don <nospam@nospam.com> wrote:
> 
>> Steven Schveighoffer wrote:
>>> On Mon, 09 Aug 2010 09:57:47 -0400, bearophile <bearophileHUGS@lycos.com> wrote:
>>>
>>>> Steven Schveighoffer:
>>>>> I thought it was "you're on your own", not undefined behavior.  The
>>>>> former
>>>>> implies there is some "right" way to do this if you know more about
>>>>> the
>>>>> data than the compiler, the latter implies that there is no right
>>>>> way to
>>>>> cast away const.  Am I wrong?
>>>>
>>>> In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.
>>>  Casting away const just to read the data is useless.  You can read
>>> const data without a cast.
>>
>> No you can't. You can't pass it to a C function.
> 
> Sure you can.
> 
> extern(C) int strlen(const(char) *arg);
> 
> 
>>>  But my understanding is that casting away const to write should be
>>> doable if you know what you're doing.  If this is undefined behavior,
>>> then that's fine, I'm just unclear on what "undefined behavior"
>>> actually means.  I thought "undefined behavior" means that you cannot
>>> count on the behavior to work in the future.
>>>  An example of where casting away const to write should be allowed is
>>> for a hypothetical mutable member:
>>>  class C
>>> {
>>>    private Mutable!(int) cache = -1;
>>>    int expensiveFunction() const { return cache == -1 ? cache =
>>> _expensiveFunctionImpl() : cache; }
>>>    private int _expensiveFunctionImpl() const {...}
>>> }
>>>  If this is undefined, then something like this cannot be relied on,
>>> even when performance is critical.
>>>  -Steve
>>
>> I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.
> 
> Why not?  What possible optimization can the compiler do here?  Mutable has an assign operation that is labeled as const, it should be callable by the compiler.
> 
> I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.

The compiler does an "optimization" on const/immutable struct members which I reported as a bug:

  http://d.puremagic.com/issues/show_bug.cgi?id=3449

-Lars
August 09, 2010
Mafi:
> I think, that isn't a good idea.

I agree, that idea doesn't work well.

Bye,
bearophile
August 09, 2010
bearophile wrote:

> Mafi:
>> I think, that isn't a good idea.
> 
> I agree, that idea doesn't work well.
> 
> Bye,
> bearophile

I think it still is a good idea to forbid this in safe mode. Perhaps in trusted too.