www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Pure functions and pointers (yes, again)

reply Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
Since issue 8185 has been closed, I'm still very confused. I just 
understood that endless discussion doesn't result in anything.

See example from http://d.puremagic.com/issues/show_bug.cgi?id=8185#c40
---
int f(size_t p) pure
{
     return *cast(int*) p;
}

void g(size_t p, ref size_t) pure
{
     ++*cast(int*) p;
}

void h(size_t a, size_t b) pure
{
     int res = f(a);
     g(b, b);
     assert(res == f(a)); // may fail, no guaranties by language!
}

void main()
{
     int a;
     h(cast(size_t) &a, cast(size_t) &a);
}
---

Jonathan M Davis (whose documentation correcting pull closed the issue) 
told me that this code result in undefined behaviour. What _exectly_ 
language rule this violates? I don't see this rule, but if there is no 
such rule, how can we treat anything as strongly pure function?

-- 
Денис В. Шеломовский
Denis V. Shelomovskij
Jul 04 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, July 04, 2012 11:59:10 Denis Shelomovskij wrote:
 Since issue 8185 has been closed, I'm still very confused. I just
 understood that endless discussion doesn't result in anything.
 
 See example from http://d.puremagic.com/issues/show_bug.cgi?id=8185#c40
 ---
 int f(size_t p) pure
 {
      return *cast(int*) p;
 }
 
 void g(size_t p, ref size_t) pure
 {
      ++*cast(int*) p;
 }
 
 void h(size_t a, size_t b) pure
 {
      int res = f(a);
      g(b, b);
      assert(res == f(a)); // may fail, no guaranties by language!
 }
 
 void main()
 {
      int a;
      h(cast(size_t) &a, cast(size_t) &a);
 }
 ---
 
 Jonathan M Davis (whose documentation correcting pull closed the issue)
 told me that this code result in undefined behaviour. What _exectly_
 language rule this violates? I don't see this rule, but if there is no
 such rule, how can we treat anything as strongly pure function?
You're violating the type system. You've claimed that a size_t is a pointer by casting it to one and then operating on it as if it were one. Whether the value was originally a pointer or not is irrelevant. You've lied to the compiler about the type, which means that you've subverted the type system, and it's up to _you_ to guarantee that the guarantees that the compiler is supposed to be making hold. The compiler has to use the type system to make its gurantees, so if you lie to it about types, it will make incorrect assumptions, you'll end up with wrong code, and it'll be your fault. It's like with const. Don't cast away const and then modify the variable. It's undefined behavior. Don't do it. You're lying to the compiler about types, and it bases its guarantees on types. So, if you've lied to it, _of course_ it's going to break. D is a systems language, so you can do really dirty things in system code if you need to, but don't expect the compiler to magically figure out what you're doing when you're lying to it through casts. - Jonathan M Davis
Jul 04 2012
prev sibling parent reply travert phare.normalesup.org (Christophe Travert) writes:
Denis Shelomovskij , dans le message (digitalmars.D:171072), a écrit :
 Since issue 8185 has been closed, I'm still very confused. I just 
 understood that endless discussion doesn't result in anything.
 
 See example from http://d.puremagic.com/issues/show_bug.cgi?id=8185#c40
 ---
 int f(size_t p) pure
 {
      return *cast(int*) p;
 }
 
 void g(size_t p, ref size_t) pure
 {
      ++*cast(int*) p;
 }
 
 void h(size_t a, size_t b) pure
 {
      int res = f(a);
      g(b, b);
      assert(res == f(a)); // may fail, no guaranties by language!
 }
 
 void main()
 {
      int a;
      h(cast(size_t) &a, cast(size_t) &a);
 }
 ---
 
 Jonathan M Davis (whose documentation correcting pull closed the issue) 
 told me that this code result in undefined behaviour. What _exectly_ 
 language rule this violates? I don't see this rule, but if there is no 
 such rule, how can we treat anything as strongly pure function?
Casting a value to a pointer clearly subvert the type system. A value with no reference (like size_t) is convertible to immutable, but a pointer cannot. Thus, a function with f's signature is strongly pure, but it is not if takes an int*. That's how you can create a bug. The same issue occurs if you 'create' a pointer by illegal pointer arithmetic for instance: this is undefined behavior. Creating and using any kind reference by casting is undefined. That's not only a purity problem, it is a safety, a garbage collection, an and optimisation issue. In your case, you know what you ar doing regarding safety and garbage collection. Fine. But you do not respect optimisations linked with purity. Too bad, you can't ignore that the compiler is going to make assumptions regarding your datas types. What change would you expect in the langage? making pure function automatically safe? That may not be such a ba idea. However, that is not even enough, you could still create bug from optimizations with casting outside the pure function (for instance, you could alias variables that should not be aliased). The only possibility to completely avoid this kind of bug is to forbid either optimization or casting. That's not going to happen in D.
Jul 04 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, July 04, 2012 09:21:35 Christophe Travert wrote:
 What change would you expect in the langage? making pure function
 automatically  safe? That may not be such a ba idea. However, that is
 not even enough, you could still create bug from optimizations with
 casting outside the pure function (for instance, you could alias
 variables that should not be aliased).
An safe function is only as safe as the trusted functions that it calls. With trusted, it's up to the programmer to determine that the system stuff being done is actually being done in a way which is ultimately safe (e.g. not using undefined behavior). If the programmer screwed up, and the trusted stuff has buffer overruns or whatnot, then the safe code isn't really safe. With both trusted and casts, it's up to the programmer to get them right, because what the programmer is doing is telling the compiler that they know better than the compiler and that they know what they're doing. If they do know better, then great. But if they don't, say hello to some nasty bugs. _All_ of the guarantees that the compiler gives are based on the type system. So, anything that the programmer does to work around the type system must be verified and guaranteed by the programmer to ultimately maintain the guarantees that compiler expects. Otherwise, the compiler's guarantees are based on wrong assumptions, and they're going to be invalid. - Jonathan M Davis
Jul 04 2012