Recent

Author Topic: Why with allows assignment  (Read 39585 times)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10786
  • Debugger - SynEdit - and more
    • wiki
Re: Why with allows assignment
« Reply #165 on: January 12, 2025, 06:30:31 pm »
Does the above mean that FPC will "eventually" forbid "changing fields of a record that is a return value of a function to matter if with is used or not." ?

The answer is indeed yes. When is a different question however. 😅

Out of interest:
Is that "forbid direct assignment" or "forbid changing fields of ..."? Or where (and maybe how) will that line be drawn.

Because, of course read access remains allowed.
Code: Pascal  [Select][+][-]
  1. if GetPoint().x > 1 then
is valid.

But then, there are typeHelpers (and advanced records.
Code: Pascal  [Select][+][-]
  1. if MyFuncForRecord.MethodOfRecordOrHelper(1) > 1 then
is that valid?

Because such a method could modify the fields in the record. But it could also be something like "toString" which prints the record into a string, and surely should be allowed.



Of course, if such method calls are allowed, then the original issue remains

Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().SetX(42);

has exactly the same potential of confusion as
Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().X := 42;

440bx

  • Hero Member
  • *****
  • Posts: 4984
Re: Why with allows assignment
« Reply #166 on: January 12, 2025, 06:51:41 pm »
Of course, if such method calls are allowed, then the original issue remains

Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().SetX(42);

has exactly the same potential of confusion as
Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().X := 42;
I just want to make sure I understand the pair of code snippets above correctly.  In the first case, SetX invokes some code that changes the value of a field, obviously that is perfectly fine since no function result is being modified.

If the second piece of code is really/fully equivalent then "X" is not part of a function's result and, if it isn't then it's fine.

Did I interpret the code you showed correctly ?
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10786
  • Debugger - SynEdit - and more
    • wiki
Re: Why with allows assignment
« Reply #167 on: January 12, 2025, 07:53:10 pm »
Of course, if such method calls are allowed, then the original issue remains

Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().SetX(42);

has exactly the same potential of confusion as
Code: Pascal  [Select][+][-]
  1. MyObject.PropertyWithGetterForSomeTPoint().X := 42;
I just want to make sure I understand the pair of code snippets above correctly.  In the first case, SetX invokes some code that changes the value of a field, obviously that is perfectly fine since no function result is being modified.

If the second piece of code is really/fully equivalent then "X" is not part of a function's result and, if it isn't then it's fine.

Did I interpret the code you showed correctly ?

"SetX(1)" modifies the X field in the temporary storage of the function result.

In both cases the function returns a TPoint (record), and in both cases, the result is held in a temp var.


The 2nd example obviously already does not compile. Because it has no "with".
But the same examples can be done using a "with" block. And within such a with block you should (afaik) be allowed to
- read fields of the temp var holding the function results
- call methods or helper methods (like "toString")

BrunoK

  • Hero Member
  • *****
  • Posts: 664
  • Retired programmer
Re: Why with allows assignment
« Reply #168 on: January 12, 2025, 09:26:15 pm »
neo luddism.
+++ You're quite in an accurate sarcasm vein lately ...

LV

  • Full Member
  • ***
  • Posts: 204
Re: Why with allows assignment
« Reply #169 on: January 12, 2025, 11:01:40 pm »
Code: Pascal  [Select][+][-]
  1.   Test.V := 1; //Error: Argument cannot be assigned to
  2.  

And on the same topic, is there a way in Pascal that I can return a record by the reference so any change to the result, changes the source?
For example changing V changes the original X.
I know I can return the pointer, but is there any other way?

The answer is no without using a pointer or reference type object.
Use a class. Class is inherently a reference type. Changes to its fields affect the original object:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. type
  3.   TTest = class
  4.     V: Integer;
  5.   end;
  6.  
  7. var
  8.   X: TTest;
  9.  
  10. function Test: TTest;
  11. begin
  12.   Result := X; // Return the reference to the object
  13. end;
  14.  
  15. begin
  16.   X := TTest.Create;
  17.   Test.V := 1; // Directly modifies X.V
  18. end.  
  19.  

Warfley

  • Hero Member
  • *****
  • Posts: 1863
Re: Why with allows assignment
« Reply #170 on: January 13, 2025, 01:12:14 am »
If the second piece of code is really/fully equivalent then "X" is not part of a function's result and, if it isn't then it's fine.

Did I interpret the code you showed correctly ?

See the following full code example: https://godbolt.org/z/6eTnofqee
Code: Pascal  [Select][+][-]
  1. {$Mode ObjFPC}
  2. {$ModeSwitch TypeHelpers}
  3.  
  4. type
  5.   TMyRec = record
  6.     X: Integer;
  7.   end;
  8.  
  9.   TMyHelper = type helper for TMyRec
  10.     procedure SetX(const NewX: Integer); inline;
  11.   end;
  12.  
  13. procedure TMyHelper.SetX(const NewX: Integer);
  14. begin
  15.   Self.X := NewX;
  16. end;
  17.  
  18. function Test: TMyRec;
  19. begin
  20.  
  21. end;
  22.  
  23. procedure TestWithSetResult;
  24. begin
  25.   with Test do
  26.     X := 42;
  27. end;
  28.  
  29. procedure TestHelperSetResult;
  30. begin
  31.   Test.SetX(42);
  32. end;

Type Helpers are just syntactic sugar and because pascal does not have a concept of constness for method calls (unlike for example C++), if you look at the assembly generated:
Code: ASM  [Select][+][-]
  1. testwithsetresult():
  2.         leaq    -8(%rsp),%rsp
  3.         call    test()
  4.         movl    %eax,(%rsp)
  5.         movq    %rsp,%rax
  6.         movl    $42,(%rax)
  7.         leaq    8(%rsp),%rsp
  8.         ret
  9.  
  10. testhelpersetresult():
  11.         leaq    -8(%rsp),%rsp
  12.         call    test()
  13.         movl    %eax,(%rsp)
  14.         movq    %rsp,%rax
  15.         movl    $42,(%rax)
  16.         leaq    8(%rsp),%rsp
  17.         ret
The resulting assembly code is bitwise identical.

So there is absolutely nothing that not allowing to set the field of a function result (with or without with) would prevent that is not otherwise already possible.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10786
  • Debugger - SynEdit - and more
    • wiki
Re: Why with allows assignment
« Reply #171 on: January 13, 2025, 01:51:59 am »
So there is absolutely nothing that not allowing to set the field of a function result (with or without with) would prevent that is not otherwise already possible.

Not the field of the original data. It was never my intend to say that. And I actually don't think I did say it.

You can however write to the temporary storage (the non-declared helper variable in which fpc holds the value for read access, until its out of scope).

As it was discussed, when using "with" this is currently allowed (despite it more often is done due to mislead ideas, rather than actually in fashion of a temp local var)
At least such misleading potential was stated, and given as reason for one day changing the "with" abilities.

I merely was curious, because I do not see how this could easily be extended to cover *all* possible cases. And how it would affect method calls that could still do exactly the behaviour that is said to be of such misleading nature.

All I wanted to know is, if there was any comment on that.

No judgment if the planned change is good or not, or anything else. Merely: is it known to be incomplete, or are there ways (that I can not see) to make it complete?


Somehow, the only replies I got where entirely offtopic to that question. :(

440bx

  • Hero Member
  • *****
  • Posts: 4984
Re: Why with allows assignment
« Reply #172 on: January 13, 2025, 03:02:26 am »
Somehow, the only replies I got where entirely offtopic to that question. :(
My reply was more than anything an attempt to understand the behavior of the code you posted.

Your question is good because, as you pointed out, there are other ways to modify the function's result.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Warfley

  • Hero Member
  • *****
  • Posts: 1863
Re: Why with allows assignment
« Reply #173 on: January 13, 2025, 01:27:42 pm »
I merely was curious, because I do not see how this could easily be extended to cover *all* possible cases. And how it would affect method calls that could still do exactly the behaviour that is said to be of such misleading nature.

All I wanted to know is, if there was any comment on that.

No judgment if the planned change is good or not, or anything else. Merely: is it known to be incomplete, or are there ways (that I can not see) to make it complete?

I think it would be interesting to extend the concept of constness to methods, e.g. in C++ you can have the following:
Code: C  [Select][+][-]
  1. class MyClass {
  2.   ...
  3.   void myMethod() const {
  4.     ...
  5.   }
  6. }
In myMethod "this" is const. It would be equivalent to the function in Pascal being:
Code: Pascal  [Select][+][-]
  1. procedure myVoid(const self: TMyClass);
instead of:
Code: Pascal  [Select][+][-]
  1. procedure myVoid(self: TMyClass);
Which is how methods usually work right now.

The problem is that this is not really backwards compatible, e.g. if you make the change that on a constant "object" (not to be confused with class instances or object types, just some thing in memory) can only call const methods, would result in breaking a lot of existing code with type helpers and advanced records, which currently rely on const (but not tagged as such) methods. Maybe static analysis to implicitly tag methods as const could be used, but this would be quite a big project

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10786
  • Debugger - SynEdit - and more
    • wiki
Re: Why with allows assignment
« Reply #174 on: January 13, 2025, 02:10:52 pm »
I think it would be interesting to extend the concept of constness to methods, e.g. in C++ you can have the following:

Maybe check the discussion on "pure" functions on the mail list...?

Though, for the case of fields in a temp, its not an exact match. You can call
   MyFunc().toString;

And toString could be allowed to modify e.g. a global var, counting how often it was called.

It just mustn't modify its own "self" (the temp record var). But then you also need to ensure it does not by calling anything else. And take old style "type foo=object" with virtual methods. You must be sure no inherited method does => and they may not even be known to the compiler at that time.


Warfley

  • Hero Member
  • *****
  • Posts: 1863
Re: Why with allows assignment
« Reply #175 on: January 13, 2025, 02:49:58 pm »
Yeah the pure functions are interesting especially as it allows some really interesting concepts (optimizations of recursive functions using fixpoint analysis, compile time evaluation of constants, automated paralelization, etc.), but it's of course a lot stricter than "just" self constness.

But the thing with enforcing constness is not just backwards compatibility, but also that it can be very restrictive. I have worked in C++ projects with some newcomers to C++ who were not that familiar with the concept of constness, and just forgot to declare many methods as const, so they couldn't be called from const contexts.

So while being potentially quite a powerful concept, to be useful it needs to be consistently enforced, which carries it's own problems. Theres a reason many C based languages discarded the concept of constness from C

PascalDragon

  • Hero Member
  • *****
  • Posts: 5851
  • Compiler Developer
Re: Why with allows assignment
« Reply #176 on: January 13, 2025, 09:53:19 pm »
This will stop working in the future, because the value returned by the functions (in your case GetRange and GetRangeObj) will only be temporary and won't propagate back to the original memory. So in that case one needs to explicitly capture the returned value in a variable and then use with. Users using this accidentally or wondering why it doesn't work the way they expect outweigh useful cases like this.

Will this also stop working in the future?

Code: Pascal  [Select][+][-]
  1.   with TfrmVIES.Create(nil) do
  2.   try
  3.     ShowModal;
  4.   finally
  5.     Release;
  6.   end;

Classes are implicit pointer types. This change is about directly accessing records and objects.


Speaking of readability: please use [code=pascal][/code] to improve readabililty and to avoid the forum software interpreting the code.

The forum software is a compiler that will run code in a post?

No, but it has an interpreter for its own functionality. For example if you have a Pascal code that reads SomeArray[i] then the forum software will interpret the [i] as the start of an italic text block, thus all following text will be italic. Similiar if an emoticon is encountered: e.g. SomeCode(Foo, 8) will turn into SomeCode(Foo, 8).
In addition to that [code][/code] allows for better readability due to syntax highlighting (especilly if the correct language is selected, e.g. with [code=pascal][/code]).

PascalDragon, I confess I have not followed this thread closely because of the unpleasantness exhibited. But I do use TestOne below and would like you to assure me it is future safe.

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}
  3.  
  4. type
  5.     TaRec = record
  6.         TheInt : integer;
  7.         TheString : string;
  8.     end;
  9.      
  10. function Test() : TaRec;
  11. begin
  12.     result.TheInt := 42;
  13.     result.TheString := 'FortyTwo';
  14. end;
  15.    
  16. var
  17.         aRec : TaRec;
  18. begin
  19.     aRec := Test();          // Test One
  20.     WriteLn('Test One TheInt ', aRec.TheInt);
  21.     writeln('Test One TheString ', aRec.TheString);
  22.  
  23.     with Test() do begin     // Test Two
  24.         WriteLn('Test Two TheInt ', TheInt);
  25.         writeln('Test Two TheString ', TheString);
  26.     end;
  27. end.

Read access is safe.

yeah well, I have a rather large app that I was able to get fpc to compile that uses lots of code like this.

Code: Pascal  [Select][+][-]
  1.  
  2.   with GetNodeByIndex(1{one for now}) do
  3.   begin
  4.     NormalizeValues;
  5.     if X = -1 then X := Adefault; //Constant for now but changes.
  6.     if Y = -1 then Y := ADefault; //Place Null nodes to a common place.
  7.     if Z = -1 then Z := ADefault;
  8.     ProcessNode;
  9.     // more code after this.
  10.   end;                                              
  11.  
  12.  

That function GetNodeByIndex returns a Record, in Delphi mode to get advanced records and as you can see, I have procedures, and I do change the values within.
That does not mean they get changed in the original source, they don't.

The point is that there are enough people that assume that this will change their original value and then are confused when it doesn't. Due to there being more people that stumble upon this than people that can make productive use of this the Delphi developers decided to forbid it to directly modify fields/properties of records that are accessed using a with-statements that directly captures a function result. So your code will fail in current Delphi versions as well.

Does the above mean that FPC will "eventually" forbid "changing fields of a record that is a return value of a function to matter if with is used or not." ?

The answer is indeed yes. When is a different question however. 😅

Out of interest:
Is that "forbid direct assignment" or "forbid changing fields of ..."? Or where (and maybe how) will that line be drawn.

In the end it will follow what Delphi does. There it's assignments to fields and properties that are forbidden if the record (or object) is accessed using a with-statement directly capturing a function result.

egsuh

  • Hero Member
  • *****
  • Posts: 1529
Re: Why with allows assignment
« Reply #177 on: January 14, 2025, 06:46:30 am »
Quote
There it's assignments to fields and properties that are forbidden if the record (or object) is accessed using a with-statement directly capturing a function result.

Does this mean calling methods will be allowed?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5851
  • Compiler Developer
Re: Why with allows assignment
« Reply #178 on: January 16, 2025, 09:17:50 pm »
Quote
There it's assignments to fields and properties that are forbidden if the record (or object) is accessed using a with-statement directly capturing a function result.

Does this mean calling methods will be allowed?

If Delphi allows them, then yes.

 

TinyPortal © 2005-2018