www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [test] A D-joke?

reply Manfred Nowak <svv1999 hotmail.com> writes:
1) Review this code:

void f( inout int i, inout int j){
  i= 1;
  j= 2;
}


2) Pass or Fail?

3) Explain.


-manfred
Aug 06 2005
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Manfred Nowak wrote:
 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }
 
 
 2) Pass or Fail?
 
 3) Explain.
 
 
 -manfred

I don't get it.
Aug 06 2005
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:

 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.
 2) Pass or Fail?

Yes and no.
 3) Explain.

I am unable to explain because I haven't the foggiest idea what you are on about. Explain *what*? -- Derek Parnell Melbourne, Australia 7/08/2005 6:15:41 PM
Aug 07 2005
next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Derek Parnell <derek psych.ward> wrote:

 On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:
 
 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.

Okay, you are the quality assurer and I want to implement this routine into a piece of software, that on failure cause severe harm, i.e. loss of lives or money. So I do what you want me to: void letThem( inout int becomesOne, inout int becomesTwo) { becomesOne = 1; becomesTwo = 2; } [...]
 I am unable to explain because I haven't the foggiest idea what
 you are on about. Explain *what*? 

See above and repeat the three steps. Hint: try harder. -manfred
Aug 07 2005
next sibling parent Nick <Nick_member pathlink.com> writes:
In article <dd4hhk$30fd$1 digitaldaemon.com>, Manfred Nowak says...
Okay, you are the quality assurer and I want to implement this 
routine into a piece of software, that on failure cause severe 
harm, i.e. loss of lives or money. So I do what you want me to:

void letThem( inout int becomesOne, inout int becomesTwo)
{
    becomesOne = 1;
    becomesTwo = 2;
}

[...]

Perhaps you're thinking of calls of the kind int i; letThem(i,i); in which case i==2? This can be avoided by adding a contract, ie. | void letThem( inout int becomesOne, inout int becomesTwo) | out | { | assert(becomesOne == 1); | assert(becomesTwo == 2); | } | body | { | becomesOne = 1; | becomesTwo = 2; | } Nick
Aug 07 2005
prev sibling next sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Sun, 7 Aug 2005 08:42:28 +0000 (UTC), Manfred Nowak  
<svv1999 hotmail.com> wrote:
 Derek Parnell <derek psych.ward> wrote:

 On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:

 1) Review this code:

 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.

Okay, you are the quality assurer and I want to implement this routine into a piece of software, that on failure cause severe harm, i.e. loss of lives or money. So I do what you want me to: void letThem( inout int becomesOne, inout int becomesTwo) { becomesOne = 1; becomesTwo = 2; }

And what happens when it's called like: int a; letThem(a,a); Your function should be coded as: void letThem( inout int becomesOne, inout int becomesTwo) out() { assert(becomesOne == 1); assert(becomesTwo == 2); } body { becomesOne = 1; becomesTwo = 2; } or similar (I can't remember the syntax - bad me)
 [...]
 I am unable to explain because I haven't the foggiest idea what
 you are on about. Explain *what*?

See above and repeat the three steps. Hint: try harder.

I assume there is a point to this? Regan
Aug 07 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 7 Aug 2005 08:42:28 +0000 (UTC), Manfred Nowak wrote:

 Derek Parnell <derek psych.ward> wrote:
 
 On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:
 
 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.

Okay, you are the quality assurer and I want to implement this routine into a piece of software, that on failure cause severe harm, i.e. loss of lives or money. So I do what you want me to: void letThem( inout int becomesOne, inout int becomesTwo) { becomesOne = 1; becomesTwo = 2; } [...]
 I am unable to explain because I haven't the foggiest idea what
 you are on about. Explain *what*? 

See above and repeat the three steps. Hint: try harder.

Okay ... (G) The specification does not say what to do with the passed values of the arguments. (H) The specification does not define if setting both values is to be atomic; that is, either none is set or both are set, or it doesn't matter. (J) The specification does not define what to do when the arguments refer to the same memory location. Should this routine be a bit more defensive, maybe along the lines of ... void letThem( inout int becomesOne, inout int becomesTwo) { int localOne; int localTwo; // Ensure the arguments reference two different integer values. if (RAM_Overlaps(&becomesOne, int.sizeof, &becomesTwo, int.sizeof)) { throw ReferenceException( ...whatever...); } // Take a local backup of value in case the transaction fails localOne = becomesOne; localTwo = becomesTwo; try { becomesOne = 1; becomesTwo = 2; } catch(Object e) { // Transaction failed, so restore previous values. becomesOne = localOne; becomesTwo = localTwo; throw e; // Pass exception up the tree. } } -- Derek Parnell Melbourne, Australia 7/08/2005 7:31:28 PM
Aug 07 2005
next sibling parent reply zwang <nehzgnaw gmail.com> writes:
Derek Parnell wrote:
 On Sun, 7 Aug 2005 08:42:28 +0000 (UTC), Manfred Nowak wrote:
 
 
Derek Parnell <derek psych.ward> wrote:


On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:


1) Review this code:

void f( inout int i, inout int j){
  i= 1;
  j= 2;
}

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.

Okay, you are the quality assurer and I want to implement this routine into a piece of software, that on failure cause severe harm, i.e. loss of lives or money. So I do what you want me to: void letThem( inout int becomesOne, inout int becomesTwo) { becomesOne = 1; becomesTwo = 2; } [...]
I am unable to explain because I haven't the foggiest idea what
you are on about. Explain *what*? 

See above and repeat the three steps. Hint: try harder.

Okay ... (G) The specification does not say what to do with the passed values of the arguments. (H) The specification does not define if setting both values is to be atomic; that is, either none is set or both are set, or it doesn't matter. (J) The specification does not define what to do when the arguments refer to the same memory location. Should this routine be a bit more defensive, maybe along the lines of ... void letThem( inout int becomesOne, inout int becomesTwo) { int localOne; int localTwo; // Ensure the arguments reference two different integer values. if (RAM_Overlaps(&becomesOne, int.sizeof, &becomesTwo, int.sizeof)) { throw ReferenceException( ...whatever...); } // Take a local backup of value in case the transaction fails localOne = becomesOne; localTwo = becomesTwo; try { becomesOne = 1; becomesTwo = 2; } catch(Object e) { // Transaction failed, so restore previous values. becomesOne = localOne; becomesTwo = localTwo; throw e; // Pass exception up the tree. } }

The code isn't thread-safe. Try harder :)
Aug 07 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 07 Aug 2005 18:20:17 +0800, zwang wrote:

 Derek Parnell wrote:
 On Sun, 7 Aug 2005 08:42:28 +0000 (UTC), Manfred Nowak wrote:
 
 
Derek Parnell <derek psych.ward> wrote:


On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:


1) Review this code:

void f( inout int i, inout int j){
  i= 1;
  j= 2;
}

Okay, done that. (A) Function name 'f' is not meaningful enough. (B) Argument 'i' is not meaningful enough for the context. (C) Argument 'j' is not meaningful enough for the context. (D) Inconsistent spacing on lines 2 and 3. Use either <identifer>=<expression>; or <identfier> = <expression>; (E) The opening brace should not be on the function declaration line. (F) The function body lines are not indented correctly. The standard calls for four spaces.

Okay, you are the quality assurer and I want to implement this routine into a piece of software, that on failure cause severe harm, i.e. loss of lives or money. So I do what you want me to: void letThem( inout int becomesOne, inout int becomesTwo) { becomesOne = 1; becomesTwo = 2; } [...]
I am unable to explain because I haven't the foggiest idea what
you are on about. Explain *what*? 

See above and repeat the three steps. Hint: try harder.

Okay ... (G) The specification does not say what to do with the passed values of the arguments. (H) The specification does not define if setting both values is to be atomic; that is, either none is set or both are set, or it doesn't matter. (J) The specification does not define what to do when the arguments refer to the same memory location. Should this routine be a bit more defensive, maybe along the lines of ... void letThem( inout int becomesOne, inout int becomesTwo) { int localOne; int localTwo; // Ensure the arguments reference two different integer values. if (RAM_Overlaps(&becomesOne, int.sizeof, &becomesTwo, int.sizeof)) { throw ReferenceException( ...whatever...); } // Take a local backup of value in case the transaction fails localOne = becomesOne; localTwo = becomesTwo; try { becomesOne = 1; becomesTwo = 2; } catch(Object e) { // Transaction failed, so restore previous values. becomesOne = localOne; becomesTwo = localTwo; throw e; // Pass exception up the tree. } }

The code isn't thread-safe. Try harder :)

Yeah ... As soon as I posted it, it occurred to me too. However, I don't know how to 'do threads' in D yet. Manfred, is there a point to all of this? Is it possible for you to speak clearly, and in simple terms, about why you suggested this 'joke'? I'm guessing that you are hinting that D has some really big security holes in it and that is simply a 'bad joke' waiting to laugh at us. But I might be reading in to your minimalistic posting. -- Derek Parnell Melbourne, Australia 7/08/2005 8:33:15 PM
Aug 07 2005
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 7 Aug 2005 20:37:49 +1000, Derek Parnell wrote:
Is this all I have to do to make it thread safe?

void letThem( inout int becomesOne, inout int becomesTwo)
{
    int localOne;
    int localTwo;
    // Ensure the arguments reference two different integer values.
    if (RAM_Overlaps(&becomesOne, int.sizeof, &becomesTwo, int.sizeof))
    {
       throw ReferenceException( ...whatever...);
    }

    // Take a local backup of value in case the transaction fails
    localOne = becomesOne;
    localTwo = becomesTwo;

    synchronized  /// << THREAD SAFE?
    try {     
        becomesOne = 1;
        becomesTwo = 2;
    }
    catch(Object e)
    {
     // Transaction failed, so restore previous values.
      becomesOne = localOne;
      becomesTwo = localTwo;
      throw e;  // Pass exception up the tree.
    }
}


-- 
Derek Parnell
Melbourne, Australia
7/08/2005 8:40:36 PM
Aug 07 2005
parent Nod <Nod_member pathlink.com> writes:
In article <wfhd177f2kmv.18w8hgs4gvztb$.dlg 40tude.net>, Derek Parnell says...
On Sun, 7 Aug 2005 20:37:49 +1000, Derek Parnell wrote:
Is this all I have to do to make it thread safe?

void letThem( inout int becomesOne, inout int becomesTwo)
{
    int localOne;
    int localTwo;
    // Ensure the arguments reference two different integer values.
    if (RAM_Overlaps(&becomesOne, int.sizeof, &becomesTwo, int.sizeof))
    {
       throw ReferenceException( ...whatever...);
    }

    // Take a local backup of value in case the transaction fails
    localOne = becomesOne;
    localTwo = becomesTwo;

    synchronized  /// << THREAD SAFE?
    try {     
        becomesOne = 1;
        becomesTwo = 2;
    }
    catch(Object e)
    {
     // Transaction failed, so restore previous values.
      becomesOne = localOne;
      becomesTwo = localTwo;
      throw e;  // Pass exception up the tree.
    }
}


-- 
Derek Parnell
Melbourne, Australia
7/08/2005 8:40:36 PM

Hmm, what if the function is passed data which is in read-only memory? Is it possible to test for this in an in-contract? I could also be reading too much into the post, but perhaps it is a kind of zen-riddle to demonstrate the hoops you have to jump through to make a seemingly simple function robust in D. Not that you always need robustness on this scale, though... -Nod-
Aug 07 2005
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Derek Parnell <derek psych.ward> wrote:
[...]
 I'm guessing that you are hinting that D has some really big
 security holes in it

You are right. And because I have learned in this group, that I have to talk until I am blue in the face when I present such a problem, I now try the other way round: let all of you talk until you are blue in the face and do find the problem, which releases me of that burdon, or do not find the problem, which makes you aware of the fact that there might be a problem as your post shows. If on the other hand nobody starts talking, then it is of no interest to the language and I have not wasted my time. So, because you got this pack running I answer your question as a representative of this group. The problem with this function is indeed, that the outcome of giving the same variable to it, is undefined by design. Therefore the task of any quality assurer is to reject this implementation: i.e. the outcome of step 2 is FAIL, with the reason above used for step 3. So that piece of work comes back to the developing team. Now the team has at least two choices from which this two are closest: 1) define the outcome 2) forbid this case But according to the specs none of these can succesfully be implemented in D. -manfred
Aug 08 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <dd8360$2sm9$1 digitaldaemon.com>, Manfred Nowak says...
  
So that piece of work comes back to the developing team.


Now the team has at least two choices from which this two are 
closest:

1) define the outcome
2) forbid this case

But according to the specs none of these can succesfully be 
implemented in D.

Why not? DBC should take care of both these problems: # void f( inout int a, inout int b ) { # in { # assert( &a != &b ); # } # out { # assert( &a != &b || ( a == b && b == 2 ) ); # } # body { # a = 1; b = 2; # } I grant that the in and out clauses are a bit contradictory, but I'm assuming that either one or the other would be implemented. As for contracts not being included in release builds--some applications ship with contract checking in place. If this is a system where lives hang in the balance, I would expect the contract code to remain (coupled with an improved assertion handler). Sean
Aug 08 2005
next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Sean Kelly <sean f4.ca> wrote:

[...]
But according to the specs none of these can succesfully be 
implemented in D.

Why not?

Because the specs dont say what you get if you take the address of a formal inout parameter. This can be the address of the actual parameter as it is currently implemented in the reference compiler, but this is not guaranteed. The same holds for the values of the formal inout parameters. So: if you cannot change the interface and the specs stay as they are, you are lost. To make clear what I mean: <code> int i= 0; void f( inout int i){ i= 2; assert( .i == i); } void main(){ f( i); } </code> may throw an exception according to the specs. -manfred
Aug 08 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <dd89n6$2qd$1 digitaldaemon.com>, Manfred Nowak says...
Sean Kelly <sean f4.ca> wrote:

[...]
But according to the specs none of these can succesfully be 
implemented in D.

Why not?

Because the specs dont say what you get if you take the address of a formal inout parameter. This can be the address of the actual parameter as it is currently implemented in the reference compiler, but this is not guaranteed. The same holds for the values of the formal inout parameters.

Then this should be changed. The properties of inout parameters should be well-defined. Seam
Aug 08 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
Sean Kelly <sean f4.ca> wrote:

[...]
 Then this should be changed.  The properties of inout parameters
 should be well-defined.

True. But this deficiency of the spec is not my main target. Lets wait for Burton to solve the task he cried for. -manfred
Aug 08 2005
prev sibling parent reply Burton Radons <burton-radons smocky.com> writes:
Manfred Nowak wrote:

 Sean Kelly <sean f4.ca> wrote:
 
 [...]
 
But according to the specs none of these can succesfully be 
implemented in D.

Why not?

[...] Because the specs dont say what you get if you take the address of a formal inout parameter. This can be the address of the actual parameter as it is currently implemented in the reference compiler, but this is not guaranteed.

Quit digging this hole. You're wrong. DMD IS the specification except in cases of direct contradiction. This issue has come up before and that's how inout/out properties are defined because any other definition changes their semantics hugely, as you know. Having the reference implementation partially define the specification is the nature of a beta language (and Walter being a lazy bastard). You know, you could've just asked, "Hey what does this do, and is that standard?" instead of making a complete ass out of yourself.
Aug 08 2005
parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Burton Radons <burton-radons smocky.com> wrote:

[ some remarks about some people ]

I really like developers getting personal and through my hole 
business life I could not resist mentioning them to my boss with the 
emphasis that these are really nice people.

What I never understood in total was, that these people usually 
resignated to the next possible date.

So, if you want me that I praise your skills to the quality assurer 
of your company send me a private mail.

Meanwhile simply explain how you would solve the task that your 
implementation should forbid any call with identical actual 
parameters.

Thanks in advance.

-manfred
Aug 08 2005
next sibling parent Burton Radons <burton-radons smocky.com> writes:
Manfred Nowak wrote:

 Burton Radons <burton-radons smocky.com> wrote:
 
 [ some remarks about some people ]
 
 I really like developers getting personal and through my hole 
 business life I could not resist mentioning them to my boss with the 
 emphasis that these are really nice people.

It isn't "getting personal" to comment on your conduct, which was to find some place in the spec where no behaviour is defined at all, to assume that an ambiguity must mean that an implementation is free to act in any way it wants (which would mean that any use of inout/out would be undefined!), to make a silly thread where your intentions could not be less clear, and then to act like you duped us all when a simple question could have cleared up the issue right away. You acted like an ass; I noted it. By contrast an ad hominem attack would be for me to say you can't be right because you're a Spaniard, or that you must believe something because you come from a FORTH background.
 What I never understood in total was, that these people usually 
 resignated to the next possible date.

Okay, you really don't write good English. This is fine and I won't punish you for that, but please be as elaborate as you can so that we can figure out what you mean, repeating important segments with different wordings. Did you mean "resigned at", as in quit the job, and is this sarcastic/coy/ironic/etc? Please don't use sarcasm when typing in English, it's difficult enough to detect it in plaintext when conversing with another English speaker.
 So, if you want me that I praise your skills to the quality assurer 
 of your company send me a private mail.

Oh, it's a threat. Great.
 Meanwhile simply explain how you would solve the task that your 
 implementation should forbid any call with identical actual 
 parameters.

I can't even begin to discuss this until you say straight out what your problem is with the way D implements it! What problem am I trying to solve in my implementation? What am I implementing? What task? I'm not even sure if you're using "quality assurance" with the same meaning that I have of it.
Aug 09 2005
prev sibling parent reply Alan West <alan alanz.com> writes:
Manfred Nowak wrote:
 I really like developers getting personal and through my hole 

This part was the joke right?
Aug 11 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
Alan West <alan alanz.com> wrote:

 Manfred Nowak wrote:
 I really like developers getting personal and through my hole 

This part was the joke right?

Yeah. The only expression that rhymed to wording _and_ subject. -manfred
Aug 11 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 8 Aug 2005 18:31:10 +0000 (UTC), Sean Kelly wrote:

 In article <dd8360$2sm9$1 digitaldaemon.com>, Manfred Nowak says...
  
So that piece of work comes back to the developing team.


Now the team has at least two choices from which this two are 
closest:

1) define the outcome
2) forbid this case

But according to the specs none of these can succesfully be 
implemented in D.

Why not? DBC should take care of both these problems: # void f( inout int a, inout int b ) { # in { # assert( &a != &b ); # } # out { # assert( &a != &b || ( a == b && b == 2 ) ); # } # body { # a = 1; b = 2; # } I grant that the in and out clauses are a bit contradictory, but I'm assuming that either one or the other would be implemented. As for contracts not being included in release builds--some applications ship with contract checking in place. If this is a system where lives hang in the balance, I would expect the contract code to remain (coupled with an improved assertion handler).

There are at least two problems with using Contract code as an input argument checking device. One is that one cannot always guarantee that it will be compiled in; by accident it might be compiled with "-release". The other problem is that some contract code might only be intended for non-production editions of the application and there is no way to separate the two uses of Contract code. The simpler, and more *reliable*, method is to perform argument checking outside of contract code and provide messages that will always get to the user. In other words, never assume that your application will always be compiled without "-release", because someday it will be. (Murphy's law?) I still think that in{} and out{} blocks are useful for in-house testing, just not for production editions of the application. If you suspect that 'testing' might be needed at some customer's site, provide two editions of the application - one without contracts and one with contracts (and unit tests). -- Derek Melbourne, Australia 9/08/2005 12:42:52 PM
Aug 08 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
Derek Parnell <derek psych.ward> wrote:

[ wise arguments]

Applause.

-manfred
Aug 09 2005
prev sibling next sibling parent reply llothar <llothar_member pathlink.com> writes:
The problem with this function is indeed, that the outcome of giving 
the same variable to it, is undefined by design.

Don't know what you are talking about. Program statements are executed in serial order. So it is well defined order. This design is the same as in all languages that use out or inout parameter. Sometimes it even makes sense to provide the same inout reference, for example when you are not interested in the results. If you don't want something like this switch to a language that don't have this kind of side effects, there are enough of them. They come up with other problems in real world use, so just remember the famous saying "there is no silver bullet" in software engineering.
Aug 08 2005
next sibling parent reply Burton Radons <burton-radons smocky.com> writes:
llothar wrote:
The problem with this function is indeed, that the outcome of giving 
the same variable to it, is undefined by design.

Don't know what you are talking about. Program statements are executed in serial order. So it is well defined order. This design is the same as in all languages that use out or inout parameter.

Now this was not part of the topic, but that's not true, and the current design is flawed enough that I think it should be reexamined. The other way to implement inout/out arguments is to read them on the function call, then write them after the call returns. This is how any language with potential latency in message sends works (like CORBA), and it allows properties and opIndex to be used with inout parameters. This lack of orthogonality is a problem. The two are too different to interoperate. If a current D function throws, any changes to an inout are stored; with a CORBA function they'd be scratched (this is the mechanically more useful option). Then there's the fact that it removes pointer aliasing, which can be useful in some situations. With CORBA it would be illegal to provide the same identifier to two inout parameters because of the identified but misapplied problem. Then again, the language could just define that call evaluation order is undefined, but store evaluation order is well-defined.
Aug 08 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <dd8a19$321$1 digitaldaemon.com>, Burton Radons says...
llothar wrote:
The problem with this function is indeed, that the outcome of giving 
the same variable to it, is undefined by design.

Don't know what you are talking about. Program statements are executed in serial order. So it is well defined order. This design is the same as in all languages that use out or inout parameter.

Now this was not part of the topic, but that's not true, and the current design is flawed enough that I think it should be reexamined. The other way to implement inout/out arguments is to read them on the function call, then write them after the call returns. This is how any language with potential latency in message sends works (like CORBA), and it allows properties and opIndex to be used with inout parameters. This lack of orthogonality is a problem.

This is great for CORBA, but I'm not sure I would like it within D. I would much rather have a new attribute to specify this sort of behavior if in-language distributed programming were supported.
The two are too different to interoperate.  If a current D function 
throws, any changes to an inout are stored; with a CORBA function they'd 
be scratched (this is the mechanically more useful option).  Then 
there's the fact that it removes pointer aliasing, which can be useful 
in some situations.

Agreed. And I grant that the CORBA model is nice in some respects--it allows for some degree of automatic exception safety--but it's not worthwhile in a systems programming language. I would much rather have a valid address for passed parameters, etc. Sean
Aug 08 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Sean Kelly wrote:

 In article <dd8a19$321$1 digitaldaemon.com>, Burton Radons says...
 
llothar wrote:

The problem with this function is indeed, that the outcome of giving 
the same variable to it, is undefined by design.

Don't know what you are talking about. Program statements are executed in serial order. So it is well defined order. This design is the same as in all languages that use out or inout parameter.

Now this was not part of the topic, but that's not true, and the current design is flawed enough that I think it should be reexamined. The other way to implement inout/out arguments is to read them on the function call, then write them after the call returns. This is how any language with potential latency in message sends works (like CORBA), and it allows properties and opIndex to be used with inout parameters. This lack of orthogonality is a problem.

This is great for CORBA, but I'm not sure I would like it within D. I would much rather have a new attribute to specify this sort of behavior if in-language distributed programming were supported.

But those ARE the keywords for that sort of behaviour. D stole them from IDL, the language for defining CORBA interfaces. Get your own damned keywords. ;)
The two are too different to interoperate.  If a current D function 
throws, any changes to an inout are stored; with a CORBA function they'd 
be scratched (this is the mechanically more useful option).  Then 
there's the fact that it removes pointer aliasing, which can be useful 
in some situations.

Agreed. And I grant that the CORBA model is nice in some respects--it allows for some degree of automatic exception safety--but it's not worthwhile in a systems programming language. I would much rather have a valid address for passed parameters, etc.

When that is the actual intention, you have pointers or maybe references if non-null addresses are vital. If you want a function to have multiple returns, you have inout/out. I don't think the valid address problem's as notable in D as it was in C++. I mean, there's no way to ensure that you're passed a valid object reference, right? It's certainly a big problem when it happens, but I haven't seen anyone ask for a non-null object qualifier. Just stuff in an assert and move on to bigger problems.
Aug 08 2005
parent Sean Kelly <sean f4.ca> writes:
In article <dd8cu4$6ac$1 digitaldaemon.com>, Burton Radons says...
Sean Kelly wrote:
 
 Agreed.  And I grant that the CORBA model is nice in some respects--it allows
 for some degree of automatic exception safety--but it's not worthwhile in a
 systems programming language.  I would much rather have a valid address for
 passed parameters, etc.

When that is the actual intention, you have pointers or maybe references if non-null addresses are vital. If you want a function to have multiple returns, you have inout/out.

But inout *is* the D reference type :)
I don't think the valid address problem's as notable in D as it was in 
C++.  I mean, there's no way to ensure that you're passed a valid object 
reference, right?  It's certainly a big problem when it happens, but I 
haven't seen anyone ask for a non-null object qualifier.  Just stuff in 
an assert and move on to bigger problems.

Personally, I vastly prefer value semantics to reference semantics--if I want references, I can always use pointers. But that's another issue ;) Perhaps 'is' could be extended to support this? It's nice to have some way to determine if two parameters refer to the same thing. Sean
Aug 08 2005
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
llothar <llothar_member pathlink.com> wrote:

[...]
 Program statements are executed in serial order. So it is well
 defined order.

Please read the specs. Applying the same actual parameter to both formal parameters would render the first assignement in the function body useless. And in D dead code is as illegal as expressions that do not have an effect. Instead of undefined I also could have choosen the wording "illegal by design". Decide yourself what you will get from your boss when returning from your quality assurer with the message that you have tried to deliver illegal code. -manfred
Aug 08 2005
parent reply llothar <llothar_member pathlink.com> writes:
In article <dd8b1o$4an$1 digitaldaemon.com>, Manfred Nowak says...

Applying the same actual parameter to both formal parameters would 
render the first assignement in the function body useless.

No. It will just be overwritten. And if you don't want this then don't use inout parameter. If you want the language to protect you for this then do it as all the languages out there: forbid "inout", if you don't do this, this code is absolutely legal. And if you use inout parameter like a local variable then your personal programming skills must be improved, not the language.
And in D dead code is as illegal as expressions that do not have an 
effect.

Sorry but there is no reason to forbid a effectless expressions, and there is no language in the world that forbids this. So this statement is simply wrong. And it is not dead code, dead code is defined as code that is not can't be reached based on static programming analysis. So a useless statement is a useless statement but not dead code. It is also not illegal as there are many good reasons to write dead code, especially if you have a "version" feature.
Aug 09 2005
next sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
llothar <llothar_member pathlink.com> wrote:

[...]
 And if you use inout parameter like a local variable then your 
 personal programming skills must be improved, not the language.

Sorry. If this is your professional opinion for the needs of QA in the production of a piece of software that by malfunction my cause loss of lives, would you trust your life to such software delivered by your own company?
And in D dead code is as illegal as expressions that do not have
an effect.

Sorry but there is no reason to forbid a effectless expressions, and there is no language in the world that forbids this. So this statement is simply wrong. And it is not dead code, dead code is defined as code that is not can't be reached based on static programming analysis. So a useless statement is a useless statement but not dead code. It is also not illegal as there are many good reasons to write dead code, especially if you have a "version" feature.

Nice try. But I do not see any use of a 'version' feature in the presented piece of software. Furthermore, I ask whether the conjecture is true, that you as the member of a QA-team would let pass the presented piece of software even if the first statement in the body of the function would be prepended by something like if( ackermann( long.max / 2 ) > 42) transferLotsOfMoneyTo( "llothar"); accompanied by the appropriate definitions of the two functions used, because the two assignment statements of the program originally presented may still not be dead code and still are not forbidden by the language? -manfred
Aug 10 2005
next sibling parent reply llothar <llothar_member pathlink.com> writes:
In article <ddej2h$20ha$1 digitaldaemon.com>, Manfred Nowak says...
llothar <llothar_member pathlink.com> wrote:

[...]
 And if you use inout parameter like a local variable then your 
 personal programming skills must be improved, not the language.

Sorry. If this is your professional opinion for the needs of QA in the production of a piece of software that by malfunction my cause loss of lives, would you trust your life to such software delivered by your own company?

For this tasks you shouldn't either use dynamically allocated memory and also no inout parameter and also a few other things. Get a lesson in 'Z' and try to formally verify the code. This is what is done in so critical pieces of software, like aviation or nuclear power plant control systems, I think you should rethink the purpose of a language in a safe design, as it is almost a minor part. And by the way did you ever work with QA teams in the real world ? Sounds more like you are still part of the strange academic world.
Aug 11 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
llothar <llothar_member pathlink.com> wrote:

[...]
 Get a lesson in 'Z' and try to formally verify the code.

Oooops. I am trustfully looking forward to your formal proof of correctness in "Z" for the call of `ackermann( uint.max / 2)'. -manfred
Aug 12 2005
prev sibling parent Georg Bauhaus <bauhaus futureapps.de> writes:
Manfred Nowak wrote:
 llothar <llothar_member pathlink.com> wrote:
 
 [...]
 
And if you use inout parameter like a local variable then your 
personal programming skills must be improved, not the language.

Sorry. If this is your professional opinion for the needs of QA in the production of a piece of software that by malfunction my cause loss of lives, would you trust your life to such software delivered by your own company?

I don't think there is a way to solve the problem of f(i, i) in an engineering/QA sense (with safety in mind), even when your language were to forbid two paths of access to a variable. Thinking otherwise can be a dangerous illusion, I think. Suppose in f(i, j), &i not &j, assigning to i and j has external effects, direct or indirect. How can a compiler cover external effects, and show that at some point in time, the external effects don't incur risk? Relying on &i not being &j isn't a bit more secure then. Or suppose that in f(i, j), i and j denote composite values and the composite values happen to share a common object, at some point while the programs is running. For example, via a pointer. What now? Can/Should proper program design be delegated to the language definition here, too? Extending the argument, if two simple pointers point to the same object, deallocating via one pointer has the effect of ... So simple pointers should be disallowed in languages? ;-) Or some_list.some_algorithm(here, there) A perfectly useful call is some_list.some_algorithm(this_place, this_place) The algorithm just happens to work on only one list element, possibly modyfing this_place after the work is done (and the element has been moved or some such). Georg
Aug 11 2005
prev sibling parent reply =?UTF-8?B?VGhvbWFzIEvDvGhuZQ==?= writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

llothar schrieb:
 In article <dd8b1o$4an$1 digitaldaemon.com>, Manfred Nowak says...

[snip]
And in D dead code is as illegal as expressions that do not have an 
effect.

Sorry but there is no reason to forbid a effectless expressions, and there is no language in the world that forbids this. So this statement is simply wrong.

http://www.digitalmars.com/d/statement.html#expression # Expressions that have no effect, like (x + x), are illegal in # expression statements. http://www.digitalmars.com/d/warnings.html # While dead code is poor style in released code, it can legitimately # happen a lot when rapidly trying to isolate down a bug or experiment # with different bits of code. While effectless expressions are illegal, dead code is legal. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFC/L0l3w+/yD4P9tIRAsWuAJ9S3jZeHjefqFP9YvxkULW79VyKjACeP0ve ErxgPeDUKUepY77yXJOyLRc= =NsyW -----END PGP SIGNATURE-----
Aug 12 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

http://www.digitalmars.com/d/statement.html#expression
# Expressions that have no effect, like (x + x), are illegal in
# expression statements.

Why is it assumed that expression has no effect? x could have + overloaded. If so, is it still illegal? Thanks, --AJG.
Aug 12 2005
parent =?ISO-8859-1?Q?Thomas_K=FChne?= <thomas-dloop kuehne.THISISSPAM.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

AJG schrieb:
http://www.digitalmars.com/d/statement.html#expression
# Expressions that have no effect, like (x + x), are illegal in
# expression statements.

Why is it assumed that expression has no effect? x could have + overloaded. If so, is it still illegal?

An overloaded commutative binary operator that changes an operant is at the very least a questionable style. As currently documented an overloaded opAdd without side effects - neglecting the allocation of memory for the return value - is illegal. If there could be any side effects - e.g. if(..) throw ... - the expression would be legal. Note: DMD-0.129 doesn't enforce this rule Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFC/SRY3w+/yD4P9tIRAsHvAJ90/HU1fCvqFkMoUh5SxkU13mjNQwCdH+q6 r12JLNPNJDipsumfaSQFg4c= =NsFH -----END PGP SIGNATURE-----
Aug 12 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 8 Aug 2005 17:01:52 +0000 (UTC), Manfred Nowak wrote:

 Derek Parnell <derek psych.ward> wrote:
 [...]
 I'm guessing that you are hinting that D has some really big
 security holes in it

You are right. And because I have learned in this group, that I have to talk until I am blue in the face when I present such a problem, I now try the other way round: let all of you talk until you are blue in the face and do find the problem, which releases me of that burdon, or do not find the problem, which makes you aware of the fact that there might be a problem as your post shows.

You may not be aware, but these are not the only two possible alternatives. Even if you do (secretly?) regard the rest of us unwashed and incompetent neophytes, there are still some 'rules' of social etiquette that allows patricians to discourse with plebeians. For example, you may have chosen to phrase your concern thusly ... It appears that DMD allows this sort of situation to exist without reporting on it ... void f( inout int i, inout int j){ i= 1; j= 2; } . . . int a; f(a,a); Do you think that this should be allowed to continue? Or is there some functionality that can be given to either D or DMD to help coders become aware of this situation if they choose to? Or one of the myriad of alternative, but less abrasive and more informative, methods than the method that you did employ.
 If on the other hand nobody starts talking, then it is of no interest 
 to the language and I have not wasted my time. So, because you got 
 this pack running I answer your question as a representative of this 
 group.

LOL ... I don't think so ... ;-)
 The problem with this function is indeed, that the outcome of giving 
 the same variable to it, is undefined by design.

Well, the outcome is not 'undefined' in that the outcome is quite predicable, but it is still likely to be not what was expected.
 Therefore the task of any quality assurer is to reject this 
 implementation: i.e. the outcome of step 2 is FAIL, with the reason 
 above used for step 3.

Well, actually I see this slightly differently. It depends a lot on what 'quality' is declared to be. Our simple definition here is that something that conforms to all the specifications will pass the quality check. Thus, if the *User* specification for this function stated that overlapping memory was allowable then it would pass. If the specification stated the opposite then it would fail. If it wasn't stated, then the specification is technically at fault and whatever the user received would pass. Of course, the unwritten expectation, or commonly assumed position, is that overlapping arguments would be a mistake and ought not to be provided to the function. In the case of unspecified behaviour, what usually happens is that runtime performance is given priority. However, you did mention the critical nature of the impact that this function could have, so I would be leaning more towards runtime checking of arguments, in that case.
   
 So that piece of work comes back to the developing team.
 
 Now the team has at least two choices from which this two are 
 closest:
 
 1) define the outcome
 2) forbid this case
 
 But according to the specs none of these can succesfully be 
 implemented in D.

Which specs? The D Language specs or the user requirement specs? In my opinion, the team could implement either of these two choices. I think the most useful position for us would be to have D continue to allow this possibility, and provide a standard function that we can use to detect overlapping arguments. Then the coder can choose how to react to the situation. -- Derek Melbourne, Australia 9/08/2005 12:13:56 PM
Aug 08 2005
next sibling parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 You are right. And because I have learned in this group, that I have
 to talk until I am blue in the face when I present such a problem,  I
 now try the other way round: let all of you talk until you are blue
 in the face and do find the problem, which releases me of that
 burdon, or do not find the problem, which makes you aware of the fact
 that there might be a problem as your post shows.

You may not be aware, but these are not the only two possible alternatives. Even if you do (secretly?) regard the rest of us unwashed and incompetent neophytes, there are still some 'rules' of social etiquette that allows patricians to discourse with plebeians. [snip] Or one of the myriad of alternative, but less abrasive and more informative, methods than the method that you did employ.

I feel this way, too. The way you did it sounded like "I see something you don't see ... hehe". I did not even bother to answer because of that, and i wonder why anyone else did... Too much spare time, obviously. :) Anyways, i do not understand the whole point. If somebody passes overlapping out/inout arguments to a function, it is a niffy little bug of the programmer, not something i would write into a spec or even try to prevent with compiler errors. Neither would i write a assert into the in contract to test for that. As far as i can remember, i never made such an error in my programming practice. And yes, i do make some errors, and i program for 10+ years now. So this seems simply irrelevant to me. Remember that this is also possible with this function: #### void doSomething(int* i, int* j) { *i = 1; *j = 3; } #### But i did not read anything like "you must not pass two identical addresses to a function that is going to write to them" in the C specs, even if this could be a big security hole in the C/C++ and a lot of other languages... ;o) Anyways, i could even think of senseful applications: #### struct SomeStruct { // members, lots of members. } void someFunc(inout SomeStruct st1, int i, inout SomeStruct st2) { st1.someMember = someValue; st2.someOtherMember = someOtherValue; } #### Eh? Ciao uwe
Aug 08 2005
next sibling parent "Uwe Salomon" <post uwesalomon.de> writes:
Bad grammar in this posting... Sorry :(

Ciao
uwe
Aug 08 2005
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
"Uwe Salomon" <post uwesalomon.de> wrote:

[...]
 The way you did it sounded like "I see something you  don't see
 ... hehe".

Have you ever received a compiler error or taken over a piece of source code with an error, because your colleague was trapped in a street accident? How do you feel, when your machine says "GPF"? Do you think: "Oh you little bastard! If you say again 'I see something you don't see ... hehe' I will power you off and will work elsewhere."
 void doSomething(int* i, int* j)

Dont you see the differece? Here your interface requires and gives you full control over the actual parameters. But still the outcome is undefined by design. -manfred
Aug 09 2005
parent reply "Uwe Salomon" <post uwesalomon.de> writes:
 void doSomething(int* i, int* j)

[snip] But still the outcome is undefined by design.

Yep. Other code, same problem. That's what i was trying to say.
Aug 09 2005
parent Manfred Nowak <svv1999 hotmail.com> writes:
"Uwe Salomon" <post uwesalomon.de> wrote:

[...]
 Yep. Other code, same problem. That's what i was trying to say.

Agreed. The problem inherently presented by my OP is likely to come in many different shapes. -manfred
Aug 10 2005
prev sibling parent reply Manfred Nowak <svv1999 hotmail.com> writes:
Derek Parnell <derek psych.ward> wrote:

[...]
 You may not be aware, but these are not the only two possible
 alternatives. Even if you do (secretly?) regard the rest of us
 unwashed and incompetent neophytes, there are still some 'rules'
 of social etiquette that allows patricians to discourse with
 plebeians. 
 
 For example, you may have chosen to phrase your concern thusly
 ... 

You are right. In order to become a loved person to everyone you can do a lot of things. But if this little example makes you feel unwashed and incompetent then please never try to become a quality assurer. My management once decided to buy a company. One of the first steps was to forbid to give out unauthorized patches, which was routine in that company. So my first contact to them was a developer coming with a patch in a 4000 lines program, written in basic, and declared that there is no documentation for this peace of software and that the originator of this whole thing was forced to leave the company. [...]
 The problem with this function is indeed, that the outcome of
 giving the same variable to it, is undefined by design.

Well, the outcome is not 'undefined' in that the outcome is quite predicable, but it is still likely to be not what was expected.

Please be aware of the differences of "by design" and "by implementation". The outcome by implementation is defined by the order of assignments and the other restrictions that are already mentioned in this thread. [...]
 If it wasn't stated, then the specification is technically at
 fault and whatever the user received would pass. 

And in this case the QA wouldn't have seen the code at all, because that would be a mere waste of time? [...]
 Which specs? The D Language specs or the user requirement specs?

D language. [...]
 I think the most useful position for us would be to have D
 continue to allow this possibility, and provide a standard
 function that we can use to detect overlapping arguments. Then
 the coder can choose how to react to the situation.

Agreed. But now to something completely different: the details :-) -manfred
Aug 09 2005
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Manfred Nowak wrote:
 Derek Parnell <derek psych.ward> wrote:
I think the most useful position for us would be to have D
continue to allow this possibility, and provide a standard
function that we can use to detect overlapping arguments. Then
the coder can choose how to react to the situation.

Agreed. But now to something completely different: the details :-) -manfred

assert( !std.memOverlap( i, j ) );
Aug 09 2005
prev sibling parent Nick <Nick_member pathlink.com> writes:
In article <188ahyevatlas.1ufld3sj4yymc.dlg 40tude.net>, Derek Parnell says...
Okay ...

(G) The specification does not say what to do with the passed values of the
arguments.
(H) The specification does not define if setting both values is to be
atomic; that is, either none is set or both are set, or it doesn't matter.
(J) The specification does not define what to do when the arguments refer
to the same memory location.

[..]

In fact, there is no specification, documentation or code comments what so ever. It's impossible to know what this function is supposed to do and why. I got the feeling this is the kind of "joke" where the replies make up the funny part, and the poster is the one to laugh :) Nick
Aug 07 2005
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Derek Parnell wrote:

<snip>
 (A) Function name 'f' is not meaningful enough.
 (B) Argument 'i' is not meaningful enough for the context.
 (C) Argument 'j' is not meaningful enough for the context.

You mean the function's supposed to be meaningful?
 (D) Inconsistent spacing on lines 2 and 3. Use either 
     <identifer>=<expression>; 
   or 
     <identfier> = <expression>;
 (E) The opening brace should not be on the function declaration line.

That's a matter of personal preference. D doesn't care what whitespace you put between which tokens.
 (F) The function body lines are not indented correctly. The standard calls
 for four spaces.

What standard is this? Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Aug 08 2005
parent Derek Parnell <derek psych.ward> writes:
On Mon, 08 Aug 2005 15:56:53 +0100, Stewart Gordon wrote:

 Derek Parnell wrote:
 
 <snip>
 (A) Function name 'f' is not meaningful enough.
 (B) Argument 'i' is not meaningful enough for the context.
 (C) Argument 'j' is not meaningful enough for the context.

You mean the function's supposed to be meaningful?
 (D) Inconsistent spacing on lines 2 and 3. Use either 
     <identifer>=<expression>; 
   or 
     <identfier> = <expression>;
 (E) The opening brace should not be on the function declaration line.

That's a matter of personal preference. D doesn't care what whitespace you put between which tokens.
 (F) The function body lines are not indented correctly. The standard calls
 for four spaces.

What standard is this?

Stewart, its a joke. I was only having a lend of Manfred. He asked for a 'review' so I gave a fictitious review in keeping with the tone of the original data. It was meant to be light-hearted and not to be taken seriously. -- Derek Parnell Melbourne, Australia 9/08/2005 2:17:37 AM
Aug 08 2005
prev sibling next sibling parent John Demme <me teqdruid.com> writes:
The only thing I can think of is that the inout parameters are being
used as out parameters, which isn't necessarily incorrect behavior.

-John Demme

On Sun, 2005-08-07 at 01:05 +0000, Manfred Nowak wrote:
 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }
 
 
 2) Pass or Fail?
 
 3) Explain.
 
 
 -manfred

Aug 07 2005
prev sibling next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Manfred Nowak wrote:
 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }
 
 
 2) Pass or Fail?
 
 3) Explain.
 
 
 -manfred

hmmm, from the other posts I gather there is a small problem with this kind of code: #int i = 5; #f(i,i); ^ such calls should be illegal; that is: no variable can be passed as an "out" parameter more than once. Is that -by any chance- what you are trying to hint to?
Aug 07 2005
parent reply Georg Wrede <georg.wrede nospam.org> writes:
Hasan Aljudy wrote:
 Manfred Nowak wrote:
 
 1) Review this code:

 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }


 2) Pass or Fail?

 3) Explain.


 -manfred

hmmm, from the other posts I gather there is a small problem with this kind of code: #int i = 5; #f(i,i); ^ such calls should be illegal; that is: no variable can be passed as an "out" parameter more than once. Is that -by any chance- what you are trying to hint to?

Gee, this really ought to get into the language spec!
Aug 07 2005
parent reply Greg Smith <greg siliconoptix.com> writes:
Georg Wrede wrote:

 hmmm, from the other posts I gather there is a small problem with this 
 kind of code:
 #int i = 5;
 #f(i,i);
 ^
 such calls should be illegal; that is: no variable can be passed as an 
 "out" parameter more than once.

 Is that -by any chance- what you are trying to hint to?

Gee, this really ought to get into the language spec!

What about (a) when it's useful; (b)when it's happening but the compiler can't tell, as in int [100] ar; int p; void g(inout int ii, int j ) { f(ii,ar[j]) } void glop(int k) { g( ar[p], k ) } when you call glop(20) and p == 20, you are calling f(ar[20],ar[20]). You can't expect the compiler to detect this. What exactly are you proposing to ban? This comes under the heading of "Doctor, it hurts when I do this". I once used a language called Euclid which managed to ban this sort of thing by making a huge number of useful things illegal. You essentially couldn't have more than one reference to any aggregate object (e.g array) around unless they were all read-only. To allow this to be enforced, the language's 'pointers' all had to be declared as tied to specific aggregate variables. So you couldn't shoot yourself in the foot, because your hands were tied behind your back and your feet were locked in a safe. The road to Euclid, was, indeed, paved with excellent intentions. - Greg
Aug 08 2005
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Greg Smith wrote:
 Georg Wrede wrote:
 
 hmmm, from the other posts I gather there is a small problem with 
 this kind of code:
 #int i = 5;
 #f(i,i);
 ^
 such calls should be illegal; that is: no variable can be passed as 
 an "out" parameter more than once.

 Is that -by any chance- what you are trying to hint to?

Gee, this really ought to get into the language spec!

What about (a) when it's useful; (b)when it's happening but the compiler can't tell, as in int [100] ar; int p; void g(inout int ii, int j ) { f(ii,ar[j]) } void glop(int k) { g( ar[p], k ) } when you call glop(20) and p == 20, you are calling f(ar[20],ar[20]). You can't expect the compiler to detect this. What exactly are you proposing to ban?

True. I wasn't actually propsing anything, I was just wondering whether or not that was the intent of the OP. Maybe a better idea would be to assert that passed parameters don't overlap in that particular function. void f( inout int i, inout int j ) in { assert( !std.overlap(i,j) ); //a standard function, or something like that } body { .... }
 
 This comes under the heading of "Doctor, it hurts when I do this".
 
 I once used a language called Euclid which managed to ban this sort of 
 thing by making a huge number of useful things illegal. You essentially 
 couldn't have more than one reference to any aggregate object (e.g 
 array) around unless they were all read-only. To allow this to be 
 enforced, the language's 'pointers' all had to be declared as tied to 
 specific aggregate variables. So you couldn't shoot yourself in the 
 foot, because your hands were tied behind your back and your feet were 
 locked in a safe. The road to Euclid, was, indeed, paved with excellent 
 intentions.
 
 - Greg
 

Aug 08 2005
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
1) Review this code:

void f( inout int i, inout int j){
  i= 1;
  j= 2;
}

# void f( inout int i, inout int j ) { # in { # assert( &i != &j ); # } # body { # i = 1; j = 2; # } Without this check, passing the same variable as both arguments will result in an undefined result, as whether the value is equal to 1 or 2 after the function call depends on the calling convention (C/D, Pascal, etc). Sean
Aug 07 2005
next sibling parent reply Dejan Lekic <leka entropy.tmok.com> writes:
Yeah Mr. Kelly, that is how I would write the code...

Kind regards

Dejan

-- 
...........
Dejan Lekic
  http://dejan.lekic.org
  
Aug 07 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 07 Aug 2005 19:01:18 +0200, Dejan Lekic wrote:

 Yeah Mr. Kelly, that is how I would write the code...
 
 Kind regards
 
 Dejan

But that doesn't check for overlapping arguments, just ones that start on the same RAM address, plus the checks disappear in '-release' editions. So this is not how I would be coding it. This problem is not unique to D. It is also an issue with any language that passes the address of variables. -- Derek Parnell Melbourne, Australia 8/08/2005 7:01:35 AM
Aug 07 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <138h7vhtkvnm7$.1i39l7uetkpdf$.dlg 40tude.net>, Derek Parnell says...
On Sun, 07 Aug 2005 19:01:18 +0200, Dejan Lekic wrote:

 Yeah Mr. Kelly, that is how I would write the code...
 
 Kind regards
 
 Dejan

But that doesn't check for overlapping arguments, just ones that start on the same RAM address, plus the checks disappear in '-release' editions. So this is not how I would be coding it.

But do overlapping arguments matter in this case? The values passed are integers. Frankly, if someone is passing oddly aligned primitives then he has more problems than the side effects of the posted function. Sean
Aug 07 2005
parent Derek Parnell <derek psych.ward> writes:
On Mon, 8 Aug 2005 02:07:36 +0000 (UTC), Sean Kelly wrote:

 In article <138h7vhtkvnm7$.1i39l7uetkpdf$.dlg 40tude.net>, Derek Parnell
says...
On Sun, 07 Aug 2005 19:01:18 +0200, Dejan Lekic wrote:

 Yeah Mr. Kelly, that is how I would write the code...
 
 Kind regards
 
 Dejan

But that doesn't check for overlapping arguments, just ones that start on the same RAM address, plus the checks disappear in '-release' editions. So this is not how I would be coding it.

But do overlapping arguments matter in this case? The values passed are integers. Frankly, if someone is passing oddly aligned primitives then he has more problems than the side effects of the posted function. Sean

The short answer is "I don't know. What do the specifications say?". I was just trying to be a pedantic bastard ;-) And in any either case, the "-release" switch still blows away the contract checks and asserts. If you need runtime checking in production code, don't rely on using input validation in contract code. Contract code should only be for testing applications before they are shipped to customers, IMHO. But now we are off-topic ;-) -- Derek Melbourne, Australia 8/08/2005 12:15:20 PM
Aug 07 2005
prev sibling parent reply Burton Radons <burton-radons smocky.com> writes:
Sean Kelly wrote:
 In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
 
1) Review this code:

void f( inout int i, inout int j){
 i= 1;
 j= 2;
}

# void f( inout int i, inout int j ) { # in { # assert( &i != &j ); # } # body { # i = 1; j = 2; # } Without this check, passing the same variable as both arguments will result in an undefined result, as whether the value is equal to 1 or 2 after the function call depends on the calling convention (C/D, Pascal, etc).

No, it's well-defined and independent of calling convention: inout/out parameters are defined as pointers. There is absolutely nothing wrong here and changing the language would limit the flexibility of the calling convention because you don't need to write to all inout parameters.
Aug 07 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <dd655j$13ve$1 digitaldaemon.com>, Burton Radons says...
Sean Kelly wrote:
 In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
 
1) Review this code:

void f( inout int i, inout int j){
 i= 1;
 j= 2;
}

# void f( inout int i, inout int j ) { # in { # assert( &i != &j ); # } # body { # i = 1; j = 2; # } Without this check, passing the same variable as both arguments will result in an undefined result, as whether the value is equal to 1 or 2 after the function call depends on the calling convention (C/D, Pascal, etc).

No, it's well-defined and independent of calling convention: inout/out parameters are defined as pointers. There is absolutely nothing wrong here and changing the language would limit the flexibility of the calling convention because you don't need to write to all inout parameters.

I'm not suggesting that the language be changed. But the results are unpredictable specifically because of calling convention rules. Sean
Aug 07 2005
next sibling parent reply AJG <AJG_member pathlink.com> writes:
In article <dd6erg$1am3$1 digitaldaemon.com>, Sean Kelly says...
In article <dd655j$13ve$1 digitaldaemon.com>, Burton Radons says...
Sean Kelly wrote:
 In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
 
1) Review this code:

void f( inout int i, inout int j){
 i= 1;
 j= 2;
}

# void f( inout int i, inout int j ) { # in { # assert( &i != &j ); # } # body { # i = 1; j = 2; # } Without this check, passing the same variable as both arguments will result in an undefined result, as whether the value is equal to 1 or 2 after the function call depends on the calling convention (C/D, Pascal, etc).

No, it's well-defined and independent of calling convention: inout/out parameters are defined as pointers. There is absolutely nothing wrong here and changing the language would limit the flexibility of the calling convention because you don't need to write to all inout parameters.

I'm not suggesting that the language be changed. But the results are unpredictable specifically because of calling convention rules.

Um... how exactly is the result unpredictable? I don't see how calling conventions have anything to do with this. # int i = 0, j = 0; # void f( inout int i, inout int j ) { i = 1; j = 2; } # f(i, j); # // Here i is 1, j is 2. # int i = 0; # void f( inout int i, inout int j ) { i = 1; j = 2; } # f(i, i); # // Here i is 2. The results are quite predictable, IMHO. I don't think there is anything wrong with the language. How could this result be any different? Cheers, --AJG.
Aug 07 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 8 Aug 2005 02:19:50 +0000 (UTC), AJG <AJG_member pathlink.com>  
wrote:
 # int i = 0;
 # void f( inout int i, inout int j ) { i = 1; j = 2; }
 # f(i, i);
 # // Here i is 2.

 The results are quite predictable, IMHO. I don't think there is anything  
 wrong with the language. How could this result be any different?

Is it not possible that an optimisiton compiler might re-order these statements? Is it not possible that an optimising compiler might do these operations in parrallel on a multi cpu platform? Is there any guarantee that the operations are carried out in the order they have been typed? (if so, what is the point of the volatile block statement?) http://www.digitalmars.com/d/statement.html#volatile In other words, is it possible for i to be 1 or 2 depending on what the optimising compiler decides to do and/or the enviroment the code is running in. Regan
Aug 07 2005
next sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 08 Aug 2005 14:37:16 +1200, Regan Heath wrote:

 On Mon, 8 Aug 2005 02:19:50 +0000 (UTC), AJG <AJG_member pathlink.com>  
 wrote:
 # int i = 0;
 # void f( inout int i, inout int j ) { i = 1; j = 2; }
 # f(i, i);
 # // Here i is 2.

 The results are quite predictable, IMHO. I don't think there is anything  
 wrong with the language. How could this result be any different?

Is it not possible that an optimisiton compiler might re-order these statements? Is it not possible that an optimising compiler might do these operations in parrallel on a multi cpu platform? Is there any guarantee that the operations are carried out in the order they have been typed? (if so, what is the point of the volatile block statement?) http://www.digitalmars.com/d/statement.html#volatile In other words, is it possible for i to be 1 or 2 depending on what the optimising compiler decides to do and/or the enviroment the code is running in.

Very true, however Sean was talking about "calling conventions" and that *alone* would not effect this issue. True, the order that the operations is done in is more significant than the order that the parameters are passed. -- Derek Melbourne, Australia 8/08/2005 1:08:22 PM
Aug 07 2005
prev sibling next sibling parent reply AJG <AJG_member pathlink.com> writes:
Hi,

Playing the devil's advocate, huh? ;)

On Mon, 8 Aug 2005 02:19:50 +0000 (UTC), AJG <AJG_member pathlink.com>  
wrote:
 # int i = 0;
 # void f( inout int i, inout int j ) { i = 1; j = 2; }
 # f(i, i);
 # // Here i is 2.

 The results are quite predictable, IMHO. I don't think there is anything  
 wrong with the language. How could this result be any different?

Is it not possible that an optimisiton compiler might re-order these statements?

Why on earth would it do that? You specifically told it the order you wanted them in. Such an "optimization" is erroneous. That's more like a transmogrification.
Is it not possible that an optimising compiler might do these operations  
in parrallel on a multi cpu platform?

No. Because such code is specifically not instrinsically parallelisable. If I want extra threads, I can ask for them myself, thank you, and I would make my code thread-safe. The exception to that rule is when the compiler can guarantee identical behaviour. This is not such a case, as we can see. This is similar to the const optimization Walter was talking about; he couldn't optimize that one either. There are languages that specialize in writing easily parallelisable code (via syntax). D, coming roughly from normal C/C++, is not one of them. An extended version of C, called Cilk, does just that: http://supertech.lcs.mit.edu/cilk/
Is there any guarantee that the operations are carried out in the order  
they have been typed? (if so, what is the point of the volatile block  
statement?)
http://www.digitalmars.com/d/statement.html#volatile

The guarantee (real or simulated) of order of execution is the very foundation of logical programming. If you couldn't rely on your statements being executed in the order you intended, then there is utter chaos. Btw, that's why the NON-guarantee of order of execution in parameter calls (in C) is such a hotly debated issue. It is one exception to the rule. It's a mistake, IMO, that should be fixed.
In other words, is it possible for i to be 1 or 2 depending on what the  
optimising compiler decides to do and/or the enviroment the code is  
running in.

Optimization should not alter the behaviour (intrinsic order) of such code, otherwise it is erroneous. I do not recall seeing anywhere "variable references may not refer to the same thing" but please do correct me if I'm wrong. --AJG.
Aug 07 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 8 Aug 2005 03:20:00 +0000 (UTC), AJG <AJG_member pathlink.com>  
wrote:
 Playing the devil's advocate, huh? ;)

No. Just curious. After a brief google: http://www.nationmaster.com/encyclopedia/Compiler-optimization Search for "reorder" within this page. Not sure if it applies in this case, to this code, but it is talking about compiler optimistations that reorder code. Likewise: http://www.absoluteastronomy.com/encyclopedia/c/co/compiler_optimization.htm Notably: "Compilers can schedule, or reorder, instructions so that pipeline stalls occur less frequently." Regan
Aug 07 2005
parent Sean Kelly <sean f4.ca> writes:
In article <opsu6axdoj23k2f5 nrage.netwin.co.nz>, Regan Heath says...
Notably:
"Compilers can schedule, or reorder, instructions so that pipeline stalls  
occur less frequently."

They can, but this is superceded by the "as if" rule. That is, the compiler can do whatever the heck it wants so long as the resulting code behaves in the intended fashion (assuming a single-threaded virtual machine). As the two parameters for this function are pointers, the compiler must assume that both parameters could refer to the same memory location. Sean
Aug 07 2005
prev sibling parent reply Greg Smith <greg siliconoptix.com> writes:
AJG wrote:
Regan Heath:
AJG:

# int i = 0;
# void f( inout int i, inout int j ) { i = 1; j = 2; }
# f(i, i);
# // Here i is 2.

The results are quite predictable, IMHO. I don't think there is anything  
wrong with the language. How could this result be any different?



Is it not possible that an optimisiton compiler might re-order these  
statements?

Why on earth would it do that? You specifically told it the order you wanted them in. Such an "optimization" is erroneous. That's more like a transmogrification.

the result. If i and j are local variables, it can prove this and reorder. If they are passed-in references, possibly to the same place, it clearly can't and won't. In general, if it wants to put a *test* in there to see if things overlap before doing some weird optimized thing, it can do that too (I think gcc4 will do that in vectorized code under some circumstances).
 
 There are languages that specialize in writing easily parallelisable code (via
 syntax). D, coming roughly from normal C/C++, is not one of them. An extended
 version of C, called Cilk, does just that:
 
 http://supertech.lcs.mit.edu/cilk/
 

more clear to me that C's biggest weakness (for certain purposes, anyhow) is that, because it lets you do so much, without being explicit about what you really *intend*, the compiler really can't infer too much about what you're specifically asking for; much of what your code asks for is not actually important. In the example, if you know that i and j will never refer to the same thing, (and there's no thread issues) then the order doesn't matter. But you have to write one before the other, and the C (or D) compiler will have to do them in that order. So a lot of optimizations which you'd like to happen become unsafe and don't get done. Contrast to fortran: there are very few ways to manipulate a fortran array. This means coding tends to be long and tedious, but the compiler can infer what you are doing more easily.
 
Is there any guarantee that the operations are carried out in the order  
they have been typed? (if so, what is the point of the volatile block  
statement?)
http://www.digitalmars.com/d/statement.html#volatile

The guarantee (real or simulated) of order of execution is the very foundation of logical programming. If you couldn't rely on your statements being executed in the order you intended, then there is utter chaos.

key issue a bit. 'real or simulated' is the key thing. Statements aren't always executed in the order you wrote them, but the results must be indistinguishable. For instance if the example were: void f( inout int i, inout int j, int k, int m ) { i = 22 + k*m - (k<<3); j = (k*m & 0x3FF)- (k^1); } ... then the evaluation of the two expressions could of course be overlapped and interleaved, and k*m done once only, etc, but the write to i still needs to take place before the write to j. So they're not really being executed as you wrote them, but the results are the same.
 
 Btw, that's why the NON-guarantee of order of execution in parameter calls (in
 C) is such a hotly debated issue. It is one exception to the rule. It's a
 mistake, IMO, that should be fixed.

that the order of calls in f(x) + g(x) is undefined. The cost (in terms of code efficiency) of breaking up your expressions to eliminate problems with side-effects has basically gone to zero. I.e. consider func(2*i,i++,f2(i)); /* not predictable, some wish it were */ vs. int f2res = f2(i+1) func(2*i, i, f2res ); /*assuming this is what you wanted :-)*/ i++; The second one is predictable, won't warp your brain, and should not be any less efficient with a modern compiler (as opposed to a 20-year C compiler, where it could be somewhat slower). An expression is really just a flat description of a tree, I've never seen any reason why the different parts of the tree should be evaluated in any order (except as specified for || etc, of course), especially when the compiler can make better code by doing it in some particular order. Again, the more implicit control you give to the programmer, the more you are tying the compiler's hands in all the many cases where that control isn't actually needed. Having said that - If C were going to be designed again - we could say, "Hey, if the compiler wants to evaluate function parms in some other order, then it can darn well prove first that it makes no difference relative to the language-specified order, and if it can't prove that, it can't reorder". This should still leave a lot of code reorderable. The state of compiler technology when C was designed was such that this would be an unreasonable limitation, equivalent to banning reordering. Even now, I'm guessing there would be a lot of cases where a compiler would need to generate extra code to evaluate them in order, even when it actually makes no difference, because it can't prove that. And, unfortunately, if K&R had been forced to define an order, I suspect they would have gone with right-to-left (stack-push order) which is probably why they didn't. On some machines (especially older ones) there could be a serious penalty for left-to-right evaluation.
 
In other words, is it possible for i to be 1 or 2 depending on what the  
optimising compiler decides to do and/or the enviroment the code is  
running in.

Optimization should not alter the behaviour (intrinsic order) of such code, otherwise it is erroneous. I do not recall seeing anywhere "variable references may not refer to the same thing" but please do correct me if I'm wrong.

As I see it, the solution is this: if you write a function where two or more reference parameters could refer to the same thing, and at least one of them is non-const, you should make an explicit note to potential users of the function as to whether that's allowed, and what the results are. Sometimes it's quite reasonable to pass the same thing in twice, and, by careful ordering of the writes (& reads) you can arrange to get the expected result. If you intend to *call* such a function with overlapped operands, and the author has *not* made such a note, then you're on your own... Even if the behaviour happens to be what you want, there's no contract that it won't change. Example (in c): - Greg
Aug 08 2005
next sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
Greg Smith <greg siliconoptix.com> wrote:

[ well thought arguments]

Applause.

-manfred
Aug 08 2005
prev sibling parent reply AJG <AJG_member pathlink.com> writes:
Hi,

 There are languages that specialize in writing easily parallelisable code (via
 syntax). D, coming roughly from normal C/C++, is not one of them. An extended
 version of C, called Cilk, does just that:
 
 http://supertech.lcs.mit.edu/cilk/
 


Sure thing.
It is becoming more and 
more clear to me that C's biggest weakness (for certain purposes, 
anyhow) is that, because it lets you do so much, without being explicit 
about what you really *intend*, the compiler really can't infer too much 
  about what you're specifically asking for; much of what your code asks 
for is not actually important. In the example, if you know that i and j 
will never refer to the same thing, (and there's no thread issues) then 
the order doesn't matter. But you have to write one before the other, 
and the C (or D) compiler will have to do them in that order.

That's why I'm a fan of more "dense" expressions. The less fluff the better. Of course, that's not to say sacrifice readability gratuitously.
So a lot of optimizations which you'd like to happen become unsafe and 
don't get done. Contrast to fortran: there are very few ways to 
manipulate a fortran array. This means coding tends to be long and 
tedious, but the compiler can infer what you are doing more easily.

Have you checked restricted C pointers? They accomplish some of this, but are hella ugly.
Is there any guarantee that the operations are carried out in the order  
they have been typed? (if so, what is the point of the volatile block  
statement?)
http://www.digitalmars.com/d/statement.html#volatile

The guarantee (real or simulated) of order of execution is the very foundation of logical programming. If you couldn't rely on your statements being executed in the order you intended, then there is utter chaos.

key issue a bit. 'real or simulated' is the key thing. Statements aren't always executed in the order you wrote them, but the results must be indistinguishable.

You are right, I did gloss over it. My IA32 is a little rusty ;). And the "simulated" part can get _real_ gory under the hood.
 Btw, that's why the NON-guarantee of order of execution in parameter calls
 C) is such a hotly debated issue. It is one exception to the rule. It's a
 mistake, IMO, that should be fixed.

that the order of calls in f(x) + g(x) is undefined. The cost (in terms of code efficiency) of breaking up your expressions to eliminate problems with side-effects has basically gone to zero. I.e. consider func(2*i,i++,f2(i)); /* not predictable, some wish it were */

Myself included.
vs.
       int f2res = f2(i+1)
       func(2*i, i, f2res );	/*assuming this is what you wanted :-)*/
       i++;

The second one is predictable, won't warp your brain, and should not be 
any less efficient with a modern compiler (as opposed to a 20-year C 
compiler, where it could be somewhat slower).

Hehehe... I'm a fan of the first one, to be honest. The second one has some of that fluff I was talking about. ;)
An expression is really just a flat description of a tree, I've never 
seen any reason why the different parts of the tree should be evaluated 
in any order (except as specified for || etc, of course), especially 
when the compiler can make better code by doing it in some particular 
order. Again, the more implicit control you give to the programmer, the 
more you are tying the compiler's hands in all the many cases where that 
control isn't actually needed.

You're probably right. Though I'm not sure what else could be done syntax-wise to help.
And, unfortunately, if K&R had been forced to define an order, I suspect 
they would have gone with right-to-left (stack-push order) which is 
probably why they didn't. On some machines (especially older ones) there 
could be a serious penalty for left-to-right evaluation.

Ah, yes. This delicious relic still haunts me at night.
In other words, is it possible for i to be 1 or 2 depending on what the  
optimising compiler decides to do and/or the enviroment the code is  
running in.

Optimization should not alter the behaviour (intrinsic order) of such code, otherwise it is erroneous. I do not recall seeing anywhere "variable references may not refer to the same thing" but please do correct me if I'm wrong.

As I see it, the solution is this: if you write a function where two or more reference parameters could refer to the same thing, and at least one of them is non-const, you should make an explicit note to potential users of the function as to whether that's allowed, and what the results are. Sometimes it's quite reasonable to pass the same thing in twice, and, by careful ordering of the writes (& reads) you can arrange to get the expected result. If you intend to *call* such a function with overlapped operands, and the author has *not* made such a note, then you're on your own... Even if the behaviour happens to be what you want, there's no contract that it won't change.

I think we're back to restricted pointers here. IIRC, the compiler trusts the programmer not to send the same thing twice. I wish there were automated ways to check for this kind of thing. Walter said it's not possible "in the general case," so it always kinda comes down to trusting the programmer. Cheers, --AJG.
Aug 08 2005
next sibling parent Derek Parnell <derek psych.ward> writes:
On Tue, 9 Aug 2005 03:00:49 +0000 (UTC), AJG wrote:


[snip]
 I think we're back to restricted pointers here. IIRC, the compiler trusts the
 programmer not to send the same thing twice. I wish there were automated ways
to
 check for this kind of thing. Walter said it's not possible "in the general
 case," so it always kinda comes down to trusting the programmer.

Well... there are ways of automating it, but just not at compile time ;-) -- Derek Melbourne, Australia 9/08/2005 1:15:00 PM
Aug 08 2005
prev sibling parent Greg Smith <greg siliconoptix.com> writes:
AJG wrote:

 Hi,
As I see it, the solution is this: if you write a function where two or 
more reference parameters could refer to the same thing, and at least 
one of them is non-const, you should make an explicit note to potential 
users of the function as to whether that's allowed, and what the results 
are. Sometimes it's quite reasonable to pass the same thing in twice, 
and, by careful ordering of the writes (& reads) you can arrange to get 
the expected result. If you intend to *call* such a function with 
overlapped operands, and the author has *not* made such a note, then 
you're on your own... Even if the behaviour happens to be what you want, 
there's no contract that it won't change.

I think we're back to restricted pointers here. IIRC, the compiler trusts the programmer not to send the same thing twice. I wish there were automated ways to check for this kind of thing. Walter said it's not possible "in the general case," so it always kinda comes down to trusting the programmer.

Here's a very specific case. Suppose you want to write a function which negates a bunch of int16's and stores them to a separate array (or possibly back to the same place). The core might be for( int i = 0; i < n; ++i ){ d[i]= -s[i];} /* (1) */ A common trick to make this go a little faster, by reducing loop overhead (assume n is a multiple of 4), is: for( int i = 0; i < n; i+=4 ){ /* (2) */ d[i]= -s[i]; d[i+1] = -s[i+1]; d[i+2] = -s[i+2]; d[i+3] = -s[i+3]; } Some processors have zero-overhead loop modes, which means the above won't help. What can be surprisingly helpful is: for( int i = 0; i < n; i+= 4 ){ /* (3) */ int16 d0 = -s[i]; int16 d1 = -s[i+1]; int16 d2 = -s[i+2]; int16 d3 = -s[i+3]; d[i] = d0; d[i+1] = d1; d[i+2] = d2; d[i+3] = d3; } The transformation from (2) to (3) may change the results, but only if the src and dst buffers are overlapping and non-identical. But it can result in a noticeable speedup on some machines. What happens is, you are separating the four reads from the four writes, and giving the compiler explicit permission to overlap the four read/negate/write operations. It can't do this itself, since the writes to d[] may affect the reads from s[]. On x86 this doesn't help, I think; it deals with overlapping on-the-fly in silicon.(it might actually make things much worse on an x86, since the compiler has to read all four s[] values before writing any d[] values, and there's not enough registers to store them all, and 'i' and the pointers. So there may be copies done to the stack, which the silicon then tries to optimize out. ick. Of course, you'd really want to use MMX in this particular case). On a Risc processor, typically there are effect latencies after load operations, and lots of regs to store concurrent operations, so (3) can make a very noticeable difference in inner loops. The code can "read t0=s[i], read t1=s[i+1], neg d0=-t0, neg d1=-t1," etc. The interesting thing is, in (3), you're not telling the compiler how to optimize it (as was the case with the good ol' *d++ = *s++, for PDP-11's or 68K); you are simply giving it some space in which to work. The compiler's instruction scheduler knows all the effect latencies of different operations, but it can't make use of them if there's only one thread of evaluation to schedule. In (2), each d[]=-s[] has to be completed before the next one starts. However, I really don't like having to do this. It's a quite machine-specific thing, which is exactly what compilers are supposed to take care of. But they can't, since the language doesn't give you a way to tell the compiler that s and d are independent. The best you can say, is that it's better than writing it in assembler, and allows you to get close to the same result, if you have a good optimizer to work with. Some compilers have extensions which allow you to state that 's' and 'd' are 'noalias' pointers. This gives the compiler permission to make optimizations that affect the results only when the buffers overlap; which means it becomes possible for a compiler to see the code in (1) and actually generate the code in (3), or something else, according to the underlying machine. This is the kind of improvement I'd really like to see over C. (but I'm aware that, unless they are writing speed-critical signal processing code, most programmers won't see much point to this). At first, this seems dangerous, since somebody might call the function with overlapping buffers, and the optimization would change the effect of the code. But, of course, you can write functions in ordinary C or D which work fine on non-overlapping buffers and blow up when overlapping buffers are supplied. You really aren't any further behind, it's still, as you say, a question of trusting the programmer to make their own rules and follow them. I'm not sure this kind of extension is a good idea for C++ or D; there are more hidden things going on, and it's harder for the programmer to understand where the real dangers for aliasing are (e.g, a function parameter might point to the same memory pointed to by a member variable of 'this') Another issue, a big one, is that C compilers generally need to assume that all function calls can change any data, except for local variables whose addresses have not been made known. Clearly this inhibits optimization of operations on global variables, or operations on memory pointed at by local variables, both of which are extremely common. A little-appreciated benefit of C++ (or gcc C) inline functions is that the inlined code becomes part of the data-flow analysis in the calling function; so any side-effects of that code are known, rather than assumed worst case. This benefit can be greater than that arising from the elimination of the call overhead. I feel that this sort of thing is the brick wall up against which C code optimization finds itself. It's been a very long time since I used fortran, but, in that language, the compiler generally can know exactly what you are doing with arrays (since you can do a lot less), so optimizations like (1)->(3) are at least possible in theory. I think the original appeal of C came from the fact that it let you do your own optimizations when the compiler couldn't; programmers, at that time, generally switched from assembler to C, and were quite happy using pointers etc. Now that programmers are more concerned with higher-level problems (esp. maintainabily) and not code efficiency, advanced code optimization technology is much more important, and C's flexibity is holding it back IMO. C's relatively recent alias rules change (gcc 3 knows about them, gcc 2 didn't) are a step in the right direction, anyway. Basically, the compiler is allowed to assume, for instance, that a 'double *' and 'int *' can't point at the same thing. --Greg
Aug 10 2005
prev sibling next sibling parent Dave <Dave_member pathlink.com> writes:
In article <opsu538ejr23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Mon, 8 Aug 2005 02:19:50 +0000 (UTC), AJG <AJG_member pathlink.com>  
wrote:
 # int i = 0;
 # void f( inout int i, inout int j ) { i = 1; j = 2; }
 # f(i, i);
 # // Here i is 2.

 The results are quite predictable, IMHO. I don't think there is anything  
 wrong with the language. How could this result be any different?

Is it not possible that an optimisiton compiler might re-order these statements?

No - C aliasing rules (which D uses) prohibit that. Google for documents describing Restricted Pointers for C99.
Is it not possible that an optimising compiler might do these operations  
in parrallel on a multi cpu platform?

No - same reason as above.
Is there any guarantee that the operations are carried out in the order  
they have been typed? (if so, what is the point of the volatile block  
statement?)
http://www.digitalmars.com/d/statement.html#volatile

Basically, the parameters i and j in the OP are treated as 'volatile' by the compiler - in a manner of speaking - inside the function because of the aliasing rules followed by D (and C, C++, Pascal, etc.). Because they /can/ refer to the same memory, the memory that each refers to has to be updated before reads and after writes.
In other words, is it possible for i to be 1 or 2 depending on what the  
optimising compiler decides to do and/or the enviroment the code is  
running in.

No - the D language follows the same aliasing rules that C, C++ and the Pascal derivitives do, along with just about any other language that can pass parameters byref. One notable exception is Fortran. D won't (well, shouldn't) act any different than C/C++ given the equivalent C/C++ code in the OP - If the same variable is passed in for both parameters, it will be '2' after the function returns.
Regan

Aug 07 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <opsu538ejr23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Mon, 8 Aug 2005 02:19:50 +0000 (UTC), AJG <AJG_member pathlink.com>  
wrote:
 # int i = 0;
 # void f( inout int i, inout int j ) { i = 1; j = 2; }
 # f(i, i);
 # // Here i is 2.

 The results are quite predictable, IMHO. I don't think there is anything  
 wrong with the language. How could this result be any different?

Is it not possible that an optimisiton compiler might re-order these statements? Is it not possible that an optimising compiler might do these operations in parrallel on a multi cpu platform?

Good point. Though I'm not sure that an optimizer could legally reorder these assignments, as it can't be sure poth pointers won't be referring to the same memory location. I think this is one reason 'restrict' was added to the C spec--it allows the optimizer to do things it otherwise couldn't.
Is there any guarantee that the operations are carried out in the order  
they have been typed? (if so, what is the point of the volatile block  
statement?)
http://www.digitalmars.com/d/statement.html#volatile

In other words, is it possible for i to be 1 or 2 depending on what the  
optimising compiler decides to do and/or the enviroment the code is  
running in.

I'm fairly sure that the IA-32 spec guarantees writes will occur in process order--it's reads and writes that might be arranged with respect to one another (the IA-32 spec isn't completely clear on this). I suppose some architecture might not provide this guarantee, but I would think that two writes to the same memory location would still not be rearranged by such a CPU. And for what it's worth, 'volatile' is intended to prevent the optimizer from rearranging reads and writes in a code block, but it has no impact on CPU behavior. Sean
Aug 07 2005
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <dd6fg6$1b70$1 digitaldaemon.com>, AJG says...
I'm not suggesting that the language be changed.  But the results are
unpredictable specifically because of calling convention rules.

Um... how exactly is the result unpredictable? I don't see how calling conventions have anything to do with this.

I was being dumb. The result depends on the order of assignments in the function. And this is something that could easily change during maintenance. Sean
Aug 07 2005
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 8 Aug 2005 02:08:48 +0000 (UTC), Sean Kelly wrote:

 In article <dd655j$13ve$1 digitaldaemon.com>, Burton Radons says...
Sean Kelly wrote:
 In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
 
1) Review this code:

void f( inout int i, inout int j){
 i= 1;
 j= 2;
}

# void f( inout int i, inout int j ) { # in { # assert( &i != &j ); # } # body { # i = 1; j = 2; # } Without this check, passing the same variable as both arguments will result in an undefined result, as whether the value is equal to 1 or 2 after the function call depends on the calling convention (C/D, Pascal, etc).

No, it's well-defined and independent of calling convention: inout/out parameters are defined as pointers. There is absolutely nothing wrong here and changing the language would limit the flexibility of the calling convention because you don't need to write to all inout parameters.

I'm not suggesting that the language be changed. But the results are unpredictable specifically because of calling convention rules.

I don't understand that. Why would the order of the addresses matter, its the order of the executions that matter. In this example, assuming that both pointed to the same RAM location, one of the address locations would get set to 1, then the other address location would get set to 2; in that order. It wouldn't matter which address location got used first. -- Derek Melbourne, Australia 8/08/2005 12:22:12 PM
Aug 07 2005
prev sibling next sibling parent reply Oskar <Oskar_member pathlink.com> writes:
In article <dd3mon$2eds$1 digitaldaemon.com>, Manfred Nowak says...
1) Review this code:

void f( inout int i, inout int j){
  i= 1;
  j= 2;
}


2) Pass or Fail?

Pass. The call f(i,i) should probably fail to compile. It is atleast undefined.
3) Explain.

This is another reason to forbid aliasing between function arguments. (i.e. implicit "restrict" on all function parameters.) IIRC this was suggested by Walter a while back. That would define the call f(i,i) as illegal. (But not always checkable at compile time). It would also allow for more optimizations... *ducks* /O
Aug 09 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

2) Pass or Fail?


Why would it fail to compile? The call is _not_ undefined. If we take this step by step, the function: FIRST : sets param1 (i) to 1. SECOND: sets param2 (j) to 2. If param1 happens to also be param2 (&i == &j), then that one, single variable will: FIRST : Be set to 1. SECOND: Be set to 2. Why does everybody keep saying this is undefined? It is _perfectly_ well defined. I know _exactly_ what it will do, and it will always do the same thing. No calling convention or (correct) optimization will alter this fact. If you consider the function/call to be semantically confusing, then that's another thing entirely. Anyway, please, the code the OP posted is: A) Not undefined. B) Not unstable. C) Not unpredictable. D) Not inherently unsafe. E) Not a "fault" of the language. F) Not even remotely related to application-level security. Consider this the "punchline" of this whole pseudo-ironic joke.
3) Explain.

This is another reason to forbid aliasing between function arguments. (i.e. implicit "restrict" on all function parameters.) IIRC this was suggested by Walter a while back.

Ah. Now we are getting somewhere because this changes the rules of the game. Sure, if you say "no aliased pointers/references," then the OP's code becomes illegal/undefined. But as it stands, that is _not_ the case. FWIW, I like the idea of implicit restrict. It could even help with the "const" optimization that Walter mentioned, which was also due to aliasing.
That would define the call f(i,i) as illegal. (But not always checkable at
compile time).

Yes. This could get tricky. If people aren't careful, it could lead to some really interesting bugs down the road. Then again, I've never sent aliased pointers/references. So in the end, I am for it. Perhaps there could be an implicit run-time check (in -debug) to see if the restrict contract was broken at call time. This would help a lot against the subtle bugs.
It would also allow for more optimizations... *ducks* 

I like this kind of thinking ;). Cheers, --AJG.
Aug 09 2005
parent reply Oskar <Oskar_member pathlink.com> writes:
In article <ddb17v$b8k$1 digitaldaemon.com>, AJG says...
Hi,

2) Pass or Fail?


Why would it fail to compile? The call is _not_ undefined.

Is the spec clear on that inout arguments are passed as pointers? Is it unthinkable to pass them as (caller save) registers and let the caller then do the assignment to i in the f(i,i) case? (Unthinkable if you want aliased parameters to work) I just can't find it in the spec. Therefore undefined.
FWIW, I like the idea of implicit restrict. It could even help with the "const"
optimization that Walter mentioned, which was also due to aliasing.

Perhaps there could be an implicit run-time check (in -debug) to see if the
restrict contract was broken at call time. This would help a lot against the
subtle bugs.

Yes. It would probably catch most cases, though I guess it is possible to make obscure ones where such a check would fail too. /O
Aug 10 2005
parent AJG <AJG_member pathlink.com> writes:
Hi,

2) Pass or Fail?


Why would it fail to compile? The call is _not_ undefined.

Is the spec clear on that inout arguments are passed as pointers? Is it unthinkable to pass them as (caller save) registers and let the caller then do the assignment to i in the f(i,i) case? (Unthinkable if you want aliased parameters to work) I just can't find it in the spec. Therefore undefined.

No, that would be an "optimization," and it would an an erroneous one at that, because it's based on an incorrect assumption. Nowhere does it say pointers/references can't be aliased. Therefore, defined.
FWIW, I like the idea of implicit restrict. It could even help with the "const"
optimization that Walter mentioned, which was also due to aliasing.

Perhaps there could be an implicit run-time check (in -debug) to see if the
restrict contract was broken at call time. This would help a lot against the
subtle bugs.

Yes. It would probably catch most cases, though I guess it is possible to make obscure ones where such a check would fail too.

Perhaps. To make it more robust the whole program could be statically analyzed for restriction violations, but this also only works if enough info is available. Sigh... Cheers, --AJG.
Aug 10 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:

 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }
 
 2) Pass or Fail?
 
 3) Explain.
 
 -manfred

A colleague has suggested that all we need is to be able to specify both compile-time and run-time checks for overlapping arguments in order to capture unexpected situations whenever we use 'inout' arguments. We need run-time checks because such a function could be incorporated into a dll or closed-source library, where not all source is available at compile time and/or link time. We need compile-time checks because there are some situations in which a run-time check is too late, such as when lives are at stake. -- Derek (skype: derek.j.parnell) Melbourne, Australia 12/08/2005 9:41:53 AM
Aug 11 2005
parent Dave <Dave_member pathlink.com> writes:
In article <1mlum6oxxo3p7.1n415a8bwdrzr$.dlg 40tude.net>, Derek Parnell says...
On Sun, 7 Aug 2005 01:05:27 +0000 (UTC), Manfred Nowak wrote:

 1) Review this code:
 
 void f( inout int i, inout int j){
   i= 1;
   j= 2;
 }
 
 2) Pass or Fail?
 
 3) Explain.
 
 -manfred

A colleague has suggested that all we need is to be able to specify both compile-time and run-time checks for overlapping arguments in order to capture unexpected situations whenever we use 'inout' arguments. We need run-time checks because such a function could be incorporated into a dll or closed-source library, where not all source is available at compile time and/or link time. We need compile-time checks because there are some situations in which a run-time check is too late, such as when lives are at stake.

IMO - I would think runtime checks for overlapping references would be Ok, plus a simple compile-check check to see if the same variable was being passed for more than one argument. So it would be like this: - Pointers stay as-is - Non -release runtime check of the same reference passed for more than one param. of the same type. - Non -release runtime check for overlapping arrays (what would be a good algorithm for checking for overlapping multi-dim arrays or a combination of single and multi-dim arrays of the same type passed?). As far as aliasing goes, that still leaves the case of: int i; void foo(inout int param) { i++; param++; } void main() { foo(i); } But at least the above criteria for overlapping parameters would be useful, IMO. - Dave
-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
12/08/2005 9:41:53 AM

Aug 12 2005