[fpc-devel] Overlapping addresses between 2 procedures in dwarf info / 3.2.0

2021-12-25 Thread Martin Frb via fpc-devel
I am looking at some oddity I found following up 
https://forum.lazarus.freepascal.org/index.php/topic,57568.0.html


Unfortunately, I have no code yet to reproduce it.
I have looked at the generated debug info, as given by objdump.

file format pei-x86-64
  Compilation Unit @ offset 0x0:
   Length:    0xa1 (32-bit)
   Version:   2
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <18>   DW_AT_producer    : Free Pascal 3.2.0 2021/02/21

The following part of the info looks wrong to me (original names replaced):

 <1>: Abbrev Number: 8 (DW_TAG_subprogram)
       DW_AT_name    : FOO
       DW_AT_prototyped  : 1
       DW_AT_calling_convention: 65    (user defined)
       DW_AT_external    : 1
       DW_AT_low_pc  : 0x10004a990
       DW_AT_high_pc : 0x10004adc4 < NOTE THE 
END ADDR


 <1>: Abbrev Number: 9 (DW_TAG_subprogram)
       DW_AT_name    : fin$00B0
       DW_AT_prototyped  : 1
       DW_AT_calling_convention: 65    (user defined)
       DW_AT_low_pc  : 0x10004a950
       DW_AT_high_pc : 0x10004a988

 <1>: Abbrev Number: 8 (DW_TAG_subprogram)
       DW_AT_name    : XYZ
       DW_AT_low_pc  : 0x0 <  code not included, 
not used/called

       DW_AT_high_pc : 0x0

 <1>: Abbrev Number: 6 (DW_TAG_subprogram)
... Several other DW_TAG_subprogram, with address BEFORE FOO, and (as 
far as I checked also line-numbers before FOO.
... I guess those are included here, because they did not get called 
before, and the compiler adds the debug info only now ???

... Or could there be other reasons for the out-of-order inclusion???

 <1>: Abbrev Number: 8 (DW_TAG_subprogram)
       DW_AT_name    : BAR
       DW_AT_prototyped  : 1
       DW_AT_calling_convention: 65    (user defined)
       DW_AT_external    : 1
       DW_AT_low_pc  : 0x10004adb0 < NOTE THE 
START ADDR

       DW_AT_high_pc : 0x10004ae26

BAR starts within FOO. That seems wrong? (also see the line info below)
From the code samples I received from the author, this is NOT a nested 
proc.


This (and the line-info) leads to a crash of the app, when run under 
gdb, as gdb adjusts the position of breakpoints, and ends setting it 
into the middle of an asm instruction.


According to the author, the crash only happens if there are no calls to 
XYZ. Otherwise the crash does not happen. I do not have info (yet), if 
the overlapping addresses are present or not, if XYZ is used.
The inclusion/exclusion of XYZ triggers 
https://gitlab.com/freepascal.org/fpc/source/-/issues/38117  which can 
be seen in the line-info of this app too (i.e. line info contains none 
relocated entries that compute to invalid addresses for those lines).




** The line-info of "FOO"
  [0x00011cee]  Extended opcode 2: set Address to 0x10004a990
  [0x00011cf9]  Set column to 1
  [0x00011cfb]  Advance Line by 2452 to 2453
  [0x00011cfe]  Copy
  [0x00011cff]  Advance PC by 37 to 0x10004a9b5
  [0x00011d01]  Copy

  [0x00011d5b]  Advance Line by -2 to 2453
  [0x00011d5d]  Copy
  [0x00011d5e]  Advance PC by 9 to 0x10004ad95
  [0x00011d60]  Special opcode 22: advance Address by 0 to 0x10004ad95 
and Line by 23 to 2476

  [0x00011d61]  Extended opcode 2: set Address to 0x10004adc4
  [0x00011d6c]  Extended opcode 1: End of Sequence

It advances all the way to line 2476 / 0x10004ad95
Then it goes to address 0x10004adc4 (before  "End of Sequence")
This is inside of "BAR".

** The line info shows that the next line (2484) is within the "not 
included / not called" proc XYZ

  [0x00011d6f]  Extended opcode 2: set Address to 0x0
  [0x00011d7a]  Set column to 1
  [0x00011d7c]  Advance Line by 2483 to 2484
  [0x00011d7f]  Copy
  [0x00011d80]  Advance PC by 13 to 0xd


** The line-info of "BAR"
    This continues from the line-info of XYZ (as seen by the none 
relocated entries)


  [0x00011db8]  Special opcode 9: advance Address by 0 to 0x12c and 
Line by 10 to 2500


  [0x00011dc2]  Special opcode 0: advance Address by 0 to 0x17c and 
Line by 1 to 2504

  [0x00011dc3]  Advance PC by 80 to 0x1cc
  [0x00011dc5]  Set column to 1
  [0x00011dc7]  Special opcode 12: advance Address by 0 to 0x1cc and 
Line by 13 to 2517

  [0x00011dc8]  Extended opcode 2: set Address to 0x0
  [0x00011dd3]  Extended opcode 1: End of Sequence

** BAR
  [0x00011dd6]  Extended opcode 2: set Address to 0x10004adb0
  [0x00011de1]  Set column to 1
  [0x00011de3]  Advance Line by 2521 to 2522
  [0x00011de6]  Copy
  [0x00011de7]  Advance PC by 21 to 0x10004adc5
  [0x00011de9]  Set column to 3
  [0x00011deb]  Special opcode 0: advance Address by 0 to 0x10004adc5 
and Line by 1 to 2523


BAR starts at line 2522 (confirmed by the author).
The address 0x10004adb0  could be correct, as procs should afaik start 
at 16 byte boundaries?


Line 2522 at 0x10004adb0 is an entry in the line info table, made by the 
"copy" statement.


-
So in conclusion something is wrong there.

As for 

[fpc-devel] $modeswitch Closures

2021-12-25 Thread Blaise--- via fpc-devel

The attached modeswitch_closures.patch introduces {$modeswitch Closures}; it is 
included in {$mode Delphi}.

There is a distinction between anonymous routines (defined in-place, without a 
name) and closures (capture the context they are invoked with). The switch 
encompasses both, but goes for the shorter and catchier name.

--
βþ
# HG changeset patch
# User Blaise.ru
# Date 1640457371 -10800
#  Sat Dec 25 21:36:11 2021 +0300
+ new {$modeswitch Closures}, included in {$mode Delphi}

diff -r d880e6695537 -r 3ecaef5e9a49 globals.pas
--- a/globals.pas   Mon Dec 20 20:55:22 2021 +0300
+++ b/globals.pas   Sat Dec 25 21:36:11 2021 +0300
@@ -55,7 +55,7 @@
   
m_pointer_2_procedure,m_autoderef,m_tp_procvar,m_initfinal,m_default_ansistring,
   m_out,m_default_para,m_duplicate_names,m_hintdirective,
   m_property,m_default_inline,m_except,m_advanced_records,
-  m_array_operators,m_prefixed_attributes];
+  m_closures,m_array_operators,m_prefixed_attributes];
delphiunicodemodeswitches = delphimodeswitches + 
[m_systemcodepage,m_default_unicodestring];
fpcmodeswitches =
  [m_fpc,m_string_pchar,m_nested_comment,m_repeat_forward,
diff -r d880e6695537 -r 3ecaef5e9a49 globtype.pas
--- a/globtype.pas  Mon Dec 20 20:55:22 2021 +0300
+++ b/globtype.pas  Sat Dec 25 21:36:11 2021 +0300
@@ -520,6 +520,7 @@
 ansistring; similarly, char becomes 
unicodechar rather than ansichar }
  m_type_helpers,{ allows the declaration of "type helper" for 
all supported types
   (primitive types, records, classes, 
interfaces) }
+ m_closures,{ anonymous routines and closures }
  m_blocks,  { support for 
http://en.wikipedia.org/wiki/Blocks_(C_language_extension) }
  m_isolike_io,  { I/O as it required by an ISO compatible 
compiler }
  m_isolike_program_para, { program parameters as it required by an ISO 
compatible compiler }
@@ -712,6 +713,7 @@
  'FINALFIELDS',
  'UNICODESTRINGS',
  'TYPEHELPERS',
+ 'CLOSURES',
  'CBLOCKS',
  'ISOIO',
  'ISOPROGRAMPARAS',
___
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel


[fpc-devel] Functors

2021-12-25 Thread Blaise--- via fpc-devel

I propose that the support for https://en.wikipedia.org/wiki/Function_object be 
added to the FPC.

A subset of such functionality already existed as a part of my implementation 
of closures, so I extended that part to implement the core feature for allowing 
functors -- overloading of the call operator: when round brackets are applied 
to an instance of a record, object/class, or interface type, they are 
translated into a call to the method Invoke of that instance. The attached 
proof-of-concept functors-1.patch allows the following test case to be compiled:
---8<---
type I = interface
procedure Invoke;
end;

type C = class(TInterfacedObject, I)
class function Invoke(const N: Integer): Integer; overload;
procedure Invoke; overload;
end;
class function C.Invoke(const N: Integer): Integer;
begin
result := N + 9
end;
procedure C.Invoke;
begin
writeln(ClassName, '.Invoke')
end;

type H = class helper for C
procedure Invoke(const S: string); overload;
end;
procedure H.Invoke(const S: string);
begin
writeln('H.Invoke("', S, '")')
end;

var aC: C;
var anI: I;
begin
aC := C.Create;
writeln( aC(33) );
aC('hello');

anI := aC;
anI()
end.
---8<---
Important design points:
1) Applying round brackets to instances does not collide with the existing 
syntax;
2) Naturally, helpers are able to turn helpees into functors;
3) Operator () cannot be applied to types -- that would clash with explicit 
type conversions;
4) Explicit empty argument lists are required -- unorthogonal to routines and 
procedural variables, but clarity must win here;
5) {$modeswitch Closures} is required (modeswitch_closures.patch from 
https://lists.freepascal.org/pipermail/fpc-devel/2021-December/044261.html) -- 
functors are closure-adjacent in the area of functional programming.

The parts that are currently missing:
1) Implicit conversion from functors to method pointers -- should be fairly 
trivial to implement;
2) Support for generics -- should be straightforward as well;
3) The OPERATOR keyword instead of PROCEDURE/FUNCTION for methods Invoke -- 
should we choose to require it -- would be somewhat more complicated.

--
βþ
# HG changeset patch
# User Blaise.ru
# Date 1640402948 -10800
#  Sat Dec 25 06:29:08 2021 +0300
+ Functors: applying round brackets to instances calls their method Invoke

diff -r 3ecaef5e9a49 -r 0ac7231ddc94 pexpr.pas
--- a/pexpr.pas Sat Dec 25 21:36:11 2021 +0300
+++ b/pexpr.pas Sat Dec 25 06:29:08 2021 +0300
@@ -2762,45 +2762,73 @@
 
   else
 begin
-  { is this a procedure variable ? }
-  if assigned(p1.resultdef) and
- (p1.resultdef.typ=procvardef) then
-begin
-  { Typenode for typecasting or expecting a procvar }
-  if (p1.nodetype=typen) or
- (
-  assigned(getprocvardef) and
-  equal_defs(p1.resultdef,getprocvardef)
- ) then
+  if assigned(p1.resultdef) then
+case p1.resultdef.typ of
+  { a procedural variable }
+  procvardef:
 begin
-  if try_to_consume(_LKLAMMER) then
+  { Typenode for typecasting or expecting a procvar }
+  if (p1.nodetype=typen) or
+ (
+  assigned(getprocvardef) and
+  equal_defs(p1.resultdef,getprocvardef)
+ ) then
 begin
-  p1:=comp_expr([ef_accept_equal]);
-  consume(_RKLAMMER);
-  p1:=ctypeconvnode.create_explicit(p1,p1.resultdef);
+  if try_to_consume(_LKLAMMER) then
+begin
+  p1:=comp_expr([ef_accept_equal]);
+  consume(_RKLAMMER);
+  
p1:=ctypeconvnode.create_explicit(p1,p1.resultdef);
+end
+  else
+again:=false
 end
   else
-again:=false
-end
+begin
+  if try_to_consume(_LKLAMMER) then
+begin
+  p2:=parse_paras(false,false,_RKLAMMER);
+  consume(_RKLAMMER);
+  p1:=ccallnode.create_procvar(p2,p1);
+  { proc():= is never possible }
+  if token=_ASSIGNMENT then
+begin
+  Message(parser_e_illegal_expression);
+  p1.free;
+  p1:=cer