Recent

Author Topic: Why can't assign to a field of returned record?  (Read 3664 times)

PascalDragon

  • Hero Member
  • *****
  • Posts: 6398
  • Compiler Developer
Re: Why can't assign to a field of returned record?
« Reply #15 on: September 14, 2022, 09:05:26 am »
@Remy Lebeau
Consider the following additions:
Code: Pascal  [Select][+][-]
  1. { TGSyncedItem }
  2.  
  3.   generic TGSyncedItem<T: class> = record
  4.   private
  5.     FHidden: Integer;
  6.     FItem: T;
  7.     FCrit: TCriticalSection;
  8.     FAcquired: Boolean;
  9.     function GetItem: T; inline;
  10.     class operator Finalize(var Self: TGSyncedItem); inline;
  11.   public
  12.     K: Integer;
  13.     class operator := (I: Integer): TGSyncedItem;
  14.     procedure SetHidden(I: Integer);
  15.     property Hidden: Integer write FHidden;
  16.     property Item: T read GetItem;
  17.   end;
  18.  
  19. class operator TGSyncedItem. := (I: Integer): TGSyncedItem;
  20. begin
  21.   Result.FHidden := I;
  22. end;
  23.  
  24. procedure TGSyncedItem.SetHidden(I: Integer);
  25. begin
  26.   Self.FHidden := I;
  27. end;

And then into the main body:
Code: Pascal  [Select][+][-]
  1. begin
  2.   WithProp.Item := TWithProp.Create;
  3.   try
  4.     WithProp.Synced.Item.N := 42;
  5.     WithProp.Synced.Item.N := 666;
  6.     WithProp.Synced.Hidden := 13; // OK, access to a field via property
  7.     WithProp.Synced.SetHidden(5); // OK, invoke method with non-const Self
  8.     WithProp.Synced.K := 6; // Error: Argument cannot be assigned to
  9.     WithProp.Synced := 8;   // Error: Argument cannot be assigned to
  10.   finally
  11.     WithProp.Item.Free;
  12.   end;
  13. end.

That is because the compiler can not know what the methods do. So it can't prohibit you from calling them cause it's just as likely that they won't modify the record. This doesn't change however that any change these methods do to the record itself will be lost as soon as the temporary leaves the scope. The same is true for direct field access however there the compiler can tell you that what you're doing is useless and protect you from having to find a bug (and yes, these problems happened!).


Since, in a case with a simple record field, I don't see the problem to modify something that will disappear anyway.

Because it is redundant and unnecessary, and likely a code smell, if not an actual logic bug.  Nothing good can come from assigning a value to a field of a temporary, but something bad might happen if that value was meant to be used with a non-temporary instead.
Don't you think this serves rather as an excuse than an explanation? When I program it is my call what is unnecessary and likely a code smell ... nothing good ... something bad ... meant to be used .... Already shown above how a temporary can be modified by undoubtedly legal way (using property) - so finally - is it allowed or not?

This is the explanation and Delphi forbids it as well due to this. If the compiler could detect that you're changing the temporary record using a property or method it would forbid that as well.

I have yet to find an example where allowing and thus using this wouldn't be a bug (and even then it would probably such a narrow usecase that it wouldn't even remotely be more important than how this protection helps users not to introduce hard do find bugs).

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Why can't assign to a field of returned record?
« Reply #16 on: September 14, 2022, 10:02:33 am »
*snip*

That is because the compiler can not know what the methods do. So it can't prohibit you from calling them cause it's just as likely that they won't modify the record. This doesn't change however that any change these methods do to the record itself will be lost as soon as the temporary leaves the scope. The same is true for direct field access however there the compiler can tell you that what you're doing is useless and protect you from having to find a bug (and yes, these problems happened!).
Putting aside that fruitless dispute for the simple record field, I would like to know if it is appropriate to use the temporary and it's limited scope to do acquire/release into the initialize/finalize methods and thus to achieve thread-safe access to the underlying Item (as sketched in my last example). Is it feasible? I mean - is there some compiler particularities which may break the proper pairing - e.g. temporary reuse, delayed finalization, etc?

*snip*
This is the explanation and Delphi forbids it as well due to this. If the compiler could detect that you're changing the temporary record using a property or method it would forbid that as well.
I suppose that changing a field using property write field specifier is quite trivial to detect, isn't it?
BTW I'm little tired of "Delphi doing it, so are we..." argument. I haven't used Delphi for decades and I probably never will, so I just don't care about it.

I have yet to find an example where allowing and thus using this wouldn't be a bug (and even then it would probably such a narrow usecase that it wouldn't even remotely be more important than how this protection helps users not to introduce hard do find bugs).
You shouldn't do that. I found something inconsistent (IMO) and just asked a question.

More important for me is the feasibility of using the temporary scope for the mutual exclusion. Is it OK?
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

PascalDragon

  • Hero Member
  • *****
  • Posts: 6398
  • Compiler Developer
Re: Why can't assign to a field of returned record?
« Reply #17 on: September 14, 2022, 01:27:34 pm »
*snip*

That is because the compiler can not know what the methods do. So it can't prohibit you from calling them cause it's just as likely that they won't modify the record. This doesn't change however that any change these methods do to the record itself will be lost as soon as the temporary leaves the scope. The same is true for direct field access however there the compiler can tell you that what you're doing is useless and protect you from having to find a bug (and yes, these problems happened!).
Putting aside that fruitless dispute for the simple record field, I would like to know if it is appropriate to use the temporary and it's limited scope to do acquire/release into the initialize/finalize methods and thus to achieve thread-safe access to the underlying Item (as sketched in my last example). Is it feasible? I mean - is there some compiler particularities which may break the proper pairing - e.g. temporary reuse, delayed finalization, etc?

And that is exactly the problem with temporaries: the compiler gives no guarantee when they are finalized only that they are. So you absolutely can't rely on them being finalized when you expect them to, because that's completely implementation defined.

*snip*
This is the explanation and Delphi forbids it as well due to this. If the compiler could detect that you're changing the temporary record using a property or method it would forbid that as well.
I suppose that changing a field using property write field specifier is quite trivial to detect, isn't it?

No, it's not trivial, because the compiler does not keep track of such information and data flow analysis is both complex and expensive. The compiler only sees a call and does not know what that call does.

BTW I'm little tired of "Delphi doing it, so are we..." argument. I haven't used Delphi for decades and I probably never will, so I just don't care about it.

And we don't care that you don't care.

More important for me is the feasibility of using the temporary scope for the mutual exclusion. Is it OK?

As written above, no, it's not.

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1315
Re: Why can't assign to a field of returned record?
« Reply #18 on: September 14, 2022, 02:22:38 pm »
A programming language is, by itself, a set of restrictions. It starts with boolean logic and then makes a list of things you must do and that you are forbidden to do. Like, the reserved words are restricted to make it easier, not harder for the user. They provide a standard interface.

Likewise, the compiler and the user should both parse the code the exact same way. If inconsistencies and illogical constructions should be allowed, it increases the chances that the compiler sees it differently than the user, and that other users have yet another interpretation.

That's why the readability is so important, and everything that is different from the one and only correct interpretation of the syntax has to be considered a bug.

It's like the example above with the random number: if we all pick our own interpretation of the syntax and expect the compiler to respect that, each program will work differently. Essentially, everyone has their own, personal compiler, that is incompatible with all others.

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Why can't assign to a field of returned record?
« Reply #19 on: September 14, 2022, 02:53:10 pm »

*snip*
This is the explanation and Delphi forbids it as well due to this. If the compiler could detect that you're changing the temporary record using a property or method it would forbid that as well.
I suppose that changing a field using property write field specifier is quite trivial to detect, isn't it?

No, it's not trivial, because the compiler does not keep track of such information and data flow analysis is both complex and expensive. The compiler only sees a call and does not know what that call does.

Please read the
Quote
changing a field using property write field specifier
as stated into the FPC documentation as:
Code: Pascal  [Select][+][-]
  1. TRec = record
  2.   private
  3.     FN: Integer;
  4.   public
  5.     property N: Integer write FN;
  6.   end;
Not a call, neither a flow analysis.

BTW I'm little tired of "Delphi doing it, so are we..." argument. I haven't used Delphi for decades and I probably never will, so I just don't care about it.

And we don't care that you don't care.
We're all fine then.

More important for me is the feasibility of using the temporary scope for the mutual exclusion. Is it OK?

As written above, no, it's not.
What if it is used into a nested procedure? Is there a guarantee that it will be finalized after the nested procedure finishes?

After all it always can be assigned to a local variable. At least the scope of locals is indisputable.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

PascalDragon

  • Hero Member
  • *****
  • Posts: 6398
  • Compiler Developer
Re: Why can't assign to a field of returned record?
« Reply #20 on: September 15, 2022, 09:25:09 am »

*snip*
This is the explanation and Delphi forbids it as well due to this. If the compiler could detect that you're changing the temporary record using a property or method it would forbid that as well.
I suppose that changing a field using property write field specifier is quite trivial to detect, isn't it?

No, it's not trivial, because the compiler does not keep track of such information and data flow analysis is both complex and expensive. The compiler only sees a call and does not know what that call does.

Please read the
Quote
changing a field using property write field specifier
as stated into the FPC documentation as:
Code: Pascal  [Select][+][-]
  1. TRec = record
  2.   private
  3.     FN: Integer;
  4.   public
  5.     property N: Integer write FN;
  6.   end;
Not a call, neither a flow analysis.

In that case that simply is not handled by the compiler yet which does not mean that it won't be caught by the compiler in the future.

More important for me is the feasibility of using the temporary scope for the mutual exclusion. Is it OK?

As written above, no, it's not.
What if it is used into a nested procedure? Is there a guarantee that it will be finalized after the nested procedure finishes?

That's essentially the only guarantee you get: once the function returns all temporaries are released. That's true no matter what the function is.

After all it always can be assigned to a local variable. At least the scope of locals is indisputable.

If you assign it to something it no longer is a temporary or never was one in the first place (cause it will simply use the target location).

 

TinyPortal © 2005-2018