DWARF: Lineinfo for "finally" with Windows SEH-64 is wrong ( **partial patch attached** )
FPC 3.3.1 (also any earlier, since SEH64 was added).
```
procedure bar(s: AnsiString);
begin
write(s);
try
write;
finally // same line as "try"
write;
end; // prologue (first line) of the finally sub-routine
write;
end;
```
FPC will generate lineinfo that has
- `finally` at the same line as `try` (this includes the implicit finally in the `end` statement)
- the prologue of the `$fin_$` procedure containing the actual finally code has the line number of the `end` of the finally block.
This causes all kind of forward/backward jumps while debugging
----
The patch below fixes most of it.
- `finally` lines will be correct (including the implicit in `end`
- $fin subroutines prologue will have the line number of the first line of code (this could be argued, if it should instead use the `finally` line?)
#### It does not yet fix
The implicit finally $fin subroutine for dec-ref local vars. This has its actual code (not the prologue, but the code after) on the line number of the `begin` but should be at the `end` line.
Edit: patch is obsolete, see https://gitlab.com/martin_frb/fpc-src/-/compare/main...mfr%2Fdwarf-seh64-finally-linenum?from_project_id=28644964
<details>
<summary>Click to expand / old patch</summary>
```
diff --git a/compiler/nflw.pas b/compiler/nflw.pas
index 768c56f1f5..f68b7047cc 100644
--- a/compiler/nflw.pas
+++ b/compiler/nflw.pas
@@ -30,7 +30,7 @@ interface
cclasses,
node,cpubase,
symconst,symtype,symbase,symdef,symsym,
- optloop;
+ optloop,globtype;
type
{ flags used by loop nodes }
@@ -242,6 +242,7 @@ ttryexceptnodeclass = class of ttryexceptnode;
one in case no exception occurs }
ttryfinallynode = class(ttertiarynode)
implicitframe : boolean;
+ fileinfo_fin : tfileposinfo;
constructor create(l,r:tnode);virtual;reintroduce;
constructor create_implicit(l,r:tnode);virtual;
function pass_typecheck:tnode;override;
@@ -297,7 +298,7 @@ tonnodeclass = class of tonnode;
implementation
uses
- globtype,systems,constexp,compinnr,
+ systems,constexp,compinnr,
cutils,verbose,globals,ppu,
symtable,paramgr,defcmp,defutil,htypechk,pass_1,
ncal,nadd,ncon,nmem,nld,ncnv,nbas,nutils,ninl,nset,ngenutil,
@@ -2802,6 +2803,7 @@ tassignmentquery = record
inherited create(tryfinallyn,l,r,nil);
third:=nil;
implicitframe:=false;
+ fileinfo_fin:=current_filepos;
end;
@@ -2810,6 +2812,7 @@ tassignmentquery = record
inherited create(tryfinallyn,l,r,nil);
third:=nil;
implicitframe:=true;
+ fileinfo_fin:=current_filepos;
end;
@@ -2881,6 +2884,7 @@ tassignmentquery = record
begin
result:=inherited dogetcopy;
ttryfinallynode(result).implicitframe:=implicitframe;
+ ttryfinallynode(result).fileinfo_fin:=fileinfo_fin;
end;
diff --git a/compiler/pstatmnt.pas b/compiler/pstatmnt.pas
index 2be25101bd..3bf4f40a1b 100644
--- a/compiler/pstatmnt.pas
+++ b/compiler/pstatmnt.pas
@@ -896,7 +896,7 @@ ((p.resultdef.typ=recorddef) or
t:ttoken;
unit_found:boolean;
oldcurrent_exceptblock: integer;
- filepostry : tfileposinfo;
+ filepostry,filepos_fin : tfileposinfo;
begin
p_default:=nil;
p_specific:=nil;
@@ -931,6 +931,7 @@ ((p.resultdef.typ=recorddef) or
end;
p_try_block:=cblocknode.create(first);
+ filepos_fin:=current_filepos;
if try_to_consume(_FINALLY) then
begin
inc(exceptblockcounter);
@@ -938,6 +939,7 @@ ((p.resultdef.typ=recorddef) or
p_finally_block:=statements_til_end;
try_statement:=ctryfinallynode.create(p_try_block,p_finally_block);
try_statement.fileinfo:=filepostry;
+ ttryfinallynode(try_statement).fileinfo_fin:=filepos_fin;
end
else
begin
diff --git a/compiler/psub.pas b/compiler/psub.pas
index 1a1e849754..6a180d5b85 100644
--- a/compiler/psub.pas
+++ b/compiler/psub.pas
@@ -874,7 +874,7 @@ implementation
newblock : tnode;
codestatement,
newstatement : tstatementnode;
- oldfilepos : tfileposinfo;
+ oldfilepos: tfileposinfo;
is_constructor: boolean;
begin
is_constructor:=assigned(procdef.struct) and
@@ -953,6 +953,7 @@ implementation
current_filepos:=entrypos;
wrappedbody:=ctryfinallynode.create_implicit(code,finalcode);
+ ttryfinallynode(wrappedbody).fileinfo_fin:=exitpos;
{ afterconstruction must be called after finalizetemps, because it
has to execute after the temps have been finalised in case of a
refcounted class (afterconstruction decreases the refcount
diff --git a/compiler/x86_64/nx64flw.pas b/compiler/x86_64/nx64flw.pas
index 50fb9d09f6..19b4a46596 100644
--- a/compiler/x86_64/nx64flw.pas
+++ b/compiler/x86_64/nx64flw.pas
@@ -167,6 +167,7 @@ constructor tx64tryfinallynode.create(l, r: TNode);
not (df_generic in current_procinfo.procdef.defoptions) then
begin
finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r));
+ finalizepi.procdef.fileinfo := r.fileinfo; // the prologue of the $fin$ routine will get the line number of the first statement (rather than the last)
{ the init/final code is messing with asm nodes, so inform the compiler about this }
include(finalizepi.flags,pi_has_assembler_block);
{ Regvar optimization for symbols is suppressed when using exceptions, but
@@ -185,6 +186,7 @@ constructor tx64tryfinallynode.create_implicit(l, r: TNode);
InternalError(2013012501);
finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r));
+ finalizepi.procdef.fileinfo := r.fileinfo; // the prologue of the $fin$ routine will get the line number of the first statement (rather than the last)
include(finalizepi.flags,pi_do_call);
{ the init/final code is messing with asm nodes, so inform the compiler about this }
include(finalizepi.flags,pi_has_assembler_block);
@@ -213,6 +215,7 @@ function tx64tryfinallynode.dogetcopy: tnode;
begin
n.finalizepi.code:=finalizepi.code.getcopy;
n.right:=ccallnode.create(nil,tprocsym(n.finalizepi.procdef.procsym),nil,nil,[],nil);
+ n.right.fileinfo := n.fileinfo_fin;
firstpass(n.right);
end;
end;
@@ -233,6 +236,7 @@ function tx64tryfinallynode.simplify(forinline: boolean): tnode;
finalizepi.code:=right;
foreachnodestatic(right,@copy_parasize,finalizepi);
right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[],nil);
+ right.fileinfo := fileinfo_fin;
firstpass(right);
{ For implicit frames, no actual code is available at this time,
it is added later in assembler form. So store the nested procinfo
@@ -268,6 +272,7 @@ procedure tx64tryfinallynode.pass_generate_code;
oldexitlabel: tasmlabel;
oldflowcontrol: tflowcontrol;
catch_frame: boolean;
+ oldpos: tfileposinfo;
begin
if (target_info.system<>system_x86_64_win64) then
begin
@@ -330,6 +335,7 @@ procedure tx64tryfinallynode.pass_generate_code;
{ finallylabel is only used in implicit frames as an exit point from nested try..finally
statements, if any. To prevent finalizer from being executed twice, it must come before
endtrylabel (bug #34772) }
+
if catch_frame then
begin
current_asmdata.getjumplabel(templabel);
@@ -352,10 +358,12 @@ procedure tx64tryfinallynode.pass_generate_code;
else
begin
{ same as emit_nop but using finallylabel instead of dummy }
+ current_filepos:=right.fileinfo;
cg.a_label(current_asmdata.CurrAsmList,finallylabel);
finallylabel.increfs;
current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP,S_NO));
cg.a_label(current_asmdata.CurrAsmList,endtrylabel);
+ right.fileinfo:=current_filepos;
end;
{ i32913 - if the try..finally block is also inside a try..finally or
```
</details>
issue