Before 123
In Test 123
After 123
But if use -gt options (or "Trash local variables" in Lazarus/Options/Debugging), than behavior is changed. On my machine:Before 123
In Test 1431655765
After 1431655765
I want to determine if this option is "on" at compile time to issue a warning that this can cause errors. Ideally, disable this option locally.
Remark: Note that specifying const is a contract between the programmer and the compiler. It is the programmer who tells the compiler that the contents of the const parameter will not be changed when the routine is executed, it is not the compiler who tells the programmer that the parameter will not be changed.
"Out O" also is passed by ref. That means if you pass the same variable to R and O, then you are not allowed to change O (because it would change R.Nice observation. I totally missed that he was passing the same variable for both parameters.
With -gt you change O.That I don't understand. -gt is supposed to trash locals not arguments. I don't see how or why the -gt switch would affect O (or any of the arguments for that matter, since they are not local variables, unless FPC considers arguments local variables, which would not make any sense.)
The error you get is a warning that it is wrong to pass the same variable as R and O.But, when his example is compiled in 32bit, everything works fine (as I believe it should, in spite of his breaking the implied contract with the const parameter, since there are no locals in his function.)
That I don't understand. -gt is supposed to trash locals not arguments.
But, when his example is compiled in 32bit, everything works fine (as I believe it should, in spite of his breaking the implied contract with the const parameter, since there are no locals in his function.)No it does not! If it worked for you that is by accident.
No it does not! If it worked for you that is by accident.Your "example" only shows that the compiler generates different code for a different CPU. That's what you're calling an accident.
Also note that out implies local.That statement implies something very different. Locals are variables for which space is reserved when setting up the stack frame. All parameters, whether var, out, or value, unless they are being passed in registers, are already on the stack _before_ the stack frame is set up, which means they are most definitely not locals (they could be locals in the caller's frame but certainly not in the callee's.)
trashlocalsdelphi.dpr.29: begin
00405A7C 55 push ebp
00405A7D 8BEC mov ebp,esp
00405A7F 51 push ecx
trashlocalsdelphi.dpr.30: AVar := $ABCDEF12;
00405A80 C745FC12EFCDAB mov [ebp-$04],$abcdef12 <- THAT is a local
negative offset from ebp.
trashlocalsdelphi.dpr.34: Writeln('In Test ', R.F);
00405A87 A118784000 mov eax,[$00407818]
00405A8C BAC45A4000 mov edx,$00405ac4
00405A91 E802E5FFFF call @Write0UString
00405A96 8B5508 mov edx,[ebp+$08]
00405A99 E81AE3FFFF call @Write0Long
00405A9E E8F5E5FFFF call @WriteLn
00405AA3 E860D3FFFF call @_IOTest
trashlocalsdelphi.dpr.37: O := R;
00405AA8 8B450C mov eax,[ebp+$0c] <- parameters, positive
00405AAB 8B5508 mov edx,[ebp+$08] <- offset from ebp (unless in a register)
00405AAE 8910 mov [eax],edx
trashlocalsdelphi.dpr.40: end;
00405AB0 59 pop ecx
00405AB1 5D pop ebp <- tear down the frame
00405AB2 C20800 ret $0008 <- get rid of arguments
Your statement implies that var parameters are also locals,
Also note that out implies local.No they are not. But as Marcov wrote, they (like locals) have no defined values when the procedure is entered.
Var parameters have a defined value on entry. OUT parameters not, hence that the fuzzer also randomizes them.
If anything, the doc may need an update to mention this.I was typing my reply while you posted yours. I wholeheartedly agree with your suggestion.
You are using "const" incorrectly.The const parameter in the procedure is not changed.
...
You must make sure, that while you are in "Test" nothing will change "R".
"Out O" also is passed by ref. That means if you pass the same variable to R and O, then you are not allowed to change O (because it would change R.I don't agree. It's just a side effect. If the procedure is written correctly, there is no problem. I have never seen such a restriction.
With -gt you change O.
And besides, it's inconsistent.Consistency, as you pointed out, that's the real problem with having -gt fuzz out parameters.
This is the code that runs the same regardless of option -gtThat always works because you're not assigning one parameter to the other. You are returning a value, which is not dependent on the other parameter, that should make -gt happy and, as expected, it does.Code: [Select]{$APPTYPE CONSOLE}
var
Vglb: Integer = 1;
procedure Test(const Vin: Integer; out Vout: Integer);
begin
Vout := 2;
Writeln('In test: ', Vin);
end;
begin
Test(Vglb, Vglb);
Writeln('After: ', Vglb);
Readln;
end.
Microsoft chose to use $CC, which is the opcode for int 3. If the stack gets corrupted, when the ret is executed, it's likely, though, not guaranteed of course, that the code will end up executing a breakpoint, which is good because, it's consistent.
It only shows you did not provide full information.....Be careful... Rubbish in, rubbish out.. Popper's falsification hits again... 8-) 8-) :PNo it does not! If it worked for you that is by accident.Your "example" only shows that the compiler generates different code for a different CPU. That's what you're calling an accident.
OK, lets so:And besides, it's inconsistent.Consistency, as you pointed out, that's the real problem with having -gt fuzz out parameters.
That always works because you're not assigning one parameter to the other. You are returning a value, which is not dependent on the other parameter, that should make -gt happy and, as expected, it does.
It only shows you did not provide full information.....Be careful... Rubbish in, rubbish out.. Popper's falsification hits again... 8-) 8-) :PNo it does not! If it worked for you that is by accident.Your "example" only shows that the compiler generates different code for a different CPU. That's what you're calling an accident.
BTW: on a 64 bit windows (10.2 developer preview, running updates) , it gets also trashed for a 32 bit executable, so test more carefully. (I suspect you forgot -gt.....you actually deserve a grumpy)
It is not an accident.
And besides, it's inconsistent.Consistency, as you pointed out, that's the real problem with having -gt fuzz out parameters.
It may be a side-effect, but that side effect changed the const param (if it is passed by ref). And (according to the doc) you promised to the compiler, that you would write the code in such way that this would not happen (neither directly nor by side effect).Quote"Out O" also is passed by ref. That means if you pass the same variable to R and O, then you are not allowed to change O (because it would change R.I don't agree. It's just a side effect.
With -gt you change O.
And as I said, I'm not the one who called Test(R, R), but the one who writes the Test() procedure. She looks correct. What to do to make it work correctly?I don't see a clean way of reliably getting around the stack and parameter fuzzer. I can suggest a horrible hack in assembler but, "horrible" actually falls short of describing it accurately. Are you even allowed to use assembler in your test routine ?
It's quite unfortunate that you don't have something better to do than whine and expose your ignorance in public.Oh, well...< people who do understand this is a valid one: >:D >:D >:D >:D >:D >
I don't see a clean way of reliably getting around the stack and parameter fuzzer. I can suggest a horrible hack in assembler but, "horrible" actually falls short of describing it accurately. Are you even allowed to use assembler in your test routine ?Well, that is called a Stack Canary... (google for it I am fed up with you (at the moment)).
use "constref" if you always want by reference.Once again, the problem is that in code that does not know how it will be used (with the same parameter or not) no protection (warn at compile time) that it cannot be used when using the -gt option.
...
It may be a side-effect, but that side effect changed the const param (if it is passed by ref). And (according to the doc) you promised to the compiler, that you would write the code in such way that this would not happen (neither directly nor by side effect).
I respect your knowledge, but it is not a side-effect. It is the consequence of using out. And that is also basic.Rather the unexpected behavior of the -gt option for out parameters. More precisely, the impossibility to avoid this behavior. After all, this does not happen for var parameters.
Constref was about the inconsistency (32 vs 64 bit results).use "constref" if you always want by reference.Once again, the problem is that in code that does not know how it will be used (with the same parameter or not) no protection (warn at compile time) that it cannot be used when using the -gt option.
...
It may be a side-effect, but that side effect changed the const param (if it is passed by ref). And (according to the doc) you promised to the compiler, that you would write the code in such way that this would not happen (neither directly nor by side effect).
Constref does not solve the problem, and there are no compiler warnings.
As I understand now there is no such way, and if someone gets an obvious error using my code, then I will explain to him that so use the procedure is not very good, because it is not reflected in the language and documentation as a bad side effect.The problem that may not be documented (well it may, but I am not aware off) is how the "out" param works.
As for solving your "Test", do not use any const at all.Structure may be big, and input parameter not changed.
But there is no saying that the exact same code, without -gt will work in future versions of fpc. Maybe some optimization that future fpc will apply makes this exact same code crash.Do you think this code is a problematic code?
The problem is in the code (unless you stick with current fpc forever), with or without -gt. -gt just makes you aware of the problem.
As I understand now there is no such way, and if someone gets an obvious error using my code, then I will explain to him that so use the procedure is not very good, because it is not reflected in the language and documentation as a bad side effect.My tests shows he has a rather liberate opinion of truth... He did not test. I did.
Do you think this code is a problematic code?No. It is not correct. The compiler should make a copy in this case. (or warn).In fact, the procedure can be quite correctly handle the situation when the const and out parameter is the same (the same memory area), but to report this to the options -gt fails.
procedure Test(const R: TR; out O: TR); begin O := R; end;
You misquoted. I did not say, what you attributed to me. I myself quoted it (in order to reply to it), and it was marked as such.As I understand now there is no such way, and if someone gets an obvious error using my code, then I will explain to him that so use the procedure is not very good, because it is not reflected in the language and documentation as a bad side effect.My tests shows he has a rather liberate opinion of truth... He did not test. I did.
In current fpc, or in all future?In fact, the procedure can be quite correctly handle the situation when the const and out parameter is the same (the same memory area), but to report this to the options -gt fails.
procedure Test(const R: TR; out O: TR); begin O := R; end;
And (according to the doc) you promised to the compiler, that you would write the code in such way that this would not happen (neither directly nor by side effect).
And (according to the doc) you promised to the compiler, that you would write the code in such way that this would not happen (neither directly nor by side effect).
Martin,
What you point out in that statement is absolutely correct BUT, it's not only the user that has to respect that promise, the compiler is the first one that is expected to honor that promise. The -gt switch causes the compiler to emit code that overwrites the const parameter, thereby causing all the problems.
-gt causes the compiler to break the promise it expects the user to uphold. What that switch is doing, admittedly with good intentions, is clearly incorrect.
Well, that only comes into play, if the the "out param" is otherwise never written too.
So yes, if I declare an out param, to which I never write, then it is this switch that brakes my promise -> alternative reading: I broke the promise by using the switch.No, the switch breaks the promise whenever the function/procedure is called with both arguments referring to the same variable. In that case, which is the case Serge has demonstrated, that switch breaks the promise before the user has a chance to.
But if I declare an out param, I am likely to write to it, am I not? (Yeah, other cases can be constructed, like check that the const param is different before writing the out param / But even that breaks, see my prev post).No because the programmer isn't creating a problem by writing the same value onto itself (the promise is kept that way). -gt on the other hand, tramples on the constant by overwriting it with an arbitrary value of its choice.
If indeed I am writing to the out param, the all -gt does is, it pulls the broken promise to the top of the procedure, so it is easier to spot.
If "TR" include ref counted types (or ever will in future):1. If (as Spartans :))
- const will not increase the ref count
- out may clear refcounted data before it passes it in (it may not always do, not sure, but afaik it can)
That is the entire intent of -gt.Well, that only comes into play, if the the "out param" is otherwise never written too.No, because the code that implements the -gt switch will have corrupted the parameters before the user/programmer gets a chance to do it.
Again, so what? Goal archived, the user knows there is an error. Better than shipping a broken app, and learn from your customer that it is broken.So yes, if I declare an out param, to which I never write, then it is this switch that brakes my promise -> alternative reading: I broke the promise by using the switch.No, the switch breaks the promise whenever the function/procedure is called with both arguments referring to the same variable. In that case, which is the case Serge has demonstrated, that switch breaks the promise before the user has a chance to.
<repeated>But if I declare an out param, I am likely to write to it, am I not? (Yeah, other cases can be constructed, like check that the const param is different before writing the out param / But even that breaks, see my prev post).No because the programmer isn't creating a problem by writing the same value onto itself (the promise is kept that way). -gt on the other hand, tramples on the constant by overwriting it with an arbitrary value of its choice.
If indeed I am writing to the out param, the all -gt does is, it pulls the broken promise to the top of the procedure, so it is easier to spot.
This is a typical pointer aliasing problem and -gt a priory assumed that there was no aliasing. What it does _depends_ on the parameters not being aliased, whereas what Serge is doing does not. He is writing to a const (which is admittedly questionable) but, unlike the -gt switch he preserves the value which meets the essence of the promise made to the compiler.Again not for refcounted types.
A compiler should not assume that a pointer and a variable or two pointers are not aliases of each other unless it can deterministically determine they are not. Serge's procedure call is perfectly valid, the assumption made by the -gt switch isn't, thus leading to an incorrect result.Well, 50% disagree.
That behavior of the -gt switch is, and should be considered a bug. It unwarrantedly assumed that the parameters are not aliases of each other thus causing a problem.
R.X will be output as 1 (of course, S is empty as you mentioned above). But, if we comment out the field S, the garbage will be printed again.
I think:
1. Using const and out parameters - it's normal. In this case, if the types are the same, the developer should take into account that they can pass the same value (pointer).
2. Option -gt poorly documented. I.e. it is not clear what and in what cases it does.
3. If it makes the worked code non-functional, then there must be a possibility of disabling it locally or at least defining it to issue a warning.
4. Perhaps the compiler should issue a warning if the same variable is used more than once when calling procedure, provided that one of the parameters is out. And make a temporary copy of the variable to eliminate the side effect.
Maybe there generally is room to lobby for a note/hint from the compiler for any case where the same variable is passed twice or more by reference to the same procedure.
The function is correct. It's the caller that must pay attention to what it is passing to Test.Do you think this code is a problematic code?No. It is not correct. The compiler should make a copy in this case. (or warn).In fact, the procedure can be quite correctly handle the situation when the const and out parameter is the same (the same memory area), but to report this to the options -gt fails.
procedure Test(const R: TR; out O: TR); begin O := R; end;
<sigh, sorry> This is about deep copy and shallow copy after all. That's why I hate C++ and still teach it.