Recent

Author Topic: [SOLVED] functions with same return record-type share a record(?)  (Read 5507 times)

jamie

  • Hero Member
  • *****
  • Posts: 6733
Re: functions with same return record-type share a record(?)
« Reply #15 on: May 19, 2024, 04:39:16 pm »
Sorry, but I disagree.

Its valid code. I've used such code myself

Records are passed by reference in the background if the size exceeds register bounds on return.

Have a good day.

The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 16135
  • Censorship about opinions does not belong here.
Re: functions with same return record-type share a record(?)
« Reply #16 on: May 19, 2024, 04:41:32 pm »
It is not that strange that it works, because the record is allocated on the heap. Problems only arise if allocated on the stack.
IMO the heap is not involved at all. The OP example works because it has just one field and,  by coincidence, A reuses result variable of B as its own result. In other scenarios it just may not work properly.
Then you do not think: a function result ( as a record, but actually always ) needs to be on the heap to be safe in both global and local calls. Or in a register for simple calls, not records.
« Last Edit: May 19, 2024, 04:43:43 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Fibonacci

  • Hero Member
  • *****
  • Posts: 593
  • Internal Error Hunter
Re: functions with same return record-type share a record(?)
« Reply #17 on: May 19, 2024, 04:47:10 pm »
Its valid code. I've used such code myself

So why does this line which does nothing and is executed after writeln change the value of .bar?

jamie

  • Hero Member
  • *****
  • Posts: 6733
Re: functions with same return record-type share a record(?)
« Reply #18 on: May 19, 2024, 05:05:17 pm »
its left-over junk in the record.

RESULT variables for records exceeding register sizes, depending on compiler optimizing settings, will simply point to an existing
records.

 While you are inside the function using the RESULT variable, it still contains all the data from the last use.

 if you are concern about this, you need to initialize the return record, which normally involves, zero it out or simply directly defining the fields without combining existing junk data.

 You can in theory, examine large records via the result variable if you are sure, it has already been defined earlier.



The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 16135
  • Censorship about opinions does not belong here.
Re: functions with same return record-type share a record(?)
« Reply #19 on: May 19, 2024, 05:12:44 pm »
RESULT variables for records exceeding register sizes, depending on compiler optimizing settings, will simply point to an existing
iI you can prove that, that is a major compiler bug. You can't , so shut up.
If I smell bad code it usually is bad code and that includes my own code.

alpine

  • Hero Member
  • *****
  • Posts: 1288
Re: functions with same return record-type share a record(?)
« Reply #20 on: May 19, 2024, 05:21:08 pm »
RESULT variables for records exceeding register sizes, depending on compiler optimizing settings, will simply point to an existing
iI you can prove that, that is a major compiler bug. You can't , so shut up.
From what I have observed jamie is right. I have noticed even function returning strings to keep their outer values. The case is when you have an expression at the right side involving a function call, it may need a temporaries, and then the things are going to be messy.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

jamie

  • Hero Member
  • *****
  • Posts: 6733
Re: functions with same return record-type share a record(?)
« Reply #21 on: May 19, 2024, 05:24:03 pm »
RESULT variables for records exceeding register sizes, depending on compiler optimizing settings, will simply point to an existing
iI you can prove that, that is a major compiler bug. You can't , so shut up.
From what I have observed jamie is right. I have noticed even function returning strings to keep their outer values. The case is when you have an expression at the right side involving a function call, it may need a temporaries, and then the things are going to be messy.


Thank you very much for being a real coder and observing the tools at hand.

Such effects are actually beneficial, but we have some here that would rather bog down the code.
The only true wisdom is knowing you know nothing

MMarie

  • New Member
  • *
  • Posts: 37
  • Right, lets bodge this pisspot
    • Homepage
Re: functions with same return record-type share a record(?)
« Reply #22 on: May 19, 2024, 05:30:01 pm »
So my key take aways from all the initial replies and the discussion that ensued: I should /not/ rely on this behaviour.

I also thought that I should clarify that the record type also possesses more than just the string, which i tried to imply with the comment.
For clarities sake, this is the entire unit (please note that I wrote this in the middle of the night and as such is not particularly well written):
Code: Pascal  [Select][+][-]
  1. unit uTranslator;
  2.  
  3. {$H+}
  4.  
  5. interface
  6.  
  7. uses StrUtils, SysUtils, Types, uSADParser, uShared;
  8.  
  9. type
  10.   TTranslateError = (treNONE, treUNKNOWN, treINVALID_SYNTAX);
  11.  
  12.   TTranslateResult = record
  13.     is_ok   : Boolean;
  14.     err     : TTranslateError;
  15.     err_msg : String;
  16.     value   : String;
  17.   end;
  18.  
  19. {
  20.   All Translate functions are intended to append to TTranslateResult.value, except
  21.   for TranslateSource (whose job is to begin translation
  22. }
  23.  
  24. function TranslateHeader(generator: TGenerator; header: String): TTranslateResult;
  25. function TranslateSubHeader(generator: TGenerator; header: String): TTranslateResult;
  26. function TranslateSection(generator: TGenerator; section: TSection): TTranslateResult;
  27. function TranslateSource(generator: TGenerator): TTranslateResult;
  28.  
  29. implementation
  30.  
  31. function TranslateHeader(generator: TGenerator; header: String): TTranslateResult;
  32. begin
  33.   TranslateHeader.is_ok   := True;
  34.   TranslateHeader.err     := treNONE;
  35.   TranslateHeader.err_msg := '';
  36.  
  37.   TranslateHeader.value := TranslateHeader.value +
  38.                            generator.template.head_format.prefix_text +
  39.                            header +
  40.                            generator.template.head_format.postfix_text;
  41. end;
  42.  
  43. function TranslateSubHeader(generator: TGenerator; header: String): TTranslateResult;
  44. begin
  45.   TranslateSubHeader.is_ok   := True;
  46.   TranslateSubHeader.err     := treNONE;
  47.   TranslateSubHeader.err_msg := '';
  48.  
  49.   TranslateSubHeader.value := TranslateSubHeader.value +
  50.                               generator.template.sub_head_format.prefix_text +
  51.                               header +
  52.                               generator.template.sub_head_format.postfix_text;
  53. end;
  54.  
  55. function TranslateSection(generator: TGenerator; section: TSection): TTranslateResult;
  56. const
  57.   SECTION_NAME_MARKER = '$$SECTION_NAME$$';
  58.   SECTION_START_MARKER = '$$SECTION_START$$';
  59. var
  60.   nline, nword, child_ix: Integer;
  61.   prefix, cline, _word: String;
  62.   lines, words: TStringDynArray;
  63.  
  64.   in_text: Boolean;
  65. begin
  66.   TranslateSection.is_ok   := True;
  67.   TranslateSection.err     := treNONE;
  68.   TranslateSection.err_msg := '';
  69.  
  70.   prefix := StringReplace(generator.template.section_format.prefix_text,
  71.                           SECTION_NAME_MARKER, section.name, [rfReplaceAll]);
  72.   TranslateSection.value := TranslateSection.value + prefix;
  73.  
  74.   lines := SplitString(section.contents, sLineBreak);
  75.  
  76.   in_text := False;
  77.   child_ix := 0;
  78.  
  79.   for nline := 0 to Length(lines) - 1 do
  80.   begin
  81.     if Length(lines[nline]) < 1 then
  82.       continue;
  83.  
  84.     words := SplitString(lines[nline], ' ');
  85.  
  86.     for nword := 0 to Length(words) - 1 do
  87.     begin
  88.       _word := words[nword];
  89.  
  90.       case _word of
  91.         SECTION_START_MARKER: begin
  92.           if in_text then
  93.           begin
  94.             in_text := False;
  95.             TranslateSection.value := TranslateSection.value +
  96.                                       generator.template.text_format.postfix_text;
  97.           end;
  98.  
  99.           TranslateSection := TranslateSection(generator, section.children[child_ix]);
  100.           if not TranslateSection.is_ok then
  101.             exit;
  102.         end;
  103.         HEADER: begin
  104.           { TODO: How to properly de-duplicate this code? }
  105.           if in_text then
  106.           begin
  107.             in_text := False;
  108.             TranslateSection.value := TranslateSection.value +
  109.                                       generator.template.text_format.postfix_text;
  110.           end;
  111.  
  112.           TranslateSection := TranslateHeader(generator, MergeStringArray(
  113.                                 Copy(words, nword+1, Length(words)-1),
  114.                                 ' '
  115.                               ));
  116.           if not TranslateSection.is_ok then
  117.             exit;
  118.  
  119.           break;
  120.         end;
  121.         SUB_HEADER: begin
  122.           { TODO: How to properly de-duplicate this code? }
  123.           if in_text then
  124.           begin
  125.             in_text := False;
  126.             TranslateSection.value := TranslateSection.value +
  127.                                       generator.template.text_format.postfix_text;
  128.           end;
  129.  
  130.           TranslateSection := TranslateSubHeader(generator, MergeStringArray(
  131.                                 Copy(words, nword+1, Length(words)-1),
  132.                                 ' '
  133.                               ));
  134.           if not TranslateSection.is_ok then
  135.             exit;
  136.  
  137.           break;
  138.         end;
  139.         { regular text }
  140.         else begin
  141.           if not in_text then
  142.           begin
  143.             in_text := True;
  144.             TranslateSection.value := TranslateSection.value +
  145.                                       generator.template.text_format.prefix_text;
  146.           end;
  147.  
  148.           TranslateSection.value := TranslateSection.value + _word + ' ';
  149.         end;
  150.       end;
  151.     end;
  152.  
  153.     if in_text and generator.options.auto_break then
  154.       TranslateSection.value := TranslateSection.value + '<br>';
  155.  
  156.     TranslateSection.value := TranslateSection.value + sLineBreak;
  157.   end;
  158.  
  159.   TranslateSection.value := TranslateSection.value +
  160.                             generator.template.section_format.postfix_text;
  161. end;
  162.  
  163. function TranslateSource(generator: TGenerator): TTranslateResult;
  164. const
  165.   DOCUMENT_TITLE_MARKER = '$$DOCUMENT_TITLE$$';
  166. begin
  167.   TranslateSource.is_ok   := True;
  168.   TranslateSource.err     := treNONE;
  169.   TranslateSource.err_msg := '';
  170.  
  171.   TranslateSource.value := generator.template.output_format.prefix_text;
  172.  
  173.   TranslateSource := TranslateSection(generator, generator.source.root_section);
  174.   if not TranslateSource.is_ok then
  175.     exit;
  176.  
  177.   TranslateSource.value := TranslateSource.value + generator.template.output_format.postfix_text;
  178.   TranslateSource.value := StringReplace(TranslateSource.value, DOCUMENT_TITLE_MARKER,
  179.                                          generator.source.title, [rfReplaceAll]);
  180. end;
  181.  
  182. end.
  183.  
i use arch btw

Fibonacci

  • Hero Member
  • *****
  • Posts: 593
  • Internal Error Hunter
Re: functions with same return record-type share a record(?)
« Reply #23 on: May 19, 2024, 05:31:07 pm »
@jamie
So you say if im inside function func1 I can append something to .bar? And I can be 100% sure the "foo" string will be already there in .bar? And you code like this? May I see some example?

Code: Pascal  [Select][+][-]
  1. type
  2.   tfoo = record
  3.     bar: string;
  4.   end;
  5.  
  6. function func1: tfoo;
  7. begin
  8.   result.bar += ' bar';
  9. end;
  10.  
  11. function func2: tfoo;
  12. begin
  13.   result.bar := 'foo';
  14.   result := func1;
  15.   writeln(result.bar);        // prints "foo bar"
  16.   if dword(@result) = 0 then; // <-- with this line prints " bar"
  17. end;
  18.  
  19. begin
  20.   func2;
  21.   readln;
  22. end.

alpine

  • Hero Member
  • *****
  • Posts: 1288
Re: functions with same return record-type share a record(?)
« Reply #24 on: May 19, 2024, 06:19:23 pm »
@jamie
So you say if im inside function func1 I can append something to .bar? And I can be 100% sure the "foo" string will be already there in .bar? And you code like this? May I see some example?

As you can see (put a watch '@Result' and breakpoints at lines 8,14) when you comment the line 16, both results will share the same address. If you uncomment  line 16 they'll be different.

My guess is that is a part of the compiler optimization and I found it quite clever. On a few occasions I was surprised to see strange results from my String functions, but after all it is a programmer error not to initialize the result variable, isn't it. (And to ignore the compiler hints)

EDIT: Perhaps some operations (getting the address of) breaks that optimization, making a temporary for the result should be the usual way.

Your example should be a clear hint for the OP not to rely on that.
« Last Edit: May 19, 2024, 06:29:43 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MMarie

  • New Member
  • *
  • Posts: 37
  • Right, lets bodge this pisspot
    • Homepage
Re: functions with same return record-type share a record(?)
« Reply #25 on: May 19, 2024, 06:31:51 pm »
@jamie
So you say if im inside function func1 I can append something to .bar? And I can be 100% sure the "foo" string will be already there in .bar? And you code like this? May I see some example?

As you can see (put a watch '@Result' and breakpoints at lines 8,14) when you comment the line 16, both results will share the same address. If you uncomment  line 16 they'll be different.

My guess is that is a part of the compiler optimization and I found it quite clever. On a few occasions I was surprised to see strange results from my String functions, but after all it is a programmer error not to initialize the result variable, isn't it. (And to ignore the compiler hints)

EDIT: Perhaps some operations (getting the address of) breaks that optimization, making a temporary for the result should be the usual way.

Your example should be a clear hint for the OP not to rely on that.

as stated in my previous reply i took the hint  :D  I was wildly confused at 2am why my code even worked, spawning this thread and discussion  :)
i use arch btw

alpine

  • Hero Member
  • *****
  • Posts: 1288
Re: functions with same return record-type share a record(?)
« Reply #26 on: May 19, 2024, 06:53:45 pm »
as stated in my previous reply i took the hint  :D  I was wildly confused at 2am why my code even worked, spawning this thread and discussion  :)
I've always considered functions as something without side-effects, so I never used them that way. My faults were with Strings - the Result, as a managed temporary, should be always empty (so I thought) - it turned out not always.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MMarie

  • New Member
  • *
  • Posts: 37
  • Right, lets bodge this pisspot
    • Homepage
Re: functions with same return record-type share a record(?)
« Reply #27 on: May 19, 2024, 06:57:06 pm »
as stated in my previous reply i took the hint  :D  I was wildly confused at 2am why my code even worked, spawning this thread and discussion  :)
I've always considered functions as something without side-effects, so I never used them that way. My faults were with Strings - the Result, as a managed temporary, should be always empty (so I thought) - it turned out not always.

same applies for me, I just remembered that at my old working place where we used Delphi I had a similar problem with a function returning a string. The result of the previous call would persist and I was also quite confused  :D
i use arch btw

MarkMLl

  • Hero Member
  • *****
  • Posts: 7998
Re: functions with same return record-type share a record(?)
« Reply #28 on: May 19, 2024, 07:14:31 pm »
I admit to not understanding why those functions aren't going recursive.
Also a good point.

Isn't it that the Result variable was introduced to solve?

Yes. There's something weird going on.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jamie

  • Hero Member
  • *****
  • Posts: 6733
Re: functions with same return record-type share a record(?)
« Reply #29 on: May 19, 2024, 10:28:54 pm »
Somewhere in the documentation it explains the process of handling large objects as the return and it clearly states that in the background, the compiler actually generates a PROCEDURE(Your Variables, Var AHiddenRef);

So, in general, large objects are handled via a VAR reference.

This is more efficient than placing one on the stack and then doing a copy over, also it could complete the stack.

 After some examination of the ASM code, it's clear the compiler will generate a real function using register pairs if needed to write back to the record, if the record is small enough.

  Playing with optimization you can see where the compiler will decide on small size records, so the compiler will shift this around a bit.

  I did ask for a feature for the compiler to force a REF on the output so that this can be done on any type, including those that can clearly fit in registers.

  C/C++ language and others like it have prevision to indicate using a REF return on any type.

 Function Name(.....) REF TheReturnType
 
Etc.
 Have a good day.


The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018