summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsvenbarth <svenbarth@3ad0048d-3df7-0310-abae-a5850022a9f2>2020-04-25 22:12:35 +0000
committersvenbarth <svenbarth@3ad0048d-3df7-0310-abae-a5850022a9f2>2020-04-25 22:12:35 +0000
commit3ba1f1aac5cf84e29a21dc14356e79bc03e12ffc (patch)
treefdfc7b0ffcc2e8472da5616c2449514d3942d075
parent8b0c364e7dbc9f36cf350ffc3fb6ac477e79bd2e (diff)
downloadfpc-3ba1f1aac5cf84e29a21dc14356e79bc03e12ffc.tar.gz
* fix for Mantis #35140: apply patch by Ryan Joseph together with some further changes by me to add support for constant parameters in generics
+ added tests git-svn-id: https://svn.freepascal.org/svn/fpc/trunk@45080 3ad0048d-3df7-0310-abae-a5850022a9f2
-rw-r--r--compiler/defcmp.pas8
-rw-r--r--compiler/htypechk.pas2
-rw-r--r--compiler/ncnv.pas3
-rw-r--r--compiler/ncon.pas41
-rw-r--r--compiler/nmat.pas5
-rw-r--r--compiler/node.pas20
-rw-r--r--compiler/nset.pas5
-rw-r--r--compiler/pass_1.pas5
-rw-r--r--compiler/pdecl.pas26
-rw-r--r--compiler/pdecsub.pas32
-rw-r--r--compiler/pdecvar.pas4
-rw-r--r--compiler/pexpr.pas17
-rw-r--r--compiler/pgentype.pas6
-rw-r--r--compiler/pgenutil.pas695
-rw-r--r--compiler/pparautl.pas75
-rw-r--r--compiler/ppu.pas2
-rw-r--r--compiler/pstatmnt.pas4
-rw-r--r--compiler/ptype.pas12
-rw-r--r--compiler/symconst.pas11
-rw-r--r--compiler/symdef.pas50
-rw-r--r--compiler/symsym.pas37
-rw-r--r--compiler/utils/ppuutils/ppudump.pp11
-rw-r--r--tests/test/tgenconst1.pp39
-rw-r--r--tests/test/tgenconst10.pp14
-rw-r--r--tests/test/tgenconst11.pp13
-rw-r--r--tests/test/tgenconst12.pp15
-rw-r--r--tests/test/tgenconst13.pp51
-rw-r--r--tests/test/tgenconst14.pp42
-rw-r--r--tests/test/tgenconst15.pp15
-rw-r--r--tests/test/tgenconst16.pp79
-rw-r--r--tests/test/tgenconst17.pp27
-rw-r--r--tests/test/tgenconst18.pp12
-rw-r--r--tests/test/tgenconst19.pp24
-rw-r--r--tests/test/tgenconst2.pp14
-rw-r--r--tests/test/tgenconst20.pp24
-rw-r--r--tests/test/tgenconst21.pp16
-rw-r--r--tests/test/tgenconst22.pp16
-rw-r--r--tests/test/tgenconst23.pp19
-rw-r--r--tests/test/tgenconst24.pp19
-rw-r--r--tests/test/tgenconst25.pp18
-rw-r--r--tests/test/tgenconst26.pp18
-rw-r--r--tests/test/tgenconst27.pp17
-rw-r--r--tests/test/tgenconst28.pp17
-rw-r--r--tests/test/tgenconst29.pp14
-rw-r--r--tests/test/tgenconst3.pp20
-rw-r--r--tests/test/tgenconst30.pp14
-rw-r--r--tests/test/tgenconst4.pp15
-rw-r--r--tests/test/tgenconst5.pp28
-rw-r--r--tests/test/tgenconst6.pp25
-rw-r--r--tests/test/tgenconst7.pp14
-rw-r--r--tests/test/tgenconst8.pp14
-rw-r--r--tests/test/tgenconst9.pp12
52 files changed, 1462 insertions, 274 deletions
diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 67f3119557..e4e15182ab 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -345,9 +345,13 @@ implementation
internalerror(2012091302);
symfrom:=ttypesym(tstoreddef(def_from).genericparas[i]);
symto:=ttypesym(tstoreddef(def_to).genericparas[i]);
- if not (symfrom.typ=typesym) or not (symto.typ=typesym) then
+ if not (symfrom.typ in [typesym,constsym]) or not (symto.typ in [typesym,constsym]) then
internalerror(2012121401);
- if not equal_defs(ttypesym(symfrom).typedef,ttypesym(symto).typedef) then
+ if symto.typ<>symfrom.typ then
+ diff:=true
+ else if (symfrom.typ=constsym) and (symto.typ=constsym) and not equal_constsym(tconstsym(symfrom),tconstsym(symto),true) then
+ diff:=true
+ else if not equal_defs(ttypesym(symfrom).typedef,ttypesym(symto).typedef) then
diff:=true;
if diff then
break;
diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
index 743ec69b10..b989fb2259 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2779,7 +2779,7 @@ implementation
internalerror(2015060301);
{ check whether the given parameters are compatible
to the def's constraints }
- if not check_generic_constraints(pd,spezcontext.genericdeflist,spezcontext.poslist) then
+ if not check_generic_constraints(pd,spezcontext.paramlist,spezcontext.poslist) then
exit;
def:=generate_specialization_phase2(spezcontext,pd,false,'');
case def.typ of
diff --git a/compiler/ncnv.pas b/compiler/ncnv.pas
index 32053b4adb..deb2ab96c3 100644
--- a/compiler/ncnv.pas
+++ b/compiler/ncnv.pas
@@ -3102,7 +3102,8 @@ implementation
{ for constant values on absolute variables, swapping is required }
if (target_info.endian = endian_big) and (nf_absolute in flags) then
swap_const_value(tordconstnode(left).value,tordconstnode(left).resultdef.size);
- adaptrange(resultdef,tordconstnode(left).value,([nf_internal,nf_absolute]*flags)<>[],nf_explicit in flags,cs_check_range in localswitches);
+ if not(nf_generic_para in flags) then
+ adaptrange(resultdef,tordconstnode(left).value,([nf_internal,nf_absolute]*flags)<>[],nf_explicit in flags,cs_check_range in localswitches);
{ swap value back, but according to new type }
if (target_info.endian = endian_big) and (nf_absolute in flags) then
swap_const_value(tordconstnode(left).value,resultdef.size);
diff --git a/compiler/ncon.pas b/compiler/ncon.pas
index 61255c6c48..f723750917 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -306,6 +306,7 @@ implementation
p1 : tnode;
len : longint;
pc : pchar;
+ value_set : pconstset;
begin
p1:=nil;
case p.consttyp of
@@ -331,18 +332,50 @@ implementation
constwstring :
p1:=cstringconstnode.createunistr(pcompilerwidestring(p.value.valueptr));
constreal :
- p1:=crealconstnode.create(pbestreal(p.value.valueptr)^,p.constdef);
+ begin
+ if (sp_generic_para in p.symoptions) and not (sp_generic_const in p.symoptions) then
+ p1:=crealconstnode.create(default(bestreal),p.constdef)
+ else
+ p1:=crealconstnode.create(pbestreal(p.value.valueptr)^,p.constdef);
+ end;
constset :
- p1:=csetconstnode.create(pconstset(p.value.valueptr),p.constdef);
+ begin
+ if sp_generic_const in p.symoptions then
+ begin
+ new(value_set);
+ value_set^:=pconstset(p.value.valueptr)^;
+ p1:=csetconstnode.create(value_set,p.constdef);
+ end
+ else if sp_generic_para in p.symoptions then
+ begin
+ new(value_set);
+ p1:=csetconstnode.create(value_set,p.constdef);
+ end
+ else
+ p1:=csetconstnode.create(pconstset(p.value.valueptr),p.constdef);
+ end;
constpointer :
- p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
+ begin
+ if sp_generic_para in p.symoptions then
+ p1:=cpointerconstnode.create(default(tconstptruint),p.constdef)
+ else
+ p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
+ end;
constnil :
p1:=cnilnode.create;
constguid :
- p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
+ begin
+ if sp_generic_para in p.symoptions then
+ p1:=cguidconstnode.create(default(tguid))
+ else
+ p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
+ end;
else
internalerror(200205103);
end;
+ { transfer generic param flag from symbol to node }
+ if sp_generic_para in p.symoptions then
+ include(p1.flags,nf_generic_para);
genconstsymtree:=p1;
end;
diff --git a/compiler/nmat.pas b/compiler/nmat.pas
index b4a51f2521..91ce6726d2 100644
--- a/compiler/nmat.pas
+++ b/compiler/nmat.pas
@@ -131,7 +131,10 @@ implementation
end;
if rv = 0 then
begin
- Message(parser_e_division_by_zero);
+ { if the node is derived from a generic const parameter
+ then don't issue an error }
+ if not (nf_generic_para in flags) then
+ Message(parser_e_division_by_zero);
{ recover }
tordconstnode(right).value := 1;
end;
diff --git a/compiler/node.pas b/compiler/node.pas
index 0c2ba4efbb..2b81f63377 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -276,10 +276,13 @@ interface
nf_block_with_exit,
{ tloadvmtaddrnode }
- nf_ignore_for_wpo { we know that this loadvmtaddrnode cannot be used to construct a class instance }
+ nf_ignore_for_wpo, { we know that this loadvmtaddrnode cannot be used to construct a class instance }
- { WARNING: there are now 31 elements in this type, and a set of this
- type is written to the PPU. So before adding more than 32 elements,
+ { node is derived from generic parameter }
+ nf_generic_para
+
+ { WARNING: there are now 32 elements in this type, and a set of this
+ type is written to the PPU. So before adding more elements,
either move some flags to specific nodes, or stream a normalset
to the ppu
}
@@ -1380,6 +1383,9 @@ implementation
constructor tunarynode.create(t:tnodetype;l : tnode);
begin
inherited create(t);
+ { transfer generic paramater flag }
+ if assigned(l) and (nf_generic_para in l.flags) then
+ include(flags,nf_generic_para);
left:=l;
end;
@@ -1482,7 +1488,10 @@ implementation
constructor tbinarynode.create(t:tnodetype;l,r : tnode);
begin
inherited create(t,l);
- right:=r
+ { transfer generic paramater flag }
+ if assigned(r) and (nf_generic_para in r.flags) then
+ include(flags,nf_generic_para);
+ right:=r;
end;
@@ -1635,6 +1644,9 @@ implementation
constructor ttertiarynode.create(_t:tnodetype;l,r,t : tnode);
begin
inherited create(_t,l,r);
+ { transfer generic parameter flag }
+ if assigned(t) and (nf_generic_para in t.flags) then
+ include(flags,nf_generic_para);
third:=t;
end;
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 0832b97b20..0b31de9342 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -424,8 +424,9 @@ implementation
{ both types must be compatible }
if compare_defs(left.resultdef,right.resultdef,left.nodetype)=te_incompatible then
IncompatibleTypes(left.resultdef,right.resultdef);
- { Check if only when its a constant set }
- if (left.nodetype=ordconstn) and (right.nodetype=ordconstn) then
+ { check if only when its a constant set and
+ ignore range nodes which are generic parameter derived }
+ if not (nf_generic_para in flags) and (left.nodetype=ordconstn) and (right.nodetype=ordconstn) then
begin
{ upper limit must be greater or equal than lower limit }
if (tordconstnode(left).value>tordconstnode(right).value) and
diff --git a/compiler/pass_1.pas b/compiler/pass_1.pas
index adbc6d1a42..0e80d10f22 100644
--- a/compiler/pass_1.pas
+++ b/compiler/pass_1.pas
@@ -62,6 +62,7 @@ implementation
procedure typecheckpass_internal_loop(var p : tnode; out node_changed: boolean);
var
hp : tnode;
+ oldflags : tnodeflags;
begin
codegenerror:=false;
repeat
@@ -73,9 +74,13 @@ implementation
if assigned(hp) then
begin
node_changed:=true;
+ oldflags:=p.flags;
p.free;
{ switch to new node }
p:=hp;
+ { transfer generic paramter flag }
+ if nf_generic_para in oldflags then
+ include(p.flags,nf_generic_para);
end;
until not assigned(hp) or
assigned(hp.resultdef);
diff --git a/compiler/pdecl.pas b/compiler/pdecl.pas
index e42574044e..1c52d152e3 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -135,7 +135,10 @@ implementation
setconstn :
begin
new(ps);
- ps^:=tsetconstnode(p).value_set^;
+ if assigned(tsetconstnode(p).value_set) then
+ ps^:=tsetconstnode(p).value_set^
+ else
+ ps^:=[];
hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
end;
pointerconstn :
@@ -185,8 +188,22 @@ implementation
end;
end;
else
- Message(parser_e_illegal_expression);
+ begin
+ { the node is from a generic parameter constant and is
+ untyped so we need to pass a placeholder constant
+ instead of givng an error }
+ if nf_generic_para in p.flags then
+ hp:=cconstsym.create_ord(orgname,constnil,0,p.resultdef)
+ else
+ Message(parser_e_illegal_expression);
+ end;
end;
+ { transfer generic param flag from node to symbol }
+ if nf_generic_para in p.flags then
+ begin
+ include(hp.symoptions,sp_generic_const);
+ include(hp.symoptions,sp_generic_para);
+ end;
current_tokenpos:=storetokenpos;
p.free;
readconstant:=hp;
@@ -716,8 +733,9 @@ implementation
{ we are not freeing the type parameters, so register them }
for i:=0 to generictypelist.count-1 do
begin
- ttypesym(generictypelist[i]).register_sym;
- tstoreddef(ttypesym(generictypelist[i]).typedef).register_def;
+ tstoredsym(generictypelist[i]).register_sym;
+ if tstoredsym(generictypelist[i]).typ=typesym then
+ tstoreddef(ttypesym(generictypelist[i]).typedef).register_def;
end;
str(generictypelist.Count,s);
diff --git a/compiler/pdecsub.pas b/compiler/pdecsub.pas
index 7e750eb8bc..45b42857f5 100644
--- a/compiler/pdecsub.pas
+++ b/compiler/pdecsub.pas
@@ -628,7 +628,7 @@ implementation
for i:=0 to genericparams.count-1 do
begin
sym:=ttypesym(genericparams[i]);
- if tstoreddef(sym.typedef).is_registered then
+ if (sym.typ<>constsym) and tstoreddef(sym.typedef).is_registered then
begin
sym.typedef.free;
sym.typedef:=nil;
@@ -813,9 +813,11 @@ implementation
function check_generic_parameters(def:tstoreddef):boolean;
var
i : longint;
- decltype,
- impltype : ttypesym;
+ declsym,
+ implsym : tsym;
+ impltype : ttypesym absolute implsym;
implname : tsymstr;
+ fileinfo : tfileposinfo;
begin
result:=true;
if not assigned(def.genericparas) then
@@ -826,18 +828,23 @@ implementation
internalerror(2018090104);
for i:=0 to def.genericparas.count-1 do
begin
- decltype:=ttypesym(def.genericparas[i]);
- impltype:=ttypesym(genericparams[i]);
+ declsym:=tsym(def.genericparas[i]);
+ implsym:=tsym(genericparams[i]);
implname:=upper(genericparams.nameofindex(i));
- if decltype.name<>implname then
+ if declsym.name<>implname then
begin
- messagepos1(impltype.fileinfo,sym_e_generic_type_param_mismatch,impltype.realname);
- messagepos1(decltype.fileinfo,sym_e_generic_type_param_decl,decltype.realname);
+ messagepos1(implsym.fileinfo,sym_e_generic_type_param_mismatch,implsym.realname);
+ messagepos1(declsym.fileinfo,sym_e_generic_type_param_decl,declsym.realname);
result:=false;
end;
- if df_genconstraint in impltype.typedef.defoptions then
+ if ((implsym.typ=typesym) and (df_genconstraint in impltype.typedef.defoptions)) or
+ (implsym.typ=constsym) then
begin
- messagepos(tstoreddef(impltype.typedef).genconstraintdata.fileinfo,parser_e_generic_constraints_not_allowed_here);
+ if implsym.typ=constsym then
+ fileinfo:=impltype.fileinfo
+ else
+ fileinfo:=tstoreddef(impltype.typedef).genconstraintdata.fileinfo;
+ messagepos(fileinfo,parser_e_generic_constraints_not_allowed_here);
result:=false;
end;
end;
@@ -1127,8 +1134,9 @@ implementation
{ register the parameters }
for i:=0 to genericparams.count-1 do
begin
- ttypesym(genericparams[i]).register_sym;
- tstoreddef(ttypesym(genericparams[i]).typedef).register_def;
+ tsym(genericparams[i]).register_sym;
+ if tsym(genericparams[i]).typ=typesym then
+ tstoreddef(ttypesym(genericparams[i]).typedef).register_def;
end;
insert_generic_parameter_types(pd,nil,genericparams);
{ the list is no longer required }
diff --git a/compiler/pdecvar.pas b/compiler/pdecvar.pas
index 85c47b5132..57f2cf5c1f 100644
--- a/compiler/pdecvar.pas
+++ b/compiler/pdecvar.pas
@@ -1707,6 +1707,10 @@ implementation
hdef:=generrordef;
end;
+ { field type is a generic param so set a flag in the struct }
+ if assigned(hdef.typesym) and (sp_generic_para in hdef.typesym.symoptions) then
+ include(current_structdef.defoptions,df_has_generic_fields);
+
{ Process procvar directives }
if maybe_parse_proc_directives(hdef) then
semicoloneaten:=true;
diff --git a/compiler/pexpr.pas b/compiler/pexpr.pas
index 250c96c668..95eb9a7eb8 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -447,6 +447,9 @@ implementation
{ no packed bit support for these things }
if l=in_bitsizeof_x then
statement_syssym:=caddnode.create(muln,statement_syssym,cordconstnode.create(8,sizesinttype,true));
+ { type sym is a generic parameter }
+ if assigned(p1.resultdef.typesym) and (sp_generic_para in p1.resultdef.typesym.symoptions) then
+ include(statement_syssym.flags,nf_generic_para);
end
else
begin
@@ -467,6 +470,9 @@ implementation
end
else
statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sizesinttype,true);
+ { type def is a struct with generic fields }
+ if df_has_generic_fields in p1.resultdef.defoptions then
+ include(statement_syssym.flags,nf_generic_para);
{ p1 not needed !}
p1.destroy;
end;
@@ -4247,7 +4253,10 @@ implementation
gendef:=generate_specialization_phase2(spezcontext,tstoreddef(gendef),false,'');
spezcontext.free;
spezcontext:=nil;
- gensym:=gendef.typesym;
+ if gendef.typ=errordef then
+ gensym:=generrorsym
+ else
+ gensym:=gendef.typesym;
end;
procdef:
begin
@@ -4601,7 +4610,7 @@ implementation
filepos : tfileposinfo;
oldafterassignment,
updatefpos : boolean;
-
+ oldflags : tnodeflags;
begin
oldafterassignment:=afterassignment;
p1:=sub_expr(opcompare,[ef_accept_equal],nil);
@@ -4658,10 +4667,14 @@ implementation
else
updatefpos:=false;
end;
+ oldflags:=p1.flags;
{ get the resultdef for this expression }
if not assigned(p1.resultdef) and
dotypecheck then
do_typecheckpass(p1);
+ { transfer generic paramter flag }
+ if nf_generic_para in oldflags then
+ include(p1.flags,nf_generic_para);
afterassignment:=oldafterassignment;
if updatefpos then
p1.fileinfo:=filepos;
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..948353298d 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -42,7 +42,7 @@ type
tspecializationcontext=class
public
- genericdeflist : tfpobjectlist;
+ paramlist : tfpobjectlist;
poslist : tfplist;
prettyname : ansistring;
specializename : ansistring;
@@ -58,7 +58,7 @@ implementation
constructor tspecializationcontext.create;
begin
- genericdeflist:=tfpobjectlist.create(false);
+ paramlist:=tfpobjectlist.create(false);
poslist:=tfplist.create;
end;
@@ -66,7 +66,7 @@ destructor tspecializationcontext.destroy;
var
i : longint;
begin
- genericdeflist.free;
+ paramlist.free;
for i:=0 to poslist.count-1 do
dispose(pfileposinfo(poslist[i]));
poslist.free;
diff --git a/compiler/pgenutil.pas b/compiler/pgenutil.pas
index e8489726aa..e73f112fa9 100644
--- a/compiler/pgenutil.pas
+++ b/compiler/pgenutil.pas
@@ -42,9 +42,9 @@ uses
function generate_specialization_phase1(out context:tspecializationcontext;genericdef:tdef;symname:string):tdef;inline;
function generate_specialization_phase1(out context:tspecializationcontext;genericdef:tdef;parsedtype:tdef;symname:string;parsedpos:tfileposinfo):tdef;
function generate_specialization_phase2(context:tspecializationcontext;genericdef:tstoreddef;parse_class_parent:boolean;_prettyname:ansistring):tdef;
- function check_generic_constraints(genericdef:tstoreddef;paradeflist:tfpobjectlist;poslist:tfplist):boolean;
+ function check_generic_constraints(genericdef:tstoreddef;paramlist:tfpobjectlist;poslist:tfplist):boolean;
function parse_generic_parameters(allowconstraints:boolean):tfphashobjectlist;
- function parse_generic_specialization_types(genericdeflist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring):boolean;
+ function parse_generic_specialization_types(paramlist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring):boolean;
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:tfphashobjectlist);
procedure maybe_insert_generic_rename_symbol(const name:tidstring;genericlist:tfphashobjectlist);
function generate_generic_name(const name:tidstring;specializename:ansistring;owner_hierarchy:string):tidstring;
@@ -65,16 +65,148 @@ uses
{ common }
cutils,fpccrc,
{ global }
- globals,tokens,verbose,finput,
+ globals,tokens,verbose,finput,constexp,
{ symtable }
- symconst,symsym,symtable,defcmp,procinfo,
+ symconst,symsym,symtable,defcmp,defutil,procinfo,
{ modules }
fmodule,
- node,nobj,
+ node,nobj,ncon,
{ parser }
scanner,
pbase,pexpr,pdecsub,ptype,psub,pparautl;
+ type
+ tdeftypeset = set of tdeftyp;
+ const
+ tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,enumdef];
+ tgeneric_param_nodes : tnodetypeset = [typen,ordconstn,stringconstn,realconstn,setconstn,niln];
+
+ function get_generic_param_def(sym:tsym):tdef;
+ begin
+ if sym.typ=constsym then
+ result:=tconstsym(sym).constdef
+ else
+ result:=ttypesym(sym).typedef;
+ end;
+
+ function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
+ begin
+ if (value.valueord<param2.low) or (value.valueord>param2.high) then
+ result:=false
+ else
+ result:=true;
+ end;
+
+ function compare_generic_params(param1,param2:tdef;constparamsym:tconstsym):boolean;
+ begin
+ if (param1.typ=orddef) and (param2.typ=orddef) then
+ begin
+ if is_boolean(param2) then
+ result:=is_boolean(param1)
+ else if is_char(param2) then
+ result:=is_char(param1)
+ else if compare_orddef_by_range(torddef(param1),torddef(param2),constparamsym.value) then
+ result:=true
+ else
+ result:=false;
+ end
+ { arraydef is string constant so it's compatible with stringdef }
+ else if (param1.typ=arraydef) and (param2.typ=stringdef) then
+ result:=true
+ { integer ords are compatible with float }
+ else if (param1.typ=orddef) and is_integer(param1) and (param2.typ=floatdef) then
+ result:=true
+ { undefined def is compatible with all types }
+ else if param2.typ=undefineddef then
+ result:=true
+ { sets require stricter checks }
+ else if is_set(param2) then
+ result:=equal_defs(param1,param2)
+ else
+ result:=param1.typ=param2.typ;
+ end;
+
+ function create_generic_constsym(fromdef:tdef;node:tnode;out prettyname:string):tconstsym;
+ const
+ undefinedname = 'undefined';
+ var
+ sym : tconstsym;
+ setdef : tsetdef;
+ enumsym : tsym;
+ enumname : string;
+ sp : pchar;
+ ps : ^tconstset;
+ pd : ^bestreal;
+ i : integer;
+ begin
+ if node=nil then
+ internalerror(2020011401);
+ case node.nodetype of
+ ordconstn:
+ begin
+ sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
+ prettyname:=tostr(tordconstnode(node).value.svalue);
+ end;
+ stringconstn:
+ begin
+ getmem(sp,tstringconstnode(node).len+1);
+ move(tstringconstnode(node).value_str^,sp^,tstringconstnode(node).len+1);
+ sym:=cconstsym.create_string(undefinedname,conststring,sp,tstringconstnode(node).len,fromdef);
+ prettyname:=''''+tstringconstnode(node).value_str+'''';
+ end;
+ realconstn:
+ begin
+ new(pd);
+ pd^:=trealconstnode(node).value_real;
+ sym:=cconstsym.create_ptr(undefinedname,constreal,pd,fromdef);
+ prettyname:=realtostr(trealconstnode(node).value_real);
+ end;
+ setconstn:
+ begin
+ new(ps);
+ ps^:=tsetconstnode(node).value_set^;
+ sym:=cconstsym.create_ptr(undefinedname,constset,ps,fromdef);
+ setdef:=tsetdef(tsetconstnode(node).resultdef);
+ prettyname:='[';
+ for i := setdef.setbase to setdef.setmax do
+ if i in tsetconstnode(node).value_set^ then
+ begin
+ if setdef.elementdef.typ=enumdef then
+ enumsym:=tenumdef(setdef.elementdef).int2enumsym(i)
+ else
+ enumsym:=nil;
+ if assigned(enumsym) then
+ enumname:=enumsym.realname
+ else if setdef.elementdef.typ=orddef then
+ begin
+ if torddef(setdef.elementdef).ordtype=uchar then
+ enumname:=chr(i)
+ else
+ enumname:=tostr(i);
+ end
+ else
+ enumname:=tostr(i);
+ if length(prettyname) > 1 then
+ prettyname:=prettyname+','+enumname
+ else
+ prettyname:=prettyname+enumname;
+ end;
+ prettyname:=prettyname+']';
+ end;
+ niln:
+ begin
+ { only "nil" is available for pointer constants }
+ sym:=cconstsym.create_ord(undefinedname,constnil,0,fromdef);
+ prettyname:='nil';
+ end;
+ else
+ internalerror(2019021601);
+ end;
+ { the sym needs an owner for later checks so use the typeparam owner }
+ sym.owner:=fromdef.owner;
+ include(sym.symoptions,sp_generic_const);
+ result:=sym;
+ end;
procedure maybe_add_waiting_unit(tt:tdef);
var
@@ -104,203 +236,231 @@ uses
end;
end;
- function check_generic_constraints(genericdef:tstoreddef;paradeflist:tfpobjectlist;poslist:tfplist):boolean;
+ function check_generic_constraints(genericdef:tstoreddef;paramlist:tfpobjectlist;poslist:tfplist):boolean;
var
i,j,
intfcount : longint;
formaldef,
paradef : tstoreddef;
+ genparadef : tdef;
objdef,
paraobjdef,
formalobjdef : tobjectdef;
intffound : boolean;
filepos : tfileposinfo;
+ is_const : boolean;
begin
{ check whether the given specialization parameters fit to the eventual
constraints of the generic }
if not assigned(genericdef.genericparas) or (genericdef.genericparas.count=0) then
internalerror(2012101001);
- if genericdef.genericparas.count<>paradeflist.count then
+ if genericdef.genericparas.count<>paramlist.count then
internalerror(2012101002);
- if paradeflist.count<>poslist.count then
+ if paramlist.count<>poslist.count then
internalerror(2012120801);
result:=true;
for i:=0 to genericdef.genericparas.count-1 do
begin
filepos:=pfileposinfo(poslist[i])^;
- formaldef:=tstoreddef(ttypesym(genericdef.genericparas[i]).typedef);
- if formaldef.typ=undefineddef then
- { the parameter is of unspecified type, so no need to check }
- continue;
- if not (df_genconstraint in formaldef.defoptions) or
- not assigned(formaldef.genconstraintdata) then
- internalerror(2013021602);
- paradef:=tstoreddef(paradeflist[i]);
- { undefineddef is compatible with anything }
- if formaldef.typ=undefineddef then
- continue;
- if paradef.typ<>formaldef.typ then
+ paradef:=tstoreddef(get_generic_param_def(tsym(paramlist[i])));
+ is_const:=tsym(paramlist[i]).typ=constsym;
+ genparadef:=genericdef.get_generic_param_def(i);
+ { validate const params }
+ if not genericdef.is_generic_param_const(i) and is_const then
begin
- case formaldef.typ of
- recorddef:
- { delphi has own fantasy about record constraint
- (almost non-nullable/non-nilable value type) }
- if m_delphi in current_settings.modeswitches then
- case paradef.typ of
- floatdef,enumdef,orddef:
- continue;
- objectdef:
- if tobjectdef(paradef).objecttype=odt_object then
- continue
- else
- MessagePos(filepos,type_e_record_type_expected);
+ MessagePos(filepos,type_e_mismatch);
+ exit(false);
+ end
+ else if genericdef.is_generic_param_const(i) then
+ begin
+ { param type mismatch (type <> const) }
+ if genericdef.is_generic_param_const(i)<>is_const then
+ begin
+ MessagePos(filepos,type_e_mismatch);
+ exit(false);
+ end;
+ { type constrained param doesn't match type }
+ if not compare_generic_params(paradef,genericdef.get_generic_param_def(i),tconstsym(paramlist[i])) then
+ begin
+ MessagePos2(filepos,type_e_incompatible_types,FullTypeName(paradef,genparadef),FullTypeName(genparadef,paradef));
+ exit(false);
+ end;
+ end;
+ { test constraints for non-const params }
+ if not genericdef.is_generic_param_const(i) then
+ begin
+ formaldef:=tstoreddef(ttypesym(genericdef.genericparas[i]).typedef);
+ if formaldef.typ=undefineddef then
+ { the parameter is of unspecified type, so no need to check }
+ continue;
+ if not (df_genconstraint in formaldef.defoptions) or
+ not assigned(formaldef.genconstraintdata) then
+ internalerror(2013021602);
+ { undefineddef is compatible with anything }
+ if formaldef.typ=undefineddef then
+ continue;
+ if paradef.typ<>formaldef.typ then
+ begin
+ case formaldef.typ of
+ recorddef:
+ { delphi has own fantasy about record constraint
+ (almost non-nullable/non-nilable value type) }
+ if m_delphi in current_settings.modeswitches then
+ case paradef.typ of
+ floatdef,enumdef,orddef:
+ continue;
+ objectdef:
+ if tobjectdef(paradef).objecttype=odt_object then
+ continue
+ else
+ MessagePos(filepos,type_e_record_type_expected);
+ else
+ MessagePos(filepos,type_e_record_type_expected);
+ end
else
MessagePos(filepos,type_e_record_type_expected);
- end
- else
- MessagePos(filepos,type_e_record_type_expected);
- objectdef:
- case tobjectdef(formaldef).objecttype of
- odt_class,
- odt_javaclass:
- MessagePos1(filepos,type_e_class_type_expected,paradef.typename);
- odt_interfacecom,
- odt_interfacecorba,
- odt_dispinterface,
- odt_interfacejava:
- MessagePos1(filepos,type_e_interface_type_expected,paradef.typename);
+ objectdef:
+ case tobjectdef(formaldef).objecttype of
+ odt_class,
+ odt_javaclass:
+ MessagePos1(filepos,type_e_class_type_expected,paradef.typename);
+ odt_interfacecom,
+ odt_interfacecorba,
+ odt_dispinterface,
+ odt_interfacejava:
+ MessagePos1(filepos,type_e_interface_type_expected,paradef.typename);
+ else
+ internalerror(2012101003);
+ end;
+ errordef:
+ { ignore }
+ ;
else
- internalerror(2012101003);
+ internalerror(2012101004);
end;
- errordef:
- { ignore }
- ;
- else
- internalerror(2012101004);
- end;
- result:=false;
- end
- else
- begin
- { the paradef types are the same, so do special checks for the
- cases in which they are needed }
- if formaldef.typ=objectdef then
+ result:=false;
+ end
+ else
begin
- paraobjdef:=tobjectdef(paradef);
- formalobjdef:=tobjectdef(formaldef);
- if not (formalobjdef.objecttype in [odt_class,odt_javaclass,odt_interfacecom,odt_interfacecorba,odt_interfacejava,odt_dispinterface]) then
- internalerror(2012101102);
- if formalobjdef.objecttype in [odt_interfacecom,odt_interfacecorba,odt_interfacejava,odt_dispinterface] then
+ { the paradef types are the same, so do special checks for the
+ cases in which they are needed }
+ if formaldef.typ=objectdef then
begin
- { this is either a concerete interface or class type (the
- latter without specific implemented interfaces) }
- case paraobjdef.objecttype of
- odt_interfacecom,
- odt_interfacecorba,
- odt_interfacejava,
- odt_dispinterface:
- begin
- if (oo_is_forward in paraobjdef.objectoptions) and
- (paraobjdef.objecttype=formalobjdef.objecttype) and
- (df_genconstraint in formalobjdef.defoptions) and
- (
- (formalobjdef.objecttype=odt_interfacecom) and
- (formalobjdef.childof=interface_iunknown)
- )
- or
- (
- (formalobjdef.objecttype=odt_interfacecorba) and
- (formalobjdef.childof=nil)
- ) then
- continue;
- if not def_is_related(paraobjdef,formalobjdef.childof) then
+ paraobjdef:=tobjectdef(paradef);
+ formalobjdef:=tobjectdef(formaldef);
+ if not (formalobjdef.objecttype in [odt_class,odt_javaclass,odt_interfacecom,odt_interfacecorba,odt_interfacejava,odt_dispinterface]) then
+ internalerror(2012101102);
+ if formalobjdef.objecttype in [odt_interfacecom,odt_interfacecorba,odt_interfacejava,odt_dispinterface] then
+ begin
+ { this is either a concerete interface or class type (the
+ latter without specific implemented interfaces) }
+ case paraobjdef.objecttype of
+ odt_interfacecom,
+ odt_interfacecorba,
+ odt_interfacejava,
+ odt_dispinterface:
begin
- MessagePos2(filepos,type_e_incompatible_types,paraobjdef.typename,formalobjdef.childof.typename);
- result:=false;
+ if (oo_is_forward in paraobjdef.objectoptions) and
+ (paraobjdef.objecttype=formalobjdef.objecttype) and
+ (df_genconstraint in formalobjdef.defoptions) and
+ (
+ (formalobjdef.objecttype=odt_interfacecom) and
+ (formalobjdef.childof=interface_iunknown)
+ )
+ or
+ (
+ (formalobjdef.objecttype=odt_interfacecorba) and
+ (formalobjdef.childof=nil)
+ ) then
+ continue;
+ if not def_is_related(paraobjdef,formalobjdef.childof) then
+ begin
+ MessagePos2(filepos,type_e_incompatible_types,paraobjdef.typename,formalobjdef.childof.typename);
+ result:=false;
+ end;
end;
- end;
- odt_class,
- odt_javaclass:
- begin
- objdef:=paraobjdef;
- intffound:=false;
- while assigned(objdef) do
+ odt_class,
+ odt_javaclass:
begin
- for j:=0 to objdef.implementedinterfaces.count-1 do
- if timplementedinterface(objdef.implementedinterfaces[j]).intfdef=formalobjdef.childof then
- begin
- intffound:=true;
+ objdef:=paraobjdef;
+ intffound:=false;
+ while assigned(objdef) do
+ begin
+ for j:=0 to objdef.implementedinterfaces.count-1 do
+ if timplementedinterface(objdef.implementedinterfaces[j]).intfdef=formalobjdef.childof then
+ begin
+ intffound:=true;
+ break;
+ end;
+ if intffound then
break;
- end;
- if intffound then
- break;
- objdef:=objdef.childof;
+ objdef:=objdef.childof;
+ end;
+ result:=intffound;
+ if not result then
+ MessagePos2(filepos,parser_e_class_doesnt_implement_interface,paraobjdef.typename,formalobjdef.childof.typename);
+ end;
+ else
+ begin
+ MessagePos1(filepos,type_e_class_or_interface_type_expected,paraobjdef.typename);
+ result:=false;
end;
- result:=intffound;
- if not result then
- MessagePos2(filepos,parser_e_class_doesnt_implement_interface,paraobjdef.typename,formalobjdef.childof.typename);
- end;
- else
- begin
- MessagePos1(filepos,type_e_class_or_interface_type_expected,paraobjdef.typename);
- result:=false;
end;
- end;
- end
- else
- begin
- { this is either a "class" or a concrete instance with
- or without implemented interfaces }
- if not (paraobjdef.objecttype in [odt_class,odt_javaclass]) then
- begin
- MessagePos1(filepos,type_e_class_type_expected,paraobjdef.typename);
- result:=false;
- continue;
- end;
- { for forward declared classes we allow pure TObject/class declarations }
- if (oo_is_forward in paraobjdef.objectoptions) and
- (df_genconstraint in formaldef.defoptions) then
- begin
- if (formalobjdef.childof=class_tobject) and
- not formalobjdef.implements_any_interfaces then
- continue;
- end;
- if assigned(formalobjdef.childof) and
- not def_is_related(paradef,formalobjdef.childof) then
- begin
- MessagePos2(filepos,type_e_incompatible_types,paraobjdef.typename,formalobjdef.childof.typename);
- result:=false;
- end;
- intfcount:=0;
- for j:=0 to formalobjdef.implementedinterfaces.count-1 do
+ end
+ else
begin
- objdef:=paraobjdef;
- while assigned(objdef) do
+ { this is either a "class" or a concrete instance with
+ or without implemented interfaces }
+ if not (paraobjdef.objecttype in [odt_class,odt_javaclass]) then
+ begin
+ MessagePos1(filepos,type_e_class_type_expected,paraobjdef.typename);
+ result:=false;
+ continue;
+ end;
+ { for forward declared classes we allow pure TObject/class declarations }
+ if (oo_is_forward in paraobjdef.objectoptions) and
+ (df_genconstraint in formaldef.defoptions) then
begin
- intffound:=assigned(
- find_implemented_interface(objdef,
- timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
- )
- );
+ if (formalobjdef.childof=class_tobject) and
+ not formalobjdef.implements_any_interfaces then
+ continue;
+ end;
+ if assigned(formalobjdef.childof) and
+ not def_is_related(paradef,formalobjdef.childof) then
+ begin
+ MessagePos2(filepos,type_e_incompatible_types,paraobjdef.typename,formalobjdef.childof.typename);
+ result:=false;
+ end;
+ intfcount:=0;
+ for j:=0 to formalobjdef.implementedinterfaces.count-1 do
+ begin
+ objdef:=paraobjdef;
+ while assigned(objdef) do
+ begin
+ intffound:=assigned(
+ find_implemented_interface(objdef,
+ timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
+ )
+ );
+ if intffound then
+ break;
+ objdef:=objdef.childof;
+ end;
if intffound then
- break;
- objdef:=objdef.childof;
+ inc(intfcount)
+ else
+ MessagePos2(filepos,parser_e_class_doesnt_implement_interface,paraobjdef.typename,timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef.typename);
end;
- if intffound then
- inc(intfcount)
- else
- MessagePos2(filepos,parser_e_class_doesnt_implement_interface,paraobjdef.typename,timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef.typename);
+ if intfcount<>formalobjdef.implementedinterfaces.count then
+ result:=false;
end;
- if intfcount<>formalobjdef.implementedinterfaces.count then
- result:=false;
end;
end;
end;
end;
end;
-
- function parse_generic_specialization_types_internal(genericdeflist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring;parsedtype:tdef;parsedpos:tfileposinfo):boolean;
+ function parse_generic_specialization_types_internal(paramlist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring;parsedtype:tdef;parsedpos:tfileposinfo):boolean;
var
old_block_type : tblock_type;
first : boolean;
@@ -310,9 +470,11 @@ uses
namepart : string;
prettynamepart : ansistring;
module : tmodule;
+ constprettyname : string;
+ validparam : boolean;
begin
result:=true;
- if genericdeflist=nil then
+ if paramlist=nil then
internalerror(2012061401);
{ set the block type to type, so that the parsed type are returned as
ttypenode (e.g. classes are in non type-compatible blocks returned as
@@ -324,7 +486,7 @@ uses
first:=not assigned(parsedtype);
if assigned(parsedtype) then
begin
- genericdeflist.Add(parsedtype);
+ paramlist.Add(parsedtype.typesym);
module:=find_module_from_symtable(parsedtype.owner);
if not assigned(module) then
internalerror(2016112801);
@@ -350,8 +512,10 @@ uses
consume(_COMMA);
block_type:=bt_type;
tmpparampos:=current_filepos;
- typeparam:=factor(false,[ef_type_only]);
- if typeparam.nodetype=typen then
+ typeparam:=factor(false,[ef_accept_equal]);
+ { determine if the typeparam node is a valid type or const }
+ validparam:=typeparam.nodetype in tgeneric_param_nodes;
+ if validparam then
begin
if tstoreddef(typeparam.resultdef).is_generic and
(
@@ -367,31 +531,46 @@ uses
end;
if typeparam.resultdef.typ<>errordef then
begin
- if not assigned(typeparam.resultdef.typesym) then
+ if (typeparam.nodetype=typen) and not assigned(typeparam.resultdef.typesym) then
message(type_e_generics_cannot_reference_itself)
else if (typeparam.resultdef.typ<>errordef) then
begin
- genericdeflist.Add(typeparam.resultdef);
+ { all non-type nodes are considered const }
+ if typeparam.nodetype<>typen then
+ paramlist.Add(create_generic_constsym(typeparam.resultdef,typeparam,constprettyname))
+ else
+ begin
+ constprettyname:='';
+ paramlist.Add(typeparam.resultdef.typesym);
+ end;
module:=find_module_from_symtable(typeparam.resultdef.owner);
if not assigned(module) then
internalerror(2016112802);
namepart:='_$'+hexstr(module.moduleid,8)+'$$'+typeparam.resultdef.unique_id_str;
+ if constprettyname<>'' then
+ namepart:=namepart+'$$'+constprettyname;
{ we use the full name of the type to uniquely identify it }
- if (symtablestack.top.symtabletype=parasymtable) and
- (symtablestack.top.defowner.typ=procdef) and
- (typeparam.resultdef.owner=symtablestack.top) then
+ if typeparam.nodetype=typen then
begin
- { special handling for specializations inside generic function declarations }
- prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
- end
- else
- begin
- prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);
+ if (symtablestack.top.symtabletype=parasymtable) and
+ (symtablestack.top.defowner.typ=procdef) and
+ (typeparam.resultdef.owner=symtablestack.top) then
+ begin
+ { special handling for specializations inside generic function declarations }
+ prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
+ end
+ else
+ begin
+ prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);
+ end;
end;
specializename:=specializename+namepart;
if not first then
prettyname:=prettyname+',';
- prettyname:=prettyname+prettynamepart+typeparam.resultdef.typesym.prettyname;
+ if constprettyname<>'' then
+ prettyname:=prettyname+constprettyname
+ else
+ prettyname:=prettyname+prettynamepart+typeparam.resultdef.typesym.prettyname;
end;
end
else
@@ -411,12 +590,12 @@ uses
end;
- function parse_generic_specialization_types(genericdeflist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring):boolean;
+ function parse_generic_specialization_types(paramlist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring):boolean;
var
dummypos : tfileposinfo;
begin
FillChar(dummypos, SizeOf(tfileposinfo), 0);
- result:=parse_generic_specialization_types_internal(genericdeflist,poslist,prettyname,specializename,nil,dummypos);
+ result:=parse_generic_specialization_types_internal(paramlist,poslist,prettyname,specializename,nil,dummypos);
end;
@@ -502,7 +681,7 @@ uses
context:=tspecializationcontext.create;
{ Parse type parameters }
- err:=not parse_generic_specialization_types_internal(context.genericdeflist,context.poslist,context.prettyname,context.specializename,parsedtype,parsedpos);
+ err:=not parse_generic_specialization_types_internal(context.paramlist,context.poslist,context.prettyname,context.specializename,parsedtype,parsedpos);
if err then
begin
if not try_to_consume(_GT) then
@@ -556,7 +735,7 @@ uses
{ search a generic with the given count of params }
countstr:='';
- str(context.genericdeflist.Count,countstr);
+ str(context.paramlist.Count,countstr);
genname:=genname+'$'+countstr;
ugenname:=upper(genname);
@@ -681,6 +860,8 @@ uses
tempst : tglobalsymtable;
psym,
srsym : tsym;
+ paramdef1,
+ paramdef2,
def : tdef;
old_block_type : tblock_type;
state : tspecializationstate;
@@ -708,7 +889,7 @@ uses
pd:=nil;
- if not check_generic_constraints(genericdef,context.genericdeflist,context.poslist) then
+ if not check_generic_constraints(genericdef,context.paramlist,context.poslist) then
begin
{ the parameters didn't fit the constraints, so don't continue with the
specialization }
@@ -724,20 +905,19 @@ uses
else
prettyname:=genericdef.typesym.prettyname;
prettyname:=prettyname+'<'+context.prettyname+'>';
-
generictypelist:=tfphashobjectlist.create(false);
{ build the list containing the types for the generic params }
if not assigned(genericdef.genericparas) then
internalerror(2013092601);
- if context.genericdeflist.count<>genericdef.genericparas.count then
+ if context.paramlist.count<>genericdef.genericparas.count then
internalerror(2013092603);
for i:=0 to genericdef.genericparas.Count-1 do
begin
srsym:=tsym(genericdef.genericparas[i]);
if not (sp_generic_para in srsym.symoptions) then
internalerror(2013092602);
- generictypelist.add(srsym.realname,tdef(context.genericdeflist[i]).typesym);
+ generictypelist.add(srsym.realname,context.paramlist[i]);
end;
{ Special case if we are referencing the current defined object }
@@ -792,11 +972,33 @@ uses
allequal:=true;
for i:=0 to generictypelist.count-1 do
begin
- if not equal_defs(ttypesym(generictypelist[i]).typedef,ttypesym(tstoreddef(def).genericparas[i]).typedef) then
+ if tsym(generictypelist[i]).typ<>tsym(tstoreddef(def).genericparas[i]).typ then
+ begin
+ allequal:=false;
+ break;
+ end;
+ if tsym(generictypelist[i]).typ=constsym then
+ paramdef1:=tconstsym(generictypelist[i]).constdef
+ else
+ paramdef1:=ttypesym(generictypelist[i]).typedef;
+ if tsym(tstoreddef(def).genericparas[i]).typ=constsym then
+ paramdef2:=tconstsym(tstoreddef(def).genericparas[i]).constdef
+ else
+ paramdef2:=ttypesym(tstoreddef(def).genericparas[i]).typedef;
+ if not equal_defs(paramdef2,paramdef2) then
begin
allequal:=false;
break;
end;
+ if (tsym(generictypelist[i]).typ=constsym) and
+ (
+ (tconstsym(generictypelist[i]).consttyp<>tconstsym(tstoreddef(def).genericparas[i]).consttyp) or
+ not same_constvalue(tconstsym(generictypelist[i]).consttyp,tconstsym(generictypelist[i]).value,tconstsym(tstoreddef(def).genericparas[i]).value)
+ ) then
+ begin
+ allequal:=false;
+ break;
+ end;
end;
if allequal then
begin
@@ -1159,25 +1361,43 @@ uses
function parse_generic_parameters(allowconstraints:boolean):tfphashobjectlist;
var
- generictype : ttypesym;
- i,firstidx : longint;
+ generictype : tstoredsym;
+ i,firstidx,const_list_index : longint;
srsymtable : tsymtable;
basedef,def : tdef;
defname : tidstring;
+ allowconst,
allowconstructor,
+ is_const,
doconsume : boolean;
constraintdata : tgenericconstraintdata;
old_block_type : tblock_type;
fileinfo : tfileposinfo;
+ last_token : ttoken;
+ last_type_pos : tfileposinfo;
begin
result:=tfphashobjectlist.create(false);
firstidx:=0;
+ const_list_index:=0;
old_block_type:=block_type;
block_type:=bt_type;
+ allowconst:=true;
+ is_const:=false;
+ last_token:=NOTOKEN;
+ last_type_pos:=current_filepos;
repeat
+ if allowconst and try_to_consume(_CONST) then
+ begin
+ allowconst:=false;
+ is_const:=true;
+ const_list_index:=result.count;
+ end;
if token=_ID then
begin
- generictype:=ctypesym.create(orgpattern,cundefinedtype);
+ if is_const then
+ generictype:=cconstsym.create_undefined(orgpattern,cundefinedtype)
+ else
+ generictype:=ctypesym.create(orgpattern,cundefinedtype);
{ type parameters need to be added as strict private }
generictype.visibility:=vis_strictprivate;
include(generictype.symoptions,sp_generic_para);
@@ -1185,7 +1405,43 @@ uses
end;
consume(_ID);
fileinfo:=current_tokenpos;
- if try_to_consume(_COLON) then
+ { const restriction }
+ if is_const and try_to_consume(_COLON) then
+ begin
+ def:=nil;
+ { parse the type and assign the const type to generictype }
+ single_type(def,[]);
+ for i:=const_list_index to result.count-1 do
+ begin
+ { finalize constant information once type is known }
+ if assigned(def) and (def.typ in tgeneric_param_const_types) then
+ begin
+ case def.typ of
+ orddef,
+ enumdef:
+ tconstsym(result[i]).consttyp:=constord;
+ stringdef:
+ tconstsym(result[i]).consttyp:=conststring;
+ floatdef:
+ tconstsym(result[i]).consttyp:=constreal;
+ setdef:
+ tconstsym(result[i]).consttyp:=constset;
+ { pointer always refers to nil with constants }
+ pointerdef:
+ tconstsym(result[i]).consttyp:=constnil;
+ else
+ internalerror(2020011402);
+ end;
+ tconstsym(result[i]).constdef:=def;
+ end
+ else
+ Message(type_e_mismatch);
+ end;
+ { after type restriction const list terminates }
+ is_const:=false;
+ end
+ { type restriction }
+ else if try_to_consume(_COLON) then
begin
if not allowconstraints then
Message(parser_e_generic_constraints_not_allowed_here);
@@ -1302,6 +1558,7 @@ uses
basedef:=cobjectdef.create(tobjectdef(def).objecttype,defname,tobjectdef(def),false);
constraintdata.interfaces.delete(0);
end;
+
if basedef.typ<>errordef then
with tstoreddef(basedef) do
begin
@@ -1328,21 +1585,34 @@ uses
begin
{ two different typeless parameters are considered as incompatible }
for i:=firstidx to result.count-1 do
- begin
- ttypesym(result[i]).typedef:=cundefineddef.create(false);
- ttypesym(result[i]).typedef.typesym:=ttypesym(result[i]);
- end;
+ if tsym(result[i]).typ<>constsym then
+ begin
+ ttypesym(result[i]).typedef:=cundefineddef.create(false);
+ ttypesym(result[i]).typedef.typesym:=ttypesym(result[i]);
+ end;
{ a semicolon terminates a type parameter group }
firstidx:=result.count;
end;
end;
+ if token=_SEMICOLON then
+ begin
+ is_const:=false;
+ allowconst:=true;
+ end;
+ last_token:=token;
+ last_type_pos:=current_filepos;
until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
+ { if the constant parameter is not terminated then the type restriction was
+ not specified and we need to give an error }
+ if is_const then
+ consume(_COLON);
{ two different typeless parameters are considered as incompatible }
for i:=firstidx to result.count-1 do
- begin
- ttypesym(result[i]).typedef:=cundefineddef.create(false);
- ttypesym(result[i]).typedef.typesym:=ttypesym(result[i]);
- end;
+ if tsym(result[i]).typ<>constsym then
+ begin
+ ttypesym(result[i]).typedef:=cundefineddef.create(false);
+ ttypesym(result[i]).typedef.typesym:=ttypesym(result[i]);
+ end;
block_type:=old_block_type;
end;
@@ -1350,7 +1620,9 @@ uses
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:tfphashobjectlist);
var
i : longint;
- generictype,sym : ttypesym;
+ generictype : tstoredsym;
+ generictypedef : tdef;
+ sym : tsym;
st : tsymtable;
begin
def.genericdef:=genericdef;
@@ -1375,10 +1647,23 @@ uses
def.genericparas:=tfphashobjectlist.create(false);
for i:=0 to genericlist.count-1 do
begin
- generictype:=ttypesym(genericlist[i]);
+ generictype:=tstoredsym(genericlist[i]);
if assigned(generictype.owner) then
begin
- sym:=ctypesym.create(genericlist.nameofindex(i),generictype.typedef);
+ if generictype.typ=typesym then
+ sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef)
+ else if generictype.typ=constsym then
+ { generictype is a constsym that was created in create_generic_constsym
+ during phase 1 so we pass this directly without copying }
+ begin
+ sym:=generictype;
+ { the sym name is still undefined so we set it to match
+ the generic param name so it's accessible }
+ sym.realname:=genericlist.nameofindex(i);
+ include(sym.symoptions,sp_generic_const);
+ end
+ else
+ internalerror(2019021602);
{ type parameters need to be added as strict private }
sym.visibility:=vis_strictprivate;
st.insert(sym);
@@ -1386,13 +1671,17 @@ uses
end
else
begin
- if (generictype.typedef.typ=undefineddef) and (generictype.typedef<>cundefinedtype) then
+ if generictype.typ=typesym then
begin
- { the generic parameters were parsed before the genericdef existed thus the
- undefineddefs were added as part of the parent symtable }
- if assigned(generictype.typedef.owner) then
- generictype.typedef.owner.DefList.Extract(generictype.typedef);
- generictype.typedef.changeowner(st);
+ generictypedef:=ttypesym(generictype).typedef;
+ if (generictypedef.typ=undefineddef) and (generictypedef<>cundefinedtype) then
+ begin
+ { the generic parameters were parsed before the genericdef existed thus the
+ undefineddefs were added as part of the parent symtable }
+ if assigned(generictypedef.owner) then
+ generictypedef.owner.DefList.Extract(generictypedef);
+ generictypedef.changeowner(st);
+ end;
end;
st.insert(generictype);
include(generictype.symoptions,sp_generic_para);
diff --git a/compiler/pparautl.pas b/compiler/pparautl.pas
index b61173d50c..936806b141 100644
--- a/compiler/pparautl.pas
+++ b/compiler/pparautl.pas
@@ -631,27 +631,48 @@ implementation
function check_generic_parameters(fwpd,currpd:tprocdef):boolean;
var
i : longint;
- fwtype,
- currtype : ttypesym;
+ fwsym,
+ currsym : tsym;
+ currtype : ttypesym absolute currsym;
+ fileinfo : tfileposinfo;
begin
result:=true;
if fwpd.genericparas.count<>currpd.genericparas.count then
internalerror(2018090101);
for i:=0 to fwpd.genericparas.count-1 do
begin
- fwtype:=ttypesym(fwpd.genericparas[i]);
- currtype:=ttypesym(currpd.genericparas[i]);
- if fwtype.name<>currtype.name then
+ fwsym:=tsym(fwpd.genericparas[i]);
+ currsym:=tsym(currpd.genericparas[i]);
+ if fwsym.name<>currsym.name then
begin
- messagepos1(currtype.fileinfo,sym_e_generic_type_param_mismatch,currtype.realname);
- messagepos1(fwtype.fileinfo,sym_e_generic_type_param_decl,fwtype.realname);
+ messagepos1(currsym.fileinfo,sym_e_generic_type_param_mismatch,currsym.realname);
+ messagepos1(fwsym.fileinfo,sym_e_generic_type_param_decl,fwsym.realname);
result:=false;
end;
- if (fwpd.interfacedef or assigned(fwpd.struct)) and (df_genconstraint in currtype.typedef.defoptions) then
+ if (fwpd.interfacedef or assigned(fwpd.struct)) and
+ (
+ ((currsym.typ=typesym) and (df_genconstraint in currtype.typedef.defoptions)) or
+ (currsym.typ=constsym)
+ ) then
begin
- messagepos(tstoreddef(currtype.typedef).genconstraintdata.fileinfo,parser_e_generic_constraints_not_allowed_here);
+ if currsym.typ=constsym then
+ fileinfo:=currsym.fileinfo
+ else
+ fileinfo:=tstoreddef(currtype.typedef).genconstraintdata.fileinfo;
+ messagepos(fileinfo,parser_e_generic_constraints_not_allowed_here);
result:=false;
end;
+ if not fwpd.interfacedef and not assigned(fwpd.struct) and
+ (fwsym.typ=constsym) then
+ begin
+ { without modeswitch RepeatForward we need to check here
+ if the type of the constants match }
+ if (currsym.typ<>constsym) or not equal_defs(tconstsym(fwsym).constdef,tconstsym(currsym).constdef) then
+ begin
+ messagepos1(currpd.fileinfo,parser_e_header_dont_match_forward,currpd.fullprocname(false));
+ result:=false;
+ end;
+ end;
end;
end;
@@ -659,8 +680,10 @@ implementation
function equal_generic_procdefs(fwpd,currpd:tprocdef):boolean;
var
i : longint;
- fwtype,
- currtype : ttypesym;
+ fwsym,
+ currsym : tsym;
+ fwtype : ttypesym absolute fwsym;
+ currtype : ttypesym absolute currsym;
foundretdef : boolean;
begin
result:=false;
@@ -677,14 +700,36 @@ implementation
foundretdef:=false;
for i:=0 to fwpd.genericparas.count-1 do
begin
- fwtype:=ttypesym(fwpd.genericparas[i]);
- currtype:=ttypesym(currpd.genericparas[i]);
+ fwsym:=tsym(fwpd.genericparas[i]);
+ currsym:=tsym(currpd.genericparas[i]);
{ if the type in the currpd isn't a pure undefineddef (thus there
are constraints and the fwpd was declared in the interface, then
we can stop right there }
- if fwpd.interfacedef and ((currtype.typedef.typ<>undefineddef) or (df_genconstraint in currtype.typedef.defoptions)) then
+ if fwpd.interfacedef and
+ (
+ (currsym.typ=constsym) or
+ ((currsym.typ=typesym) and
+ (
+ (currtype.typedef.typ<>undefineddef) or
+ (df_genconstraint in currtype.typedef.defoptions)
+ )
+ )
+ )then
exit;
- if not foundretdef then
+ if not fwpd.interfacedef then
+ begin
+ if (fwsym.typ=constsym) and (currsym.typ=constsym) then
+ begin
+ { check whether the constant type for forward functions match }
+ if not equal_defs(tconstsym(fwsym).constdef,tconstsym(currsym).constdef) then
+ exit;
+ end
+ else if (fwsym.typ=constsym) then
+ { if the forward sym is a constant, the implementation needs to be one
+ as well }
+ exit;
+ end;
+ if not foundretdef and (fwsym.typ=typesym) then
begin
{ if the returndef is the same as this parameter's def then this
needs to be the case for both procdefs }
diff --git a/compiler/ppu.pas b/compiler/ppu.pas
index 2fc15500d2..49c6e47647 100644
--- a/compiler/ppu.pas
+++ b/compiler/ppu.pas
@@ -50,7 +50,7 @@ const
CurrentPPUVersion = 207;
{ for any other changes to the ppu format, increase this version number
(it's a cardinal) }
- CurrentPPULongVersion = 8;
+ CurrentPPULongVersion = 9;
{ unit flags }
uf_big_endian = $000004;
diff --git a/compiler/pstatmnt.pas b/compiler/pstatmnt.pas
index ed9a3c9005..d81e151821 100644
--- a/compiler/pstatmnt.pas
+++ b/compiler/pstatmnt.pas
@@ -361,7 +361,9 @@ implementation
procedure check_range(hp:tnode; fordef: tdef);
begin
if (hp.nodetype=ordconstn) and
- (fordef.typ<>errordef) then
+ (fordef.typ<>errordef) and
+ { the node was derived from a generic parameter so ignore range check }
+ not(nf_generic_para in hp.flags) then
adaptrange(fordef,tordconstnode(hp).value,false,false,true);
end;
diff --git a/compiler/ptype.pas b/compiler/ptype.pas
index ab3db2b048..86e7dff317 100644
--- a/compiler/ptype.pas
+++ b/compiler/ptype.pas
@@ -1316,6 +1316,7 @@ implementation
procedure array_dec(is_packed:boolean;genericdef:tstoreddef;genericlist:tfphashobjectlist);
var
+ isgeneric : boolean;
lowval,
highval : TConstExprInt;
indexdef : tdef;
@@ -1362,6 +1363,7 @@ implementation
lowval:=0;
highval:=1;
indexdef:=def;
+ isgeneric:=true;
end;
else
Message(sym_e_error_in_type_def);
@@ -1409,6 +1411,7 @@ implementation
begin
{ defaults }
indexdef:=generrordef;
+ isgeneric:=false;
{ use defaults which don't overflow the compiler }
lowval:=0;
highval:=0;
@@ -1424,12 +1427,15 @@ implementation
else
begin
pt:=expr(true);
+ isgeneric:=false;
if pt.nodetype=typen then
setdefdecl(pt.resultdef)
else
begin
if pt.nodetype=rangen then
begin
+ if nf_generic_para in pt.flags then
+ isgeneric:=true;
{ pure ordconstn expressions can be checked for
generics as well, but don't give an error in case
of parsing a generic if that isn't yet the case }
@@ -1446,7 +1452,9 @@ implementation
highval:=tordconstnode(trangenode(pt).right).value;
if highval<lowval then
begin
- Message(parser_e_array_lower_less_than_upper_bound);
+ { ignore error if node is generic param }
+ if not (nf_generic_para in pt.flags) then
+ Message(parser_e_array_lower_less_than_upper_bound);
highval:=lowval;
end
else if (lowval<int64(low(asizeint))) or
@@ -1494,6 +1502,8 @@ implementation
end;
if is_packed then
include(arrdef.arrayoptions,ado_IsBitPacked);
+ if isgeneric then
+ include(arrdef.arrayoptions,ado_IsGeneric);
if token=_COMMA then
consume(_COMMA)
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index 08951b97ca..116b70883a 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -212,8 +212,9 @@ type
generic is encountered to ease inline
specializations, etc; those symbols can be
"overridden" with a completely different symbol }
- sp_explicitrename { this is used to keep track of type renames created
+ sp_explicitrename, { this is used to keep track of type renames created
by the user }
+ sp_generic_const
);
tsymoptions=set of tsymoption;
@@ -241,7 +242,10 @@ type
{ internal def that's not for any export }
df_internal,
{ the local def is referenced from a public function }
- df_has_global_ref
+ df_has_global_ref,
+ { the def was derived with generic type or const fields so the size
+ of the def can not be determined }
+ df_has_generic_fields
);
tdefoptions=set of tdefoption;
@@ -567,7 +571,8 @@ type
ado_IsArrayOfConst, // array of const
ado_IsConstString, // string constant
ado_IsBitPacked, // bitpacked array
- ado_IsVector // Vector
+ ado_IsVector, // Vector
+ ado_IsGeneric // the index of the array is generic (meaning that the size is not yet known)
);
tarraydefoptions=set of tarraydefoption;
diff --git a/compiler/symdef.pas b/compiler/symdef.pas
index a07584ab0a..09c58a09f3 100644
--- a/compiler/symdef.pas
+++ b/compiler/symdef.pas
@@ -175,6 +175,9 @@ interface
function is_generic:boolean;
{ same as above for specializations }
function is_specialization:boolean;
+ { generic utilities }
+ function is_generic_param_const(index:integer):boolean;inline;
+ function get_generic_param_def(index:integer):tdef;inline;
{ registers this def in the unit's deflist; no-op if already registered }
procedure register_def; override;
{ add the def to the top of the symtable stack if it's not yet owned
@@ -2407,14 +2410,32 @@ implementation
for i:=0 to genericparas.count-1 do
begin
sym:=tsym(genericparas[i]);
- if sym.typ<>symconst.typesym then
+ { sym must be either a type or const }
+ if not (sym.typ in [symconst.typesym,symconst.constsym]) then
internalerror(2014050903);
if sym.owner.defowner<>self then
exit(false);
+ if (sym.typ=symconst.constsym) and (sp_generic_const in sym.symoptions) then
+ exit(false);
end;
end;
+ function tstoreddef.is_generic_param_const(index:integer):boolean;
+ begin
+ result:=tsym(genericparas[index]).typ=constsym;
+ end;
+
+
+ function tstoreddef.get_generic_param_def(index:integer):tdef;
+ begin
+ if tsym(genericparas[index]).typ=constsym then
+ result:=tconstsym(genericparas[index]).constdef
+ else
+ result:=ttypesym(genericparas[index]).typedef;
+ end;
+
+
function tstoreddef.is_specialization: boolean;
var
i : longint;
@@ -2430,10 +2451,13 @@ implementation
for i:=0 to genericparas.count-1 do
begin
sym:=tsym(genericparas[i]);
- if sym.typ<>symconst.typesym then
+ { sym must be either a type or const }
+ if not (sym.typ in [symconst.typesym,symconst.constsym]) then
internalerror(2014050904);
if sym.owner.defowner<>self then
exit(true);
+ if (sym.typ=symconst.constsym) and (sp_generic_const in sym.symoptions) then
+ exit(true);
end;
result:=false;
end;
@@ -4179,7 +4203,7 @@ implementation
ppufile.getderef(rangedefderef);
lowrange:=ppufile.getasizeint;
highrange:=ppufile.getasizeint;
- ppufile.getset(tppuset1(arrayoptions));
+ ppufile.getset(tppuset2(arrayoptions));
ppuload_platform(ppufile);
symtable:=tarraysymtable.create(self);
tarraysymtable(symtable).ppuload(ppufile)
@@ -4219,7 +4243,7 @@ implementation
ppufile.putderef(rangedefderef);
ppufile.putasizeint(lowrange);
ppufile.putasizeint(highrange);
- ppufile.putset(tppuset1(arrayoptions));
+ ppufile.putset(tppuset2(arrayoptions));
writeentry(ppufile,ibarraydef);
tarraysymtable(symtable).ppuwrite(ppufile);
end;
@@ -4339,6 +4363,7 @@ implementation
(ado_IsDynamicArray in arrayoptions) or
(ado_IsConvertedPointer in arrayoptions) or
(ado_IsConstructor in arrayoptions) or
+ (ado_IsGeneric in arrayoptions) or
(highrange<lowrange)
) and
(size=-1) then
@@ -4543,7 +4568,8 @@ implementation
fullparas,
paramname : ansistring;
module : tmodule;
- sym : ttypesym;
+ sym : tsym;
+ def : tdef;
i : longint;
begin
{ we want at least enough space for an ellipsis }
@@ -4552,15 +4578,21 @@ implementation
fullparas:='';
for i:=0 to genericparas.count-1 do
begin
- sym:=ttypesym(genericparas[i]);
+ sym:=tsym(genericparas[i]);
module:=find_module_from_symtable(sym.owner);
if not assigned(module) then
internalerror(2014121202);
+ if not (sym.typ in [constsym,symconst.typesym]) then
+ internalerror(2020042501);
+ if sym.typ=constsym then
+ def:=tconstsym(sym).constdef
+ else
+ def:=ttypesym(sym).typedef;
paramname:=module.realmodulename^;
- if sym.typedef.typ in [objectdef,recorddef] then
- paramname:=paramname+'.'+tabstractrecorddef(sym.typedef).rttiname
+ if def.typ in [objectdef,recorddef] then
+ paramname:=paramname+'.'+tabstractrecorddef(def).rttiname
else
- paramname:=paramname+'.'+sym.typedef.typename;
+ paramname:=paramname+'.'+def.typename;
if length(fullparas)+commacount[i>0]+length(paramname)>maxlength then
begin
if i>0 then
diff --git a/compiler/symsym.pas b/compiler/symsym.pas
index 30f6a10f14..ced5c4fc62 100644
--- a/compiler/symsym.pas
+++ b/compiler/symsym.pas
@@ -401,6 +401,7 @@ interface
constructor create_ptr(const n : string;t : tconsttyp;v : pointer;def:tdef);virtual;
constructor create_string(const n : string;t : tconsttyp;str:pchar;l:longint;def:tdef);virtual;
constructor create_wstring(const n : string;t : tconsttyp;pw:pcompilerwidestring);virtual;
+ constructor create_undefined(const n : string;def:tdef);virtual;
constructor ppuload(ppufile:tcompilerppufile);
destructor destroy;override;
procedure buildderef;override;
@@ -491,6 +492,8 @@ interface
procedure check_hints(const srsym: tsym; const symoptions: tsymoptions; const deprecatedmsg : pshortstring);inline;
procedure check_hints(const srsym: tsym; const symoptions: tsymoptions; const deprecatedmsg : pshortstring;filepos:tfileposinfo);
+ function same_constvalue(consttyp:tconsttyp;const value1,value2:tconstvalue):boolean;
+
implementation
uses
@@ -528,6 +531,30 @@ implementation
end;
+ function same_constvalue(consttyp:tconsttyp;const value1,value2:tconstvalue):boolean;
+ begin
+ case consttyp of
+ constnone,
+ constnil:
+ result:=true;
+ constord:
+ result:=value1.valueord=value2.valueord;
+ constpointer:
+ result:=value1.valueordptr=value2.valueordptr;
+ conststring,
+ constreal,
+ constset,
+ constresourcestring,
+ constwstring,
+ constguid: begin
+ if value1.len<>value2.len then
+ exit(false);
+ result:=CompareByte(value1.valueptr^,value2.valueptr^,value1.len)=0;
+ end;
+ end;
+ end;
+
+
procedure check_hints(const srsym: tsym; const symoptions: tsymoptions; const deprecatedmsg : pshortstring);
begin
check_hints(srsym,symoptions,deprecatedmsg,current_filepos);
@@ -1618,7 +1645,6 @@ implementation
tparasymtable(parast).ppuwrite(ppufile);
end;
-
{****************************************************************************
TABSTRACTVARSYM
****************************************************************************}
@@ -2426,6 +2452,15 @@ implementation
end;
+ constructor tconstsym.create_undefined(const n : string;def: tdef);
+ begin
+ inherited create(constsym,n);
+ fillchar(value,sizeof(value),#0);
+ consttyp:=constnone;
+ constdef:=def;
+ end;
+
+
constructor tconstsym.ppuload(ppufile:tcompilerppufile);
var
pd : pbestreal;
diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
index b1724d13df..3c424725f3 100644
--- a/compiler/utils/ppuutils/ppudump.pp
+++ b/compiler/utils/ppuutils/ppudump.pp
@@ -1683,7 +1683,8 @@ const
(mask:sp_generic_para; str:'Generic Parameter'),
(mask:sp_has_deprecated_msg; str:'Has Deprecated Message'),
(mask:sp_generic_dummy; str:'Generic Dummy'),
- (mask:sp_explicitrename; str:'Explicit Rename')
+ (mask:sp_explicitrename; str:'Explicit Rename'),
+ (mask:sp_generic_const; str:'Generic Constant Parameter')
);
var
symoptions : tsymoptions;
@@ -2739,7 +2740,8 @@ const
(mask:df_not_registered_no_free; str:'Unregistered/No free (invalid)'),
(mask:df_llvm_no_struct_packing; str:'LLVM unpacked struct'),
(mask:df_internal; str:'Internal'),
- (mask:df_has_global_ref; str:'Has Global Ref')
+ (mask:df_has_global_ref; str:'Has Global Ref'),
+ (mask:df_has_generic_fields; str:'Has generic fields')
);
defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
(mask:ds_vmt_written; str:'VMT Written'),
@@ -3263,14 +3265,15 @@ const
{ ado_IsArrayOfConst } 'ArrayOfConst',
{ ado_IsConstString } 'ConstString',
{ ado_IsBitPacked } 'BitPacked',
- { ado_IsVector } 'Vector'
+ { ado_IsVector } 'Vector',
+ { ado_IsGeneric } 'Generic'
);
var
symoptions: tarraydefoptions;
i: tarraydefoption;
first: boolean;
begin
- ppufile.getset(tppuset1(symoptions));
+ ppufile.getset(tppuset2(symoptions));
if symoptions<>[] then
begin
if ado_IsDynamicArray in symoptions then Include(ArrayDef.Options, aoDynamic);
diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
new file mode 100644
index 0000000000..1cf3be5037
--- /dev/null
+++ b/tests/test/tgenconst1.pp
@@ -0,0 +1,39 @@
+{ %NORUN }
+{$mode objfpc}
+{
+ test all possible constants
+}
+program tgenconst1;
+
+type
+ TEnums = (Blaise, Pascal);
+ kNames = set of TEnums;
+ kChars = set of char;
+
+type
+ generic TBoolean<const U: boolean> = record end;
+ generic TString<const U: string> = record end;
+ generic TFloat<const U: single> = record end;
+ generic TInteger<const U: integer> = record end;
+ generic TChar<const U: char> = record end;
+ generic TByte<const U: byte> = record end;
+ generic TQWord<const U: QWord> = record end;
+ generic TEnum<const U: TEnums> = record end;
+ generic TNames<const U: kNames> = record end;
+ generic TChars<const U: kChars> = record end;
+ generic TPointer<const U: pointer> = record end;
+
+var
+ a: specialize TBoolean<true>;
+ b: specialize TString<'string'>;
+ c: specialize TFloat<1>;
+ d: specialize TInteger<10>;
+ e: specialize TByte<255>;
+ f: specialize TChar<'a'>;
+ g: specialize TEnum<Pascal>;
+ h: specialize TNames<[Blaise,Pascal]>;
+ i: specialize TChars<['a','b']>;
+ j: specialize TQWord<10>;
+ k: specialize TPointer<nil>;
+begin
+end.
diff --git a/tests/test/tgenconst10.pp b/tests/test/tgenconst10.pp
new file mode 100644
index 0000000000..eecb0bf162
--- /dev/null
+++ b/tests/test/tgenconst10.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test type mismatch when specializing generic type with constant value
+}
+program tgenconst10;
+
+type
+ generic TByte<T> = record end;
+
+var
+ a: specialize TByte<10>;
+begin
+end.
diff --git a/tests/test/tgenconst11.pp b/tests/test/tgenconst11.pp
new file mode 100644
index 0000000000..5895fd00c7
--- /dev/null
+++ b/tests/test/tgenconst11.pp
@@ -0,0 +1,13 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test def compare fail with specialized types
+}
+program tgenconst11;
+type
+ generic TConst<const U: integer> = class end;
+var
+ a:specialize TConst<10>;
+begin
+ a:=specialize TConst<'string'>.Create;
+end \ No newline at end of file
diff --git a/tests/test/tgenconst12.pp b/tests/test/tgenconst12.pp
new file mode 100644
index 0000000000..24fa5c802e
--- /dev/null
+++ b/tests/test/tgenconst12.pp
@@ -0,0 +1,15 @@
+{ %NORUN }
+{$mode objfpc}
+{
+ test def compare with specialized types
+}
+program tgenconst12;
+
+type
+ generic TTest<const U: integer> = class
+ end;
+
+type
+ ATest = specialize TTest<100>;
+begin
+end.
diff --git a/tests/test/tgenconst13.pp b/tests/test/tgenconst13.pp
new file mode 100644
index 0000000000..13235d1437
--- /dev/null
+++ b/tests/test/tgenconst13.pp
@@ -0,0 +1,51 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ test advanced record constants assigned from generic constant values
+}
+program tgenconst13;
+
+type
+ kNames = set of (Blaise,Pascal);
+ kChars = set of char;
+type
+ generic TBoolean<const U: boolean> = record const value = U; end;
+ generic TString<const U: string> = record const value = U; end;
+ generic TFloat<const U: single> = record const value = U; end;
+ generic TInteger<const U: integer> = record const value = U; end;
+ generic TByte<const U: byte> = record const value = U; end;
+ generic TChar<const U: char> = record const value = U; end;
+ generic TQWord<const U: QWord> = record const value = U; end;
+ generic TNames<const U: kNames> = record const value = U; end;
+ generic TChars<const U: kChars> = record const value = U; end;
+
+procedure Test(failed: boolean); inline;
+begin
+ if failed then
+ begin
+ writeln('failed!');
+ halt(-1);
+ end;
+end;
+
+var
+ g0: specialize TBoolean<true>;
+ g1: specialize TString<'string'>;
+ g2: specialize TFloat<10.5>;
+ g3: specialize TInteger<10>;
+ g4: specialize TByte<255>;
+ g5: specialize TChar<'a'>;
+ g6: specialize TQWord<1000000000>;
+ g7: specialize TNames<[Blaise,Pascal]>;
+ g8: specialize TChars<['a','b']>;
+begin
+ Test(g0.value <> true);
+ Test(g1.value <> 'string');
+ Test(g2.value <> 10.5);
+ Test(g3.value <> 10);
+ Test(g4.value <> 255);
+ Test(g5.value <> 'a');
+ Test(g6.value <> 1000000000);
+ Test(g7.value <> [Blaise,Pascal]);
+ Test(g8.value <> ['a','b']);
+end.
diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
new file mode 100644
index 0000000000..64e5af91b7
--- /dev/null
+++ b/tests/test/tgenconst14.pp
@@ -0,0 +1,42 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ test binary operators with generic constant params
+}
+program tgenconst14;
+
+type
+ generic TBinaryOp<const I: Integer> = record
+ const
+ d0 = I + I;
+ d1 = I - I;
+ d2 = I * I;
+ d3 = I / I;
+ d4 = I div I;
+ d5 = I mod I;
+ d6 = I and I;
+ d7 = I or I;
+ d8 = I shl 2;
+ d9 = I shr 2;
+ end;
+
+procedure Check(aExpected, aActual: Integer; aErrorCode: LongInt);
+begin
+ if aExpected <> aActual then
+ Halt(aErrorCode);
+end;
+
+var
+ op: specialize TBinaryOp<100>;
+begin
+ Check(op.d0, 100 + 100, 1);
+ Check(op.d1, 100 - 100, 2);
+ Check(op.d2, 100 * 100, 3);
+ Check(Trunc(op.d3), Trunc(100 / 100), 4);
+ Check(op.d4, 100 div 100, 5);
+ Check(op.d5, 100 mod 100, 6);
+ Check(op.d6, 100 and 100, 7);
+ Check(op.d7, 100 or 100, 8);
+ Check(op.d8, 100 shl 2, 9);
+ Check(op.d9, 100 shr 2, 10);
+end.
diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
new file mode 100644
index 0000000000..5eea8571b4
--- /dev/null
+++ b/tests/test/tgenconst15.pp
@@ -0,0 +1,15 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ test binary operator error with wrong constant type
+}
+program tgenconst15;
+
+type
+ generic TInt<const I: string> = record
+ const c = I div I;
+ end;
+
+begin
+end. \ No newline at end of file
diff --git a/tests/test/tgenconst16.pp b/tests/test/tgenconst16.pp
new file mode 100644
index 0000000000..dcfa89df2f
--- /dev/null
+++ b/tests/test/tgenconst16.pp
@@ -0,0 +1,79 @@
+{ %NORUN }
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ various operator tests
+}
+program tgenconst16;
+
+type
+ Day = (mon,tue,wed,thu,fri,sat,sun);
+ Days = set of Day;
+ generic TSet<const I: Days> = record
+ const
+ d0 = I + I; // Union
+ d1 = I - I; // Difference
+ d2 = I * I; // Intersection
+ d3 = I >< I; // Symmetric difference
+ d4 = I <= I; // Contains
+ d5 = mon in I;
+ end;
+ generic TArray<const I: integer> = record
+ type
+ t0 = array[0..I - 1] of integer;
+ t1 = array[0..high(I)] of integer;
+ t2 = array[0..low(I)] of integer;
+ t3 = array[0..sizeof(I)] of integer;
+ public
+ d0: array[0..I - 1] of integer;
+ d1: array[0..high(I)] of integer;
+ d2: array[0..low(I)] of integer;
+ d3: array[0..sizeof(I)] of integer;
+ end;
+ generic TUnaryOp<const I: integer> = record
+ const
+ d0 = -I;
+ d1 = +I;
+ d2 = not I;
+ end;
+ generic TBinaryOp<const I: integer> = record
+ const
+ // Arithmetic operators
+ // https://freepascal.org/docs-html/ref/refsu45.html
+ d0 = I + I;
+ d1 = I - I;
+ d2 = I * I;
+ d3 = I / I;
+ d4 = I div I;
+ d5 = I mod I;
+ // Boolean operators
+ // https://freepascal.org/docs-html/ref/refsu47.html
+ d6 = I and I;
+ d7 = I or I;
+ d8 = I xor I;
+ // Logical operators
+ // https://freepascal.org/docs-html/ref/refsu46.html
+ d9 = I shl I;
+ d10 = I shr I;
+ d11 = I << I;
+ d12 = I >> I;
+ // Relational operators
+ // https://freepascal.org/docs-html/ref/refsu50.html#x153-17500012.8.6
+ d13 = I <> I;
+ d14 = I < I;
+ d15 = I > I;
+ d16 = I <= I;
+ d17 = I >= I;
+ d18 = I = I;
+ end;
+ generic TOther<const I: integer> = record
+ procedure DoThis(param: integer = I);
+ end;
+
+procedure TOther.DoThis(param: integer = I);
+begin
+ writeln(param, ' default:', I);
+end;
+
+begin
+end.
diff --git a/tests/test/tgenconst17.pp b/tests/test/tgenconst17.pp
new file mode 100644
index 0000000000..ac91913e79
--- /dev/null
+++ b/tests/test/tgenconst17.pp
@@ -0,0 +1,27 @@
+{ %NORUN }
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ testing range checking for arrays and for-loops
+}
+
+program tgenconst17;
+
+type
+ generic TStaticList<T; const Length: SizeUInt> = record
+ Values: array[0..Length - 1] of T;
+ procedure Display;
+ end;
+
+procedure TStaticList.Display;
+var
+ I, n: SizeUInt;
+begin
+ for I := 0 to Length - 1 do
+ WriteLn(Values[I]);
+end;
+
+var
+ list: specialize TStaticList<Integer, 20>;
+begin
+end.
diff --git a/tests/test/tgenconst18.pp b/tests/test/tgenconst18.pp
new file mode 100644
index 0000000000..b539384759
--- /dev/null
+++ b/tests/test/tgenconst18.pp
@@ -0,0 +1,12 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test undefined constants which must be typed
+}
+program tgenconst18;
+
+type
+ generic TUndefined<const U> = record end;
+
+begin
+end.
diff --git a/tests/test/tgenconst19.pp b/tests/test/tgenconst19.pp
new file mode 100644
index 0000000000..146e05416e
--- /dev/null
+++ b/tests/test/tgenconst19.pp
@@ -0,0 +1,24 @@
+{ %NORUN }
+unit tgenconst19;
+
+{$mode objfpc}
+
+interface
+
+generic procedure Test<const A, B: LongInt>;
+generic procedure Test2<const A, B: LongInt>;
+
+implementation
+
+{ currently it does not matter whether , or ; is used in the definition (Delphi
+ compatible) }
+
+generic procedure Test<A, B>;
+begin
+end;
+
+generic procedure Test2<A; B>;
+begin
+end;
+
+end.
diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
new file mode 100644
index 0000000000..7189ca7195
--- /dev/null
+++ b/tests/test/tgenconst2.pp
@@ -0,0 +1,14 @@
+{ %NORUN }
+{$mode objfpc}
+{
+ test lists of types/contants
+}
+program tgenconst2;
+
+type
+ generic TMoreThanOne<T1,T2;const U1,U2:integer> = record end;
+
+var
+ a: specialize TMoreThanOne<integer,string,10,10>;
+begin
+end.
diff --git a/tests/test/tgenconst20.pp b/tests/test/tgenconst20.pp
new file mode 100644
index 0000000000..26b36d6984
--- /dev/null
+++ b/tests/test/tgenconst20.pp
@@ -0,0 +1,24 @@
+{ %NORUN }
+unit tgenconst20;
+
+{$mode delphi}
+
+interface
+
+procedure Test<const A, B: LongInt>;
+procedure Test2<const A, B: LongInt>;
+
+implementation
+
+{ currently it does not matter whether , or ; is used in the definition (Delphi
+ compatible) }
+
+procedure Test<A, B>;
+begin
+end;
+
+procedure Test2<A; B>;
+begin
+end;
+
+end.
diff --git a/tests/test/tgenconst21.pp b/tests/test/tgenconst21.pp
new file mode 100644
index 0000000000..eae6248eee
--- /dev/null
+++ b/tests/test/tgenconst21.pp
@@ -0,0 +1,16 @@
+unit tgenconst21;
+
+{$mode objfpc}
+
+interface
+
+implementation
+
+generic procedure Test<A; const N: LongInt>; forward;
+
+generic procedure Test<A; const N: LongInt>;
+begin
+end;
+
+end.
+
diff --git a/tests/test/tgenconst22.pp b/tests/test/tgenconst22.pp
new file mode 100644
index 0000000000..433cc50492
--- /dev/null
+++ b/tests/test/tgenconst22.pp
@@ -0,0 +1,16 @@
+unit tgenconst22;
+
+{$mode delphi}
+
+interface
+
+implementation
+
+procedure Test<A; const N: LongInt>; forward;
+
+procedure Test<A; const N: LongInt>;
+begin
+end;
+
+end.
+
diff --git a/tests/test/tgenconst23.pp b/tests/test/tgenconst23.pp
new file mode 100644
index 0000000000..2592ac32a7
--- /dev/null
+++ b/tests/test/tgenconst23.pp
@@ -0,0 +1,19 @@
+{ %FAIL }
+
+unit tgenconst23;
+
+{$mode objfpc}
+
+interface
+
+implementation
+
+generic procedure Test<A; const N: LongInt>; forward;
+
+generic procedure Test<A; const N: String>;
+begin
+end;
+
+
+end.
+
diff --git a/tests/test/tgenconst24.pp b/tests/test/tgenconst24.pp
new file mode 100644
index 0000000000..8e3c082e8b
--- /dev/null
+++ b/tests/test/tgenconst24.pp
@@ -0,0 +1,19 @@
+{ %FAIL }
+
+unit tgenconst24;
+
+{$mode delphi}
+
+interface
+
+implementation
+
+procedure Test<A; const N: LongInt>; forward;
+
+procedure Test<A; const N: String>;
+begin
+end;
+
+
+end.
+
diff --git a/tests/test/tgenconst25.pp b/tests/test/tgenconst25.pp
new file mode 100644
index 0000000000..23897e78aa
--- /dev/null
+++ b/tests/test/tgenconst25.pp
@@ -0,0 +1,18 @@
+{ %FAIL }
+
+unit tgenconst25;
+
+{$mode objfpc}
+
+interface
+
+implementation
+
+generic procedure Test<A; const N: LongInt>; forward;
+
+generic procedure Test<A; N>;
+begin
+end;
+
+end.
+
diff --git a/tests/test/tgenconst26.pp b/tests/test/tgenconst26.pp
new file mode 100644
index 0000000000..629ce36b23
--- /dev/null
+++ b/tests/test/tgenconst26.pp
@@ -0,0 +1,18 @@
+{ %FAIL }
+
+unit tgenconst26;
+
+{$mode delphi}
+
+interface
+
+implementation
+
+procedure Test<A; const N: LongInt>; forward;
+
+procedure Test<A; N>;
+begin
+end;
+
+end.
+
diff --git a/tests/test/tgenconst27.pp b/tests/test/tgenconst27.pp
new file mode 100644
index 0000000000..f31725d5b9
--- /dev/null
+++ b/tests/test/tgenconst27.pp
@@ -0,0 +1,17 @@
+{ %FAIL }
+
+unit tgenconst27;
+
+{$mode objfpc}
+
+interface
+
+generic procedure Test<const A: LongInt>;
+
+implementation
+
+generic procedure Test<const A: LongInt>;
+begin
+end;
+
+end.
diff --git a/tests/test/tgenconst28.pp b/tests/test/tgenconst28.pp
new file mode 100644
index 0000000000..56bb48f987
--- /dev/null
+++ b/tests/test/tgenconst28.pp
@@ -0,0 +1,17 @@
+{ %FAIL }
+
+unit tgenconst28;
+
+{$mode delphi}
+
+interface
+
+procedure Test<const A: LongInt>;
+
+implementation
+
+procedure Test<const A: LongInt>;
+begin
+end;
+
+end.
diff --git a/tests/test/tgenconst29.pp b/tests/test/tgenconst29.pp
new file mode 100644
index 0000000000..0e235771df
--- /dev/null
+++ b/tests/test/tgenconst29.pp
@@ -0,0 +1,14 @@
+{ %NORUN }
+program tgenconst29;
+
+{$mode objfpc}
+
+type
+ TRange = 3..4;
+
+ generic TTest<const U: TRange> = record end;
+
+var
+ t: specialize TTest<3>;
+begin
+end.
diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
new file mode 100644
index 0000000000..a85ede2169
--- /dev/null
+++ b/tests/test/tgenconst3.pp
@@ -0,0 +1,20 @@
+{ %NORUN }
+{$mode objfpc}
+{$modeswitch advancedrecords}
+{
+ test integer constants in static array ranges
+}
+program tgenconst3;
+
+type
+ generic TList<T;const U:integer> = record
+ const
+ max = U;
+ public
+ m_list: array[0..max-1] of T;
+ end;
+
+var
+ list: specialize TList<integer,128>;
+begin
+end.
diff --git a/tests/test/tgenconst30.pp b/tests/test/tgenconst30.pp
new file mode 100644
index 0000000000..07f3274dad
--- /dev/null
+++ b/tests/test/tgenconst30.pp
@@ -0,0 +1,14 @@
+{ %FAIL }
+program tgenconst30;
+
+{$mode objfpc}
+
+type
+ TRange = 3..4;
+
+ generic TTest<const U: TRange> = record end;
+
+var
+ t: specialize TTest<2>;
+begin
+end.
diff --git a/tests/test/tgenconst4.pp b/tests/test/tgenconst4.pp
new file mode 100644
index 0000000000..d401150ed2
--- /dev/null
+++ b/tests/test/tgenconst4.pp
@@ -0,0 +1,15 @@
+{ %NORUN }
+{$mode objfpc}
+{
+ test constants in generic procedures
+}
+program tgenconst4;
+
+generic procedure DoThis<T;const U:string>(msg: string = U);
+begin
+ writeln(msg, ' sizeof:',sizeof(t), ' default: ', U);
+end;
+
+begin
+ specialize DoThis<integer,'genparam'>('hello world');
+end.
diff --git a/tests/test/tgenconst5.pp b/tests/test/tgenconst5.pp
new file mode 100644
index 0000000000..56976549cb
--- /dev/null
+++ b/tests/test/tgenconst5.pp
@@ -0,0 +1,28 @@
+{ %NORUN }
+{$mode objfpc}
+{
+ test nested generic records with constants
+}
+program tgenconst5;
+
+type
+ generic THelperA<const U:integer> = record
+ list: array[0..U-1] of byte;
+ end;
+
+type
+ generic THelperB<T> = record
+ value: T;
+ end;
+
+type
+ generic TList<T; const U:integer> = record
+ helperA: specialize THelperA<U>;
+ helperB: specialize THelperB<T>;
+ end;
+
+var
+ list: specialize TList<integer,32>;
+begin
+ writeln('sizeof:',sizeof(list));
+end.
diff --git a/tests/test/tgenconst6.pp b/tests/test/tgenconst6.pp
new file mode 100644
index 0000000000..03d056ffd8
--- /dev/null
+++ b/tests/test/tgenconst6.pp
@@ -0,0 +1,25 @@
+{ %NORUN }
+{$mode delphi}
+{
+ test delphi mode
+}
+program tgenconst6;
+
+type
+ TList<T; const U: integer> = class
+ list: array[0..U-1] of T;
+ function capacity: integer;
+ end;
+
+function TList<T; U>.capacity: integer;
+begin
+ result := U;
+end;
+
+var
+ nums:TList<integer,16>;
+ strs:TList<string,16>;
+begin
+ nums := TList<integer,16>.Create;
+ strs := TList<string,16>.Create;
+end.
diff --git a/tests/test/tgenconst7.pp b/tests/test/tgenconst7.pp
new file mode 100644
index 0000000000..22bd037ebf
--- /dev/null
+++ b/tests/test/tgenconst7.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test type mismatch when specializing constant values
+}
+program tgenconst7;
+
+type
+ generic TInteger<const U: integer> = record end;
+
+var
+ a: specialize TInteger<'string'>;
+begin
+end.
diff --git a/tests/test/tgenconst8.pp b/tests/test/tgenconst8.pp
new file mode 100644
index 0000000000..418ba3c63e
--- /dev/null
+++ b/tests/test/tgenconst8.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test out of range error with constants
+}
+program tgenconst8;
+
+type
+ generic TByte<const U: Byte> = record end;
+
+var
+ a: specialize TByte<300>;
+begin
+end.
diff --git a/tests/test/tgenconst9.pp b/tests/test/tgenconst9.pp
new file mode 100644
index 0000000000..8438b70cb6
--- /dev/null
+++ b/tests/test/tgenconst9.pp
@@ -0,0 +1,12 @@
+{%FAIL}
+{$mode objfpc}
+{
+ test type mismatch when specializing constants with types
+}
+program tgenconst9;
+type
+ generic TByte<const U: Byte> = record end;
+var
+ a: specialize TByte<string>;
+begin
+end.