Skip to content

Commit 8058f63

Browse files
committed
Merge branch 'feat-multi-var-init' into devel
2 parents 1f3ea25 + f9b363c commit 8058f63

13 files changed

Lines changed: 310 additions & 49 deletions

compiler/globals.pas

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ interface
6868
m_property,m_default_inline,m_except,m_multiline_strings];
6969
unleashedmodeswitches = objfpcmodeswitches+[m_default_ansistring,m_underscoreisseparator,m_duplicate_names,
7070
m_advanced_records,m_array_operators,m_anonymous_functions,m_function_references,
71-
m_statement_expressions,m_array_equality,m_inline_var,m_multiline_strings];
71+
m_statement_expressions,m_array_equality,m_inline_var,m_multiline_strings,
72+
m_multi_var_init];
7273
tpmodeswitches =
7374
[m_tp7,m_tp_procvar,m_duplicate_names];
7475
{$ifdef gpc_mode}

compiler/globtype.pas

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ ttargetswitchinfo = record
561561
m_statement_expressions, { enables expressions using statements like if, case, try }
562562
m_array_equality, { enables equality operator in addition to ArrayOperators modeswitch }
563563
m_no_rtti, { hides RTTI ASCII text }
564-
m_inline_var { allow inline variable declarations inside statement blocks }
564+
m_inline_var, { allow inline variable declarations inside statement blocks }
565+
m_multi_var_init { allow initializing multiple variables in one declaration }
565566
);
566567
tmodeswitches = set of tmodeswitch;
567568

@@ -773,7 +774,8 @@ ttargetswitchinfo = record
773774
'STATEMENTEXPRESSIONS',
774775
'ARRAYEQUALITY',
775776
'NORTTI',
776-
'INLINEVARS'
777+
'INLINEVARS',
778+
'MULTIVARINIT'
777779
);
778780

779781

compiler/pdecl.pas

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -237,20 +237,41 @@ implementation
237237
skip_initialiser : boolean;
238238
varspez : tvarspez;
239239
asmtype : tasmlisttype;
240+
names : array of TIDString;
241+
positions : array of tfileposinfo;
242+
syms : array of tsym;
243+
namecount,ni : longint;
244+
tokenbuf : tdynamicarray;
245+
already_recording : boolean;
240246
begin
241247
old_block_type:=block_type;
242248
block_type:=bt_const;
243249
had_generic:=false;
244250
first:=true;
245251
repeat
246-
orgname:=current_scanner.orgpattern;
247-
filepos:=current_tokenpos;
248-
isgeneric:=not (m_delphi in current_settings.modeswitches) and (current_scanner.token=_ID) and (current_scanner.idtoken=_GENERIC);
249-
consume(_ID);
252+
{ collect one or more names separated by commas }
253+
namecount:=0;
254+
names:=nil;
255+
positions:=nil;
256+
syms:=nil;
257+
repeat
258+
setlength(names,namecount+1);
259+
setlength(positions,namecount+1);
260+
names[namecount]:=current_scanner.orgpattern;
261+
positions[namecount]:=current_tokenpos;
262+
inc(namecount);
263+
consume(_ID);
264+
until not((m_multi_var_init in current_settings.modeswitches) and
265+
try_to_consume(_COMMA));
266+
orgname:=names[0];
267+
filepos:=positions[0];
268+
isgeneric:=not (m_delphi in current_settings.modeswitches) and (namecount=1) and (current_scanner.idtoken=_GENERIC);
250269
case current_scanner.token of
251270

252271
_EQ:
253272
begin
273+
if namecount>1 then
274+
Message(parser_e_initialized_only_one_var);
254275
consume(_EQ);
255276
sym:=readconstant(orgname,filepos,nodetype);
256277
{ Support hint directives }
@@ -294,9 +315,6 @@ implementation
294315
consume(_COLON);
295316
read_anon_type(hdef,false,nil);
296317
block_type:=bt_const;
297-
{ create symbol }
298-
storetokenpos:=current_tokenpos;
299-
current_tokenpos:=filepos;
300318
if not (cs_typed_const_writable in current_settings.localswitches) then
301319
begin
302320
varspez:=vs_const;
@@ -307,26 +325,34 @@ implementation
307325
varspez:=vs_value;
308326
asmtype:=al_typedconsts;
309327
end;
310-
{ if we are dealing with structure const then we need to handle it as a
311-
structure static variable: create a symbol in unit symtable and a reference
312-
to it from the structure or linking will fail }
313-
if symtablestack.top.symtabletype in [recordsymtable,ObjectSymtable] then
328+
{ create symbol(s) }
329+
setlength(syms,namecount);
330+
for ni:=0 to namecount-1 do
314331
begin
315-
{ note: we keep hdef so that we might at least read the
316-
constant data correctly for error recovery }
317-
check_allowed_for_var_or_const(hdef,false);
318-
sym:=cfieldvarsym.create(orgname,varspez,hdef,[]);
319-
symtablestack.top.insertsym(sym);
320-
sym:=make_field_static(symtablestack.top,tfieldvarsym(sym));
321-
end
322-
else
323-
begin
324-
sym:=cstaticvarsym.create(orgname,varspez,hdef,[]);
325-
sym.visibility:=symtablestack.top.currentvisibility;
326-
symtablestack.top.insertsym(sym);
332+
storetokenpos:=current_tokenpos;
333+
current_tokenpos:=positions[ni];
334+
{ if we are dealing with structure const then we need to handle it as a
335+
structure static variable: create a symbol in unit symtable and a reference
336+
to it from the structure or linking will fail }
337+
if symtablestack.top.symtabletype in [recordsymtable,ObjectSymtable] then
338+
begin
339+
{ note: we keep hdef so that we might at least read the
340+
constant data correctly for error recovery }
341+
check_allowed_for_var_or_const(hdef,false);
342+
syms[ni]:=cfieldvarsym.create(names[ni],varspez,hdef,[]);
343+
symtablestack.top.insertsym(syms[ni]);
344+
syms[ni]:=make_field_static(symtablestack.top,tfieldvarsym(syms[ni]));
345+
end
346+
else
347+
begin
348+
syms[ni]:=cstaticvarsym.create(names[ni],varspez,hdef,[]);
349+
syms[ni].visibility:=symtablestack.top.currentvisibility;
350+
symtablestack.top.insertsym(syms[ni]);
351+
end;
352+
syms[ni].register_sym;
353+
current_tokenpos:=storetokenpos;
327354
end;
328-
sym.register_sym;
329-
current_tokenpos:=storetokenpos;
355+
sym:=syms[0];
330356
skip_initialiser:=false;
331357
{ Anonymous proctype definitions can have proc directives }
332358
if (
@@ -355,8 +381,31 @@ implementation
355381
if not skip_initialiser then
356382
begin
357383
consume(_EQ);
358-
maybe_guarantee_record_typesym(tstaticvarsym(sym).vardef,tstaticvarsym(sym).vardef.owner);
359-
read_typed_const(current_asmdata.asmlists[asmtype],tstaticvarsym(sym),in_structure);
384+
if namecount=1 then
385+
begin
386+
maybe_guarantee_record_typesym(tstaticvarsym(sym).vardef,tstaticvarsym(sym).vardef.owner);
387+
read_typed_const(current_asmdata.asmlists[asmtype],tstaticvarsym(sym),in_structure);
388+
end
389+
else
390+
begin
391+
{ record tokens for replay }
392+
already_recording:=current_scanner.is_recording_tokens;
393+
tokenbuf:=tdynamicarray.create(256);
394+
if not already_recording then
395+
current_scanner.startrecordtokens(tokenbuf);
396+
maybe_guarantee_record_typesym(tstaticvarsym(sym).vardef,tstaticvarsym(sym).vardef.owner);
397+
read_typed_const(current_asmdata.asmlists[asmtype],tstaticvarsym(sym),in_structure);
398+
if not already_recording then
399+
current_scanner.stoprecordtokens;
400+
for ni:=1 to namecount-1 do
401+
begin
402+
maybe_guarantee_record_typesym(tstaticvarsym(syms[ni]).vardef,tstaticvarsym(syms[ni]).vardef.owner);
403+
tokenbuf.seek(0);
404+
current_scanner.startreplaytokens(tokenbuf,false);
405+
read_typed_const(current_asmdata.asmlists[asmtype],tstaticvarsym(syms[ni]),in_structure);
406+
end;
407+
tokenbuf.free;
408+
end;
360409
end;
361410
end;
362411

compiler/pdecvar.pas

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,9 +1135,13 @@ implementation
11351135
vs : tabstractnormalvarsym;
11361136
tcsym : tstaticvarsym;
11371137
templist : tasmlist;
1138+
i : longint;
1139+
tokenbuf : tdynamicarray;
1140+
already_recording : boolean;
11381141
begin
11391142
vs:=tabstractnormalvarsym(sc[0]);
1140-
if sc.count>1 then
1143+
if (sc.count>1) and
1144+
not(m_multi_var_init in current_settings.modeswitches) then
11411145
Message(parser_e_initialized_only_one_var);
11421146
if vo_is_thread_var in vs.varoptions then
11431147
Message(parser_e_initialized_not_for_threadvar);
@@ -1156,7 +1160,11 @@ implementation
11561160
generation for LLVM) }
11571161
if not parse_generic then
11581162
begin
1159-
vs.defaultconstsym:=tcsym;
1163+
for i:=0 to sc.count-1 do
1164+
begin
1165+
tabstractnormalvarsym(sc[i]).defaultconstsym:=tcsym;
1166+
tabstractnormalvarsym(sc[i]).varstate:=vs_initialised;
1167+
end;
11601168
current_asmdata.asmlists[al_typedconsts].concatlist(templist);
11611169
end;
11621170
templist.free;
@@ -1165,12 +1173,33 @@ implementation
11651173
staticvarsym :
11661174
begin
11671175
maybe_guarantee_record_typesym(vs.vardef,vs.vardef.owner);
1168-
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(vs),false);
1176+
if sc.count<=1 then
1177+
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(vs),false)
1178+
else
1179+
begin
1180+
{ record tokens so we can replay them for each variable }
1181+
already_recording:=current_scanner.is_recording_tokens;
1182+
tokenbuf:=tdynamicarray.create(256);
1183+
if not already_recording then
1184+
current_scanner.startrecordtokens(tokenbuf);
1185+
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(vs),false);
1186+
if not already_recording then
1187+
current_scanner.stoprecordtokens;
1188+
for i:=1 to sc.count-1 do
1189+
begin
1190+
maybe_guarantee_record_typesym(tabstractnormalvarsym(sc[i]).vardef,tabstractnormalvarsym(sc[i]).vardef.owner);
1191+
tokenbuf.seek(0);
1192+
current_scanner.startreplaytokens(tokenbuf,false);
1193+
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(sc[i]),false);
1194+
end;
1195+
tokenbuf.free;
1196+
end;
11691197
end;
11701198
else
11711199
internalerror(200611051);
11721200
end;
1173-
vs.varstate:=vs_initialised;
1201+
for i:=0 to sc.count-1 do
1202+
tabstractnormalvarsym(sc[i]).varstate:=vs_initialised;
11741203
end;
11751204

11761205
{$ifdef gpc_mode}

compiler/pstatmnt.pas

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,6 +1822,8 @@ ((p.resultdef.typ=recorddef) or
18221822
i : longint;
18231823
sc : TFPObjectList;
18241824
old_block_type : tblock_type;
1825+
statements : tstatementnode;
1826+
tempnode : ttempcreatenode;
18251827
begin
18261828
result := nil;
18271829
consume(_VAR);
@@ -1876,22 +1878,45 @@ ((p.resultdef.typ=recorddef) or
18761878
if try_to_consume(_ASSIGNMENT) then
18771879
begin
18781880
{ Only one variable may be initialised at a time. }
1879-
if sc.count > 1 then
1881+
if (sc.count > 1) and
1882+
not(m_multi_var_init in current_settings.modeswitches) then
18801883
Message(parser_e_initialized_only_one_var);
18811884
block_type := old_block_type;
18821885
initexpr := expr(true);
1883-
tabstractnormalvarsym(sc[0]).varstate := vs_initialised;
1884-
result := cassignmentnode.create(
1885-
cloadnode.create(tsym(sc[0]), tsym(sc[0]).owner),
1886-
initexpr);
1886+
if sc.count = 1 then
1887+
begin
1888+
tabstractnormalvarsym(sc[0]).varstate := vs_initialised;
1889+
result := cassignmentnode.create(
1890+
cloadnode.create(tsym(sc[0]), tsym(sc[0]).owner),
1891+
initexpr);
1892+
end
1893+
else
1894+
begin
1895+
{ multi-var: temp := expr; a := temp; b := temp; }
1896+
do_typecheckpass(initexpr);
1897+
result := internalstatements(statements);
1898+
tempnode := ctempcreatenode.create(hdef, hdef.size, tt_persistent, true);
1899+
addstatement(statements, tempnode);
1900+
addstatement(statements, cassignmentnode.create(
1901+
ctemprefnode.create(tempnode), initexpr));
1902+
for i := 0 to sc.count - 1 do
1903+
begin
1904+
tabstractnormalvarsym(sc[i]).varstate := vs_initialised;
1905+
addstatement(statements, cassignmentnode.create(
1906+
cloadnode.create(tsym(sc[i]), tsym(sc[i]).owner),
1907+
ctemprefnode.create(tempnode)));
1908+
end;
1909+
addstatement(statements, ctempdeletenode.create(tempnode));
1910+
end;
18871911
end
18881912
else
18891913
result := cnothingnode.create;
18901914
end
18911915
else if current_scanner.token = _ASSIGNMENT then
18921916
begin
1893-
{ Type inference: var x := expr }
1894-
if sc.count > 1 then
1917+
{ Type inference: var x := expr or var x, y := expr }
1918+
if (sc.count > 1) and
1919+
not(m_multi_var_init in current_settings.modeswitches) then
18951920
Message(parser_e_initialized_only_one_var);
18961921
consume(_ASSIGNMENT);
18971922
vs := tabstractnormalvarsym(sc[0]);
@@ -1934,13 +1959,30 @@ ((p.resultdef.typ=recorddef) or
19341959
if not(nf_explicit in initexpr.flags) and is_integer(hdef) and
19351960
(torddef(hdef).ordtype in [s8bit,u8bit,s16bit,u16bit]) then
19361961
hdef := s32inttype;
1937-
vs.vardef := hdef;
1938-
vs.varstate := vs_initialised;
1939-
if vs.typ = staticvarsym then
1940-
cnodeutils.insertbssdata(tstaticvarsym(vs));
1941-
result := cassignmentnode.create(
1942-
cloadnode.create(vs, vs.owner),
1943-
initexpr);
1962+
for i := 0 to sc.count - 1 do
1963+
begin
1964+
tabstractnormalvarsym(sc[i]).vardef := hdef;
1965+
tabstractnormalvarsym(sc[i]).varstate := vs_initialised;
1966+
if tsym(sc[i]).typ = staticvarsym then
1967+
cnodeutils.insertbssdata(tstaticvarsym(sc[i]));
1968+
end;
1969+
if sc.count = 1 then
1970+
result := cassignmentnode.create(
1971+
cloadnode.create(vs, vs.owner),
1972+
initexpr)
1973+
else
1974+
begin
1975+
result := internalstatements(statements);
1976+
tempnode := ctempcreatenode.create(hdef, hdef.size, tt_persistent, true);
1977+
addstatement(statements, tempnode);
1978+
addstatement(statements, cassignmentnode.create(
1979+
ctemprefnode.create(tempnode), initexpr));
1980+
for i := 0 to sc.count - 1 do
1981+
addstatement(statements, cassignmentnode.create(
1982+
cloadnode.create(tsym(sc[i]), tsym(sc[i]).owner),
1983+
ctemprefnode.create(tempnode)));
1984+
addstatement(statements, ctempdeletenode.create(tempnode));
1985+
end;
19441986
end;
19451987
end
19461988
else

compiler/utils/ppuutils/ppudump.pp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2497,7 +2497,8 @@ (* tsettings = record
24972497
'm_statement_expressions', { enables expressions using statements like if, case, try }
24982498
'm_array_equality', { enables equality operator in addition to ArrayOperators modeswitch }
24992499
'm_no_rtti', { hides RTTI ASCII text }
2500-
'm_inline_var' { allow inline variable declarations inside statement blocks }
2500+
'm_inline_var', { allow inline variable declarations inside statement blocks }
2501+
'm_multi_var_init' { allow initializing multiple variables in one declaration }
25012502
);
25022503
{ optimizer }
25032504
optimizerswitchname : array[toptimizerswitch] of string[50] =

tests/test/tmultivarinit1.pp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{ test multi-var init: global static vars }
2+
{$mode objfpc}
3+
{$modeswitch multivarinit}
4+
5+
var
6+
a, b, c: integer = 42;
7+
x, y: boolean = true;
8+
s1, s2: string = 'hello';
9+
10+
begin
11+
if (a <> 42) or (b <> 42) or (c <> 42) then
12+
halt(1);
13+
if not x or not y then
14+
halt(2);
15+
if (s1 <> 'hello') or (s2 <> 'hello') then
16+
halt(3);
17+
{ values are independent copies }
18+
a := 0;
19+
if (b <> 42) or (c <> 42) then
20+
halt(4);
21+
s1 := 'changed';
22+
if s2 <> 'hello' then
23+
halt(5);
24+
end.

tests/test/tmultivarinit2.pp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{ test multi-var init: local vars }
2+
{$mode objfpc}
3+
{$modeswitch multivarinit}
4+
5+
procedure test;
6+
var
7+
a, b, c: integer = 10;
8+
begin
9+
if (a <> 10) or (b <> 10) or (c <> 10) then
10+
halt(1);
11+
a := 0;
12+
if (b <> 10) or (c <> 10) then
13+
halt(2);
14+
end;
15+
16+
begin
17+
test;
18+
end.

0 commit comments

Comments
 (0)