Skip to content

Commit cfbd996

Browse files
committed
Merge branch 'devel'
2 parents 7b57387 + 9f81b54 commit cfbd996

47 files changed

Lines changed: 3444 additions & 94 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/defcmp.pas

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,13 @@ interface
192192
{ procdefs the parameters defs shall belong to. }
193193
function equal_genfunc_paradefs(fwdef,currdef:tdef;fwpdst,currpdst:tsymtable):boolean;
194194

195+
function tuples_have_equal_shape(a,b:trecorddef):boolean;
196+
195197

196198
implementation
197199

198200
uses
201+
cutils,
199202
verbose,systems,constexp,
200203
symtable,symsym,symcpu,
201204
defutil,symutil;
@@ -213,6 +216,60 @@ implementation
213216
end;
214217

215218

219+
{ true if a tuple record uses auto-generated _1, _2, _3 ... field names }
220+
function is_positional_tuple(t:trecorddef):boolean;
221+
var
222+
i,idx : longint;
223+
sym : tsym;
224+
begin
225+
result:=false;
226+
idx:=0;
227+
for i:=0 to t.symtable.symlist.count-1 do
228+
begin
229+
sym:=tsym(t.symtable.symlist[i]);
230+
if sym.typ<>fieldvarsym then
231+
continue;
232+
inc(idx);
233+
if sym.name<>'_'+tostr(idx) then
234+
exit;
235+
end;
236+
result:=idx>0;
237+
end;
238+
239+
240+
{ two tuple records are shape-equal if the fields line up by index
241+
with equal types. Field names must also match, UNLESS one side is
242+
positional (auto _1, _2 ...), in which case names on the other
243+
side are ignored. }
244+
function tuples_have_equal_shape(a,b:trecorddef):boolean;
245+
var
246+
lista,listb : tfphashobjectlist;
247+
i : longint;
248+
fa,fb : tfieldvarsym;
249+
ignore_names : boolean;
250+
begin
251+
result:=false;
252+
lista:=a.symtable.symlist;
253+
listb:=b.symtable.symlist;
254+
if lista.count<>listb.count then
255+
exit;
256+
ignore_names:=is_positional_tuple(a) or is_positional_tuple(b);
257+
for i:=0 to lista.count-1 do
258+
begin
259+
if (tsym(lista[i]).typ<>fieldvarsym) or
260+
(tsym(listb[i]).typ<>fieldvarsym) then
261+
exit;
262+
fa:=tfieldvarsym(lista[i]);
263+
fb:=tfieldvarsym(listb[i]);
264+
if (not ignore_names) and (fa.name<>fb.name) then
265+
exit;
266+
if not equal_defs(fa.vardef,fb.vardef) then
267+
exit;
268+
end;
269+
result:=true;
270+
end;
271+
272+
216273
function compare_defs_ext(def_from,def_to : tdef;
217274
fromtreetype : tnodetype;
218275
var doconv : tconverttype;
@@ -307,6 +364,20 @@ implementation
307364
exit;
308365
end;
309366

367+
{ two records where at least one is a tuple: structural compat
368+
when shapes match (field count, names, types in order).
369+
Positional tuples ignore names on either side. }
370+
if (def_from.typ=recorddef) and
371+
(def_to.typ=recorddef) and
372+
((df_tuple in def_from.defoptions) or
373+
(df_tuple in def_to.defoptions)) and
374+
tuples_have_equal_shape(trecorddef(def_from),trecorddef(def_to)) then
375+
begin
376+
doconv:=tc_equal;
377+
compare_defs_ext:=te_equal;
378+
exit;
379+
end;
380+
310381
if cdo_strict_undefined_check in cdoptions then
311382
begin
312383
{ two different undefined defs are not considered equal }

compiler/globals.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ 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_tuples,m_match,m_multiline_strings,
7272
m_multi_var_init,m_unleashed];
7373
tpmodeswitches =
7474
[m_tp7,m_tp_procvar,m_duplicate_names];

compiler/globtype.pas

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,9 @@ ttargetswitchinfo = record
562562
m_array_equality, { enables equality operator in addition to ArrayOperators modeswitch }
563563
m_no_rtti, { hides RTTI ASCII text }
564564
m_inline_var, { allow inline variable declarations inside statement blocks }
565-
m_multi_var_init { allow initializing multiple variables in one declaration }
565+
m_multi_var_init, { allow initializing multiple variables in one declaration }
566+
m_tuples, { allow anonymous tuple types as function return types and related literals }
567+
m_match { match statement with first-match and fallthrough modes }
566568
);
567569
tmodeswitches = set of tmodeswitch;
568570

@@ -775,7 +777,9 @@ ttargetswitchinfo = record
775777
'ARRAYEQUALITY',
776778
'NORTTI',
777779
'INLINEVARS',
778-
'MULTIVARINIT'
780+
'MULTIVARINIT',
781+
'TUPLES',
782+
'MATCH'
779783
);
780784

781785

compiler/msg/errore.msg

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ scan_e_improperly_indented_multiline_string=02120_E_Incorrectly indented multi-l
498498
#
499499
# Parser
500500
#
501-
# 03376 is the last used one
501+
# 03377 is the last used one
502502
#
503503
% \section{Parser messages}
504504
% This section lists all parser messages. The parser takes care of the
@@ -1728,11 +1728,13 @@ parser_w_with_shadowed_field=03376_W_Field "$1" in WITH entry shadows an earlier
17281728
% Two entries in the same \var{with} clause expose a field with the same name.
17291729
% The later entry hides the earlier one, which is most likely unintended.
17301730
%
1731+
parser_e_tuple_needs_type=03377_E_Tuple fields need type annotation, e.g. (x, y, z: Integer)
1732+
%
17311733
% \end{description}
17321734
%
17331735
# Type Checking
17341736
#
1735-
# 04134 is the last used one
1737+
# 04136 is the last used one
17361738
#
17371739
% \section{Type checking errors}
17381740
% This section lists all errors that can occur when type checking is
@@ -2194,6 +2196,13 @@ type_e_cannot_determine_size_of_wasm_reference_type=04133_E_WebAssembly referenc
21942196
% WebAssembly reference types are opaque, meaning neither their size, nor their bit pattern can be observed.
21952197
type_e_ordinal_or_pointer_expr_expected=04134_E_Ordinal or pointer expression expected
21962198
% The expression must be of an ordinal or pointer type.
2199+
type_e_tuples_not_comparable=04135_E_Tuples have different shapes and cannot be compared
2200+
% The two tuple operands have different field counts or field types,
2201+
% so ordering comparison is not possible.
2202+
type_e_tuple_index_must_be_const=04136_E_Tuple index must be a compile-time constant
2203+
% Tuple fields can have different types, so the result type of t[i]
2204+
% cannot be determined when i is a variable. Use a constant index
2205+
% (t[0], t[1]) or named access (t._1, t._2).
21972206
%
21982207
% \end{description}
21992208
#

compiler/nadd.pas

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,12 +2194,166 @@ implementation
21942194
end;
21952195

21962196

2197+
{ builds a field-by-field equality/inequality chain for two tuple
2198+
records of identical shape; expects l and r already typechecked }
2199+
function build_tuple_eq_chain(l,r:tnode;wantequal:boolean):tnode;
2200+
var
2201+
recdef : trecorddef;
2202+
sym : tsym;
2203+
fsym : tfieldvarsym;
2204+
i,fcount : longint;
2205+
cmp,subl,subr,chain : tnode;
2206+
begin
2207+
recdef:=trecorddef(l.resultdef);
2208+
chain:=nil;
2209+
fcount:=0;
2210+
for i:=0 to recdef.symtable.symlist.count-1 do
2211+
begin
2212+
sym:=tsym(recdef.symtable.symlist[i]);
2213+
if sym.typ<>fieldvarsym then
2214+
continue;
2215+
fsym:=tfieldvarsym(sym);
2216+
subl:=csubscriptnode.create(fsym,l.getcopy);
2217+
subr:=csubscriptnode.create(fsym,r.getcopy);
2218+
if wantequal then
2219+
cmp:=caddnode.create(equaln,subl,subr)
2220+
else
2221+
cmp:=caddnode.create(unequaln,subl,subr);
2222+
if chain=nil then
2223+
chain:=cmp
2224+
else if wantequal then
2225+
chain:=caddnode.create(andn,chain,cmp)
2226+
else
2227+
chain:=caddnode.create(orn,chain,cmp);
2228+
inc(fcount);
2229+
end;
2230+
if chain=nil then
2231+
chain:=cordconstnode.create(ord(wantequal),pasbool1type,false);
2232+
l.free;
2233+
r.free;
2234+
result:=chain;
2235+
end;
2236+
2237+
2238+
{ Recursively builds a lexicographic comparison chain:
2239+
(l.fi < r.fi) OR ((l.fi = r.fi) AND tail), with the last level
2240+
using last_op (< or <=). The caller provides the fieldsyms list
2241+
in order and starts at field_idx=0. }
2242+
function build_lex_rest(l,r:tnode;const fields:array of tfieldvarsym;
2243+
field_idx:longint;last_op:tnodetype):tnode;
2244+
var
2245+
subl,subr,subl_eq,subr_eq,eqcmp,ltcmp,tail : tnode;
2246+
begin
2247+
if field_idx=high(fields) then
2248+
begin
2249+
subl:=csubscriptnode.create(fields[field_idx],l.getcopy);
2250+
subr:=csubscriptnode.create(fields[field_idx],r.getcopy);
2251+
result:=caddnode.create(last_op,subl,subr);
2252+
exit;
2253+
end;
2254+
subl:=csubscriptnode.create(fields[field_idx],l.getcopy);
2255+
subr:=csubscriptnode.create(fields[field_idx],r.getcopy);
2256+
ltcmp:=caddnode.create(ltn,subl,subr);
2257+
subl_eq:=csubscriptnode.create(fields[field_idx],l.getcopy);
2258+
subr_eq:=csubscriptnode.create(fields[field_idx],r.getcopy);
2259+
eqcmp:=caddnode.create(equaln,subl_eq,subr_eq);
2260+
tail:=build_lex_rest(l,r,fields,field_idx+1,last_op);
2261+
result:=caddnode.create(orn,ltcmp,caddnode.create(andn,eqcmp,tail));
2262+
end;
2263+
2264+
2265+
{ Expands a tuple < / <= / > / >= comparison into a lexicographic
2266+
chain. > and >= are rewritten by swapping l and r so the chain
2267+
builder only needs to handle < and <=. }
2268+
function build_tuple_lex_chain(l,r:tnode;op:tnodetype):tnode;
2269+
var
2270+
recdef : trecorddef;
2271+
sym : tsym;
2272+
fields : array of tfieldvarsym;
2273+
i,fcount : longint;
2274+
last_op : tnodetype;
2275+
tmp : tnode;
2276+
begin
2277+
if op in [gtn,gten] then
2278+
begin
2279+
tmp:=l; l:=r; r:=tmp;
2280+
if op=gtn then op:=ltn else op:=lten;
2281+
end;
2282+
last_op:=op;
2283+
recdef:=trecorddef(l.resultdef);
2284+
fcount:=0;
2285+
setlength(fields,recdef.symtable.symlist.count);
2286+
for i:=0 to recdef.symtable.symlist.count-1 do
2287+
begin
2288+
sym:=tsym(recdef.symtable.symlist[i]);
2289+
if sym.typ=fieldvarsym then
2290+
begin
2291+
fields[fcount]:=tfieldvarsym(sym);
2292+
inc(fcount);
2293+
end;
2294+
end;
2295+
setlength(fields,fcount);
2296+
if fcount=0 then
2297+
result:=cordconstnode.create(0,pasbool1type,false)
2298+
else
2299+
result:=build_lex_rest(l,r,fields,0,last_op);
2300+
l.free;
2301+
r.free;
2302+
end;
2303+
2304+
21972305
function taddnode.pass_typecheck:tnode;
21982306
begin
21992307
{ This function is small to keep the stack small for recursive of
22002308
large + operations }
22012309
typecheckpass(left);
22022310
typecheckpass(right);
2311+
{ tuple equality / inequality }
2312+
if (nodetype in [equaln,unequaln]) and
2313+
assigned(left.resultdef) and assigned(right.resultdef) and
2314+
(left.resultdef.typ=recorddef) and (right.resultdef.typ=recorddef) and
2315+
(df_tuple in left.resultdef.defoptions) and
2316+
(df_tuple in right.resultdef.defoptions) then
2317+
begin
2318+
if tuples_have_equal_shape(trecorddef(left.resultdef),trecorddef(right.resultdef)) then
2319+
begin
2320+
result:=build_tuple_eq_chain(left,right,nodetype=equaln);
2321+
left:=nil;
2322+
right:=nil;
2323+
typecheckpass(result);
2324+
end
2325+
else
2326+
begin
2327+
{ different shapes: always false for =, always true for <> }
2328+
left.free; left:=nil;
2329+
right.free; right:=nil;
2330+
result:=cordconstnode.create(ord(nodetype=unequaln),pasbool1type,false);
2331+
end;
2332+
exit;
2333+
end;
2334+
{ tuple lexicographic ordering: < <= > >= }
2335+
if (nodetype in [ltn,lten,gtn,gten]) and
2336+
assigned(left.resultdef) and assigned(right.resultdef) and
2337+
(left.resultdef.typ=recorddef) and (right.resultdef.typ=recorddef) and
2338+
(df_tuple in left.resultdef.defoptions) and
2339+
(df_tuple in right.resultdef.defoptions) then
2340+
begin
2341+
if tuples_have_equal_shape(trecorddef(left.resultdef),trecorddef(right.resultdef)) then
2342+
begin
2343+
result:=build_tuple_lex_chain(left,right,nodetype);
2344+
left:=nil;
2345+
right:=nil;
2346+
typecheckpass(result);
2347+
end
2348+
else
2349+
begin
2350+
Message(type_e_tuples_not_comparable);
2351+
left.free; left:=nil;
2352+
right.free; right:=nil;
2353+
result:=cordconstnode.create(0,pasbool1type,false);
2354+
end;
2355+
exit;
2356+
end;
22032357
result:=pass_typecheck_internal;
22042358
end;
22052359

compiler/ngenutil.pas

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,15 @@ implementation
523523
end;
524524
{ units have separate code for initialization and finalization }
525525
potype_unitfinalize: ;
526-
{ program init/final is generated in separate procedure }
527-
potype_proginit: ;
526+
{ program init/final is generated in separate procedure,
527+
but block-scoped local vars (from inline var / for-in
528+
destructuring) live on the stack and need explicit init }
529+
potype_proginit:
530+
begin
531+
if assigned(pd.blocklocalsymtables) then
532+
for blk_i:=0 to pd.blocklocalsymtables.count-1 do
533+
TSymtable(pd.blocklocalsymtables[blk_i]).SymList.ForEachCall(@sym_maybe_initialize,@stat);
534+
end;
528535
else
529536
begin
530537
current_procinfo.procdef.localst.SymList.ForEachCall(@sym_maybe_initialize,@stat);
@@ -560,8 +567,14 @@ implementation
560567
end;
561568
{ units/progs have separate code for initialization and finalization }
562569
potype_unitinit: ;
563-
{ program init/final is generated in separate procedure }
564-
potype_proginit: ;
570+
{ program init/final is generated in separate procedure,
571+
but block-scoped local vars need explicit finalization }
572+
potype_proginit:
573+
begin
574+
if assigned(pd.blocklocalsymtables) then
575+
for blk_i:=0 to pd.blocklocalsymtables.count-1 do
576+
TSymtable(pd.blocklocalsymtables[blk_i]).SymList.ForEachCall(@local_varsyms_finalize,@stat);
577+
end;
565578
else
566579
begin
567580
current_procinfo.procdef.localst.SymList.ForEachCall(@local_varsyms_finalize,@stat);

0 commit comments

Comments
 (0)