Skip to content

Commit cebefc4

Browse files
committed
scope with var NAME : TYPE := EXPR do to the with-body
The inline holder var was inserted into `procdef.localst`, so it lived for the whole routine and a sibling `with var p ...` collided on `p`. Push a dedicated `tblocksymtable` for the lifetime form, hand it to the procdef on exit, and attach it to the wrapping `newblock` for debug scoping. The simple-load detection further down already covers localst/parast/static/global; add `blocksymtable` so the holder still takes the direct-ref path. `make_mangledname` walked localsymtable/parasymtable/object/recordsymtable but bailed on `blocksymtable`, hitting IE 200204175 for an anonymous record type declared inside `with var q : record ... end := ...`. Peel blocksymtable layers via `blockparentst` at the top of the walk - they are transparent for mangling. Suffix the hidden tcsym with the line number so two consecutive `with var p : T := (a; b) do ...` in the same routine produce distinct asm labels.
1 parent fb5b5f2 commit cebefc4

3 files changed

Lines changed: 101 additions & 8 deletions

File tree

compiler/pstatmnt.pas

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,8 @@ twithshadowcand = class
16301630
lifetime_name : TIDString;
16311631
lifetime_filepos : tfileposinfo;
16321632
lifetime_tcsym : tstaticvarsym;
1633+
lifetime_name_suffix : string[16];
1634+
withblockst : tblocksymtable;
16331635

16341636
procedure pushobjchild(withdef,obj:tobjectdef);
16351637
var
@@ -1669,11 +1671,20 @@ twithshadowcand = class
16691671
lifetime_autofree := false;
16701672
lifetime_init := nil;
16711673
lifetime_tcsym := nil;
1674+
withblockst := nil;
16721675
hdef := nil;
16731676
if (m_autofree in current_settings.modeswitches) and
16741677
(current_scanner.token in [_VAR,_AUTOFREE]) then
16751678
begin
16761679
lifetime_filepos := current_filepos;
1680+
{ scope the inline var (and any hidden tcsym) to the with-body
1681+
via a dedicated block symtable, so the same name can appear
1682+
in a later sibling `with var NAME` in the same routine }
1683+
if assigned(current_procinfo) then
1684+
begin
1685+
withblockst := tblocksymtable.create(symtablestack.top);
1686+
symtablestack.push(withblockst);
1687+
end;
16771688
if current_scanner.token = _VAR then
16781689
begin
16791690
{ Form C: with var NAME := [autofree] EXPR do BODY
@@ -1682,6 +1693,11 @@ twithshadowcand = class
16821693
if current_scanner.token <> _ID then
16831694
begin
16841695
consume(_ID); { trigger expected-identifier error }
1696+
if assigned(withblockst) then
1697+
begin
1698+
symtablestack.pop(withblockst);
1699+
withblockst.free;
1700+
end;
16851701
result := cerrornode.create;
16861702
exit;
16871703
end;
@@ -1711,8 +1727,13 @@ twithshadowcand = class
17111727
else if (current_scanner.token = _LKLAMMER) and
17121728
((hdef.typ = arraydef) or (hdef.typ = recorddef)) then
17131729
begin
1730+
{ suffix with line number so multiple sibling
1731+
`with var NAME : TYPE := (...)` in the same
1732+
routine don't share an asm label }
1733+
system.str(lifetime_filepos.line, lifetime_name_suffix);
17141734
lifetime_tcsym := cstaticvarsym.create(
1715-
'$with_tc_' + lifetime_name, vs_const, hdef, []);
1735+
'$with_tc_' + lifetime_name + '_' + lifetime_name_suffix,
1736+
vs_const, hdef, []);
17161737
include(lifetime_tcsym.symoptions, sp_internal);
17171738
symtablestack.top.insertsym(lifetime_tcsym);
17181739
read_typed_const(current_asmdata.asmlists[al_typedconsts],
@@ -1758,6 +1779,11 @@ ((hdef.typ = arraydef) or (hdef.typ = recorddef
17581779
if not assigned(hdef) or (hdef = generrordef) then
17591780
begin
17601781
if assigned(lifetime_init) then lifetime_init.free;
1782+
if assigned(withblockst) then
1783+
begin
1784+
symtablestack.pop(withblockst);
1785+
withblockst.free;
1786+
end;
17611787
result := cerrornode.create;
17621788
exit;
17631789
end;
@@ -1766,18 +1792,23 @@ ((hdef.typ = arraydef) or (hdef.typ = recorddef
17661792
begin
17671793
Message(parser_e_autofree_requires_class);
17681794
if assigned(lifetime_init) then lifetime_init.free;
1795+
if assigned(withblockst) then
1796+
begin
1797+
symtablestack.pop(withblockst);
1798+
withblockst.free;
1799+
end;
17691800
result := cerrornode.create;
17701801
exit;
17711802
end;
1772-
{ create the holder variable in the enclosing routine scope
1773-
(skip past any with-symtables already on the stack from
1774-
earlier multi-with entries). }
1775-
if assigned(current_procinfo) and
1776-
assigned(current_procinfo.procdef.localst) then
1803+
{ create the holder variable scoped to the with-body. With
1804+
`withblockst` pushed, symtablestack.top is the block symtable
1805+
that gets popped at the end of `with`, so a sibling `with var
1806+
NAME` can reuse the same name. }
1807+
if assigned(withblockst) then
17771808
begin
17781809
lifetime_var := clocalvarsym.create(lifetime_name, vs_value, hdef, []);
17791810
lifetime_var.register_sym;
1780-
current_procinfo.procdef.localst.insertsym(lifetime_var);
1811+
symtablestack.top.insertsym(lifetime_var);
17811812
end
17821813
else
17831814
begin
@@ -1904,7 +1935,7 @@ ((hdef.typ = arraydef) or (hdef.typ = recorddef
19041935
(
19051936
(tloadnode(hp).symtable=current_procinfo.procdef.localst) or
19061937
(tloadnode(hp).symtable=current_procinfo.procdef.parast) or
1907-
(tloadnode(hp).symtable.symtabletype in [staticsymtable,globalsymtable])
1938+
(tloadnode(hp).symtable.symtabletype in [staticsymtable,globalsymtable,blocksymtable])
19081939
) and
19091940
{ MacPas objects are mapped to classes, and the MacPas compilers
19101941
interpret with-statements with MacPas objects the same way
@@ -2097,6 +2128,19 @@ ((p.resultdef.typ=recorddef) or
20972128
withsymtablelist.free;
20982129
withsymtablelist := nil;
20992130

2131+
{ pop the with-var block symtable (if any) and hand it to the
2132+
procdef so its locals get stack space allocated; the wrapping
2133+
newblock keeps a reference for debug-info scoping }
2134+
if assigned(withblockst) then
2135+
begin
2136+
symtablestack.pop(withblockst);
2137+
if not assigned(current_procinfo.procdef.blocklocalsymtables) then
2138+
current_procinfo.procdef.blocklocalsymtables := tfpobjectlist.create(true);
2139+
current_procinfo.procdef.blocklocalsymtables.add(withblockst);
2140+
if assigned(newblock) then
2141+
tblocknode(newblock).blocksymtable := withblockst;
2142+
end;
2143+
21002144
{ FPC Unleashed: scoped-with -- rewrite any defers the body
21012145
registered (e.g. `with X do defer Foo;` or stray defers in
21022146
a begin..end body that wasn't already a defer-scope) so they

compiler/symdef.pas

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,10 @@ implementation
16721672
if not assigned(st) then
16731673
internalerror(200204212);
16741674
repeat
1675+
{ peel transparent block-scope layers - they don't contribute
1676+
to the mangled name (Delphi-style inline-var scopes) }
1677+
while st.symtabletype=blocksymtable do
1678+
st:=tblocksymtable(st).blockparentst;
16751679
{ sub procedures }
16761680
while (st.symtabletype in [localsymtable,parasymtable]) do
16771681
begin

tests/tbs/tb9003.pp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{ scoped-with: `with var NAME : TYPE := EXPR do` ends the var's scope at the
2+
end of the with-body, so the same name can be reused in a sibling with }
3+
program tb9003;
4+
5+
{$mode unleashed}
6+
7+
uses
8+
sysutils;
9+
10+
type
11+
TPoint = record
12+
x, y: integer;
13+
end;
14+
15+
var
16+
log: string;
17+
18+
procedure run;
19+
var
20+
src: TPoint;
21+
begin
22+
with var p: TPoint := (x: 1; y: 2) do
23+
log := log + IntToStr(x) + ',' + IntToStr(y) + ';';
24+
25+
src.x := 100;
26+
src.y := 200;
27+
with var p: TPoint := src do
28+
log := log + IntToStr(p.x) + ',' + IntToStr(p.y) + ';';
29+
30+
with var q: record x, y: integer; end := (x: 5; y: 6) do
31+
log := log + IntToStr(x) + ',' + IntToStr(y) + ';';
32+
33+
with var p: TPoint := (x: 7; y: 8) do
34+
log := log + IntToStr(p.x) + ',' + IntToStr(p.y) + ';';
35+
end;
36+
37+
begin
38+
log := '';
39+
run;
40+
if log <> '1,2;100,200;5,6;7,8;' then
41+
begin
42+
WriteLn('FAIL: ', log);
43+
Halt(1);
44+
end;
45+
end.

0 commit comments

Comments
 (0)