View Issue Details

IDProjectCategoryView StatusLast Update
0035140FPCPatchpublic2019-07-31 15:48
ReporterRyan JosephAssigned To 
PrioritynormalSeverityminorReproducibilityN/A
Status newResolutionopen 
Product Version3.3.1Product Build 
Target VersionFixed in Version 
Summary0035140: Patch for constants in generics
DescriptionFeature which adds constants in generic parameters. Full source at https://github.com/genericptr/freepascal/tree/generic_constants
Additional InformationI merged the patch into a single file and removed commit history. This is my first patch submission so please let me know if you need me to correct my submission. I've included tests with the prefix tgenconst but I'm sure if these are done correctly (again my first time).
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files
  • tgenconst-patch.txt (86,910 bytes)
    diff --git a/.gitignore b/.gitignore
    new file mode 100644
    index 0000000000..64fdb156d0
    --- /dev/null
    +++ b/.gitignore
    @@ -0,0 +1,23 @@
    +# files
    +pp
    +fpmake
    +rtl/darwin/fpcmade.x86_64-darwin
    +fpmake_proc1 copy.inc
    +tests/*.x86_64-darwin
    +rtl/Package.fpc
    +tests/createlst
    +tests/gparmake
    +
    +# directories
    +lazbuild/
    +x86_64-darwin/
    +tests/tstunits/
    +tests/utils
    +
    +# patterns
    +*.app
    +*.o
    +*.ppu
    +*.fpm
    +*.rsj
    +*.lst
    \ No newline at end of file
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 3f5882f762..793dbbbe76 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -337,9 +336,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 07c035dc26..bd51cebdf3 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -152,22 +152,22 @@ interface
         function token2managementoperator(optoken:ttoken):tmanagementoperator;
     
         { check operator args and result type }
    -
    -    type
    -      toverload_check_flag = (
    -        ocf_check_non_overloadable, { also check operators that are (currently) considered as
    -                                      not overloadable (e.g. the "+" operator for dynamic arrays
    -                                      if modeswitch arrayoperators is active) }
    -        ocf_check_only              { only check whether the operator is overloaded, but don't
    -                                      modify the passed in node (return true if the operator is
    -                                      overloaded, false otherwise) }
    -      );
    -      toverload_check_flags = set of toverload_check_flag;
    -
    +
    +    type
    +      toverload_check_flag = (
    +        ocf_check_non_overloadable, { also check operators that are (currently) considered as
    +                                      not overloadable (e.g. the "+" operator for dynamic arrays
    +                                      if modeswitch arrayoperators is active) }
    +        ocf_check_only              { only check whether the operator is overloaded, but don't
    +                                      modify the passed in node (return true if the operator is
    +                                      overloaded, false otherwise) }
    +      );
    +      toverload_check_flags = set of toverload_check_flag;
    +
         function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
         function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
    -    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    -    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
     
         { Register Allocation }
         procedure make_not_regable(p : tnode; how: tregableinfoflags);
    @@ -515,9 +515,9 @@ implementation
                         end;
     
                      { <dyn. array> + <dyn. array> is handled by the compiler }
    -                 if (m_array_operators in current_settings.modeswitches) and
    -                     (treetyp=addn) and
    -                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
    +                 if (m_array_operators in current_settings.modeswitches) and
    +                     (treetyp=addn) and
    +                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
                         begin
                           allowed:=false;
                           exit;
    @@ -720,7 +720,7 @@ implementation
           end;
     
     
    -    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
           var
             ld      : tdef;
             optoken : ttoken;
    @@ -742,11 +742,11 @@ implementation
             else
               inlinenumber:=in_none;
     
    -        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
    +        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
               exit;
     
             { operator overload is possible }
    -        result:=not (ocf_check_only in ocf);
    +        result:=not (ocf_check_only in ocf);
     
             optoken:=NOTOKEN;
             case t.nodetype of
    @@ -766,11 +766,11 @@ implementation
             end;
             if (optoken=NOTOKEN) then
               begin
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage(parser_e_operator_not_overloaded);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage(parser_e_operator_not_overloaded);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
    @@ -790,11 +790,11 @@ implementation
               begin
                 candidates.free;
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
    @@ -811,16 +811,16 @@ implementation
               begin
                 candidates.free;
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
             { Multiple candidates left? }
    -        if (cand_cnt>1) and not (ocf_check_only in ocf) then
    +        if (cand_cnt>1) and not (ocf_check_only in ocf) then
               begin
                 CGMessage(type_e_cant_choose_overload_function);
     {$ifdef EXTDEBUG}
    @@ -833,13 +833,13 @@ implementation
               end;
             candidates.free;
     
    -        if ocf_check_only in ocf then
    -          begin
    -            ppn.free;
    -            result:=true;
    -            exit;
    -          end;
    -
    +        if ocf_check_only in ocf then
    +          begin
    +            ppn.free;
    +            result:=true;
    +            exit;
    +          end;
    +
             addsymref(operpd.procsym);
     
             { the nil as symtable signs firstcalln that this is
    @@ -852,7 +852,7 @@ implementation
           end;
     
     
    -    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
           var
             rd,ld   : tdef;
             optoken : ttoken;
    @@ -945,14 +945,14 @@ implementation
             { load easier access variables }
             ld:=tbinarynode(t).left.resultdef;
             rd:=tbinarynode(t).right.resultdef;
    -        if not (ocf_check_non_overloadable in ocf) and
    -            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
    +        if not (ocf_check_non_overloadable in ocf) and
    +            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
               exit;
     
             { operator overload is possible }
    -        { if we only check for the existance of the overload, then we assume that
    -          it is not overloaded }
    -        result:=not (ocf_check_only in ocf);
    +        { if we only check for the existance of the overload, then we assume that
    +          it is not overloaded }
    +        result:=not (ocf_check_only in ocf);
     
             case t.nodetype of
                equaln:
    @@ -997,19 +997,19 @@ implementation
                  optoken:=_OP_IN;
                else
                  begin
    -               if not (ocf_check_only in ocf) then
    -                 begin
    -                   CGMessage(parser_e_operator_not_overloaded);
    -                   t:=cnothingnode.create;
    -                 end;
    +               if not (ocf_check_only in ocf) then
    +                 begin
    +                   CGMessage(parser_e_operator_not_overloaded);
    +                   t:=cnothingnode.create;
    +                 end;
                    exit;
                  end;
             end;
     
    -        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
    +        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
     
             { no operator found for "<>" then search for "=" operator }
    -        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
    +        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
               begin
                 ppn.free;
                 ppn:=nil;
    @@ -1021,15 +1021,15 @@ implementation
             if (cand_cnt=0) then
               begin
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              t:=cnothingnode.create;
    -            exit;
    -          end;
    -
    -        if ocf_check_only in ocf then
    -          begin
    -            ppn.free;
    -            result:=true;
    +            if not (ocf_check_only in ocf) then
    +              t:=cnothingnode.create;
    +            exit;
    +          end;
    +
    +        if ocf_check_only in ocf then
    +          begin
    +            ppn.free;
    +            result:=true;
                 exit;
               end;
     
    @@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
    index ae94637c28..e1617b3e96 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -311,11 +311,21 @@ implementation
                 p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
               constnil :
                 p1:=cnilnode.create;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef := p.constdef;
    +            end;
               constguid :
                 p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
               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/node.pas b/compiler/node.pas
    index b8600000bf..f9ab8ec521 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -194,7 +194,8 @@ interface
               'loadparentfpn',
               'objcselectorn',
               'objcprotocoln',
    -          'specializen');
    +          'specializen'
    +          );
     
           { a set containing all const nodes }
           nodetype_const = [ordconstn,
    @@ -272,10 +273,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
              }
    @@ -1078,7 +1082,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 6270ec582e..bd031e6a86 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -239,7 +239,7 @@ implementation
                internalerror(20021126);
     
              t:=self;
    -         if isbinaryoverloaded(t,[]) then
    +         if isbinaryoverloaded(t,[]) then
                begin
                  result:=t;
                  exit;
    @@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
    index c5b5bcc921..583d00c17b 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -141,18 +141,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -179,6 +179,9 @@ implementation
                else
                  Message(parser_e_illegal_expression);
             end;
    +        { transfer generic param flag from node to symbol }
    +        if nf_generic_para in p.flags then
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -507,8 +510,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/pdecvar.pas b/compiler/pdecvar.pas
    index 4d39397e46..8121d87853 100644
    --- a/compiler/pdecvar.pas
    +++ b/compiler/pdecvar.pas
    @@ -1675,6 +1675,10 @@ implementation
                        end;
                    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 bc0606ed4b..e6d9633ebd 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4078,7 +4084,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
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 7760a4e134..33daf3b06a 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;
    @@ -63,18 +63,163 @@ implementation
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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;
     
    +  type
    +    tdeftypeset = set of tdeftyp;
    +  const
    +    tgeneric_param_const_types:tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ = constsym then
    +          result := tconstsym(sym).consttyp<>constundefined
    +        else
    +          result := false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue): boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
    +        sym.owner:=fromdef.owner;
    +        result:=sym;
    +      end;
     
         procedure maybe_add_waiting_unit(tt:tdef);
           var
    @@ -104,203 +249,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                                intffound:=assigned(
    -                                             find_implemented_interface(objdef,
    -                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
    -                                             )
    -                                           );
    +                                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
    +                              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 +484,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +501,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);
    @@ -351,7 +528,9 @@ uses
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +546,47 @@ 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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +606,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;
     
     
    @@ -578,7 +773,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
    @@ -627,7 +822,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);
    @@ -656,7 +851,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +942,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +951,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 }
    @@ -771,20 +967,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 }
    @@ -1196,8 +1391,8 @@ 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;
    @@ -1205,22 +1400,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1335,6 +1595,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
    @@ -1360,21 +1621,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1382,7 +1649,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;
    @@ -1407,10 +1676,22 @@ 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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
    index 10c42e7eb8..31011be3e8 100644
    --- a/compiler/ppu.pas
    +++ b/compiler/ppu.pas
    @@ -43,7 +43,7 @@ type
     {$endif Test_Double_checksum}
     
     const
    -  CurrentPPUVersion = 201;
    +  CurrentPPUVersion = 203;
     
     { unit flags }
       uf_init                = $000001; { unit has initialization section }
    diff --git a/compiler/ptype.pas b/compiler/ptype.pas
    index 38e2526e9f..28cd0f94f8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/ryan_ppcx64.lpi b/compiler/ryan_ppcx64.lpi
    new file mode 100644
    index 0000000000..68008e4ab3
    --- /dev/null
    +++ b/compiler/ryan_ppcx64.lpi
    @@ -0,0 +1,77 @@
    +<?xml version="1.0"?>
    +<CONFIG>
    +  <ProjectOptions>
    +    <Version Value="9"/>
    +    <PathDelim Value="\"/>
    +    <General>
    +      <Flags>
    +        <MainUnitHasUsesSectionForAllUnits Value="False"/>
    +        <MainUnitHasCreateFormStatements Value="False"/>
    +        <MainUnitHasTitleStatement Value="False"/>
    +        <LRSInOutputDirectory Value="False"/>
    +      </Flags>
    +      <SessionStorage Value="InProjectDir"/>
    +      <MainUnit Value="0"/>
    +      <Title Value="ppcx64"/>
    +    </General>
    +    <BuildModes Count="1">
    +      <Item1 Name="default" Default="True"/>
    +    </BuildModes>
    +    <PublishOptions>
    +      <Version Value="2"/>
    +      <DestinationDirectory Value="$(TestDir)\publishedproject\"/>
    +      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
    +      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
    +    </PublishOptions>
    +    <RunParams>
    +      <local>
    +        <FormatVersion Value="1"/>
    +        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
    +      </local>
    +    </RunParams>
    +    <Units Count="2">
    +      <Unit0>
    +        <Filename Value="pp.pas"/>
    +        <IsPartOfProject Value="True"/>
    +        <UnitName Value="pp"/>
    +      </Unit0>
    +      <Unit1>
    +        <Filename Value="x86\aasmcpu.pas"/>
    +        <IsPartOfProject Value="True"/>
    +        <UnitName Value="aasmcpu"/>
    +      </Unit1>
    +    </Units>
    +  </ProjectOptions>
    +  <CompilerOptions>
    +    <Version Value="11"/>
    +    <PathDelim Value="\"/>
    +    <Target>
    +      <Filename Value="x86_64\pp"/>
    +    </Target>
    +    <SearchPaths>
    +      <IncludeFiles Value="x86_64"/>
    +      <OtherUnitFiles Value="x86_64;x86;systems"/>
    +      <UnitOutputDirectory Value="x86_64\lazbuild"/>
    +    </SearchPaths>
    +    <Parsing>
    +      <SyntaxOptions>
    +        <CStyleOperator Value="False"/>
    +        <AllowLabel Value="False"/>
    +        <CPPInline Value="False"/>
    +        <UseAnsiStrings Value="False"/>
    +      </SyntaxOptions>
    +    </Parsing>
    +    <Other>
    +      <Verbosity>
    +        <ShowWarn Value="False"/>
    +        <ShowNotes Value="False"/>
    +        <ShowHints Value="False"/>
    +      </Verbosity>
    +      <ConfigFile>
    +        <StopAfterErrCount Value="50"/>
    +      </ConfigFile>
    +      <CustomOptions Value="-dx86_64 -gw -godwarfcpp -Fl/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"/>
    +      <CompilerPath Value="$(CompPath)"/>
    +    </Other>
    +  </CompilerOptions>
    +</CONFIG>
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index a5ae7e0fb9..e02ce3a8ca 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -232,7 +232,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -651,7 +654,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -692,7 +695,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 4a260c46b9..0f7a2e4c06 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -129,6 +129,9 @@ interface
               function is_generic:boolean;inline;
               { same as above for specializations }
               function is_specialization:boolean;inline;
    +          { 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
    @@ -2197,13 +2200,26 @@ 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);
                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
    @@ -2220,12 +2236,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index b21a5f9de9..04c07a5ec7 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1581,7 +1582,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2344,6 +2344,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2416,7 +2423,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2448,7 +2456,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2461,7 +2469,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2476,7 +2484,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2627,7 +2636,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index 796b2d6736..ae82024b03 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2781,7 +2781,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    
    tgenconst-patch.txt (86,910 bytes)
  • gen-const.diff (68,610 bytes)
    From e298c138a26964a9e960f868c8f821564ac52e55 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Tue, 6 Nov 2018 13:58:49 +0700
    Subject: [PATCH] constants in generics
    
    ---
     compiler/defcmp.pas       |   9 +-
     compiler/htypechk.pas     |   2 +-
     compiler/ncon.pas         |  10 +
     compiler/node.pas         |  16 +-
     compiler/nset.pas         |   5 +-
     compiler/pdecl.pas        |  32 ++-
     compiler/pexpr.pas        |  11 +-
     compiler/pgentype.pas     |   8 +-
     compiler/pgenutil.pas     | 540 ++++++++++++++++++++++++--------------
     compiler/ptype.pas        |   4 +-
     compiler/symconst.pas     |  12 +-
     compiler/symdef.pas       |  19 +-
     compiler/symsym.pas       |  22 +-
     compiler/symtable.pas     |   2 +-
     tests/test/tgenconst1.pp  |  33 +++
     tests/test/tgenconst10.pp |  13 +
     tests/test/tgenconst11.pp |  21 ++
     tests/test/tgenconst12.pp |  16 ++
     tests/test/tgenconst13.pp |  20 ++
     tests/test/tgenconst2.pp  |  12 +
     tests/test/tgenconst3.pp  |  16 ++
     tests/test/tgenconst4.pp  |  11 +
     tests/test/tgenconst5.pp  |  24 ++
     tests/test/tgenconst6.pp  |  21 ++
     tests/test/tgenconst7.pp  |  11 +
     tests/test/tgenconst8.pp  |  11 +
     tests/test/tgenconst9.pp  |  11 +
     27 files changed, 667 insertions(+), 245 deletions(-)
     create mode 100644 tests/test/tgenconst1.pp
     create mode 100644 tests/test/tgenconst10.pp
     create mode 100644 tests/test/tgenconst11.pp
     create mode 100644 tests/test/tgenconst12.pp
     create mode 100644 tests/test/tgenconst13.pp
     create mode 100644 tests/test/tgenconst2.pp
     create mode 100644 tests/test/tgenconst3.pp
     create mode 100644 tests/test/tgenconst4.pp
     create mode 100644 tests/test/tgenconst5.pp
     create mode 100644 tests/test/tgenconst6.pp
     create mode 100644 tests/test/tgenconst7.pp
     create mode 100644 tests/test/tgenconst8.pp
     create mode 100644 tests/test/tgenconst9.pp
    
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 9fc5b29119..87797faf3e 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -345,9 +344,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 4e97f903a9..89d32fa966 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2705,7 +2705,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/ncon.pas b/compiler/ncon.pas
    index 392e11ea1d..90087c265f 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -311,11 +311,21 @@ implementation
                 p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
               constnil :
                 p1:=cnilnode.create;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef := p.constdef;
    +            end;
               constguid :
                 p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
               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/node.pas b/compiler/node.pas
    index a0aad228eb..468907a886 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -274,10 +274,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
              }
    @@ -1080,7 +1083,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 684eafd79a..e6e1d51a16 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -401,8 +401,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/pdecl.pas b/compiler/pdecl.pas
    index fcb19c2c3d..82c0160805 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -141,18 +141,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -179,6 +179,9 @@ implementation
                else
                  Message(parser_e_illegal_expression);
             end;
    +        { transfer generic param flag from node to symbol }
    +        if nf_generic_para in p.flags then
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -510,8 +513,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/pexpr.pas b/compiler/pexpr.pas
    index 251c613ef1..caf1ef2774 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4167,7 +4173,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
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 4e52761d73..4c634904a6 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;
    @@ -104,203 +104,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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 +339,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +356,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);
    @@ -351,7 +383,9 @@ uses
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +401,47 @@ 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
    +                    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 +461,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;
     
     
    @@ -578,7 +628,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
    @@ -627,7 +677,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);
    @@ -656,7 +706,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +797,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +806,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 }
    @@ -771,20 +822,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 }
    @@ -1199,8 +1249,8 @@ 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;
    @@ -1208,22 +1258,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1338,6 +1453,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
    @@ -1363,21 +1479,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1385,7 +1507,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;
    @@ -1410,10 +1534,22 @@ 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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1421,13 +1557,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/ptype.pas b/compiler/ptype.pas
    index 5236f253f1..6b642803b8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index bf284cb58f..ad7424f60a 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -231,7 +231,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -659,7 +662,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -700,7 +703,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -840,7 +844,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 7db695005e..4be6f572af 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -2293,13 +2293,26 @@ 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);
                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
    @@ -2316,12 +2329,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index 2384509f47..2d0e499f54 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1584,7 +1585,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2356,6 +2356,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2428,7 +2435,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2460,7 +2468,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2473,7 +2481,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2488,7 +2496,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2641,7 +2650,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index c7abd7da58..906e2da4a3 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2916,7 +2916,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    -- 
    2.17.2 (Apple Git-113)
    
    
    gen-const.diff (68,610 bytes)
  • gen-const-clean.diff (81,965 bytes)
    From 57a8c0589a5c881dd55d36dbf03832cb024e3cf3 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Sun, 17 Mar 2019 16:57:25 -0400
    Subject: [PATCH] constants in generics
    
    ---
     compiler/defcmp.pas                |   9 +-
     compiler/htypechk.pas              |   2 +-
     compiler/ncon.pas                  |  38 +-
     compiler/nmat.pas                  |   5 +-
     compiler/node.pas                  |  19 +-
     compiler/nset.pas                  |   5 +-
     compiler/pdecl.pas                 |  43 +-
     compiler/pdecvar.pas               |   4 +
     compiler/pexpr.pas                 |  11 +-
     compiler/pgentype.pas              |   8 +-
     compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
     compiler/ppu.pas                   |   2 +-
     compiler/ptype.pas                 |   4 +-
     compiler/symconst.pas              |  12 +-
     compiler/symdef.pas                |  22 +-
     compiler/symsym.pas                |  22 +-
     compiler/symtable.pas              |   2 +-
     compiler/utils/ppuutils/ppudump.pp |   3 +-
     tests/test/tgenconst1.pp           |  33 ++
     tests/test/tgenconst10.pp          |  13 +
     tests/test/tgenconst11.pp          |  21 +
     tests/test/tgenconst12.pp          |  16 +
     tests/test/tgenconst13.pp          |  20 +
     tests/test/tgenconst14.pp          |  29 ++
     tests/test/tgenconst15.pp          |  30 ++
     tests/test/tgenconst2.pp           |  12 +
     tests/test/tgenconst3.pp           |  16 +
     tests/test/tgenconst4.pp           |  11 +
     tests/test/tgenconst5.pp           |  24 +
     tests/test/tgenconst6.pp           |  21 +
     tests/test/tgenconst7.pp           |  11 +
     tests/test/tgenconst8.pp           |  11 +
     tests/test/tgenconst9.pp           |  11 +
     33 files changed, 923 insertions(+), 260 deletions(-)
     create mode 100644 tests/test/tgenconst1.pp
     create mode 100644 tests/test/tgenconst10.pp
     create mode 100644 tests/test/tgenconst11.pp
     create mode 100644 tests/test/tgenconst12.pp
     create mode 100644 tests/test/tgenconst13.pp
     create mode 100644 tests/test/tgenconst14.pp
     create mode 100644 tests/test/tgenconst15.pp
     create mode 100644 tests/test/tgenconst2.pp
     create mode 100644 tests/test/tgenconst3.pp
     create mode 100644 tests/test/tgenconst4.pp
     create mode 100644 tests/test/tgenconst5.pp
     create mode 100644 tests/test/tgenconst6.pp
     create mode 100644 tests/test/tgenconst7.pp
     create mode 100644 tests/test/tgenconst8.pp
     create mode 100644 tests/test/tgenconst9.pp
    
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 3f5882f762..793dbbbe76 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -337,9 +336,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 07c035dc26..2358ea4b6d 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
    index ae94637c28..fb5e94c09f 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -304,18 +304,48 @@ 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 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_para in p.symoptions then
    +                p1:=csetconstnode.create(default(pconstset),p.constdef)
    +              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;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef := p.constdef;
    +            end;
               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 355b493da4..d10dff6128 100644
    --- a/compiler/nmat.pas
    +++ b/compiler/nmat.pas
    @@ -129,7 +129,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 b8600000bf..f9ab8ec521 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -194,7 +194,8 @@ interface
               'loadparentfpn',
               'objcselectorn',
               'objcprotocoln',
    -          'specializen');
    +          'specializen'
    +          );
     
           { a set containing all const nodes }
           nodetype_const = [ordconstn,
    @@ -272,10 +273,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
              }
    @@ -1078,7 +1082,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 6270ec582e..4360a7340d 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
    index c5b5bcc921..767eec22f7 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -126,9 +126,14 @@ implementation
                  end;
                setconstn :
                  begin
    -               new(ps);
    -               ps^:=tsetconstnode(p).value_set^;
    -               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +               if nf_generic_para in p.flags then
    +                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
    +               else
    +                 begin
    +                   new(ps);
    +                   ps^:=tsetconstnode(p).value_set^;
    +                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +                 end;
                  end;
                pointerconstn :
                  begin
    @@ -141,18 +146,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -179,6 +184,9 @@ implementation
                else
                  Message(parser_e_illegal_expression);
             end;
    +        { transfer generic param flag from node to symbol }
    +        if nf_generic_para in p.flags then
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -507,8 +515,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/pdecvar.pas b/compiler/pdecvar.pas
    index 4d39397e46..8121d87853 100644
    --- a/compiler/pdecvar.pas
    +++ b/compiler/pdecvar.pas
    @@ -1675,6 +1675,10 @@ implementation
                        end;
                    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 bc0606ed4b..e6d9633ebd 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4078,7 +4084,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
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 7760a4e134..33daf3b06a 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;
    @@ -63,18 +63,163 @@ implementation
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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;
     
    +  type
    +    tdeftypeset = set of tdeftyp;
    +  const
    +    tgeneric_param_const_types:tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ = constsym then
    +          result := tconstsym(sym).consttyp<>constundefined
    +        else
    +          result := false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue): boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
    +        sym.owner:=fromdef.owner;
    +        result:=sym;
    +      end;
     
         procedure maybe_add_waiting_unit(tt:tdef);
           var
    @@ -104,203 +249,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                                intffound:=assigned(
    -                                             find_implemented_interface(objdef,
    -                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
    -                                             )
    -                                           );
    +                                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
    +                              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 +484,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +501,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);
    @@ -351,7 +528,9 @@ uses
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +546,47 @@ 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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +606,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;
     
     
    @@ -578,7 +773,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
    @@ -627,7 +822,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);
    @@ -656,7 +851,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +942,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +951,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 }
    @@ -771,20 +967,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 }
    @@ -1196,8 +1391,8 @@ 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;
    @@ -1205,22 +1400,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1335,6 +1595,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
    @@ -1360,21 +1621,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1382,7 +1649,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;
    @@ -1407,10 +1676,22 @@ 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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
    index 10c42e7eb8..31011be3e8 100644
    --- a/compiler/ppu.pas
    +++ b/compiler/ppu.pas
    @@ -43,7 +43,7 @@ type
     {$endif Test_Double_checksum}
     
     const
    -  CurrentPPUVersion = 201;
    +  CurrentPPUVersion = 203;
     
     { unit flags }
       uf_init                = $000001; { unit has initialization section }
    diff --git a/compiler/ptype.pas b/compiler/ptype.pas
    index 38e2526e9f..28cd0f94f8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index a5ae7e0fb9..e02ce3a8ca 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -232,7 +232,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -651,7 +654,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -692,7 +695,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 4a260c46b9..0f7a2e4c06 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -129,6 +129,9 @@ interface
               function is_generic:boolean;inline;
               { same as above for specializations }
               function is_specialization:boolean;inline;
    +          { 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
    @@ -2197,13 +2200,26 @@ 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);
                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
    @@ -2220,12 +2236,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index b21a5f9de9..04c07a5ec7 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1581,7 +1582,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2344,6 +2344,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2416,7 +2423,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2448,7 +2456,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2461,7 +2469,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2476,7 +2484,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2627,7 +2636,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index 796b2d6736..ae82024b03 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2781,7 +2781,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
    index 74fde5c6c2..80d9d4df11 100644
    --- a/compiler/utils/ppuutils/ppudump.pp
    +++ b/compiler/utils/ppuutils/ppudump.pp
    @@ -1552,7 +1552,8 @@ const
          { this should never happen for defs stored to a ppu file }
          (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_internal;       str:'Internal'),
    +     (mask:df_has_generic_fields; str:'Has generic fields')
       );
       defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
          (mask:ds_vmt_written;           str:'VMT Written'),
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
    new file mode 100644
    index 0000000000..7f98086630
    --- /dev/null
    +++ b/tests/test/tgenconst14.pp
    @@ -0,0 +1,29 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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;
    +  end;
    +
    +var
    +	op: specialize TBinaryOp<100>;
    +begin
    +	writeln(op.d0);
    +	writeln(op.d1);
    +	writeln(op.d2);
    +	writeln(op.d3:1:1);
    +	writeln(op.d4);
    +	writeln(op.d5);
    +	writeln(op.d6);
    +	writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
    new file mode 100644
    index 0000000000..56744cd0a7
    --- /dev/null
    +++ b/tests/test/tgenconst15.pp
    @@ -0,0 +1,30 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst15;
    +
    +type
    +	kNames = set of (Blaise, Pascal);
    +  generic TSet<const I: kNames> = record
    +    const c = I;
    +  end;
    +  generic TString<const I: String> = record
    +    const c = I;
    +  end;
    +  generic TWideString<const I: WideString> = record
    +    const c = I;
    +  end;
    +  generic TSingle<const I: Single> = record
    +    const c = I; 
    +  end;
    +  generic TDouble<const I: Double> = record
    +    const c = I; 
    +  end;
    +  generic TReal<const I: Real> = record
    +    const c = I; 
    +  end;
    +
    +var
    +	a0: specialize TReal<100>;
    +begin
    +	writeln(a0.c);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    -- 
    2.17.2 (Apple Git-113)
    
    
    gen-const-clean.diff (81,965 bytes)
  • gen-const-3-23.diff (96,712 bytes)
    From 376a757c8340f9c8995c9ba25cc09400d0ce9280 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Tue, 6 Nov 2018 13:58:49 +0700
    Subject: [PATCH] constants in generics
    
    ---
     .gitignore                         |  24 +
     compiler/defcmp.pas                |   9 +-
     compiler/htypechk.pas              | 132 +++---
     compiler/ncon.pas                  |  42 +-
     compiler/nmat.pas                  |   5 +-
     compiler/node.pas                  |  22 +-
     compiler/nset.pas                  |   7 +-
     compiler/pdecl.pas                 |  53 ++-
     compiler/pdecvar.pas               |   4 +
     compiler/pexpr.pas                 |  11 +-
     compiler/pgentype.pas              |   8 +-
     compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
     compiler/ppu.pas                   |   2 +-
     compiler/ptype.pas                 |   4 +-
     compiler/symconst.pas              |  12 +-
     compiler/symdef.pas                |  22 +-
     compiler/symsym.pas                |  22 +-
     compiler/symtable.pas              |   2 +-
     compiler/utils/ppuutils/ppudump.pp |   3 +-
     tests/test/tgenconst1.pp           |  33 ++
     tests/test/tgenconst10.pp          |  13 +
     tests/test/tgenconst11.pp          |  21 +
     tests/test/tgenconst12.pp          |  16 +
     tests/test/tgenconst13.pp          |  20 +
     tests/test/tgenconst14.pp          |  29 ++
     tests/test/tgenconst15.pp          |  30 ++
     tests/test/tgenconst16.pp          |  86 ++++
     tests/test/tgenconst17.pp          |  36 ++
     tests/test/tgenconst18.pp          |  12 +
     tests/test/tgenconst2.pp           |  12 +
     tests/test/tgenconst3.pp           |  16 +
     tests/test/tgenconst4.pp           |  11 +
     tests/test/tgenconst5.pp           |  24 +
     tests/test/tgenconst6.pp           |  21 +
     tests/test/tgenconst7.pp           |  11 +
     tests/test/tgenconst8.pp           |  11 +
     tests/test/tgenconst9.pp           |  11 +
     37 files changed, 1163 insertions(+), 327 deletions(-)
     create mode 100644 .gitignore
     create mode 100644 tests/test/tgenconst1.pp
     create mode 100644 tests/test/tgenconst10.pp
     create mode 100644 tests/test/tgenconst11.pp
     create mode 100644 tests/test/tgenconst12.pp
     create mode 100644 tests/test/tgenconst13.pp
     create mode 100644 tests/test/tgenconst14.pp
     create mode 100644 tests/test/tgenconst15.pp
     create mode 100644 tests/test/tgenconst16.pp
     create mode 100644 tests/test/tgenconst17.pp
     create mode 100644 tests/test/tgenconst18.pp
     create mode 100644 tests/test/tgenconst2.pp
     create mode 100644 tests/test/tgenconst3.pp
     create mode 100644 tests/test/tgenconst4.pp
     create mode 100644 tests/test/tgenconst5.pp
     create mode 100644 tests/test/tgenconst6.pp
     create mode 100644 tests/test/tgenconst7.pp
     create mode 100644 tests/test/tgenconst8.pp
     create mode 100644 tests/test/tgenconst9.pp
    
    diff --git a/.gitignore b/.gitignore
    new file mode 100644
    index 0000000000..a82c960cbe
    --- /dev/null
    +++ b/.gitignore
    @@ -0,0 +1,24 @@
    +# files
    +.gitignore
    +pp
    +fpmake
    +rtl/darwin/fpcmade.x86_64-darwin
    +fpmake_proc1 copy.inc
    +tests/*.x86_64-darwin
    +rtl/Package.fpc
    +tests/createlst
    +tests/gparmake
    +
    +# directories
    +lazbuild/
    +x86_64-darwin/
    +tests/tstunits/
    +tests/utils
    +
    +# patterns
    +*.app
    +*.o
    +*.ppu
    +*.fpm
    +*.rsj
    +*.lst
    \ No newline at end of file
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 3f5882f762..793dbbbe76 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -337,9 +336,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 07c035dc26..bd51cebdf3 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -152,22 +152,22 @@ interface
         function token2managementoperator(optoken:ttoken):tmanagementoperator;
     
         { check operator args and result type }
    -
    -    type
    -      toverload_check_flag = (
    -        ocf_check_non_overloadable, { also check operators that are (currently) considered as
    -                                      not overloadable (e.g. the "+" operator for dynamic arrays
    -                                      if modeswitch arrayoperators is active) }
    -        ocf_check_only              { only check whether the operator is overloaded, but don't
    -                                      modify the passed in node (return true if the operator is
    -                                      overloaded, false otherwise) }
    -      );
    -      toverload_check_flags = set of toverload_check_flag;
    -
    +
    +    type
    +      toverload_check_flag = (
    +        ocf_check_non_overloadable, { also check operators that are (currently) considered as
    +                                      not overloadable (e.g. the "+" operator for dynamic arrays
    +                                      if modeswitch arrayoperators is active) }
    +        ocf_check_only              { only check whether the operator is overloaded, but don't
    +                                      modify the passed in node (return true if the operator is
    +                                      overloaded, false otherwise) }
    +      );
    +      toverload_check_flags = set of toverload_check_flag;
    +
         function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
         function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
    -    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    -    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
     
         { Register Allocation }
         procedure make_not_regable(p : tnode; how: tregableinfoflags);
    @@ -515,9 +515,9 @@ implementation
                         end;
     
                      { <dyn. array> + <dyn. array> is handled by the compiler }
    -                 if (m_array_operators in current_settings.modeswitches) and
    -                     (treetyp=addn) and
    -                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
    +                 if (m_array_operators in current_settings.modeswitches) and
    +                     (treetyp=addn) and
    +                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
                         begin
                           allowed:=false;
                           exit;
    @@ -720,7 +720,7 @@ implementation
           end;
     
     
    -    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
           var
             ld      : tdef;
             optoken : ttoken;
    @@ -742,11 +742,11 @@ implementation
             else
               inlinenumber:=in_none;
     
    -        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
    +        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
               exit;
     
             { operator overload is possible }
    -        result:=not (ocf_check_only in ocf);
    +        result:=not (ocf_check_only in ocf);
     
             optoken:=NOTOKEN;
             case t.nodetype of
    @@ -766,11 +766,11 @@ implementation
             end;
             if (optoken=NOTOKEN) then
               begin
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage(parser_e_operator_not_overloaded);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage(parser_e_operator_not_overloaded);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
    @@ -790,11 +790,11 @@ implementation
               begin
                 candidates.free;
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
    @@ -811,16 +811,16 @@ implementation
               begin
                 candidates.free;
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              begin
    -                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    -                t:=cnothingnode.create;
    -              end;
    +            if not (ocf_check_only in ocf) then
    +              begin
    +                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
    +                t:=cnothingnode.create;
    +              end;
                 exit;
               end;
     
             { Multiple candidates left? }
    -        if (cand_cnt>1) and not (ocf_check_only in ocf) then
    +        if (cand_cnt>1) and not (ocf_check_only in ocf) then
               begin
                 CGMessage(type_e_cant_choose_overload_function);
     {$ifdef EXTDEBUG}
    @@ -833,13 +833,13 @@ implementation
               end;
             candidates.free;
     
    -        if ocf_check_only in ocf then
    -          begin
    -            ppn.free;
    -            result:=true;
    -            exit;
    -          end;
    -
    +        if ocf_check_only in ocf then
    +          begin
    +            ppn.free;
    +            result:=true;
    +            exit;
    +          end;
    +
             addsymref(operpd.procsym);
     
             { the nil as symtable signs firstcalln that this is
    @@ -852,7 +852,7 @@ implementation
           end;
     
     
    -    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
    +    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
           var
             rd,ld   : tdef;
             optoken : ttoken;
    @@ -945,14 +945,14 @@ implementation
             { load easier access variables }
             ld:=tbinarynode(t).left.resultdef;
             rd:=tbinarynode(t).right.resultdef;
    -        if not (ocf_check_non_overloadable in ocf) and
    -            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
    +        if not (ocf_check_non_overloadable in ocf) and
    +            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
               exit;
     
             { operator overload is possible }
    -        { if we only check for the existance of the overload, then we assume that
    -          it is not overloaded }
    -        result:=not (ocf_check_only in ocf);
    +        { if we only check for the existance of the overload, then we assume that
    +          it is not overloaded }
    +        result:=not (ocf_check_only in ocf);
     
             case t.nodetype of
                equaln:
    @@ -997,19 +997,19 @@ implementation
                  optoken:=_OP_IN;
                else
                  begin
    -               if not (ocf_check_only in ocf) then
    -                 begin
    -                   CGMessage(parser_e_operator_not_overloaded);
    -                   t:=cnothingnode.create;
    -                 end;
    +               if not (ocf_check_only in ocf) then
    +                 begin
    +                   CGMessage(parser_e_operator_not_overloaded);
    +                   t:=cnothingnode.create;
    +                 end;
                    exit;
                  end;
             end;
     
    -        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
    +        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
     
             { no operator found for "<>" then search for "=" operator }
    -        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
    +        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
               begin
                 ppn.free;
                 ppn:=nil;
    @@ -1021,15 +1021,15 @@ implementation
             if (cand_cnt=0) then
               begin
                 ppn.free;
    -            if not (ocf_check_only in ocf) then
    -              t:=cnothingnode.create;
    -            exit;
    -          end;
    -
    -        if ocf_check_only in ocf then
    -          begin
    -            ppn.free;
    -            result:=true;
    +            if not (ocf_check_only in ocf) then
    +              t:=cnothingnode.create;
    +            exit;
    +          end;
    +
    +        if ocf_check_only in ocf then
    +          begin
    +            ppn.free;
    +            result:=true;
                 exit;
               end;
     
    @@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
    index ae94637c28..1e203f74d6 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -279,6 +279,7 @@ implementation
             p1  : tnode;
             len : longint;
             pc  : pchar;
    +        value_set : pconstset;
           begin
             p1:=nil;
             case p.consttyp of
    @@ -304,18 +305,51 @@ 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 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_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;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef:=p.constdef;
    +            end;
               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 355b493da4..d10dff6128 100644
    --- a/compiler/nmat.pas
    +++ b/compiler/nmat.pas
    @@ -129,7 +129,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 b8600000bf..33a85b1493 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -194,7 +194,8 @@ interface
               'loadparentfpn',
               'objcselectorn',
               'objcprotocoln',
    -          'specializen');
    +          'specializen'
    +          );
     
           { a set containing all const nodes }
           nodetype_const = [ordconstn,
    @@ -272,10 +273,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
              }
    @@ -983,6 +987,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;
     
    @@ -1078,7 +1085,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 6270ec582e..bd031e6a86 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -239,7 +239,7 @@ implementation
                internalerror(20021126);
     
              t:=self;
    -         if isbinaryoverloaded(t,[]) then
    +         if isbinaryoverloaded(t,[]) then
                begin
                  result:=t;
                  exit;
    @@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
    index c5b5bcc921..d7e80b928f 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -126,9 +126,14 @@ implementation
                  end;
                setconstn :
                  begin
    -               new(ps);
    -               ps^:=tsetconstnode(p).value_set^;
    -               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +               if nf_generic_para in p.flags then
    +                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
    +               else
    +                 begin
    +                   new(ps);
    +                   ps^:=tsetconstnode(p).value_set^;
    +                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +                 end;
                  end;
                pointerconstn :
                  begin
    @@ -141,18 +146,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -177,8 +182,19 @@ 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
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -507,8 +523,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/pdecvar.pas b/compiler/pdecvar.pas
    index 4d39397e46..8121d87853 100644
    --- a/compiler/pdecvar.pas
    +++ b/compiler/pdecvar.pas
    @@ -1675,6 +1675,10 @@ implementation
                        end;
                    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 bc0606ed4b..e6d9633ebd 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4078,7 +4084,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
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 7760a4e134..ac6e59ce98 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;
    @@ -63,18 +63,163 @@ implementation
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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;
     
    +  type
    +    tdeftypeset = set of tdeftyp;
    +  const
    +    tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ=constsym then
    +          result:=tconstsym(sym).consttyp<>constundefined
    +        else
    +          result:=false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
    +        sym.owner:=fromdef.owner;
    +        result:=sym;
    +      end;
     
         procedure maybe_add_waiting_unit(tt:tdef);
           var
    @@ -104,203 +249,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                                intffound:=assigned(
    -                                             find_implemented_interface(objdef,
    -                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
    -                                             )
    -                                           );
    +                                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
    +                              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 +484,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +501,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);
    @@ -351,7 +528,9 @@ uses
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +546,47 @@ 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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +606,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;
     
     
    @@ -578,7 +773,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
    @@ -627,7 +822,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);
    @@ -656,7 +851,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +942,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +951,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 }
    @@ -771,20 +967,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 }
    @@ -1196,8 +1391,8 @@ 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;
    @@ -1205,22 +1400,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1335,6 +1595,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
    @@ -1360,21 +1621,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1382,7 +1649,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;
    @@ -1407,10 +1676,22 @@ 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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
    index 10c42e7eb8..31011be3e8 100644
    --- a/compiler/ppu.pas
    +++ b/compiler/ppu.pas
    @@ -43,7 +43,7 @@ type
     {$endif Test_Double_checksum}
     
     const
    -  CurrentPPUVersion = 201;
    +  CurrentPPUVersion = 203;
     
     { unit flags }
       uf_init                = $000001; { unit has initialization section }
    diff --git a/compiler/ptype.pas b/compiler/ptype.pas
    index 38e2526e9f..28cd0f94f8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index a5ae7e0fb9..e02ce3a8ca 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -232,7 +232,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -651,7 +654,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -692,7 +695,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 4a260c46b9..0f7a2e4c06 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -129,6 +129,9 @@ interface
               function is_generic:boolean;inline;
               { same as above for specializations }
               function is_specialization:boolean;inline;
    +          { 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
    @@ -2197,13 +2200,26 @@ 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);
                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
    @@ -2220,12 +2236,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index b21a5f9de9..04c07a5ec7 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1581,7 +1582,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2344,6 +2344,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2416,7 +2423,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2448,7 +2456,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2461,7 +2469,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2476,7 +2484,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2627,7 +2636,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index 796b2d6736..ae82024b03 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2781,7 +2781,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
    index 74fde5c6c2..80d9d4df11 100644
    --- a/compiler/utils/ppuutils/ppudump.pp
    +++ b/compiler/utils/ppuutils/ppudump.pp
    @@ -1552,7 +1552,8 @@ const
          { this should never happen for defs stored to a ppu file }
          (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_internal;       str:'Internal'),
    +     (mask:df_has_generic_fields; str:'Has generic fields')
       );
       defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
          (mask:ds_vmt_written;           str:'VMT Written'),
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
    new file mode 100644
    index 0000000000..7f98086630
    --- /dev/null
    +++ b/tests/test/tgenconst14.pp
    @@ -0,0 +1,29 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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;
    +  end;
    +
    +var
    +	op: specialize TBinaryOp<100>;
    +begin
    +	writeln(op.d0);
    +	writeln(op.d1);
    +	writeln(op.d2);
    +	writeln(op.d3:1:1);
    +	writeln(op.d4);
    +	writeln(op.d5);
    +	writeln(op.d6);
    +	writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
    new file mode 100644
    index 0000000000..56744cd0a7
    --- /dev/null
    +++ b/tests/test/tgenconst15.pp
    @@ -0,0 +1,30 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst15;
    +
    +type
    +	kNames = set of (Blaise, Pascal);
    +  generic TSet<const I: kNames> = record
    +    const c = I;
    +  end;
    +  generic TString<const I: String> = record
    +    const c = I;
    +  end;
    +  generic TWideString<const I: WideString> = record
    +    const c = I;
    +  end;
    +  generic TSingle<const I: Single> = record
    +    const c = I; 
    +  end;
    +  generic TDouble<const I: Double> = record
    +    const c = I; 
    +  end;
    +  generic TReal<const I: Real> = record
    +    const c = I; 
    +  end;
    +
    +var
    +	a0: specialize TReal<100>;
    +begin
    +	writeln(a0.c);
    +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..275867ce25
    --- /dev/null
    +++ b/tests/test/tgenconst16.pp
    @@ -0,0 +1,86 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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> = 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> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  generic TBinaryOp<const I> = 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> = record
    +    procedure DoThis(param: integer = I);
    +  end;
    +
    +procedure TOther.DoThis(param: integer = I);
    +begin
    +  writeln(param, ' default:', I);
    +end;
    +
    +var
    +  t0: specialize TBinaryOp<100>;
    +  t1: specialize TOther<100>;
    +begin
    +  //writeln(op.d0);
    +  //writeln(op.d1);
    +  //writeln(op.d2);
    +  //writeln(op.d3:1:1);
    +  //writeln(op.d4);
    +  //writeln(op.d5);
    +  //writeln(op.d6);
    +  //writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst17.pp b/tests/test/tgenconst17.pp
    new file mode 100644
    index 0000000000..26dc2ee21f
    --- /dev/null
    +++ b/tests/test/tgenconst17.pp
    @@ -0,0 +1,36 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst17;
    +
    +type
    +  generic TUnaryOp<const I: integer> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  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 xor I;
    +      d9 = I shl I;
    +      d10 = I shr I;
    +      d11 = I << I;
    +      d12 = I >> I;
    +      d13 = I <> I;
    +      d14 = I < I;
    +      d15 = I > I;
    +      d16 = I <= I;
    +      d17 = I >= I;
    +      d18 = I = I;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst18.pp b/tests/test/tgenconst18.pp
    new file mode 100644
    index 0000000000..a4ba526803
    --- /dev/null
    +++ b/tests/test/tgenconst18.pp
    @@ -0,0 +1,12 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst18;
    +
    +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/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    -- 
    2.17.2 (Apple Git-113)
    
    
    gen-const-3-23.diff (96,712 bytes)
  • bad-line-endings.png (89,530 bytes)
    bad-line-endings.png (89,530 bytes)
  • patch_3_25.diff (87,689 bytes)
    From c80227ed7597385d240aa7bdc1556798aa15ec20 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Tue, 6 Nov 2018 13:58:49 +0700
    Subject: [PATCH] constants in generics
    
    ---
     .gitignore                         |  23 +
     compiler/defcmp.pas                |   9 +-
     compiler/htypechk.pas              |   2 +-
     compiler/ncon.pas                  |  42 +-
     compiler/nmat.pas                  |   5 +-
     compiler/node.pas                  |  22 +-
     compiler/nset.pas                  |   7 +-
     compiler/pdecl.pas                 |  53 ++-
     compiler/pdecvar.pas               |   4 +
     compiler/pexpr.pas                 |  11 +-
     compiler/pgentype.pas              |   8 +-
     compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
     compiler/ppu.pas                   |   2 +-
     compiler/ptype.pas                 |   4 +-
     compiler/symconst.pas              |  12 +-
     compiler/symdef.pas                |  22 +-
     compiler/symsym.pas                |  22 +-
     compiler/symtable.pas              |   2 +-
     compiler/utils/ppuutils/ppudump.pp |   3 +-
     tests/test/tgenconst1.pp           |  33 ++
     tests/test/tgenconst10.pp          |  13 +
     tests/test/tgenconst11.pp          |  21 +
     tests/test/tgenconst12.pp          |  16 +
     tests/test/tgenconst13.pp          |  20 +
     tests/test/tgenconst14.pp          |  29 ++
     tests/test/tgenconst15.pp          |  30 ++
     tests/test/tgenconst16.pp          |  86 ++++
     tests/test/tgenconst17.pp          |  36 ++
     tests/test/tgenconst18.pp          |  12 +
     tests/test/tgenconst2.pp           |  12 +
     tests/test/tgenconst3.pp           |  16 +
     tests/test/tgenconst4.pp           |  11 +
     tests/test/tgenconst5.pp           |  24 +
     tests/test/tgenconst6.pp           |  21 +
     tests/test/tgenconst7.pp           |  11 +
     tests/test/tgenconst8.pp           |  11 +
     tests/test/tgenconst9.pp           |  11 +
     37 files changed, 1097 insertions(+), 262 deletions(-)
     create mode 100644 .gitignore
     create mode 100644 tests/test/tgenconst1.pp
     create mode 100644 tests/test/tgenconst10.pp
     create mode 100644 tests/test/tgenconst11.pp
     create mode 100644 tests/test/tgenconst12.pp
     create mode 100644 tests/test/tgenconst13.pp
     create mode 100644 tests/test/tgenconst14.pp
     create mode 100644 tests/test/tgenconst15.pp
     create mode 100644 tests/test/tgenconst16.pp
     create mode 100644 tests/test/tgenconst17.pp
     create mode 100644 tests/test/tgenconst18.pp
     create mode 100644 tests/test/tgenconst2.pp
     create mode 100644 tests/test/tgenconst3.pp
     create mode 100644 tests/test/tgenconst4.pp
     create mode 100644 tests/test/tgenconst5.pp
     create mode 100644 tests/test/tgenconst6.pp
     create mode 100644 tests/test/tgenconst7.pp
     create mode 100644 tests/test/tgenconst8.pp
     create mode 100644 tests/test/tgenconst9.pp
    
    diff --git a/.gitignore b/.gitignore
    new file mode 100644
    index 0000000000..64fdb156d0
    --- /dev/null
    +++ b/.gitignore
    @@ -0,0 +1,23 @@
    +# files
    +pp
    +fpmake
    +rtl/darwin/fpcmade.x86_64-darwin
    +fpmake_proc1 copy.inc
    +tests/*.x86_64-darwin
    +rtl/Package.fpc
    +tests/createlst
    +tests/gparmake
    +
    +# directories
    +lazbuild/
    +x86_64-darwin/
    +tests/tstunits/
    +tests/utils
    +
    +# patterns
    +*.app
    +*.o
    +*.ppu
    +*.fpm
    +*.rsj
    +*.lst
    \ No newline at end of file
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 3f5882f762..793dbbbe76 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -337,9 +336,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 07c035dc26..2358ea4b6d 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
    index ae94637c28..1e203f74d6 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -279,6 +279,7 @@ implementation
             p1  : tnode;
             len : longint;
             pc  : pchar;
    +        value_set : pconstset;
           begin
             p1:=nil;
             case p.consttyp of
    @@ -304,18 +305,51 @@ 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 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_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;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef:=p.constdef;
    +            end;
               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 355b493da4..d10dff6128 100644
    --- a/compiler/nmat.pas
    +++ b/compiler/nmat.pas
    @@ -129,7 +129,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 b8600000bf..33a85b1493 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -194,7 +194,8 @@ interface
               'loadparentfpn',
               'objcselectorn',
               'objcprotocoln',
    -          'specializen');
    +          'specializen'
    +          );
     
           { a set containing all const nodes }
           nodetype_const = [ordconstn,
    @@ -272,10 +273,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
              }
    @@ -983,6 +987,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;
     
    @@ -1078,7 +1085,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 6270ec582e..bd031e6a86 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -239,7 +239,7 @@ implementation
                internalerror(20021126);
     
              t:=self;
    -         if isbinaryoverloaded(t,[]) then
    +         if isbinaryoverloaded(t,[]) then
                begin
                  result:=t;
                  exit;
    @@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
    index c5b5bcc921..d7e80b928f 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -126,9 +126,14 @@ implementation
                  end;
                setconstn :
                  begin
    -               new(ps);
    -               ps^:=tsetconstnode(p).value_set^;
    -               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +               if nf_generic_para in p.flags then
    +                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
    +               else
    +                 begin
    +                   new(ps);
    +                   ps^:=tsetconstnode(p).value_set^;
    +                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +                 end;
                  end;
                pointerconstn :
                  begin
    @@ -141,18 +146,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -177,8 +182,19 @@ 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
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -507,8 +523,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/pdecvar.pas b/compiler/pdecvar.pas
    index 4d39397e46..8121d87853 100644
    --- a/compiler/pdecvar.pas
    +++ b/compiler/pdecvar.pas
    @@ -1675,6 +1675,10 @@ implementation
                        end;
                    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 bc0606ed4b..e6d9633ebd 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4078,7 +4084,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
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 7760a4e134..ac6e59ce98 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;
    @@ -63,18 +63,163 @@ implementation
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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;
     
    +  type
    +    tdeftypeset = set of tdeftyp;
    +  const
    +    tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ=constsym then
    +          result:=tconstsym(sym).consttyp<>constundefined
    +        else
    +          result:=false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
    +        sym.owner:=fromdef.owner;
    +        result:=sym;
    +      end;
     
         procedure maybe_add_waiting_unit(tt:tdef);
           var
    @@ -104,203 +249,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                                intffound:=assigned(
    -                                             find_implemented_interface(objdef,
    -                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
    -                                             )
    -                                           );
    +                                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
    +                              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 +484,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +501,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);
    @@ -351,7 +528,9 @@ uses
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +546,47 @@ 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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +606,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;
     
     
    @@ -578,7 +773,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
    @@ -627,7 +822,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);
    @@ -656,7 +851,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +942,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +951,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 }
    @@ -771,20 +967,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 }
    @@ -1196,8 +1391,8 @@ 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;
    @@ -1205,22 +1400,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1335,6 +1595,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
    @@ -1360,21 +1621,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1382,7 +1649,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;
    @@ -1407,10 +1676,22 @@ 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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
    index 10c42e7eb8..31011be3e8 100644
    --- a/compiler/ppu.pas
    +++ b/compiler/ppu.pas
    @@ -43,7 +43,7 @@ type
     {$endif Test_Double_checksum}
     
     const
    -  CurrentPPUVersion = 201;
    +  CurrentPPUVersion = 203;
     
     { unit flags }
       uf_init                = $000001; { unit has initialization section }
    diff --git a/compiler/ptype.pas b/compiler/ptype.pas
    index 38e2526e9f..28cd0f94f8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index a5ae7e0fb9..e02ce3a8ca 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -232,7 +232,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -651,7 +654,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -692,7 +695,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 4a260c46b9..0f7a2e4c06 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -129,6 +129,9 @@ interface
               function is_generic:boolean;inline;
               { same as above for specializations }
               function is_specialization:boolean;inline;
    +          { 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
    @@ -2197,13 +2200,26 @@ 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);
                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
    @@ -2220,12 +2236,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index b21a5f9de9..04c07a5ec7 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1581,7 +1582,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2344,6 +2344,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2416,7 +2423,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2448,7 +2456,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2461,7 +2469,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2476,7 +2484,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2627,7 +2636,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index 796b2d6736..ae82024b03 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2781,7 +2781,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
    index 74fde5c6c2..80d9d4df11 100644
    --- a/compiler/utils/ppuutils/ppudump.pp
    +++ b/compiler/utils/ppuutils/ppudump.pp
    @@ -1552,7 +1552,8 @@ const
          { this should never happen for defs stored to a ppu file }
          (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_internal;       str:'Internal'),
    +     (mask:df_has_generic_fields; str:'Has generic fields')
       );
       defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
          (mask:ds_vmt_written;           str:'VMT Written'),
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
    new file mode 100644
    index 0000000000..7f98086630
    --- /dev/null
    +++ b/tests/test/tgenconst14.pp
    @@ -0,0 +1,29 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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;
    +  end;
    +
    +var
    +	op: specialize TBinaryOp<100>;
    +begin
    +	writeln(op.d0);
    +	writeln(op.d1);
    +	writeln(op.d2);
    +	writeln(op.d3:1:1);
    +	writeln(op.d4);
    +	writeln(op.d5);
    +	writeln(op.d6);
    +	writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
    new file mode 100644
    index 0000000000..56744cd0a7
    --- /dev/null
    +++ b/tests/test/tgenconst15.pp
    @@ -0,0 +1,30 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst15;
    +
    +type
    +	kNames = set of (Blaise, Pascal);
    +  generic TSet<const I: kNames> = record
    +    const c = I;
    +  end;
    +  generic TString<const I: String> = record
    +    const c = I;
    +  end;
    +  generic TWideString<const I: WideString> = record
    +    const c = I;
    +  end;
    +  generic TSingle<const I: Single> = record
    +    const c = I; 
    +  end;
    +  generic TDouble<const I: Double> = record
    +    const c = I; 
    +  end;
    +  generic TReal<const I: Real> = record
    +    const c = I; 
    +  end;
    +
    +var
    +	a0: specialize TReal<100>;
    +begin
    +	writeln(a0.c);
    +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..275867ce25
    --- /dev/null
    +++ b/tests/test/tgenconst16.pp
    @@ -0,0 +1,86 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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> = 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> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  generic TBinaryOp<const I> = 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> = record
    +    procedure DoThis(param: integer = I);
    +  end;
    +
    +procedure TOther.DoThis(param: integer = I);
    +begin
    +  writeln(param, ' default:', I);
    +end;
    +
    +var
    +  t0: specialize TBinaryOp<100>;
    +  t1: specialize TOther<100>;
    +begin
    +  //writeln(op.d0);
    +  //writeln(op.d1);
    +  //writeln(op.d2);
    +  //writeln(op.d3:1:1);
    +  //writeln(op.d4);
    +  //writeln(op.d5);
    +  //writeln(op.d6);
    +  //writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst17.pp b/tests/test/tgenconst17.pp
    new file mode 100644
    index 0000000000..26dc2ee21f
    --- /dev/null
    +++ b/tests/test/tgenconst17.pp
    @@ -0,0 +1,36 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst17;
    +
    +type
    +  generic TUnaryOp<const I: integer> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  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 xor I;
    +      d9 = I shl I;
    +      d10 = I shr I;
    +      d11 = I << I;
    +      d12 = I >> I;
    +      d13 = I <> I;
    +      d14 = I < I;
    +      d15 = I > I;
    +      d16 = I <= I;
    +      d17 = I >= I;
    +      d18 = I = I;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst18.pp b/tests/test/tgenconst18.pp
    new file mode 100644
    index 0000000000..a4ba526803
    --- /dev/null
    +++ b/tests/test/tgenconst18.pp
    @@ -0,0 +1,12 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst18;
    +
    +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/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    -- 
    2.17.2 (Apple Git-113)
    
    
    patch_3_25.diff (87,689 bytes)
  • generic_constants_may4.patch (71,526 bytes)
    diff -ur compiler/defcmp.pas compiler/defcmp.pas
    --- compiler/defcmp.pas	2019-05-04 11:21:35.506321600 -0400
    +++ compiler/defcmp.pas	2019-05-04 10:02:12.755701000 -0400
    @@ -175,7 +175,6 @@
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -345,9 +344,13 @@
                            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 -ur compiler/htypechk.pas compiler/htypechk.pas
    --- compiler/htypechk.pas	2019-05-04 11:21:35.631332300 -0400
    +++ compiler/htypechk.pas	2019-05-04 10:02:12.349417700 -0400
    @@ -2723,7 +2723,7 @@
                   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 -ur compiler/ncon.pas compiler/ncon.pas
    --- compiler/ncon.pas	2019-05-04 11:21:35.709462700 -0400
    +++ compiler/ncon.pas	2019-05-04 10:02:03.520316200 -0400
    @@ -279,6 +279,7 @@
             p1  : tnode;
             len : longint;
             pc  : pchar;
    +        value_set : pconstset;
           begin
             p1:=nil;
             case p.consttyp of
    @@ -304,18 +305,51 @@
               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 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_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;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef:=p.constdef;
    +            end;
               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 -ur compiler/nmat.pas compiler/nmat.pas
    --- compiler/nmat.pas	2019-05-04 11:21:35.787594200 -0400
    +++ compiler/nmat.pas	2019-05-04 10:02:02.879642000 -0400
    @@ -129,7 +129,10 @@
                   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 -ur compiler/node.pas compiler/node.pas
    --- compiler/node.pas	2019-05-04 11:21:35.881351800 -0400
    +++ compiler/node.pas	2019-05-04 10:02:47.741464800 -0400
    @@ -274,10 +274,13 @@
              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
              }
    @@ -985,6 +988,9 @@
         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;
     
    @@ -1080,7 +1086,12 @@
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff -ur compiler/nset.pas compiler/nset.pas
    --- compiler/nset.pas	2019-05-04 11:21:35.959483400 -0400
    +++ compiler/nset.pas	2019-05-04 10:02:02.270218000 -0400
    @@ -401,8 +401,9 @@
              { 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 -ur compiler/pdecl.pas compiler/pdecl.pas
    --- compiler/pdecl.pas	2019-05-04 11:21:36.037614400 -0400
    +++ compiler/pdecl.pas	2019-05-04 10:02:01.895188400 -0400
    @@ -126,9 +126,14 @@
                  end;
                setconstn :
                  begin
    -               new(ps);
    -               ps^:=tsetconstnode(p).value_set^;
    -               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +               if nf_generic_para in p.flags then
    +                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
    +               else
    +                 begin
    +                   new(ps);
    +                   ps^:=tsetconstnode(p).value_set^;
    +                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
    +                 end;
                  end;
                pointerconstn :
                  begin
    @@ -141,18 +146,18 @@
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -177,8 +182,19 @@
                    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
    +          include(hp.symoptions,sp_generic_para);
             current_tokenpos:=storetokenpos;
             p.free;
             readconstant:=hp;
    @@ -510,8 +526,9 @@
                    { 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 -ur compiler/pdecvar.pas compiler/pdecvar.pas
    --- compiler/pdecvar.pas	2019-05-04 11:21:36.100120600 -0400
    +++ compiler/pdecvar.pas	2019-05-04 10:02:01.567037900 -0400
    @@ -1705,6 +1705,10 @@
                        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 -ur compiler/pexpr.pas compiler/pexpr.pas
    --- compiler/pexpr.pas	2019-05-04 11:21:36.178250700 -0400
    +++ compiler/pexpr.pas	2019-05-04 10:02:01.192002400 -0400
    @@ -446,6 +446,9 @@
                       { no packed bit support for these things }
                       if l=in_bitsizeof_x then
                         statement_syssym:=caddnode.create(muln,statement_syssym,cordconstnode.create(8,sinttype,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
    @@ -466,6 +469,9 @@
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4167,7 +4173,10 @@
                     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
    diff -ur compiler/pgentype.pas compiler/pgentype.pas
    --- compiler/pgentype.pas	2019-05-04 11:21:36.240755700 -0400
    +++ compiler/pgentype.pas	2019-05-04 10:01:26.150428600 -0400
    @@ -28,7 +28,7 @@
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -42,7 +42,7 @@
     
       tspecializationcontext=class
       public
    -    genericdeflist : tfpobjectlist;
    +    paramlist : tfpobjectlist;
         poslist : tfplist;
         prettyname : ansistring;
         specializename : ansistring;
    @@ -58,7 +58,7 @@
     
     constructor tspecializationcontext.create;
     begin
    -  genericdeflist:=tfpobjectlist.create(false);
    +  paramlist:=tfpobjectlist.create(false);
       poslist:=tfplist.create;
     end;
     
    @@ -66,7 +66,7 @@
     var
       i : longint;
     begin
    -  genericdeflist.free;
    +  paramlist.free;
       for i:=0 to poslist.count-1 do
         dispose(pfileposinfo(poslist[i]));
       poslist.free;
    diff -ur compiler/pgenutil.pas compiler/pgenutil.pas
    --- compiler/pgenutil.pas	2019-05-04 11:21:36.303260700 -0400
    +++ compiler/pgenutil.pas	2019-05-04 11:12:01.223659000 -0400
    @@ -42,9 +42,9 @@
         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;
    @@ -63,18 +63,163 @@
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ=constsym then
    +          result:=tconstsym(sym).consttyp<>constundefined
    +        else
    +          result:=false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
    +        sym.owner:=fromdef.owner;
    +        result:=sym;
    +      end;
     
         procedure maybe_add_waiting_unit(tt:tdef);
           var
    @@ -104,203 +249,232 @@
               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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                  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
    +                    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
                           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 +484,12 @@
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +501,7 @@
             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);
    @@ -351,7 +528,9 @@
                 block_type:=bt_type;
                 tmpparampos:=current_filepos;
                 typeparam:=factor(false,[ef_type_only]);
    -            if typeparam.nodetype=typen then
    +            { 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 +546,47 @@
                       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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +606,12 @@
           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;
     
     
    @@ -578,7 +773,7 @@
             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
    @@ -627,7 +822,7 @@
     
             { 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);
    @@ -656,7 +851,7 @@
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +942,7 @@
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +951,7 @@
     
             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 }
    @@ -771,20 +967,19 @@
             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 }
    @@ -1199,8 +1394,8 @@
     
         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;
    @@ -1208,22 +1403,88 @@
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
    +		last_type_pos:=default(tfileposinfo);
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const := true;
    +              const_list_index := result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1338,6 +1599,7 @@
                         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
    @@ -1363,21 +1625,27 @@
                     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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1385,7 +1653,9 @@
         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;
    @@ -1410,10 +1680,22 @@
               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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    +                  end
    +                else
    +                  internalerror(2019021602);
                     { type parameters need to be added as strict private }
                     sym.visibility:=vis_strictprivate;
                     st.insert(sym);
    @@ -1421,13 +1703,17 @@
                   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 -ur compiler/ptype.pas compiler/ptype.pas
    --- compiler/ptype.pas	2019-05-04 11:21:36.365935500 -0400
    +++ compiler/ptype.pas	2019-05-04 10:02:00.801275500 -0400
    @@ -1436,7 +1436,9 @@
                                      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
    Only in compiler: ryan_ppcx64.lpi
    diff -ur compiler/symconst.pas compiler/symconst.pas
    --- compiler/symconst.pas	2019-05-04 11:21:36.428440800 -0400
    +++ compiler/symconst.pas	2019-05-04 10:02:00.598133900 -0400
    @@ -231,7 +231,10 @@
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -659,7 +662,7 @@
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -700,7 +703,8 @@
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -840,7 +844,7 @@
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff -ur compiler/symdef.pas compiler/symdef.pas
    --- compiler/symdef.pas	2019-05-04 11:21:36.490945500 -0400
    +++ compiler/symdef.pas	2019-05-04 10:03:49.892616700 -0400
    @@ -129,6 +129,9 @@
               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
    @@ -2295,13 +2298,26 @@
              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);
                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
    @@ -2318,12 +2334,12 @@
                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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff -ur compiler/symsym.pas compiler/symsym.pas
    --- compiler/symsym.pas	2019-05-04 11:21:36.537824600 -0400
    +++ compiler/symsym.pas	2019-05-04 10:01:59.785568500 -0400
    @@ -157,7 +157,7 @@
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,7 @@
               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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1584,7 +1585,6 @@
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2356,6 +2356,13 @@
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2428,7 +2435,8 @@
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2460,7 +2468,7 @@
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2473,7 +2481,7 @@
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2488,7 +2496,8 @@
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2641,7 +2650,6 @@
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff -ur compiler/symtable.pas compiler/symtable.pas
    --- compiler/symtable.pas	2019-05-04 11:21:36.584702800 -0400
    +++ compiler/symtable.pas	2019-05-04 10:01:59.738689700 -0400
    @@ -2916,7 +2916,7 @@
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff -ur compiler/utils/ppuutils/ppudump.pp compiler/utils/ppuutils/ppudump.pp
    --- compiler/utils/ppuutils/ppudump.pp	2019-05-04 11:21:36.631582400 -0400
    +++ compiler/utils/ppuutils/ppudump.pp	2019-05-04 10:01:59.707422300 -0400
    @@ -1561,7 +1561,8 @@
          { this should never happen for defs stored to a ppu file }
          (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_internal;       str:'Internal'),
    +     (mask:df_has_generic_fields; str:'Has generic fields')
       );
       defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
          (mask:ds_vmt_written;           str:'VMT Written'),
    
  • generic_constants_may4_tests.7z (2,100 bytes)
  • patch_7_20.diff (94,414 bytes)
    From 173ee8d64e4dec40db5f7346e311bd26d4bf3766 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Tue, 6 Nov 2018 13:58:49 +0700
    Subject: [PATCH] constants in generics
    
    ---
     compiler/defcmp.pas                |   9 +-
     compiler/htypechk.pas              |   2 +-
     compiler/ncnv.pas                  |   2 +-
     compiler/ncon.pas                  |  48 +-
     compiler/nmat.pas                  |   5 +-
     compiler/node.pas                  |  22 +-
     compiler/nset.pas                  |   7 +-
     compiler/pass_1.pas                |   5 +
     compiler/pdecl.pas                 |  54 ++-
     compiler/pdecvar.pas               |   4 +
     compiler/pexpr.pas                 |  17 +-
     compiler/pgentype.pas              |   8 +-
     compiler/pgenutil.pas              | 697 ++++++++++++++++++++---------
     compiler/ppu.pas                   |   2 +-
     compiler/pstatmnt.pas              |   7 +-
     compiler/ptype.pas                 |   4 +-
     compiler/symconst.pas              |  15 +-
     compiler/symdef.pas                |  22 +-
     compiler/symsym.pas                |  22 +-
     compiler/symtable.pas              |   2 +-
     compiler/utils/ppuutils/ppudump.pp |   4 +-
     tests/test/tgenconst1.pp           |  33 ++
     tests/test/tgenconst10.pp          |  13 +
     tests/test/tgenconst11.pp          |  21 +
     tests/test/tgenconst12.pp          |  16 +
     tests/test/tgenconst13.pp          |  20 +
     tests/test/tgenconst14.pp          |  29 ++
     tests/test/tgenconst15.pp          |  30 ++
     tests/test/tgenconst16.pp          |  86 ++++
     tests/test/tgenconst17.pp          |  36 ++
     tests/test/tgenconst18.pp          |  12 +
     tests/test/tgenconst19.pp          |  49 ++
     tests/test/tgenconst2.pp           |  12 +
     tests/test/tgenconst20.pp          |  24 +
     tests/test/tgenconst3.pp           |  16 +
     tests/test/tgenconst4.pp           |  11 +
     tests/test/tgenconst5.pp           |  24 +
     tests/test/tgenconst6.pp           |  21 +
     tests/test/tgenconst7.pp           |  11 +
     tests/test/tgenconst8.pp           |  11 +
     tests/test/tgenconst9.pp           |  11 +
     41 files changed, 1176 insertions(+), 268 deletions(-)
     create mode 100644 tests/test/tgenconst1.pp
     create mode 100644 tests/test/tgenconst10.pp
     create mode 100644 tests/test/tgenconst11.pp
     create mode 100644 tests/test/tgenconst12.pp
     create mode 100644 tests/test/tgenconst13.pp
     create mode 100644 tests/test/tgenconst14.pp
     create mode 100644 tests/test/tgenconst15.pp
     create mode 100644 tests/test/tgenconst16.pp
     create mode 100644 tests/test/tgenconst17.pp
     create mode 100644 tests/test/tgenconst18.pp
     create mode 100644 tests/test/tgenconst19.pp
     create mode 100644 tests/test/tgenconst2.pp
     create mode 100644 tests/test/tgenconst20.pp
     create mode 100644 tests/test/tgenconst3.pp
     create mode 100644 tests/test/tgenconst4.pp
     create mode 100644 tests/test/tgenconst5.pp
     create mode 100644 tests/test/tgenconst6.pp
     create mode 100644 tests/test/tgenconst7.pp
     create mode 100644 tests/test/tgenconst8.pp
     create mode 100644 tests/test/tgenconst9.pp
    
    diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
    index 3f5882f762..793dbbbe76 100644
    --- a/compiler/defcmp.pas
    +++ b/compiler/defcmp.pas
    @@ -175,7 +175,6 @@ implementation
           symtable,symsym,symcpu,
           defutil,symutil;
     
    -
         function compare_defs_ext(def_from,def_to : tdef;
                                   fromtreetype : tnodetype;
                                   var doconv : tconverttype;
    @@ -337,9 +336,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 07c035dc26..2358ea4b6d 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2697,7 +2697,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 31135872da..cb246b6fc6 100644
    --- a/compiler/ncnv.pas
    +++ b/compiler/ncnv.pas
    @@ -3033,7 +3033,7 @@ implementation
                            { for constant values on absolute variables, swaping is required }
                            if (target_info.endian = endian_big) and (nf_absolute in flags) then
                              swap_const_value(tordconstnode(left).value,tordconstnode(left).resultdef.size);
    -                       if not(nf_internal in flags) then
    +                       if not((nf_internal in flags) or (nf_generic_para in flags)) then
                              testrange(resultdef,tordconstnode(left).value,(nf_explicit in flags)
                                        or (nf_absolute in flags),false);
                            { swap value back, but according to new type }
    diff --git a/compiler/ncon.pas b/compiler/ncon.pas
    index ae94637c28..fadf704935 100644
    --- a/compiler/ncon.pas
    +++ b/compiler/ncon.pas
    @@ -279,6 +279,7 @@ implementation
             p1  : tnode;
             len : longint;
             pc  : pchar;
    +        value_set : pconstset;
           begin
             p1:=nil;
             case p.consttyp of
    @@ -304,18 +305,57 @@ 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;
    +          { constundefined is a placeholder for unrestricted generic const params
    +            so we just treat it as a nil node. }
    +          constundefined :
    +            begin
    +              p1:=cnilnode.create;
    +              p1.resultdef:=p.constdef;
    +            end;
               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 355b493da4..d10dff6128 100644
    --- a/compiler/nmat.pas
    +++ b/compiler/nmat.pas
    @@ -129,7 +129,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 b8600000bf..33a85b1493 100644
    --- a/compiler/node.pas
    +++ b/compiler/node.pas
    @@ -194,7 +194,8 @@ interface
               'loadparentfpn',
               'objcselectorn',
               'objcprotocoln',
    -          'specializen');
    +          'specializen'
    +          );
     
           { a set containing all const nodes }
           nodetype_const = [ordconstn,
    @@ -272,10 +273,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
              }
    @@ -983,6 +987,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;
     
    @@ -1078,7 +1085,12 @@ implementation
         constructor tbinarynode.create(t:tnodetype;l,r : tnode);
           begin
              inherited create(t,l);
    -         right:=r
    +         { transfer generic paramater flag }
    +         if assigned(l) and (nf_generic_para in l.flags) then
    +           include(flags,nf_generic_para)
    +         else if assigned(r) and (nf_generic_para in r.flags) then
    +           include(flags,nf_generic_para);
    +         right:=r;
           end;
     
     
    diff --git a/compiler/nset.pas b/compiler/nset.pas
    index 6270ec582e..bd031e6a86 100644
    --- a/compiler/nset.pas
    +++ b/compiler/nset.pas
    @@ -239,7 +239,7 @@ implementation
                internalerror(20021126);
     
              t:=self;
    -         if isbinaryoverloaded(t,[]) then
    +         if isbinaryoverloaded(t,[]) then
                begin
                  result:=t;
                  exit;
    @@ -392,8 +392,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 b9f7f847ee..ed314072e1 100644
    --- a/compiler/pass_1.pas
    +++ b/compiler/pass_1.pas
    @@ -66,6 +66,7 @@ implementation
              oldverbosity     : longint;
              oldpos    : tfileposinfo;
              hp        : tnode;
    +         oldflags  : tnodeflags;
           begin
             node_changed:=false;
             if (p.resultdef=nil) then
    @@ -83,9 +84,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);
                    { run typecheckpass }
                    typecheckpass(p);
                 end;
    diff --git a/compiler/pdecl.pas b/compiler/pdecl.pas
    index c5b5bcc921..81c71a09a3 100644
    --- a/compiler/pdecl.pas
    +++ b/compiler/pdecl.pas
    @@ -120,14 +120,15 @@ implementation
                  end;
                realconstn :
                  begin
    -                new(pd);
    -                pd^:=trealconstnode(p).value_real;
    -                hp:=cconstsym.create_ptr(orgname,constreal,pd,p.resultdef);
    +               new(pd);
    +               pd^:=trealconstnode(p).value_real;
    +               hp:=cconstsym.create_ptr(orgname,constreal,pd,p.resultdef);
                  end;
                setconstn :
                  begin
                    new(ps);
    -               ps^:=tsetconstnode(p).value_set^;
    +               if assigned(tsetconstnode(p).value_set) then
    +                 ps^:=tsetconstnode(p).value_set^;
                    hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
                  end;
                pointerconstn :
    @@ -141,18 +142,18 @@ implementation
                typen :
                  begin
                    if is_interface(p.resultdef) then
    -                begin
    -                  if assigned(tobjectdef(p.resultdef).iidguid) then
    -                   begin
    -                     new(pg);
    -                     pg^:=tobjectdef(p.resultdef).iidguid^;
    -                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    -                   end
    -                  else
    -                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    -                end
    -               else
    -                Message(parser_e_illegal_expression);
    +                 begin
    +                   if assigned(tobjectdef(p.resultdef).iidguid) then
    +                     begin
    +                       new(pg);
    +                       pg^:=tobjectdef(p.resultdef).iidguid^;
    +                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
    +                     end
    +                    else
    +                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
    +                 end
    +               else 
    +                 Message(parser_e_illegal_expression);
                  end;
                inlinen:
                  begin
    @@ -177,8 +178,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;
    @@ -507,8 +522,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/pdecvar.pas b/compiler/pdecvar.pas
    index 4d39397e46..8121d87853 100644
    --- a/compiler/pdecvar.pas
    +++ b/compiler/pdecvar.pas
    @@ -1675,6 +1675,10 @@ implementation
                        end;
                    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 bc0606ed4b..3c1c527297 100644
    --- a/compiler/pexpr.pas
    +++ b/compiler/pexpr.pas
    @@ -446,6 +446,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,sinttype,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
    @@ -466,6 +469,9 @@ implementation
                        end
                      else
                        statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
    @@ -4078,7 +4084,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
    @@ -4417,7 +4426,7 @@ implementation
              filepos : tfileposinfo;
              oldafterassignment,
              updatefpos          : boolean;
    -
    +         oldflags : tnodeflags;
           begin
              oldafterassignment:=afterassignment;
              p1:=sub_expr(opcompare,[ef_accept_equal],nil);
    @@ -4474,6 +4483,10 @@ implementation
               else
                 updatefpos:=false;
              end;
    +         oldflags:=p1.flags;
    +         { transfer generic paramter flag }
    +         if nf_generic_para in oldflags then
    +           include(p1.flags,nf_generic_para);
              { get the resultdef for this expression }
              if not assigned(p1.resultdef) and
                 dotypecheck then
    diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
    index b2847c78f6..85270df256 100644
    --- a/compiler/pgentype.pas
    +++ b/compiler/pgentype.pas
    @@ -28,7 +28,7 @@ interface
     uses
       cclasses,
       globtype,
    -  symtype,symbase;
    +  symconst,symtype,symbase;
     
     const
       inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
    @@ -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 7760a4e134..5addd281fd 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;
    @@ -63,18 +63,164 @@ implementation
     
     uses
       { common }
    -  cutils,fpccrc,
    +  sysutils,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;
     
    +  type
    +    tdeftypeset = set of tdeftyp;
    +  const
    +    tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
    +    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 is_generic_param_const(sym:tsym):boolean;
    +      begin
    +        if sym.typ=constsym then
    +          result:=tconstsym(sym).consttyp<>constundefined
    +        else
    +          result:=false;
    +      end;
    +
    +    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
    +      begin
    +        if (value.len<param2.low) or (value.len>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
    +          begin
    +            sym:=cconstsym.create_undefined(undefinedname,fromdef);
    +            sym.owner:=fromdef.owner;
    +            prettyname:='';
    +            result:=sym;
    +            exit;
    +          end;
    +        case node.nodetype of
    +          ordconstn:
    +            begin
    +              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
    +              prettyname:=inttostr(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:=floattostr(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 us 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 +250,232 @@ 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;
    +        //paratype : tconsttyp;
    +        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:=is_generic_param_const(tsym(paramlist[i]));
    +            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
    -                                intffound:=assigned(
    -                                             find_implemented_interface(objdef,
    -                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
    -                                             )
    -                                           );
    +                                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
    +                              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 +485,12 @@ uses
             namepart : string;
             prettynamepart : ansistring;
             module : tmodule;
    +        //paramdef : tgenericparamdef;
    +        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 +502,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 +528,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 +547,47 @@ 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
    +                    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
    -                          begin
    -                            { special handling for specializations inside generic function declarations }
    -                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
    -                          end
    -                        else
    +                        if typeparam.nodetype = typen then
                               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 +607,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;
     
     
    @@ -578,7 +774,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
    @@ -627,7 +823,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);
    @@ -656,7 +852,7 @@ uses
                 result:=generrordef;
                 exit;
               end;
    -
    +        
             { we've found the correct def }
             if context.sym.typ=typesym then
               result:=tstoreddef(ttypesym(context.sym).typedef)
    @@ -747,6 +943,7 @@ uses
             hintsprocessed : boolean;
             pd : tprocdef;
             pdflags : tpdflags;
    +        typedef : tstoreddef;
           begin
             if not assigned(context) then
               internalerror(2015052203);
    @@ -755,7 +952,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 }
    @@ -771,20 +968,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 }
    @@ -1196,8 +1392,8 @@ 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;
    @@ -1205,22 +1401,87 @@ uses
             doconsume : boolean;
             constraintdata : tgenericconstraintdata;
             old_block_type : tblock_type;
    +        is_const,last_is_const : boolean;
    +        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;
    +        is_const:=false;
    +        last_is_const:=false;
    +        last_token:=NOTOKEN;
             repeat
    +          if try_to_consume(_CONST) then
    +            begin
    +              { last param was const without semicolon terminator }
    +              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +              is_const:=true;
    +              const_list_index:=result.count;
    +            end;
               if token=_ID then
                 begin
    -              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
    +              if is_const then
    +                begin
    +                  { last param was type without semicolon terminator }
    +                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
    +                end
    +              else
    +                begin
    +                  { last param was const without semicolon terminator }
    +                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
    +                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
    +                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
    +                end;
                   { type parameters need to be added as strict private }
                   generictype.visibility:=vis_strictprivate;
                   include(generictype.symoptions,sp_generic_para);
                   result.add(orgpattern,generictype);
    +              last_is_const:=is_const;
                 end;
               consume(_ID);
    -          if try_to_consume(_COLON) then
    +          { const restriction }
    +          if is_const then
    +            begin
    +              if 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:
    +                              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;
    +                          end;
    +                          tconstsym(result[i]).constdef:=def;
    +                        end
    +                      else
    +                        Message(type_e_mismatch);
    +                    end;
    +                  { after type restriction const list terminates }
    +                  is_const:=false;
    +                end;
    +            end
    +          { type restriction }
    +          else if try_to_consume(_COLON) then
                 begin
                   if not allowconstraints then
                     { TODO }
    @@ -1335,6 +1596,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
    @@ -1360,21 +1622,27 @@ 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
    +            is_const:=false;
    +          last_token:=token;
    +          last_type_pos:=current_filepos;
             until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
             { 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;
     
    @@ -1382,7 +1650,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;
    @@ -1407,10 +1677,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,true);
    +                if generictype.typ=typesym then
    +                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
    +                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);
    @@ -1418,13 +1701,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/ppu.pas b/compiler/ppu.pas
    index 10c42e7eb8..31011be3e8 100644
    --- a/compiler/ppu.pas
    +++ b/compiler/ppu.pas
    @@ -43,7 +43,7 @@ type
     {$endif Test_Double_checksum}
     
     const
    -  CurrentPPUVersion = 201;
    +  CurrentPPUVersion = 203;
     
     { unit flags }
       uf_init                = $000001; { unit has initialization section }
    diff --git a/compiler/pstatmnt.pas b/compiler/pstatmnt.pas
    index f528168733..16b5860e9d 100644
    --- a/compiler/pstatmnt.pas
    +++ b/compiler/pstatmnt.pas
    @@ -360,7 +360,12 @@ implementation
               begin
                 if (hp.nodetype=ordconstn) and
                    (fordef.typ<>errordef) then
    -              testrange(fordef,tordconstnode(hp).value,false,true);
    +              begin
    +                { the node was derived from a generic parameter so ignore range check }
    +                if nf_generic_para in hp.flags then
    +                  exit;
    +                testrange(fordef,tordconstnode(hp).value,false,true);
    +              end;
               end;
     
             function for_loop_create(hloopvar: tnode): tnode;
    diff --git a/compiler/ptype.pas b/compiler/ptype.pas
    index 38e2526e9f..28cd0f94f8 100644
    --- a/compiler/ptype.pas
    +++ b/compiler/ptype.pas
    @@ -1436,7 +1436,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
    diff --git a/compiler/symconst.pas b/compiler/symconst.pas
    index a5ae7e0fb9..d1411039fd 100644
    --- a/compiler/symconst.pas
    +++ b/compiler/symconst.pas
    @@ -205,8 +205,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;
     
    @@ -232,7 +233,10 @@ type
           because we have to access this information in the symtable unit }
         df_llvm_no_struct_packing,
         { internal def that's not for any export }
    -    df_internal
    +    df_internal,
    +    { 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;
     
    @@ -651,7 +655,7 @@ type
         arraydef,recorddef,pointerdef,orddef,
         stringdef,enumdef,procdef,objectdef,errordef,
         filedef,formaldef,setdef,procvardef,floatdef,
    -    classrefdef,forwarddef,variantdef,undefineddef
    +    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
       );
     
       { possible types for symtable entries }
    @@ -692,7 +696,8 @@ type
       tconsttyp = (constnone,
         constord,conststring,constreal,
         constset,constpointer,constnil,
    -    constresourcestring,constwstring,constguid
    +    constresourcestring,constwstring,constguid,
    +    constundefined
       );
     
       { RTTI information to store }
    @@ -831,7 +836,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
            'abstractdef','arraydef','recorddef','pointerdef','orddef',
            'stringdef','enumdef','procdef','objectdef','errordef',
            'filedef','formaldef','setdef','procvardef','floatdef',
    -       'classrefdef','forwarddef','variantdef','undefineddef'
    +       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
          );
     
          EqualTypeName : array[tequaltype] of string[16] = (
    diff --git a/compiler/symdef.pas b/compiler/symdef.pas
    index 4a260c46b9..0f7a2e4c06 100644
    --- a/compiler/symdef.pas
    +++ b/compiler/symdef.pas
    @@ -129,6 +129,9 @@ interface
               function is_generic:boolean;inline;
               { same as above for specializations }
               function is_specialization:boolean;inline;
    +          { 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
    @@ -2197,13 +2200,26 @@ 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);
                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
    @@ -2220,12 +2236,12 @@ 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);
                  end;
    -           result:=false;
              end;
          end;
     
    diff --git a/compiler/symsym.pas b/compiler/symsym.pas
    index b21a5f9de9..04c07a5ec7 100644
    --- a/compiler/symsym.pas
    +++ b/compiler/symsym.pas
    @@ -157,7 +157,7 @@ interface
               fprettyname : ansistring;
               constructor create(const n : string;def:tdef;doregister:boolean);virtual;
               destructor destroy;override;
    -          constructor ppuload(ppufile:tcompilerppufile);
    +          constructor ppuload(ppufile:tcompilerppufile);virtual;
               { do not override this routine in platform-specific subclasses,
                 override ppuwrite_platform instead }
               procedure ppuwrite(ppufile:tcompilerppufile);override;final;
    @@ -392,6 +392,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);
               constructor ppuload(ppufile:tcompilerppufile);
               destructor  destroy;override;
               procedure buildderef;override;
    @@ -1581,7 +1582,6 @@ implementation
               tparasymtable(parast).ppuwrite(ppufile);
           end;
     
    -
     {****************************************************************************
                                 TABSTRACTVARSYM
     ****************************************************************************}
    @@ -2344,6 +2344,13 @@ implementation
              value.len:=getlengthwidestring(pw);
           end;
     
    +    constructor tconstsym.create_undefined(const n : string;def: tdef);
    +      begin
    +        inherited create(constsym,n,true);
    +        fillchar(value, sizeof(value), #0);
    +        consttyp:=constundefined;
    +        constdef:=def;
    +      end;
     
         constructor tconstsym.ppuload(ppufile:tcompilerppufile);
           var
    @@ -2416,7 +2423,8 @@ implementation
                    new(pguid(value.valueptr));
                    ppufile.getdata(value.valueptr^,sizeof(tguid));
                  end;
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.getderef(constdefderef);
                else
                  Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
    @@ -2448,7 +2456,7 @@ implementation
           begin
             inherited;
             case consttyp  of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdefderef.build(constdef);
               constwstring:
                 ;
    @@ -2461,7 +2469,7 @@ implementation
         procedure tconstsym.deref;
           begin
             case consttyp of
    -          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
    +          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
                 constdef:=tdef(constdefderef.resolve);
               constwstring:
                 constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
    @@ -2476,7 +2484,8 @@ implementation
              inherited ppuwrite(ppufile);
              ppufile.putbyte(byte(consttyp));
              case consttyp of
    -           constnil :
    +           constnil,
    +           constundefined :
                  ppufile.putderef(constdefderef);
                constord :
                  begin
    @@ -2627,7 +2636,6 @@ implementation
               result:=inherited prettyname;
           end;
     
    -
     {****************************************************************************
                                       TSYSSYM
     ****************************************************************************}
    diff --git a/compiler/symtable.pas b/compiler/symtable.pas
    index 796b2d6736..ae82024b03 100644
    --- a/compiler/symtable.pas
    +++ b/compiler/symtable.pas
    @@ -2781,7 +2781,7 @@ implementation
     
         function generate_objectpascal_helper_key(def:tdef):string;
           begin
    -        if not assigned(def) then
    +        if not assigned(def) or (def.typ = errordef) then
               internalerror(2013020501);
             if def.typ in [recorddef,objectdef] then
               result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
    diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
    index 74fde5c6c2..dd1db81af7 100644
    --- a/compiler/utils/ppuutils/ppudump.pp
    +++ b/compiler/utils/ppuutils/ppudump.pp
    @@ -1389,6 +1389,7 @@ const
          (mask:sp_has_deprecated_msg; str:'Has Deprecated Message'),
          (mask:sp_generic_dummy;      str:'Generic Dummy'),
          (mask:sp_explicitrename;     str:'Explicit Rename')
    +     (mask:sp_generic_const;      str:'Generic Constant Parameter'),
       );
     var
       symoptions : tsymoptions;
    @@ -1552,7 +1553,8 @@ const
          { this should never happen for defs stored to a ppu file }
          (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_internal;       str:'Internal'),
    +     (mask:df_has_generic_fields; str:'Has generic fields')
       );
       defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
          (mask:ds_vmt_written;           str:'VMT Written'),
    diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
    new file mode 100644
    index 0000000000..297b982b0f
    --- /dev/null
    +++ b/tests/test/tgenconst1.pp
    @@ -0,0 +1,33 @@
    +{$mode objfpc}
    +program tgenconst1;
    +
    +type
    +	kNames = set of (Blaise,Pascal);
    +	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 TUndefined<const U> = 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 TUndefined<nil>;
    +	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..f05a27718c
    --- /dev/null
    +++ b/tests/test/tgenconst10.pp
    @@ -0,0 +1,13 @@
    +{%FAIL}
    +
    +{$mode objfpc}
    +
    +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..ea409bec9b
    --- /dev/null
    +++ b/tests/test/tgenconst11.pp
    @@ -0,0 +1,21 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst11;
    +type
    +	TEnum = (aaa,bbb,ccc,ddd);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<20>.Create;
    +	b:=specialize TConst<10.1>.Create;
    +	c:=specialize TConst<'_string'>.Create;
    +	d:=specialize TConst<[1,2,3,4]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
    --- /dev/null
    +++ b/tests/test/tgenconst12.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +program tgenconst12;
    +
    +type
    +  generic TTest<const U> = class
    +  		class procedure DoThis;
    +  end;
    +
    +class procedure TTest.DoThis;
    +begin
    +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..0d5f8b1813
    --- /dev/null
    +++ b/tests/test/tgenconst13.pp
    @@ -0,0 +1,20 @@
    +{$mode objfpc}
    +program tgenconst13;
    +type
    +	TEnum = (aaa,bbb,ccc);
    +type
    +	generic TConst<const U> = class end;
    +
    +var
    +	a:specialize TConst<10>;
    +	b:specialize TConst<10.5>;
    +	c:specialize TConst<'string'>;
    +	d:specialize TConst<[1,2,3]>;
    +	e:specialize TConst<[aaa,bbb,ccc]>;
    +begin
    +	a:=specialize TConst<10>.Create;
    +	b:=specialize TConst<10.5>.Create;
    +	c:=specialize TConst<'string'>.Create;
    +	d:=specialize TConst<[1,2,3]>.Create;
    +	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
    +end.
    diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
    new file mode 100644
    index 0000000000..7f98086630
    --- /dev/null
    +++ b/tests/test/tgenconst14.pp
    @@ -0,0 +1,29 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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;
    +  end;
    +
    +var
    +	op: specialize TBinaryOp<100>;
    +begin
    +	writeln(op.d0);
    +	writeln(op.d1);
    +	writeln(op.d2);
    +	writeln(op.d3:1:1);
    +	writeln(op.d4);
    +	writeln(op.d5);
    +	writeln(op.d6);
    +	writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
    new file mode 100644
    index 0000000000..56744cd0a7
    --- /dev/null
    +++ b/tests/test/tgenconst15.pp
    @@ -0,0 +1,30 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst15;
    +
    +type
    +	kNames = set of (Blaise, Pascal);
    +  generic TSet<const I: kNames> = record
    +    const c = I;
    +  end;
    +  generic TString<const I: String> = record
    +    const c = I;
    +  end;
    +  generic TWideString<const I: WideString> = record
    +    const c = I;
    +  end;
    +  generic TSingle<const I: Single> = record
    +    const c = I; 
    +  end;
    +  generic TDouble<const I: Double> = record
    +    const c = I; 
    +  end;
    +  generic TReal<const I: Real> = record
    +    const c = I; 
    +  end;
    +
    +var
    +	a0: specialize TReal<100>;
    +begin
    +	writeln(a0.c);
    +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..275867ce25
    --- /dev/null
    +++ b/tests/test/tgenconst16.pp
    @@ -0,0 +1,86 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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> = 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> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  generic TBinaryOp<const I> = 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> = record
    +    procedure DoThis(param: integer = I);
    +  end;
    +
    +procedure TOther.DoThis(param: integer = I);
    +begin
    +  writeln(param, ' default:', I);
    +end;
    +
    +var
    +  t0: specialize TBinaryOp<100>;
    +  t1: specialize TOther<100>;
    +begin
    +  //writeln(op.d0);
    +  //writeln(op.d1);
    +  //writeln(op.d2);
    +  //writeln(op.d3:1:1);
    +  //writeln(op.d4);
    +  //writeln(op.d5);
    +  //writeln(op.d6);
    +  //writeln(op.d7);
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst17.pp b/tests/test/tgenconst17.pp
    new file mode 100644
    index 0000000000..26dc2ee21f
    --- /dev/null
    +++ b/tests/test/tgenconst17.pp
    @@ -0,0 +1,36 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst17;
    +
    +type
    +  generic TUnaryOp<const I: integer> = record
    +    const
    +      d0 = -I;
    +      d1 = +I;
    +      d2 = not I;
    +  end;
    +  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 xor I;
    +      d9 = I shl I;
    +      d10 = I shr I;
    +      d11 = I << I;
    +      d12 = I >> I;
    +      d13 = I <> I;
    +      d14 = I < I;
    +      d15 = I > I;
    +      d16 = I <= I;
    +      d17 = I >= I;
    +      d18 = I = I;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tgenconst18.pp b/tests/test/tgenconst18.pp
    new file mode 100644
    index 0000000000..a4ba526803
    --- /dev/null
    +++ b/tests/test/tgenconst18.pp
    @@ -0,0 +1,12 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +program tgenconst18;
    +
    +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/tgenconst19.pp b/tests/test/tgenconst19.pp
    new file mode 100644
    index 0000000000..b7538b17a9
    --- /dev/null
    +++ b/tests/test/tgenconst19.pp
    @@ -0,0 +1,49 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +
    +program tgenconst19;
    +
    +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/tgenconst2.pp b/tests/test/tgenconst2.pp
    new file mode 100644
    index 0000000000..aa3a960634
    --- /dev/null
    +++ b/tests/test/tgenconst2.pp
    @@ -0,0 +1,12 @@
    +{$mode objfpc}
    +program tgenconst2;
    +
    +type
    +	generic TStuff1<T1,T2;const U1,U2> = record end;
    +	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
    +	
    +var
    +	a: specialize TStuff1<integer,string,10,'string'>;
    +	b: specialize TStuff2<integer,string,10,10>;
    +begin
    +end.
    diff --git a/tests/test/tgenconst20.pp b/tests/test/tgenconst20.pp
    new file mode 100644
    index 0000000000..b87f0b2af1
    --- /dev/null
    +++ b/tests/test/tgenconst20.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +
    +program tgenconst20;
    +
    +{ testing range checking for arrays and for-loops }
    +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/tgenconst3.pp b/tests/test/tgenconst3.pp
    new file mode 100644
    index 0000000000..aea0e307e2
    --- /dev/null
    +++ b/tests/test/tgenconst3.pp
    @@ -0,0 +1,16 @@
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +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/tgenconst4.pp b/tests/test/tgenconst4.pp
    new file mode 100644
    index 0000000000..a1fae00c43
    --- /dev/null
    +++ b/tests/test/tgenconst4.pp
    @@ -0,0 +1,11 @@
    +{$mode objfpc}
    +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..63514a976c
    --- /dev/null
    +++ b/tests/test/tgenconst5.pp
    @@ -0,0 +1,24 @@
    +{$mode objfpc}
    +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..3ee3785423
    --- /dev/null
    +++ b/tests/test/tgenconst6.pp
    @@ -0,0 +1,21 @@
    +{$mode delphi}
    +program tgenconst6;
    +
    +type
    +	TList<T;const U> = 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..9d8e81ef05
    --- /dev/null
    +++ b/tests/test/tgenconst7.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..75844f7181
    --- /dev/null
    +++ b/tests/test/tgenconst8.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +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..939cb90302
    --- /dev/null
    +++ b/tests/test/tgenconst9.pp
    @@ -0,0 +1,11 @@
    +{%FAIL}
    +{$mode objfpc}
    +program tgenconst9;
    +
    +type
    +	generic TByte<const U: Byte> = record end;
    +	
    +var
    +	a: specialize TByte<string>;
    +begin
    +end.
    -- 
    2.17.2 (Apple Git-113)
    
    
    patch_7_20.diff (94,414 bytes)

Activities

Ryan Joseph

2019-02-24 15:57

reporter  

tgenconst-patch.txt (86,910 bytes)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..64fdb156d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+# files
+pp
+fpmake
+rtl/darwin/fpcmade.x86_64-darwin
+fpmake_proc1 copy.inc
+tests/*.x86_64-darwin
+rtl/Package.fpc
+tests/createlst
+tests/gparmake
+
+# directories
+lazbuild/
+x86_64-darwin/
+tests/tstunits/
+tests/utils
+
+# patterns
+*.app
+*.o
+*.ppu
+*.fpm
+*.rsj
+*.lst
\ No newline at end of file
diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 3f5882f762..793dbbbe76 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -175,7 +175,6 @@ implementation
       symtable,symsym,symcpu,
       defutil,symutil;
 
-
     function compare_defs_ext(def_from,def_to : tdef;
                               fromtreetype : tnodetype;
                               var doconv : tconverttype;
@@ -337,9 +336,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 07c035dc26..bd51cebdf3 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -152,22 +152,22 @@ interface
     function token2managementoperator(optoken:ttoken):tmanagementoperator;
 
     { check operator args and result type }
-
-    type
-      toverload_check_flag = (
-        ocf_check_non_overloadable, { also check operators that are (currently) considered as
-                                      not overloadable (e.g. the "+" operator for dynamic arrays
-                                      if modeswitch arrayoperators is active) }
-        ocf_check_only              { only check whether the operator is overloaded, but don't
-                                      modify the passed in node (return true if the operator is
-                                      overloaded, false otherwise) }
-      );
-      toverload_check_flags = set of toverload_check_flag;
-
+
+    type
+      toverload_check_flag = (
+        ocf_check_non_overloadable, { also check operators that are (currently) considered as
+                                      not overloadable (e.g. the "+" operator for dynamic arrays
+                                      if modeswitch arrayoperators is active) }
+        ocf_check_only              { only check whether the operator is overloaded, but don't
+                                      modify the passed in node (return true if the operator is
+                                      overloaded, false otherwise) }
+      );
+      toverload_check_flags = set of toverload_check_flag;
+
     function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
     function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
-    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
-    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
 
     { Register Allocation }
     procedure make_not_regable(p : tnode; how: tregableinfoflags);
@@ -515,9 +515,9 @@ implementation
                     end;
 
                  { <dyn. array> + <dyn. array> is handled by the compiler }
-                 if (m_array_operators in current_settings.modeswitches) and
-                     (treetyp=addn) and
-                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
+                 if (m_array_operators in current_settings.modeswitches) and
+                     (treetyp=addn) and
+                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
                     begin
                       allowed:=false;
                       exit;
@@ -720,7 +720,7 @@ implementation
       end;
 
 
-    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
       var
         ld      : tdef;
         optoken : ttoken;
@@ -742,11 +742,11 @@ implementation
         else
           inlinenumber:=in_none;
 
-        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
+        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
           exit;
 
         { operator overload is possible }
-        result:=not (ocf_check_only in ocf);
+        result:=not (ocf_check_only in ocf);
 
         optoken:=NOTOKEN;
         case t.nodetype of
@@ -766,11 +766,11 @@ implementation
         end;
         if (optoken=NOTOKEN) then
           begin
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage(parser_e_operator_not_overloaded);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage(parser_e_operator_not_overloaded);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
@@ -790,11 +790,11 @@ implementation
           begin
             candidates.free;
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
@@ -811,16 +811,16 @@ implementation
           begin
             candidates.free;
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
         { Multiple candidates left? }
-        if (cand_cnt>1) and not (ocf_check_only in ocf) then
+        if (cand_cnt>1) and not (ocf_check_only in ocf) then
           begin
             CGMessage(type_e_cant_choose_overload_function);
 {$ifdef EXTDEBUG}
@@ -833,13 +833,13 @@ implementation
           end;
         candidates.free;
 
-        if ocf_check_only in ocf then
-          begin
-            ppn.free;
-            result:=true;
-            exit;
-          end;
-
+        if ocf_check_only in ocf then
+          begin
+            ppn.free;
+            result:=true;
+            exit;
+          end;
+
         addsymref(operpd.procsym);
 
         { the nil as symtable signs firstcalln that this is
@@ -852,7 +852,7 @@ implementation
       end;
 
 
-    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
       var
         rd,ld   : tdef;
         optoken : ttoken;
@@ -945,14 +945,14 @@ implementation
         { load easier access variables }
         ld:=tbinarynode(t).left.resultdef;
         rd:=tbinarynode(t).right.resultdef;
-        if not (ocf_check_non_overloadable in ocf) and
-            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
+        if not (ocf_check_non_overloadable in ocf) and
+            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
           exit;
 
         { operator overload is possible }
-        { if we only check for the existance of the overload, then we assume that
-          it is not overloaded }
-        result:=not (ocf_check_only in ocf);
+        { if we only check for the existance of the overload, then we assume that
+          it is not overloaded }
+        result:=not (ocf_check_only in ocf);
 
         case t.nodetype of
            equaln:
@@ -997,19 +997,19 @@ implementation
              optoken:=_OP_IN;
            else
              begin
-               if not (ocf_check_only in ocf) then
-                 begin
-                   CGMessage(parser_e_operator_not_overloaded);
-                   t:=cnothingnode.create;
-                 end;
+               if not (ocf_check_only in ocf) then
+                 begin
+                   CGMessage(parser_e_operator_not_overloaded);
+                   t:=cnothingnode.create;
+                 end;
                exit;
              end;
         end;
 
-        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
+        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
 
         { no operator found for "<>" then search for "=" operator }
-        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
+        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
           begin
             ppn.free;
             ppn:=nil;
@@ -1021,15 +1021,15 @@ implementation
         if (cand_cnt=0) then
           begin
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              t:=cnothingnode.create;
-            exit;
-          end;
-
-        if ocf_check_only in ocf then
-          begin
-            ppn.free;
-            result:=true;
+            if not (ocf_check_only in ocf) then
+              t:=cnothingnode.create;
+            exit;
+          end;
+
+        if ocf_check_only in ocf then
+          begin
+            ppn.free;
+            result:=true;
             exit;
           end;
 
@@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
index ae94637c28..e1617b3e96 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -311,11 +311,21 @@ implementation
             p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
           constnil :
             p1:=cnilnode.create;
+          { constundefined is a placeholder for unrestricted generic const params
+            so we just treat it as a nil node. }
+          constundefined :
+            begin
+              p1:=cnilnode.create;
+              p1.resultdef := p.constdef;
+            end;
           constguid :
             p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
           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/node.pas b/compiler/node.pas
index b8600000bf..f9ab8ec521 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -194,7 +194,8 @@ interface
           'loadparentfpn',
           'objcselectorn',
           'objcprotocoln',
-          'specializen');
+          'specializen'
+          );
 
       { a set containing all const nodes }
       nodetype_const = [ordconstn,
@@ -272,10 +273,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
          }
@@ -1078,7 +1082,12 @@ implementation
     constructor tbinarynode.create(t:tnodetype;l,r : tnode);
       begin
          inherited create(t,l);
-         right:=r
+         { transfer generic paramater flag }
+         if assigned(l) and (nf_generic_para in l.flags) then
+           include(flags,nf_generic_para)
+         else if assigned(r) and (nf_generic_para in r.flags) then
+           include(flags,nf_generic_para);
+         right:=r;
       end;
 
 
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 6270ec582e..bd031e6a86 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -239,7 +239,7 @@ implementation
            internalerror(20021126);
 
          t:=self;
-         if isbinaryoverloaded(t,[]) then
+         if isbinaryoverloaded(t,[]) then
            begin
              result:=t;
              exit;
@@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
index c5b5bcc921..583d00c17b 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -141,18 +141,18 @@ implementation
            typen :
              begin
                if is_interface(p.resultdef) then
-                begin
-                  if assigned(tobjectdef(p.resultdef).iidguid) then
-                   begin
-                     new(pg);
-                     pg^:=tobjectdef(p.resultdef).iidguid^;
-                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
-                   end
-                  else
-                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
-                end
-               else
-                Message(parser_e_illegal_expression);
+                 begin
+                   if assigned(tobjectdef(p.resultdef).iidguid) then
+                     begin
+                       new(pg);
+                       pg^:=tobjectdef(p.resultdef).iidguid^;
+                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
+                     end
+                    else
+                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
+                 end
+               else 
+                 Message(parser_e_illegal_expression);
              end;
            inlinen:
              begin
@@ -179,6 +179,9 @@ implementation
            else
              Message(parser_e_illegal_expression);
         end;
+        { transfer generic param flag from node to symbol }
+        if nf_generic_para in p.flags then
+          include(hp.symoptions,sp_generic_para);
         current_tokenpos:=storetokenpos;
         p.free;
         readconstant:=hp;
@@ -507,8 +510,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/pdecvar.pas b/compiler/pdecvar.pas
index 4d39397e46..8121d87853 100644
--- a/compiler/pdecvar.pas
+++ b/compiler/pdecvar.pas
@@ -1675,6 +1675,10 @@ implementation
                    end;
                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 bc0606ed4b..e6d9633ebd 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -446,6 +446,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,sinttype,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
@@ -466,6 +469,9 @@ implementation
                    end
                  else
                    statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
@@ -4078,7 +4084,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
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..85270df256 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -28,7 +28,7 @@ interface
 uses
   cclasses,
   globtype,
-  symtype,symbase;
+  symconst,symtype,symbase;
 
 const
   inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
@@ -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 7760a4e134..33daf3b06a 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;
@@ -63,18 +63,163 @@ implementation
 
 uses
   { common }
-  cutils,fpccrc,
+  sysutils,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;
 
+  type
+    tdeftypeset = set of tdeftyp;
+  const
+    tgeneric_param_const_types:tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
+    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 is_generic_param_const(sym:tsym):boolean;
+      begin
+        if sym.typ = constsym then
+          result := tconstsym(sym).consttyp<>constundefined
+        else
+          result := false;
+      end;
+
+    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue): boolean;
+      begin
+        if (value.len<param2.low) or (value.len>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
+          begin
+            sym:=cconstsym.create_undefined(undefinedname,fromdef);
+            sym.owner:=fromdef.owner;
+            prettyname:='';
+            result:=sym;
+            exit;
+          end;
+        case node.nodetype of
+          ordconstn:
+            begin
+              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
+              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
+        sym.owner:=fromdef.owner;
+        result:=sym;
+      end;
 
     procedure maybe_add_waiting_unit(tt:tdef);
       var
@@ -104,203 +249,232 @@ 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;
+        //paratype : tconsttyp;
+        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:=is_generic_param_const(tsym(paramlist[i]));
+            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
-                                intffound:=assigned(
-                                             find_implemented_interface(objdef,
-                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
-                                             )
-                                           );
+                                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
+                              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 +484,12 @@ uses
         namepart : string;
         prettynamepart : ansistring;
         module : tmodule;
+        //paramdef : tgenericparamdef;
+        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 +501,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);
@@ -351,7 +528,9 @@ uses
             block_type:=bt_type;
             tmpparampos:=current_filepos;
             typeparam:=factor(false,[ef_type_only]);
-            if typeparam.nodetype=typen then
+            { 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 +546,47 @@ 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
+                    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
-                          begin
-                            { special handling for specializations inside generic function declarations }
-                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
-                          end
-                        else
+                        if typeparam.nodetype = typen then
                           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 +606,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;
 
 
@@ -578,7 +773,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
@@ -627,7 +822,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);
@@ -656,7 +851,7 @@ uses
             result:=generrordef;
             exit;
           end;
-
+        
         { we've found the correct def }
         if context.sym.typ=typesym then
           result:=tstoreddef(ttypesym(context.sym).typedef)
@@ -747,6 +942,7 @@ uses
         hintsprocessed : boolean;
         pd : tprocdef;
         pdflags : tpdflags;
+        typedef : tstoreddef;
       begin
         if not assigned(context) then
           internalerror(2015052203);
@@ -755,7 +951,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 }
@@ -771,20 +967,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 }
@@ -1196,8 +1391,8 @@ 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;
@@ -1205,22 +1400,87 @@ uses
         doconsume : boolean;
         constraintdata : tgenericconstraintdata;
         old_block_type : tblock_type;
+        is_const,last_is_const : boolean;
+        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;
+        is_const:=false;
+        last_is_const:=false;
+        last_token:=NOTOKEN;
         repeat
+          if try_to_consume(_CONST) then
+            begin
+              { last param was const without semicolon terminator }
+              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+              is_const := true;
+              const_list_index := result.count;
+            end;
           if token=_ID then
             begin
-              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
+              if is_const then
+                begin
+                  { last param was type without semicolon terminator }
+                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
+                end
+              else
+                begin
+                  { last param was const without semicolon terminator }
+                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
+                end;
               { type parameters need to be added as strict private }
               generictype.visibility:=vis_strictprivate;
               include(generictype.symoptions,sp_generic_para);
               result.add(orgpattern,generictype);
+              last_is_const:=is_const;
             end;
           consume(_ID);
-          if try_to_consume(_COLON) then
+          { const restriction }
+          if is_const then
+            begin
+              if 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:
+                              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;
+                          end;
+                          tconstsym(result[i]).constdef:=def;
+                        end
+                      else
+                        Message(type_e_mismatch);
+                    end;
+                  { after type restriction const list terminates }
+                  is_const:=false;
+                end;
+            end
+          { type restriction }
+          else if try_to_consume(_COLON) then
             begin
               if not allowconstraints then
                 { TODO }
@@ -1335,6 +1595,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
@@ -1360,21 +1621,27 @@ 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
+            is_const:=false;
+          last_token:=token;
+          last_type_pos:=current_filepos;
         until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
         { 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;
 
@@ -1382,7 +1649,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;
@@ -1407,10 +1676,22 @@ 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,true);
+                if generictype.typ=typesym then
+                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
+                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);
+                  end
+                else
+                  internalerror(2019021602);
                 { type parameters need to be added as strict private }
                 sym.visibility:=vis_strictprivate;
                 st.insert(sym);
@@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
index 10c42e7eb8..31011be3e8 100644
--- a/compiler/ppu.pas
+++ b/compiler/ppu.pas
@@ -43,7 +43,7 @@ type
 {$endif Test_Double_checksum}
 
 const
-  CurrentPPUVersion = 201;
+  CurrentPPUVersion = 203;
 
 { unit flags }
   uf_init                = $000001; { unit has initialization section }
diff --git a/compiler/ptype.pas b/compiler/ptype.pas
index 38e2526e9f..28cd0f94f8 100644
--- a/compiler/ptype.pas
+++ b/compiler/ptype.pas
@@ -1436,7 +1436,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
diff --git a/compiler/ryan_ppcx64.lpi b/compiler/ryan_ppcx64.lpi
new file mode 100644
index 0000000000..68008e4ab3
--- /dev/null
+++ b/compiler/ryan_ppcx64.lpi
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="9"/>
+    <PathDelim Value="\"/>
+    <General>
+      <Flags>
+        <MainUnitHasUsesSectionForAllUnits Value="False"/>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <LRSInOutputDirectory Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="ppcx64"/>
+    </General>
+    <BuildModes Count="1">
+      <Item1 Name="default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <DestinationDirectory Value="$(TestDir)\publishedproject\"/>
+      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
+      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
+    </PublishOptions>
+    <RunParams>
+      <local>
+        <FormatVersion Value="1"/>
+        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+      </local>
+    </RunParams>
+    <Units Count="2">
+      <Unit0>
+        <Filename Value="pp.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="pp"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="x86\aasmcpu.pas"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="aasmcpu"/>
+      </Unit1>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="x86_64\pp"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="x86_64"/>
+      <OtherUnitFiles Value="x86_64;x86;systems"/>
+      <UnitOutputDirectory Value="x86_64\lazbuild"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <CStyleOperator Value="False"/>
+        <AllowLabel Value="False"/>
+        <CPPInline Value="False"/>
+        <UseAnsiStrings Value="False"/>
+      </SyntaxOptions>
+    </Parsing>
+    <Other>
+      <Verbosity>
+        <ShowWarn Value="False"/>
+        <ShowNotes Value="False"/>
+        <ShowHints Value="False"/>
+      </Verbosity>
+      <ConfigFile>
+        <StopAfterErrCount Value="50"/>
+      </ConfigFile>
+      <CustomOptions Value="-dx86_64 -gw -godwarfcpp -Fl/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"/>
+      <CompilerPath Value="$(CompPath)"/>
+    </Other>
+  </CompilerOptions>
+</CONFIG>
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index a5ae7e0fb9..e02ce3a8ca 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -232,7 +232,10 @@ type
       because we have to access this information in the symtable unit }
     df_llvm_no_struct_packing,
     { internal def that's not for any export }
-    df_internal
+    df_internal,
+    { 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;
 
@@ -651,7 +654,7 @@ type
     arraydef,recorddef,pointerdef,orddef,
     stringdef,enumdef,procdef,objectdef,errordef,
     filedef,formaldef,setdef,procvardef,floatdef,
-    classrefdef,forwarddef,variantdef,undefineddef
+    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
   );
 
   { possible types for symtable entries }
@@ -692,7 +695,8 @@ type
   tconsttyp = (constnone,
     constord,conststring,constreal,
     constset,constpointer,constnil,
-    constresourcestring,constwstring,constguid
+    constresourcestring,constwstring,constguid,
+    constundefined
   );
 
   { RTTI information to store }
@@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
        'abstractdef','arraydef','recorddef','pointerdef','orddef',
        'stringdef','enumdef','procdef','objectdef','errordef',
        'filedef','formaldef','setdef','procvardef','floatdef',
-       'classrefdef','forwarddef','variantdef','undefineddef'
+       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
      );
 
      EqualTypeName : array[tequaltype] of string[16] = (
diff --git a/compiler/symdef.pas b/compiler/symdef.pas
index 4a260c46b9..0f7a2e4c06 100644
--- a/compiler/symdef.pas
+++ b/compiler/symdef.pas
@@ -129,6 +129,9 @@ interface
           function is_generic:boolean;inline;
           { same as above for specializations }
           function is_specialization:boolean;inline;
+          { 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
@@ -2197,13 +2200,26 @@ 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);
            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
@@ -2220,12 +2236,12 @@ 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);
              end;
-           result:=false;
          end;
      end;
 
diff --git a/compiler/symsym.pas b/compiler/symsym.pas
index b21a5f9de9..04c07a5ec7 100644
--- a/compiler/symsym.pas
+++ b/compiler/symsym.pas
@@ -157,7 +157,7 @@ interface
           fprettyname : ansistring;
           constructor create(const n : string;def:tdef;doregister:boolean);virtual;
           destructor destroy;override;
-          constructor ppuload(ppufile:tcompilerppufile);
+          constructor ppuload(ppufile:tcompilerppufile);virtual;
           { do not override this routine in platform-specific subclasses,
             override ppuwrite_platform instead }
           procedure ppuwrite(ppufile:tcompilerppufile);override;final;
@@ -392,6 +392,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);
           constructor ppuload(ppufile:tcompilerppufile);
           destructor  destroy;override;
           procedure buildderef;override;
@@ -1581,7 +1582,6 @@ implementation
           tparasymtable(parast).ppuwrite(ppufile);
       end;
 
-
 {****************************************************************************
                             TABSTRACTVARSYM
 ****************************************************************************}
@@ -2344,6 +2344,13 @@ implementation
          value.len:=getlengthwidestring(pw);
       end;
 
+    constructor tconstsym.create_undefined(const n : string;def: tdef);
+      begin
+        inherited create(constsym,n,true);
+        fillchar(value, sizeof(value), #0);
+        consttyp:=constundefined;
+        constdef:=def;
+      end;
 
     constructor tconstsym.ppuload(ppufile:tcompilerppufile);
       var
@@ -2416,7 +2423,8 @@ implementation
                new(pguid(value.valueptr));
                ppufile.getdata(value.valueptr^,sizeof(tguid));
              end;
-           constnil :
+           constnil,
+           constundefined :
              ppufile.getderef(constdefderef);
            else
              Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
@@ -2448,7 +2456,7 @@ implementation
       begin
         inherited;
         case consttyp  of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdefderef.build(constdef);
           constwstring:
             ;
@@ -2461,7 +2469,7 @@ implementation
     procedure tconstsym.deref;
       begin
         case consttyp of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdef:=tdef(constdefderef.resolve);
           constwstring:
             constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
@@ -2476,7 +2484,8 @@ implementation
          inherited ppuwrite(ppufile);
          ppufile.putbyte(byte(consttyp));
          case consttyp of
-           constnil :
+           constnil,
+           constundefined :
              ppufile.putderef(constdefderef);
            constord :
              begin
@@ -2627,7 +2636,6 @@ implementation
           result:=inherited prettyname;
       end;
 
-
 {****************************************************************************
                                   TSYSSYM
 ****************************************************************************}
diff --git a/compiler/symtable.pas b/compiler/symtable.pas
index 796b2d6736..ae82024b03 100644
--- a/compiler/symtable.pas
+++ b/compiler/symtable.pas
@@ -2781,7 +2781,7 @@ implementation
 
     function generate_objectpascal_helper_key(def:tdef):string;
       begin
-        if not assigned(def) then
+        if not assigned(def) or (def.typ = errordef) then
           internalerror(2013020501);
         if def.typ in [recorddef,objectdef] then
           result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
new file mode 100644
index 0000000000..297b982b0f
--- /dev/null
+++ b/tests/test/tgenconst1.pp
@@ -0,0 +1,33 @@
+{$mode objfpc}
+program tgenconst1;
+
+type
+	kNames = set of (Blaise,Pascal);
+	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 TUndefined<const U> = 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 TUndefined<nil>;
+	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..f05a27718c
--- /dev/null
+++ b/tests/test/tgenconst10.pp
@@ -0,0 +1,13 @@
+{%FAIL}
+
+{$mode objfpc}
+
+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..ea409bec9b
--- /dev/null
+++ b/tests/test/tgenconst11.pp
@@ -0,0 +1,21 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst11;
+type
+	TEnum = (aaa,bbb,ccc,ddd);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<20>.Create;
+	b:=specialize TConst<10.1>.Create;
+	c:=specialize TConst<'_string'>.Create;
+	d:=specialize TConst<[1,2,3,4]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
--- /dev/null
+++ b/tests/test/tgenconst12.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+program tgenconst12;
+
+type
+  generic TTest<const U> = class
+  		class procedure DoThis;
+  end;
+
+class procedure TTest.DoThis;
+begin
+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..0d5f8b1813
--- /dev/null
+++ b/tests/test/tgenconst13.pp
@@ -0,0 +1,20 @@
+{$mode objfpc}
+program tgenconst13;
+type
+	TEnum = (aaa,bbb,ccc);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<10>.Create;
+	b:=specialize TConst<10.5>.Create;
+	c:=specialize TConst<'string'>.Create;
+	d:=specialize TConst<[1,2,3]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
+end.
diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
new file mode 100644
index 0000000000..aa3a960634
--- /dev/null
+++ b/tests/test/tgenconst2.pp
@@ -0,0 +1,12 @@
+{$mode objfpc}
+program tgenconst2;
+
+type
+	generic TStuff1<T1,T2;const U1,U2> = record end;
+	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
+	
+var
+	a: specialize TStuff1<integer,string,10,'string'>;
+	b: specialize TStuff2<integer,string,10,10>;
+begin
+end.
diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
new file mode 100644
index 0000000000..aea0e307e2
--- /dev/null
+++ b/tests/test/tgenconst3.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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/tgenconst4.pp b/tests/test/tgenconst4.pp
new file mode 100644
index 0000000000..a1fae00c43
--- /dev/null
+++ b/tests/test/tgenconst4.pp
@@ -0,0 +1,11 @@
+{$mode objfpc}
+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..63514a976c
--- /dev/null
+++ b/tests/test/tgenconst5.pp
@@ -0,0 +1,24 @@
+{$mode objfpc}
+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..3ee3785423
--- /dev/null
+++ b/tests/test/tgenconst6.pp
@@ -0,0 +1,21 @@
+{$mode delphi}
+program tgenconst6;
+
+type
+	TList<T;const U> = 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..9d8e81ef05
--- /dev/null
+++ b/tests/test/tgenconst7.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..75844f7181
--- /dev/null
+++ b/tests/test/tgenconst8.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..939cb90302
--- /dev/null
+++ b/tests/test/tgenconst9.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst9;
+
+type
+	generic TByte<const U: Byte> = record end;
+	
+var
+	a: specialize TByte<string>;
+begin
+end.
tgenconst-patch.txt (86,910 bytes)

Thaddy de Koning

2019-02-25 11:14

reporter   ~0114398

I have applied the patch and testing now.
I would have put the tests in a separate patch, though.
It is a nice feature.

Ryan Joseph

2019-02-25 15:37

reporter   ~0114411

Ok thanks, I'll put tests in another patch next time.

Akira1364

2019-02-28 15:24

reporter   ~0114506

I think this is an awesome feature! A couple of things though:

-Your patch doesn't look like it is in the "normal" SVN format that I believe is generally desired, and also seems to include extraneous files that wouldn't normally exist in a "clean" checkout (i.e. rtl/darwin/fpcmade.x86_64-darwin and such.)

-When I went to test it by first checking out your github fork, then doing a merge to update that to the latest FPC trunk (as your fork is against a revision from a couple of months ago), and finally doing a full build, there was a couple of things you'd overlooked:

A) In compiler/utils/ppuutils/ppudump.pp, in the constant array of "tdefopt" called "defopt" in the procedure "readcommondef", you hadn't added the necessary additional field to reflect your new "df_has_generic_fields" enum variant. My fix for that was to add the following code at line 1574:

(mask:df_has_generic_fields; str:'Has generic fields')

B) In compiler/pgenutil.pas, in the function "parse_generic_parameters", I got a build failure at first due to the compiler being built with "warnings as errors" by default as there was an "uninitialized" warning for the "last_type_pos" tfileposinfo variable, which you pass to MessagePos2 (depending on the outcome of an if statement) without assigning anything to it. I think there is a global "current_filepos" defined in compiler/globals.pas that might make more sense to use there, anyways?

Once I got it built, it works quite well, however there seems to be a bit more that needs to be done as far as stability. Notably, doing <const T: Single> or <const T: Double> does not currently work at all and results in an immediate crash of the compiler for me. Other issues I encountered while testing various things:

This code:

program Test;

{$mode Delphi}

type
  TDiv<const I> = record
    public const Divided = I div I;
  end;

begin
end.

makes this happen:

test.pas(7,35) Error: Illegal expression
test.pas(7,35) Error: Compilation raised exception internally

and this code:

program Test;

{$mode Delphi}

type
  TDiv<const I: SizeInt> = record
    public const Divided = I div I;
  end;

begin
end.

makes this happen:

test.pas(7,30) Error: Division by zero
test.pas(11,4) Fatal: There were 1 errors compiling module, stopping

Overall though again, I think this is a really useful feature and I for one have a lot of ideas in mind for it when it's fully ready. Keep up the good work!

Florian

2019-02-28 20:29

administrator   ~0114518

The patch format does not matter, a reference to a git branch rebased to current trunk is fine. However, it requires usefull split of commits and also commit messages. Examples:

https://github.com/genericptr/freepascal/commit/2efec219041f23036508f9a35b8a7492404f29a8

Msg: "fixed .gitignore so tests are included "

However, it contains a tests as well.

https://github.com/genericptr/freepascal/commit/ec518542b2da7d7f016702a82b2d05349a01a6fb

Msg: "first commit" this is not really helpfull.

So rebasing, reordering and using good commit messages is for sure required.

Sven Barth

2019-03-01 14:49

manager   ~0114537

The patch (and the Git branch) also contains many apparent "no-op" changes, e.g.

- function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
- function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+ function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+ function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;

Please get rid of those as these add unnecessary noise which makes it harder to see real mistakes.

Ryan Joseph

2019-03-16 16:59

reporter  

gen-const.diff (68,610 bytes)
From e298c138a26964a9e960f868c8f821564ac52e55 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Tue, 6 Nov 2018 13:58:49 +0700
Subject: [PATCH] constants in generics

---
 compiler/defcmp.pas       |   9 +-
 compiler/htypechk.pas     |   2 +-
 compiler/ncon.pas         |  10 +
 compiler/node.pas         |  16 +-
 compiler/nset.pas         |   5 +-
 compiler/pdecl.pas        |  32 ++-
 compiler/pexpr.pas        |  11 +-
 compiler/pgentype.pas     |   8 +-
 compiler/pgenutil.pas     | 540 ++++++++++++++++++++++++--------------
 compiler/ptype.pas        |   4 +-
 compiler/symconst.pas     |  12 +-
 compiler/symdef.pas       |  19 +-
 compiler/symsym.pas       |  22 +-
 compiler/symtable.pas     |   2 +-
 tests/test/tgenconst1.pp  |  33 +++
 tests/test/tgenconst10.pp |  13 +
 tests/test/tgenconst11.pp |  21 ++
 tests/test/tgenconst12.pp |  16 ++
 tests/test/tgenconst13.pp |  20 ++
 tests/test/tgenconst2.pp  |  12 +
 tests/test/tgenconst3.pp  |  16 ++
 tests/test/tgenconst4.pp  |  11 +
 tests/test/tgenconst5.pp  |  24 ++
 tests/test/tgenconst6.pp  |  21 ++
 tests/test/tgenconst7.pp  |  11 +
 tests/test/tgenconst8.pp  |  11 +
 tests/test/tgenconst9.pp  |  11 +
 27 files changed, 667 insertions(+), 245 deletions(-)
 create mode 100644 tests/test/tgenconst1.pp
 create mode 100644 tests/test/tgenconst10.pp
 create mode 100644 tests/test/tgenconst11.pp
 create mode 100644 tests/test/tgenconst12.pp
 create mode 100644 tests/test/tgenconst13.pp
 create mode 100644 tests/test/tgenconst2.pp
 create mode 100644 tests/test/tgenconst3.pp
 create mode 100644 tests/test/tgenconst4.pp
 create mode 100644 tests/test/tgenconst5.pp
 create mode 100644 tests/test/tgenconst6.pp
 create mode 100644 tests/test/tgenconst7.pp
 create mode 100644 tests/test/tgenconst8.pp
 create mode 100644 tests/test/tgenconst9.pp

diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 9fc5b29119..87797faf3e 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -175,7 +175,6 @@ implementation
       symtable,symsym,symcpu,
       defutil,symutil;
 
-
     function compare_defs_ext(def_from,def_to : tdef;
                               fromtreetype : tnodetype;
                               var doconv : tconverttype;
@@ -345,9 +344,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 4e97f903a9..89d32fa966 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2705,7 +2705,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/ncon.pas b/compiler/ncon.pas
index 392e11ea1d..90087c265f 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -311,11 +311,21 @@ implementation
             p1:=cpointerconstnode.create(p.value.valueordptr,p.constdef);
           constnil :
             p1:=cnilnode.create;
+          { constundefined is a placeholder for unrestricted generic const params
+            so we just treat it as a nil node. }
+          constundefined :
+            begin
+              p1:=cnilnode.create;
+              p1.resultdef := p.constdef;
+            end;
           constguid :
             p1:=cguidconstnode.create(pguid(p.value.valueptr)^);
           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/node.pas b/compiler/node.pas
index a0aad228eb..468907a886 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -274,10 +274,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
          }
@@ -1080,7 +1083,12 @@ implementation
     constructor tbinarynode.create(t:tnodetype;l,r : tnode);
       begin
          inherited create(t,l);
-         right:=r
+         { transfer generic paramater flag }
+         if assigned(l) and (nf_generic_para in l.flags) then
+           include(flags,nf_generic_para)
+         else if assigned(r) and (nf_generic_para in r.flags) then
+           include(flags,nf_generic_para);
+         right:=r;
       end;
 
 
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 684eafd79a..e6e1d51a16 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -401,8 +401,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/pdecl.pas b/compiler/pdecl.pas
index fcb19c2c3d..82c0160805 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -141,18 +141,18 @@ implementation
            typen :
              begin
                if is_interface(p.resultdef) then
-                begin
-                  if assigned(tobjectdef(p.resultdef).iidguid) then
-                   begin
-                     new(pg);
-                     pg^:=tobjectdef(p.resultdef).iidguid^;
-                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
-                   end
-                  else
-                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
-                end
-               else
-                Message(parser_e_illegal_expression);
+                 begin
+                   if assigned(tobjectdef(p.resultdef).iidguid) then
+                     begin
+                       new(pg);
+                       pg^:=tobjectdef(p.resultdef).iidguid^;
+                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
+                     end
+                    else
+                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
+                 end
+               else 
+                 Message(parser_e_illegal_expression);
              end;
            inlinen:
              begin
@@ -179,6 +179,9 @@ implementation
            else
              Message(parser_e_illegal_expression);
         end;
+        { transfer generic param flag from node to symbol }
+        if nf_generic_para in p.flags then
+          include(hp.symoptions,sp_generic_para);
         current_tokenpos:=storetokenpos;
         p.free;
         readconstant:=hp;
@@ -510,8 +513,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/pexpr.pas b/compiler/pexpr.pas
index 251c613ef1..caf1ef2774 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -446,6 +446,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,sinttype,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
@@ -466,6 +469,9 @@ implementation
                    end
                  else
                    statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
@@ -4167,7 +4173,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
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..85270df256 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -28,7 +28,7 @@ interface
 uses
   cclasses,
   globtype,
-  symtype,symbase;
+  symconst,symtype,symbase;
 
 const
   inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
@@ -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 4e52761d73..4c634904a6 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;
@@ -104,203 +104,232 @@ 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;
+        //paratype : tconsttyp;
+        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:=is_generic_param_const(tsym(paramlist[i]));
+            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 +339,12 @@ uses
         namepart : string;
         prettynamepart : ansistring;
         module : tmodule;
+        //paramdef : tgenericparamdef;
+        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 +356,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);
@@ -351,7 +383,9 @@ uses
             block_type:=bt_type;
             tmpparampos:=current_filepos;
             typeparam:=factor(false,[ef_type_only]);
-            if typeparam.nodetype=typen then
+            { 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 +401,47 @@ 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
+                    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 +461,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;
 
 
@@ -578,7 +628,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
@@ -627,7 +677,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);
@@ -656,7 +706,7 @@ uses
             result:=generrordef;
             exit;
           end;
-
+        
         { we've found the correct def }
         if context.sym.typ=typesym then
           result:=tstoreddef(ttypesym(context.sym).typedef)
@@ -747,6 +797,7 @@ uses
         hintsprocessed : boolean;
         pd : tprocdef;
         pdflags : tpdflags;
+        typedef : tstoreddef;
       begin
         if not assigned(context) then
           internalerror(2015052203);
@@ -755,7 +806,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 }
@@ -771,20 +822,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 }
@@ -1199,8 +1249,8 @@ 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;
@@ -1208,22 +1258,87 @@ uses
         doconsume : boolean;
         constraintdata : tgenericconstraintdata;
         old_block_type : tblock_type;
+        is_const,last_is_const : boolean;
+        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;
+        is_const:=false;
+        last_is_const:=false;
+        last_token:=NOTOKEN;
         repeat
+          if try_to_consume(_CONST) then
+            begin
+              { last param was const without semicolon terminator }
+              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+              is_const := true;
+              const_list_index := result.count;
+            end;
           if token=_ID then
             begin
-              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
+              if is_const then
+                begin
+                  { last param was type without semicolon terminator }
+                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
+                end
+              else
+                begin
+                  { last param was const without semicolon terminator }
+                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
+                end;
               { type parameters need to be added as strict private }
               generictype.visibility:=vis_strictprivate;
               include(generictype.symoptions,sp_generic_para);
               result.add(orgpattern,generictype);
+              last_is_const:=is_const;
             end;
           consume(_ID);
-          if try_to_consume(_COLON) then
+          { const restriction }
+          if is_const then
+            begin
+              if 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:
+                              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;
+                          end;
+                          tconstsym(result[i]).constdef:=def;
+                        end
+                      else
+                        Message(type_e_mismatch);
+                    end;
+                  { after type restriction const list terminates }
+                  is_const:=false;
+                end;
+            end
+          { type restriction }
+          else if try_to_consume(_COLON) then
             begin
               if not allowconstraints then
                 { TODO }
@@ -1338,6 +1453,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
@@ -1363,21 +1479,27 @@ 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
+            is_const:=false;
+          last_token:=token;
+          last_type_pos:=current_filepos;
         until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
         { 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;
 
@@ -1385,7 +1507,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;
@@ -1410,10 +1534,22 @@ 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,true);
+                if generictype.typ=typesym then
+                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
+                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);
+                  end
+                else
+                  internalerror(2019021602);
                 { type parameters need to be added as strict private }
                 sym.visibility:=vis_strictprivate;
                 st.insert(sym);
@@ -1421,13 +1557,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/ptype.pas b/compiler/ptype.pas
index 5236f253f1..6b642803b8 100644
--- a/compiler/ptype.pas
+++ b/compiler/ptype.pas
@@ -1436,7 +1436,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
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index bf284cb58f..ad7424f60a 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -231,7 +231,10 @@ type
       because we have to access this information in the symtable unit }
     df_llvm_no_struct_packing,
     { internal def that's not for any export }
-    df_internal
+    df_internal,
+    { 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;
 
@@ -659,7 +662,7 @@ type
     arraydef,recorddef,pointerdef,orddef,
     stringdef,enumdef,procdef,objectdef,errordef,
     filedef,formaldef,setdef,procvardef,floatdef,
-    classrefdef,forwarddef,variantdef,undefineddef
+    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
   );
 
   { possible types for symtable entries }
@@ -700,7 +703,8 @@ type
   tconsttyp = (constnone,
     constord,conststring,constreal,
     constset,constpointer,constnil,
-    constresourcestring,constwstring,constguid
+    constresourcestring,constwstring,constguid,
+    constundefined
   );
 
   { RTTI information to store }
@@ -840,7 +844,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
        'abstractdef','arraydef','recorddef','pointerdef','orddef',
        'stringdef','enumdef','procdef','objectdef','errordef',
        'filedef','formaldef','setdef','procvardef','floatdef',
-       'classrefdef','forwarddef','variantdef','undefineddef'
+       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
      );
 
      EqualTypeName : array[tequaltype] of string[16] = (
diff --git a/compiler/symdef.pas b/compiler/symdef.pas
index 7db695005e..4be6f572af 100644
--- a/compiler/symdef.pas
+++ b/compiler/symdef.pas
@@ -2293,13 +2293,26 @@ 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);
            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
@@ -2316,12 +2329,12 @@ 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);
              end;
-           result:=false;
          end;
      end;
 
diff --git a/compiler/symsym.pas b/compiler/symsym.pas
index 2384509f47..2d0e499f54 100644
--- a/compiler/symsym.pas
+++ b/compiler/symsym.pas
@@ -157,7 +157,7 @@ interface
           fprettyname : ansistring;
           constructor create(const n : string;def:tdef;doregister:boolean);virtual;
           destructor destroy;override;
-          constructor ppuload(ppufile:tcompilerppufile);
+          constructor ppuload(ppufile:tcompilerppufile);virtual;
           { do not override this routine in platform-specific subclasses,
             override ppuwrite_platform instead }
           procedure ppuwrite(ppufile:tcompilerppufile);override;final;
@@ -392,6 +392,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);
           constructor ppuload(ppufile:tcompilerppufile);
           destructor  destroy;override;
           procedure buildderef;override;
@@ -1584,7 +1585,6 @@ implementation
           tparasymtable(parast).ppuwrite(ppufile);
       end;
 
-
 {****************************************************************************
                             TABSTRACTVARSYM
 ****************************************************************************}
@@ -2356,6 +2356,13 @@ implementation
          value.len:=getlengthwidestring(pw);
       end;
 
+    constructor tconstsym.create_undefined(const n : string;def: tdef);
+      begin
+        inherited create(constsym,n,true);
+        fillchar(value, sizeof(value), #0);
+        consttyp:=constundefined;
+        constdef:=def;
+      end;
 
     constructor tconstsym.ppuload(ppufile:tcompilerppufile);
       var
@@ -2428,7 +2435,8 @@ implementation
                new(pguid(value.valueptr));
                ppufile.getdata(value.valueptr^,sizeof(tguid));
              end;
-           constnil :
+           constnil,
+           constundefined :
              ppufile.getderef(constdefderef);
            else
              Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
@@ -2460,7 +2468,7 @@ implementation
       begin
         inherited;
         case consttyp  of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdefderef.build(constdef);
           constwstring:
             ;
@@ -2473,7 +2481,7 @@ implementation
     procedure tconstsym.deref;
       begin
         case consttyp of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdef:=tdef(constdefderef.resolve);
           constwstring:
             constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
@@ -2488,7 +2496,8 @@ implementation
          inherited ppuwrite(ppufile);
          ppufile.putbyte(byte(consttyp));
          case consttyp of
-           constnil :
+           constnil,
+           constundefined :
              ppufile.putderef(constdefderef);
            constord :
              begin
@@ -2641,7 +2650,6 @@ implementation
           result:=inherited prettyname;
       end;
 
-
 {****************************************************************************
                                   TSYSSYM
 ****************************************************************************}
diff --git a/compiler/symtable.pas b/compiler/symtable.pas
index c7abd7da58..906e2da4a3 100644
--- a/compiler/symtable.pas
+++ b/compiler/symtable.pas
@@ -2916,7 +2916,7 @@ implementation
 
     function generate_objectpascal_helper_key(def:tdef):string;
       begin
-        if not assigned(def) then
+        if not assigned(def) or (def.typ = errordef) then
           internalerror(2013020501);
         if def.typ in [recorddef,objectdef] then
           result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
new file mode 100644
index 0000000000..297b982b0f
--- /dev/null
+++ b/tests/test/tgenconst1.pp
@@ -0,0 +1,33 @@
+{$mode objfpc}
+program tgenconst1;
+
+type
+	kNames = set of (Blaise,Pascal);
+	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 TUndefined<const U> = 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 TUndefined<nil>;
+	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..f05a27718c
--- /dev/null
+++ b/tests/test/tgenconst10.pp
@@ -0,0 +1,13 @@
+{%FAIL}
+
+{$mode objfpc}
+
+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..ea409bec9b
--- /dev/null
+++ b/tests/test/tgenconst11.pp
@@ -0,0 +1,21 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst11;
+type
+	TEnum = (aaa,bbb,ccc,ddd);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<20>.Create;
+	b:=specialize TConst<10.1>.Create;
+	c:=specialize TConst<'_string'>.Create;
+	d:=specialize TConst<[1,2,3,4]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
--- /dev/null
+++ b/tests/test/tgenconst12.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+program tgenconst12;
+
+type
+  generic TTest<const U> = class
+  		class procedure DoThis;
+  end;
+
+class procedure TTest.DoThis;
+begin
+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..0d5f8b1813
--- /dev/null
+++ b/tests/test/tgenconst13.pp
@@ -0,0 +1,20 @@
+{$mode objfpc}
+program tgenconst13;
+type
+	TEnum = (aaa,bbb,ccc);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<10>.Create;
+	b:=specialize TConst<10.5>.Create;
+	c:=specialize TConst<'string'>.Create;
+	d:=specialize TConst<[1,2,3]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
+end.
diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
new file mode 100644
index 0000000000..aa3a960634
--- /dev/null
+++ b/tests/test/tgenconst2.pp
@@ -0,0 +1,12 @@
+{$mode objfpc}
+program tgenconst2;
+
+type
+	generic TStuff1<T1,T2;const U1,U2> = record end;
+	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
+	
+var
+	a: specialize TStuff1<integer,string,10,'string'>;
+	b: specialize TStuff2<integer,string,10,10>;
+begin
+end.
diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
new file mode 100644
index 0000000000..aea0e307e2
--- /dev/null
+++ b/tests/test/tgenconst3.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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/tgenconst4.pp b/tests/test/tgenconst4.pp
new file mode 100644
index 0000000000..a1fae00c43
--- /dev/null
+++ b/tests/test/tgenconst4.pp
@@ -0,0 +1,11 @@
+{$mode objfpc}
+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..63514a976c
--- /dev/null
+++ b/tests/test/tgenconst5.pp
@@ -0,0 +1,24 @@
+{$mode objfpc}
+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..3ee3785423
--- /dev/null
+++ b/tests/test/tgenconst6.pp
@@ -0,0 +1,21 @@
+{$mode delphi}
+program tgenconst6;
+
+type
+	TList<T;const U> = 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..9d8e81ef05
--- /dev/null
+++ b/tests/test/tgenconst7.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..75844f7181
--- /dev/null
+++ b/tests/test/tgenconst8.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..939cb90302
--- /dev/null
+++ b/tests/test/tgenconst9.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst9;
+
+type
+	generic TByte<const U: Byte> = record end;
+	
+var
+	a: specialize TByte<string>;
+begin
+end.
-- 
2.17.2 (Apple Git-113)

gen-const.diff (68,610 bytes)

Ryan Joseph

2019-03-16 17:00

reporter   ~0114866

I uploaded a new patch which I made by applying the old one (to a new master repository I downloaded last week) and then squished the commit history into a single commit. Is that better?

Ryan Joseph

2019-03-16 17:06

reporter   ~0114867

Regarding the "I div I" bug I think I just need to check for the nf_generic_para flag I added and ignore the warning. I had to do something similar for array syntax like array[0..I-1]. The / operator works however so that's interesting.

Ryan Joseph

2019-03-17 22:01

reporter  

gen-const-clean.diff (81,965 bytes)
From 57a8c0589a5c881dd55d36dbf03832cb024e3cf3 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Sun, 17 Mar 2019 16:57:25 -0400
Subject: [PATCH] constants in generics

---
 compiler/defcmp.pas                |   9 +-
 compiler/htypechk.pas              |   2 +-
 compiler/ncon.pas                  |  38 +-
 compiler/nmat.pas                  |   5 +-
 compiler/node.pas                  |  19 +-
 compiler/nset.pas                  |   5 +-
 compiler/pdecl.pas                 |  43 +-
 compiler/pdecvar.pas               |   4 +
 compiler/pexpr.pas                 |  11 +-
 compiler/pgentype.pas              |   8 +-
 compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
 compiler/ppu.pas                   |   2 +-
 compiler/ptype.pas                 |   4 +-
 compiler/symconst.pas              |  12 +-
 compiler/symdef.pas                |  22 +-
 compiler/symsym.pas                |  22 +-
 compiler/symtable.pas              |   2 +-
 compiler/utils/ppuutils/ppudump.pp |   3 +-
 tests/test/tgenconst1.pp           |  33 ++
 tests/test/tgenconst10.pp          |  13 +
 tests/test/tgenconst11.pp          |  21 +
 tests/test/tgenconst12.pp          |  16 +
 tests/test/tgenconst13.pp          |  20 +
 tests/test/tgenconst14.pp          |  29 ++
 tests/test/tgenconst15.pp          |  30 ++
 tests/test/tgenconst2.pp           |  12 +
 tests/test/tgenconst3.pp           |  16 +
 tests/test/tgenconst4.pp           |  11 +
 tests/test/tgenconst5.pp           |  24 +
 tests/test/tgenconst6.pp           |  21 +
 tests/test/tgenconst7.pp           |  11 +
 tests/test/tgenconst8.pp           |  11 +
 tests/test/tgenconst9.pp           |  11 +
 33 files changed, 923 insertions(+), 260 deletions(-)
 create mode 100644 tests/test/tgenconst1.pp
 create mode 100644 tests/test/tgenconst10.pp
 create mode 100644 tests/test/tgenconst11.pp
 create mode 100644 tests/test/tgenconst12.pp
 create mode 100644 tests/test/tgenconst13.pp
 create mode 100644 tests/test/tgenconst14.pp
 create mode 100644 tests/test/tgenconst15.pp
 create mode 100644 tests/test/tgenconst2.pp
 create mode 100644 tests/test/tgenconst3.pp
 create mode 100644 tests/test/tgenconst4.pp
 create mode 100644 tests/test/tgenconst5.pp
 create mode 100644 tests/test/tgenconst6.pp
 create mode 100644 tests/test/tgenconst7.pp
 create mode 100644 tests/test/tgenconst8.pp
 create mode 100644 tests/test/tgenconst9.pp

diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 3f5882f762..793dbbbe76 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -175,7 +175,6 @@ implementation
       symtable,symsym,symcpu,
       defutil,symutil;
 
-
     function compare_defs_ext(def_from,def_to : tdef;
                               fromtreetype : tnodetype;
                               var doconv : tconverttype;
@@ -337,9 +336,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 07c035dc26..2358ea4b6d 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
index ae94637c28..fb5e94c09f 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -304,18 +304,48 @@ 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 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_para in p.symoptions then
+                p1:=csetconstnode.create(default(pconstset),p.constdef)
+              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;
+          { constundefined is a placeholder for unrestricted generic const params
+            so we just treat it as a nil node. }
+          constundefined :
+            begin
+              p1:=cnilnode.create;
+              p1.resultdef := p.constdef;
+            end;
           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 355b493da4..d10dff6128 100644
--- a/compiler/nmat.pas
+++ b/compiler/nmat.pas
@@ -129,7 +129,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 b8600000bf..f9ab8ec521 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -194,7 +194,8 @@ interface
           'loadparentfpn',
           'objcselectorn',
           'objcprotocoln',
-          'specializen');
+          'specializen'
+          );
 
       { a set containing all const nodes }
       nodetype_const = [ordconstn,
@@ -272,10 +273,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
          }
@@ -1078,7 +1082,12 @@ implementation
     constructor tbinarynode.create(t:tnodetype;l,r : tnode);
       begin
          inherited create(t,l);
-         right:=r
+         { transfer generic paramater flag }
+         if assigned(l) and (nf_generic_para in l.flags) then
+           include(flags,nf_generic_para)
+         else if assigned(r) and (nf_generic_para in r.flags) then
+           include(flags,nf_generic_para);
+         right:=r;
       end;
 
 
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 6270ec582e..4360a7340d 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
index c5b5bcc921..767eec22f7 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -126,9 +126,14 @@ implementation
              end;
            setconstn :
              begin
-               new(ps);
-               ps^:=tsetconstnode(p).value_set^;
-               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+               if nf_generic_para in p.flags then
+                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
+               else
+                 begin
+                   new(ps);
+                   ps^:=tsetconstnode(p).value_set^;
+                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+                 end;
              end;
            pointerconstn :
              begin
@@ -141,18 +146,18 @@ implementation
            typen :
              begin
                if is_interface(p.resultdef) then
-                begin
-                  if assigned(tobjectdef(p.resultdef).iidguid) then
-                   begin
-                     new(pg);
-                     pg^:=tobjectdef(p.resultdef).iidguid^;
-                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
-                   end
-                  else
-                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
-                end
-               else
-                Message(parser_e_illegal_expression);
+                 begin
+                   if assigned(tobjectdef(p.resultdef).iidguid) then
+                     begin
+                       new(pg);
+                       pg^:=tobjectdef(p.resultdef).iidguid^;
+                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
+                     end
+                    else
+                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
+                 end
+               else 
+                 Message(parser_e_illegal_expression);
              end;
            inlinen:
              begin
@@ -179,6 +184,9 @@ implementation
            else
              Message(parser_e_illegal_expression);
         end;
+        { transfer generic param flag from node to symbol }
+        if nf_generic_para in p.flags then
+          include(hp.symoptions,sp_generic_para);
         current_tokenpos:=storetokenpos;
         p.free;
         readconstant:=hp;
@@ -507,8 +515,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/pdecvar.pas b/compiler/pdecvar.pas
index 4d39397e46..8121d87853 100644
--- a/compiler/pdecvar.pas
+++ b/compiler/pdecvar.pas
@@ -1675,6 +1675,10 @@ implementation
                    end;
                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 bc0606ed4b..e6d9633ebd 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -446,6 +446,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,sinttype,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
@@ -466,6 +469,9 @@ implementation
                    end
                  else
                    statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
@@ -4078,7 +4084,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
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..85270df256 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -28,7 +28,7 @@ interface
 uses
   cclasses,
   globtype,
-  symtype,symbase;
+  symconst,symtype,symbase;
 
 const
   inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
@@ -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 7760a4e134..33daf3b06a 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;
@@ -63,18 +63,163 @@ implementation
 
 uses
   { common }
-  cutils,fpccrc,
+  sysutils,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;
 
+  type
+    tdeftypeset = set of tdeftyp;
+  const
+    tgeneric_param_const_types:tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
+    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 is_generic_param_const(sym:tsym):boolean;
+      begin
+        if sym.typ = constsym then
+          result := tconstsym(sym).consttyp<>constundefined
+        else
+          result := false;
+      end;
+
+    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue): boolean;
+      begin
+        if (value.len<param2.low) or (value.len>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
+          begin
+            sym:=cconstsym.create_undefined(undefinedname,fromdef);
+            sym.owner:=fromdef.owner;
+            prettyname:='';
+            result:=sym;
+            exit;
+          end;
+        case node.nodetype of
+          ordconstn:
+            begin
+              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
+              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
+        sym.owner:=fromdef.owner;
+        result:=sym;
+      end;
 
     procedure maybe_add_waiting_unit(tt:tdef);
       var
@@ -104,203 +249,232 @@ 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;
+        //paratype : tconsttyp;
+        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:=is_generic_param_const(tsym(paramlist[i]));
+            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
-                                intffound:=assigned(
-                                             find_implemented_interface(objdef,
-                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
-                                             )
-                                           );
+                                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
+                              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 +484,12 @@ uses
         namepart : string;
         prettynamepart : ansistring;
         module : tmodule;
+        //paramdef : tgenericparamdef;
+        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 +501,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);
@@ -351,7 +528,9 @@ uses
             block_type:=bt_type;
             tmpparampos:=current_filepos;
             typeparam:=factor(false,[ef_type_only]);
-            if typeparam.nodetype=typen then
+            { 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 +546,47 @@ 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
+                    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
-                          begin
-                            { special handling for specializations inside generic function declarations }
-                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
-                          end
-                        else
+                        if typeparam.nodetype = typen then
                           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 +606,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;
 
 
@@ -578,7 +773,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
@@ -627,7 +822,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);
@@ -656,7 +851,7 @@ uses
             result:=generrordef;
             exit;
           end;
-
+        
         { we've found the correct def }
         if context.sym.typ=typesym then
           result:=tstoreddef(ttypesym(context.sym).typedef)
@@ -747,6 +942,7 @@ uses
         hintsprocessed : boolean;
         pd : tprocdef;
         pdflags : tpdflags;
+        typedef : tstoreddef;
       begin
         if not assigned(context) then
           internalerror(2015052203);
@@ -755,7 +951,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 }
@@ -771,20 +967,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 }
@@ -1196,8 +1391,8 @@ 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;
@@ -1205,22 +1400,87 @@ uses
         doconsume : boolean;
         constraintdata : tgenericconstraintdata;
         old_block_type : tblock_type;
+        is_const,last_is_const : boolean;
+        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;
+        is_const:=false;
+        last_is_const:=false;
+        last_token:=NOTOKEN;
         repeat
+          if try_to_consume(_CONST) then
+            begin
+              { last param was const without semicolon terminator }
+              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+              is_const := true;
+              const_list_index := result.count;
+            end;
           if token=_ID then
             begin
-              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
+              if is_const then
+                begin
+                  { last param was type without semicolon terminator }
+                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
+                end
+              else
+                begin
+                  { last param was const without semicolon terminator }
+                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
+                end;
               { type parameters need to be added as strict private }
               generictype.visibility:=vis_strictprivate;
               include(generictype.symoptions,sp_generic_para);
               result.add(orgpattern,generictype);
+              last_is_const:=is_const;
             end;
           consume(_ID);
-          if try_to_consume(_COLON) then
+          { const restriction }
+          if is_const then
+            begin
+              if 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:
+                              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;
+                          end;
+                          tconstsym(result[i]).constdef:=def;
+                        end
+                      else
+                        Message(type_e_mismatch);
+                    end;
+                  { after type restriction const list terminates }
+                  is_const:=false;
+                end;
+            end
+          { type restriction }
+          else if try_to_consume(_COLON) then
             begin
               if not allowconstraints then
                 { TODO }
@@ -1335,6 +1595,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
@@ -1360,21 +1621,27 @@ 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
+            is_const:=false;
+          last_token:=token;
+          last_type_pos:=current_filepos;
         until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
         { 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;
 
@@ -1382,7 +1649,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;
@@ -1407,10 +1676,22 @@ 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,true);
+                if generictype.typ=typesym then
+                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
+                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);
+                  end
+                else
+                  internalerror(2019021602);
                 { type parameters need to be added as strict private }
                 sym.visibility:=vis_strictprivate;
                 st.insert(sym);
@@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
index 10c42e7eb8..31011be3e8 100644
--- a/compiler/ppu.pas
+++ b/compiler/ppu.pas
@@ -43,7 +43,7 @@ type
 {$endif Test_Double_checksum}
 
 const
-  CurrentPPUVersion = 201;
+  CurrentPPUVersion = 203;
 
 { unit flags }
   uf_init                = $000001; { unit has initialization section }
diff --git a/compiler/ptype.pas b/compiler/ptype.pas
index 38e2526e9f..28cd0f94f8 100644
--- a/compiler/ptype.pas
+++ b/compiler/ptype.pas
@@ -1436,7 +1436,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
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index a5ae7e0fb9..e02ce3a8ca 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -232,7 +232,10 @@ type
       because we have to access this information in the symtable unit }
     df_llvm_no_struct_packing,
     { internal def that's not for any export }
-    df_internal
+    df_internal,
+    { 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;
 
@@ -651,7 +654,7 @@ type
     arraydef,recorddef,pointerdef,orddef,
     stringdef,enumdef,procdef,objectdef,errordef,
     filedef,formaldef,setdef,procvardef,floatdef,
-    classrefdef,forwarddef,variantdef,undefineddef
+    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
   );
 
   { possible types for symtable entries }
@@ -692,7 +695,8 @@ type
   tconsttyp = (constnone,
     constord,conststring,constreal,
     constset,constpointer,constnil,
-    constresourcestring,constwstring,constguid
+    constresourcestring,constwstring,constguid,
+    constundefined
   );
 
   { RTTI information to store }
@@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
        'abstractdef','arraydef','recorddef','pointerdef','orddef',
        'stringdef','enumdef','procdef','objectdef','errordef',
        'filedef','formaldef','setdef','procvardef','floatdef',
-       'classrefdef','forwarddef','variantdef','undefineddef'
+       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
      );
 
      EqualTypeName : array[tequaltype] of string[16] = (
diff --git a/compiler/symdef.pas b/compiler/symdef.pas
index 4a260c46b9..0f7a2e4c06 100644
--- a/compiler/symdef.pas
+++ b/compiler/symdef.pas
@@ -129,6 +129,9 @@ interface
           function is_generic:boolean;inline;
           { same as above for specializations }
           function is_specialization:boolean;inline;
+          { 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
@@ -2197,13 +2200,26 @@ 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);
            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
@@ -2220,12 +2236,12 @@ 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);
              end;
-           result:=false;
          end;
      end;
 
diff --git a/compiler/symsym.pas b/compiler/symsym.pas
index b21a5f9de9..04c07a5ec7 100644
--- a/compiler/symsym.pas
+++ b/compiler/symsym.pas
@@ -157,7 +157,7 @@ interface
           fprettyname : ansistring;
           constructor create(const n : string;def:tdef;doregister:boolean);virtual;
           destructor destroy;override;
-          constructor ppuload(ppufile:tcompilerppufile);
+          constructor ppuload(ppufile:tcompilerppufile);virtual;
           { do not override this routine in platform-specific subclasses,
             override ppuwrite_platform instead }
           procedure ppuwrite(ppufile:tcompilerppufile);override;final;
@@ -392,6 +392,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);
           constructor ppuload(ppufile:tcompilerppufile);
           destructor  destroy;override;
           procedure buildderef;override;
@@ -1581,7 +1582,6 @@ implementation
           tparasymtable(parast).ppuwrite(ppufile);
       end;
 
-
 {****************************************************************************
                             TABSTRACTVARSYM
 ****************************************************************************}
@@ -2344,6 +2344,13 @@ implementation
          value.len:=getlengthwidestring(pw);
       end;
 
+    constructor tconstsym.create_undefined(const n : string;def: tdef);
+      begin
+        inherited create(constsym,n,true);
+        fillchar(value, sizeof(value), #0);
+        consttyp:=constundefined;
+        constdef:=def;
+      end;
 
     constructor tconstsym.ppuload(ppufile:tcompilerppufile);
       var
@@ -2416,7 +2423,8 @@ implementation
                new(pguid(value.valueptr));
                ppufile.getdata(value.valueptr^,sizeof(tguid));
              end;
-           constnil :
+           constnil,
+           constundefined :
              ppufile.getderef(constdefderef);
            else
              Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
@@ -2448,7 +2456,7 @@ implementation
       begin
         inherited;
         case consttyp  of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdefderef.build(constdef);
           constwstring:
             ;
@@ -2461,7 +2469,7 @@ implementation
     procedure tconstsym.deref;
       begin
         case consttyp of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdef:=tdef(constdefderef.resolve);
           constwstring:
             constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
@@ -2476,7 +2484,8 @@ implementation
          inherited ppuwrite(ppufile);
          ppufile.putbyte(byte(consttyp));
          case consttyp of
-           constnil :
+           constnil,
+           constundefined :
              ppufile.putderef(constdefderef);
            constord :
              begin
@@ -2627,7 +2636,6 @@ implementation
           result:=inherited prettyname;
       end;
 
-
 {****************************************************************************
                                   TSYSSYM
 ****************************************************************************}
diff --git a/compiler/symtable.pas b/compiler/symtable.pas
index 796b2d6736..ae82024b03 100644
--- a/compiler/symtable.pas
+++ b/compiler/symtable.pas
@@ -2781,7 +2781,7 @@ implementation
 
     function generate_objectpascal_helper_key(def:tdef):string;
       begin
-        if not assigned(def) then
+        if not assigned(def) or (def.typ = errordef) then
           internalerror(2013020501);
         if def.typ in [recorddef,objectdef] then
           result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
index 74fde5c6c2..80d9d4df11 100644
--- a/compiler/utils/ppuutils/ppudump.pp
+++ b/compiler/utils/ppuutils/ppudump.pp
@@ -1552,7 +1552,8 @@ const
      { this should never happen for defs stored to a ppu file }
      (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_internal;       str:'Internal'),
+     (mask:df_has_generic_fields; str:'Has generic fields')
   );
   defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
      (mask:ds_vmt_written;           str:'VMT Written'),
diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
new file mode 100644
index 0000000000..297b982b0f
--- /dev/null
+++ b/tests/test/tgenconst1.pp
@@ -0,0 +1,33 @@
+{$mode objfpc}
+program tgenconst1;
+
+type
+	kNames = set of (Blaise,Pascal);
+	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 TUndefined<const U> = 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 TUndefined<nil>;
+	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..f05a27718c
--- /dev/null
+++ b/tests/test/tgenconst10.pp
@@ -0,0 +1,13 @@
+{%FAIL}
+
+{$mode objfpc}
+
+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..ea409bec9b
--- /dev/null
+++ b/tests/test/tgenconst11.pp
@@ -0,0 +1,21 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst11;
+type
+	TEnum = (aaa,bbb,ccc,ddd);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<20>.Create;
+	b:=specialize TConst<10.1>.Create;
+	c:=specialize TConst<'_string'>.Create;
+	d:=specialize TConst<[1,2,3,4]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
--- /dev/null
+++ b/tests/test/tgenconst12.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+program tgenconst12;
+
+type
+  generic TTest<const U> = class
+  		class procedure DoThis;
+  end;
+
+class procedure TTest.DoThis;
+begin
+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..0d5f8b1813
--- /dev/null
+++ b/tests/test/tgenconst13.pp
@@ -0,0 +1,20 @@
+{$mode objfpc}
+program tgenconst13;
+type
+	TEnum = (aaa,bbb,ccc);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<10>.Create;
+	b:=specialize TConst<10.5>.Create;
+	c:=specialize TConst<'string'>.Create;
+	d:=specialize TConst<[1,2,3]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
+end.
diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
new file mode 100644
index 0000000000..7f98086630
--- /dev/null
+++ b/tests/test/tgenconst14.pp
@@ -0,0 +1,29 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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;
+  end;
+
+var
+	op: specialize TBinaryOp<100>;
+begin
+	writeln(op.d0);
+	writeln(op.d1);
+	writeln(op.d2);
+	writeln(op.d3:1:1);
+	writeln(op.d4);
+	writeln(op.d5);
+	writeln(op.d6);
+	writeln(op.d7);
+end.
\ No newline at end of file
diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
new file mode 100644
index 0000000000..56744cd0a7
--- /dev/null
+++ b/tests/test/tgenconst15.pp
@@ -0,0 +1,30 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+program tgenconst15;
+
+type
+	kNames = set of (Blaise, Pascal);
+  generic TSet<const I: kNames> = record
+    const c = I;
+  end;
+  generic TString<const I: String> = record
+    const c = I;
+  end;
+  generic TWideString<const I: WideString> = record
+    const c = I;
+  end;
+  generic TSingle<const I: Single> = record
+    const c = I; 
+  end;
+  generic TDouble<const I: Double> = record
+    const c = I; 
+  end;
+  generic TReal<const I: Real> = record
+    const c = I; 
+  end;
+
+var
+	a0: specialize TReal<100>;
+begin
+	writeln(a0.c);
+end.
\ No newline at end of file
diff --git a/tests/test/tgenconst2.pp b/tests/test/tgenconst2.pp
new file mode 100644
index 0000000000..aa3a960634
--- /dev/null
+++ b/tests/test/tgenconst2.pp
@@ -0,0 +1,12 @@
+{$mode objfpc}
+program tgenconst2;
+
+type
+	generic TStuff1<T1,T2;const U1,U2> = record end;
+	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
+	
+var
+	a: specialize TStuff1<integer,string,10,'string'>;
+	b: specialize TStuff2<integer,string,10,10>;
+begin
+end.
diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
new file mode 100644
index 0000000000..aea0e307e2
--- /dev/null
+++ b/tests/test/tgenconst3.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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/tgenconst4.pp b/tests/test/tgenconst4.pp
new file mode 100644
index 0000000000..a1fae00c43
--- /dev/null
+++ b/tests/test/tgenconst4.pp
@@ -0,0 +1,11 @@
+{$mode objfpc}
+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..63514a976c
--- /dev/null
+++ b/tests/test/tgenconst5.pp
@@ -0,0 +1,24 @@
+{$mode objfpc}
+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..3ee3785423
--- /dev/null
+++ b/tests/test/tgenconst6.pp
@@ -0,0 +1,21 @@
+{$mode delphi}
+program tgenconst6;
+
+type
+	TList<T;const U> = 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..9d8e81ef05
--- /dev/null
+++ b/tests/test/tgenconst7.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..75844f7181
--- /dev/null
+++ b/tests/test/tgenconst8.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..939cb90302
--- /dev/null
+++ b/tests/test/tgenconst9.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst9;
+
+type
+	generic TByte<const U: Byte> = record end;
+	
+var
+	a: specialize TByte<string>;
+begin
+end.
-- 
2.17.2 (Apple Git-113)

gen-const-clean.diff (81,965 bytes)

Ryan Joseph

2019-03-17 22:07

reporter   ~0114903

I fixed those bugs reported by Akira and uploaded a new patch again (gen-const-clean.diff). I think I've got a good workflow to make the patches now but I'm not sure until they're reviewed. The process:

- create a new repo from the existing development branch and delete any unwanted files.
- squish all commits into one and rename as something useful
- create patch using "git format-patch master --stdout > patch.diff"
- create a new repo from master and apply the patch using "git am --reject patch.diff". I had to do this because bad line endings were inserted somehow (from the original sources I think!) and this cleaned them out.
- create another patch from the final clean branch

Very complicated and timely process so I hope they're good.

Ryan Joseph

2019-03-17 22:26

reporter   ~0114904

Oops, I lied. The untyped const crash that was reported was actually not fixed. That operation is not supported (binary operators on untyped constants) but I'm not certain how to resolve it right now. The resulting constant is technically invalid but the compiler needs to just ignore it until the generic is specialized with an actual type.

Akira1364

2019-03-18 15:15

reporter   ~0114909

Last edited: 2019-03-18 15:18

View 2 revisions

I was just about to make another comment about that as I've just tested the branch again, but looks like you've caught it! I agree that even if it cannot *work* in that case if the const is unconstrained by a type, it probably shouldn't literally crash the compiler.

We're getting there though!

Ryan Joseph

2019-03-19 14:22

reporter   ~0114925

Akira, I fixed these bugs and updated git if you would like to test I'd appreciate that. I added another test tgenconst16.pp which I think covered all the different ways constants can be used in generics but I'm not sure. Please tell me if you can think of anything else.

Ryan Joseph

2019-03-23 14:04

reporter  

gen-const-3-23.diff (96,712 bytes)
From 376a757c8340f9c8995c9ba25cc09400d0ce9280 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Tue, 6 Nov 2018 13:58:49 +0700
Subject: [PATCH] constants in generics

---
 .gitignore                         |  24 +
 compiler/defcmp.pas                |   9 +-
 compiler/htypechk.pas              | 132 +++---
 compiler/ncon.pas                  |  42 +-
 compiler/nmat.pas                  |   5 +-
 compiler/node.pas                  |  22 +-
 compiler/nset.pas                  |   7 +-
 compiler/pdecl.pas                 |  53 ++-
 compiler/pdecvar.pas               |   4 +
 compiler/pexpr.pas                 |  11 +-
 compiler/pgentype.pas              |   8 +-
 compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
 compiler/ppu.pas                   |   2 +-
 compiler/ptype.pas                 |   4 +-
 compiler/symconst.pas              |  12 +-
 compiler/symdef.pas                |  22 +-
 compiler/symsym.pas                |  22 +-
 compiler/symtable.pas              |   2 +-
 compiler/utils/ppuutils/ppudump.pp |   3 +-
 tests/test/tgenconst1.pp           |  33 ++
 tests/test/tgenconst10.pp          |  13 +
 tests/test/tgenconst11.pp          |  21 +
 tests/test/tgenconst12.pp          |  16 +
 tests/test/tgenconst13.pp          |  20 +
 tests/test/tgenconst14.pp          |  29 ++
 tests/test/tgenconst15.pp          |  30 ++
 tests/test/tgenconst16.pp          |  86 ++++
 tests/test/tgenconst17.pp          |  36 ++
 tests/test/tgenconst18.pp          |  12 +
 tests/test/tgenconst2.pp           |  12 +
 tests/test/tgenconst3.pp           |  16 +
 tests/test/tgenconst4.pp           |  11 +
 tests/test/tgenconst5.pp           |  24 +
 tests/test/tgenconst6.pp           |  21 +
 tests/test/tgenconst7.pp           |  11 +
 tests/test/tgenconst8.pp           |  11 +
 tests/test/tgenconst9.pp           |  11 +
 37 files changed, 1163 insertions(+), 327 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 tests/test/tgenconst1.pp
 create mode 100644 tests/test/tgenconst10.pp
 create mode 100644 tests/test/tgenconst11.pp
 create mode 100644 tests/test/tgenconst12.pp
 create mode 100644 tests/test/tgenconst13.pp
 create mode 100644 tests/test/tgenconst14.pp
 create mode 100644 tests/test/tgenconst15.pp
 create mode 100644 tests/test/tgenconst16.pp
 create mode 100644 tests/test/tgenconst17.pp
 create mode 100644 tests/test/tgenconst18.pp
 create mode 100644 tests/test/tgenconst2.pp
 create mode 100644 tests/test/tgenconst3.pp
 create mode 100644 tests/test/tgenconst4.pp
 create mode 100644 tests/test/tgenconst5.pp
 create mode 100644 tests/test/tgenconst6.pp
 create mode 100644 tests/test/tgenconst7.pp
 create mode 100644 tests/test/tgenconst8.pp
 create mode 100644 tests/test/tgenconst9.pp

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..a82c960cbe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# files
+.gitignore
+pp
+fpmake
+rtl/darwin/fpcmade.x86_64-darwin
+fpmake_proc1 copy.inc
+tests/*.x86_64-darwin
+rtl/Package.fpc
+tests/createlst
+tests/gparmake
+
+# directories
+lazbuild/
+x86_64-darwin/
+tests/tstunits/
+tests/utils
+
+# patterns
+*.app
+*.o
+*.ppu
+*.fpm
+*.rsj
+*.lst
\ No newline at end of file
diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 3f5882f762..793dbbbe76 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -175,7 +175,6 @@ implementation
       symtable,symsym,symcpu,
       defutil,symutil;
 
-
     function compare_defs_ext(def_from,def_to : tdef;
                               fromtreetype : tnodetype;
                               var doconv : tconverttype;
@@ -337,9 +336,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 07c035dc26..bd51cebdf3 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -152,22 +152,22 @@ interface
     function token2managementoperator(optoken:ttoken):tmanagementoperator;
 
     { check operator args and result type }
-
-    type
-      toverload_check_flag = (
-        ocf_check_non_overloadable, { also check operators that are (currently) considered as
-                                      not overloadable (e.g. the "+" operator for dynamic arrays
-                                      if modeswitch arrayoperators is active) }
-        ocf_check_only              { only check whether the operator is overloaded, but don't
-                                      modify the passed in node (return true if the operator is
-                                      overloaded, false otherwise) }
-      );
-      toverload_check_flags = set of toverload_check_flag;
-
+
+    type
+      toverload_check_flag = (
+        ocf_check_non_overloadable, { also check operators that are (currently) considered as
+                                      not overloadable (e.g. the "+" operator for dynamic arrays
+                                      if modeswitch arrayoperators is active) }
+        ocf_check_only              { only check whether the operator is overloaded, but don't
+                                      modify the passed in node (return true if the operator is
+                                      overloaded, false otherwise) }
+      );
+      toverload_check_flags = set of toverload_check_flag;
+
     function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
     function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
-    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
-    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
 
     { Register Allocation }
     procedure make_not_regable(p : tnode; how: tregableinfoflags);
@@ -515,9 +515,9 @@ implementation
                     end;
 
                  { <dyn. array> + <dyn. array> is handled by the compiler }
-                 if (m_array_operators in current_settings.modeswitches) and
-                     (treetyp=addn) and
-                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
+                 if (m_array_operators in current_settings.modeswitches) and
+                     (treetyp=addn) and
+                     (is_dynamic_array(ld) or is_dynamic_array(rd)) then
                     begin
                       allowed:=false;
                       exit;
@@ -720,7 +720,7 @@ implementation
       end;
 
 
-    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isunaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
       var
         ld      : tdef;
         optoken : ttoken;
@@ -742,11 +742,11 @@ implementation
         else
           inlinenumber:=in_none;
 
-        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
+        if not (ocf_check_non_overloadable in ocf) and not isunaryoperatoroverloadable(t.nodetype,inlinenumber,ld) then
           exit;
 
         { operator overload is possible }
-        result:=not (ocf_check_only in ocf);
+        result:=not (ocf_check_only in ocf);
 
         optoken:=NOTOKEN;
         case t.nodetype of
@@ -766,11 +766,11 @@ implementation
         end;
         if (optoken=NOTOKEN) then
           begin
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage(parser_e_operator_not_overloaded);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage(parser_e_operator_not_overloaded);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
@@ -790,11 +790,11 @@ implementation
           begin
             candidates.free;
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
@@ -811,16 +811,16 @@ implementation
           begin
             candidates.free;
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              begin
-                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
-                t:=cnothingnode.create;
-              end;
+            if not (ocf_check_only in ocf) then
+              begin
+                CGMessage2(parser_e_operator_not_overloaded_2,ld.typename,arraytokeninfo[optoken].str);
+                t:=cnothingnode.create;
+              end;
             exit;
           end;
 
         { Multiple candidates left? }
-        if (cand_cnt>1) and not (ocf_check_only in ocf) then
+        if (cand_cnt>1) and not (ocf_check_only in ocf) then
           begin
             CGMessage(type_e_cant_choose_overload_function);
 {$ifdef EXTDEBUG}
@@ -833,13 +833,13 @@ implementation
           end;
         candidates.free;
 
-        if ocf_check_only in ocf then
-          begin
-            ppn.free;
-            result:=true;
-            exit;
-          end;
-
+        if ocf_check_only in ocf then
+          begin
+            ppn.free;
+            result:=true;
+            exit;
+          end;
+
         addsymref(operpd.procsym);
 
         { the nil as symtable signs firstcalln that this is
@@ -852,7 +852,7 @@ implementation
       end;
 
 
-    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
+    function isbinaryoverloaded(var t : tnode;ocf:toverload_check_flags) : boolean;
       var
         rd,ld   : tdef;
         optoken : ttoken;
@@ -945,14 +945,14 @@ implementation
         { load easier access variables }
         ld:=tbinarynode(t).left.resultdef;
         rd:=tbinarynode(t).right.resultdef;
-        if not (ocf_check_non_overloadable in ocf) and
-            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
+        if not (ocf_check_non_overloadable in ocf) and
+            not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
           exit;
 
         { operator overload is possible }
-        { if we only check for the existance of the overload, then we assume that
-          it is not overloaded }
-        result:=not (ocf_check_only in ocf);
+        { if we only check for the existance of the overload, then we assume that
+          it is not overloaded }
+        result:=not (ocf_check_only in ocf);
 
         case t.nodetype of
            equaln:
@@ -997,19 +997,19 @@ implementation
              optoken:=_OP_IN;
            else
              begin
-               if not (ocf_check_only in ocf) then
-                 begin
-                   CGMessage(parser_e_operator_not_overloaded);
-                   t:=cnothingnode.create;
-                 end;
+               if not (ocf_check_only in ocf) then
+                 begin
+                   CGMessage(parser_e_operator_not_overloaded);
+                   t:=cnothingnode.create;
+                 end;
                exit;
              end;
         end;
 
-        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
+        cand_cnt:=search_operator(optoken,(optoken<>_NE) and not (ocf_check_only in ocf));
 
         { no operator found for "<>" then search for "=" operator }
-        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
+        if (cand_cnt=0) and (optoken=_NE) and not (ocf_check_only in ocf) then
           begin
             ppn.free;
             ppn:=nil;
@@ -1021,15 +1021,15 @@ implementation
         if (cand_cnt=0) then
           begin
             ppn.free;
-            if not (ocf_check_only in ocf) then
-              t:=cnothingnode.create;
-            exit;
-          end;
-
-        if ocf_check_only in ocf then
-          begin
-            ppn.free;
-            result:=true;
+            if not (ocf_check_only in ocf) then
+              t:=cnothingnode.create;
+            exit;
+          end;
+
+        if ocf_check_only in ocf then
+          begin
+            ppn.free;
+            result:=true;
             exit;
           end;
 
@@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
index ae94637c28..1e203f74d6 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -279,6 +279,7 @@ implementation
         p1  : tnode;
         len : longint;
         pc  : pchar;
+        value_set : pconstset;
       begin
         p1:=nil;
         case p.consttyp of
@@ -304,18 +305,51 @@ 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 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_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;
+          { constundefined is a placeholder for unrestricted generic const params
+            so we just treat it as a nil node. }
+          constundefined :
+            begin
+              p1:=cnilnode.create;
+              p1.resultdef:=p.constdef;
+            end;
           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 355b493da4..d10dff6128 100644
--- a/compiler/nmat.pas
+++ b/compiler/nmat.pas
@@ -129,7 +129,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 b8600000bf..33a85b1493 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -194,7 +194,8 @@ interface
           'loadparentfpn',
           'objcselectorn',
           'objcprotocoln',
-          'specializen');
+          'specializen'
+          );
 
       { a set containing all const nodes }
       nodetype_const = [ordconstn,
@@ -272,10 +273,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
          }
@@ -983,6 +987,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;
 
@@ -1078,7 +1085,12 @@ implementation
     constructor tbinarynode.create(t:tnodetype;l,r : tnode);
       begin
          inherited create(t,l);
-         right:=r
+         { transfer generic paramater flag }
+         if assigned(l) and (nf_generic_para in l.flags) then
+           include(flags,nf_generic_para)
+         else if assigned(r) and (nf_generic_para in r.flags) then
+           include(flags,nf_generic_para);
+         right:=r;
       end;
 
 
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 6270ec582e..bd031e6a86 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -239,7 +239,7 @@ implementation
            internalerror(20021126);
 
          t:=self;
-         if isbinaryoverloaded(t,[]) then
+         if isbinaryoverloaded(t,[]) then
            begin
              result:=t;
              exit;
@@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
index c5b5bcc921..d7e80b928f 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -126,9 +126,14 @@ implementation
              end;
            setconstn :
              begin
-               new(ps);
-               ps^:=tsetconstnode(p).value_set^;
-               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+               if nf_generic_para in p.flags then
+                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
+               else
+                 begin
+                   new(ps);
+                   ps^:=tsetconstnode(p).value_set^;
+                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+                 end;
              end;
            pointerconstn :
              begin
@@ -141,18 +146,18 @@ implementation
            typen :
              begin
                if is_interface(p.resultdef) then
-                begin
-                  if assigned(tobjectdef(p.resultdef).iidguid) then
-                   begin
-                     new(pg);
-                     pg^:=tobjectdef(p.resultdef).iidguid^;
-                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
-                   end
-                  else
-                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
-                end
-               else
-                Message(parser_e_illegal_expression);
+                 begin
+                   if assigned(tobjectdef(p.resultdef).iidguid) then
+                     begin
+                       new(pg);
+                       pg^:=tobjectdef(p.resultdef).iidguid^;
+                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
+                     end
+                    else
+                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
+                 end
+               else 
+                 Message(parser_e_illegal_expression);
              end;
            inlinen:
              begin
@@ -177,8 +182,19 @@ 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
+          include(hp.symoptions,sp_generic_para);
         current_tokenpos:=storetokenpos;
         p.free;
         readconstant:=hp;
@@ -507,8 +523,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/pdecvar.pas b/compiler/pdecvar.pas
index 4d39397e46..8121d87853 100644
--- a/compiler/pdecvar.pas
+++ b/compiler/pdecvar.pas
@@ -1675,6 +1675,10 @@ implementation
                    end;
                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 bc0606ed4b..e6d9633ebd 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -446,6 +446,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,sinttype,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
@@ -466,6 +469,9 @@ implementation
                    end
                  else
                    statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
@@ -4078,7 +4084,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
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..85270df256 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -28,7 +28,7 @@ interface
 uses
   cclasses,
   globtype,
-  symtype,symbase;
+  symconst,symtype,symbase;
 
 const
   inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
@@ -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 7760a4e134..ac6e59ce98 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;
@@ -63,18 +63,163 @@ implementation
 
 uses
   { common }
-  cutils,fpccrc,
+  sysutils,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;
 
+  type
+    tdeftypeset = set of tdeftyp;
+  const
+    tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
+    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 is_generic_param_const(sym:tsym):boolean;
+      begin
+        if sym.typ=constsym then
+          result:=tconstsym(sym).consttyp<>constundefined
+        else
+          result:=false;
+      end;
+
+    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
+      begin
+        if (value.len<param2.low) or (value.len>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
+          begin
+            sym:=cconstsym.create_undefined(undefinedname,fromdef);
+            sym.owner:=fromdef.owner;
+            prettyname:='';
+            result:=sym;
+            exit;
+          end;
+        case node.nodetype of
+          ordconstn:
+            begin
+              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
+              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
+        sym.owner:=fromdef.owner;
+        result:=sym;
+      end;
 
     procedure maybe_add_waiting_unit(tt:tdef);
       var
@@ -104,203 +249,232 @@ 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;
+        //paratype : tconsttyp;
+        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:=is_generic_param_const(tsym(paramlist[i]));
+            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
-                                intffound:=assigned(
-                                             find_implemented_interface(objdef,
-                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
-                                             )
-                                           );
+                                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
+                              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 +484,12 @@ uses
         namepart : string;
         prettynamepart : ansistring;
         module : tmodule;
+        //paramdef : tgenericparamdef;
+        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 +501,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);
@@ -351,7 +528,9 @@ uses
             block_type:=bt_type;
             tmpparampos:=current_filepos;
             typeparam:=factor(false,[ef_type_only]);
-            if typeparam.nodetype=typen then
+            { 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 +546,47 @@ 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
+                    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
-                          begin
-                            { special handling for specializations inside generic function declarations }
-                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
-                          end
-                        else
+                        if typeparam.nodetype = typen then
                           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 +606,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;
 
 
@@ -578,7 +773,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
@@ -627,7 +822,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);
@@ -656,7 +851,7 @@ uses
             result:=generrordef;
             exit;
           end;
-
+        
         { we've found the correct def }
         if context.sym.typ=typesym then
           result:=tstoreddef(ttypesym(context.sym).typedef)
@@ -747,6 +942,7 @@ uses
         hintsprocessed : boolean;
         pd : tprocdef;
         pdflags : tpdflags;
+        typedef : tstoreddef;
       begin
         if not assigned(context) then
           internalerror(2015052203);
@@ -755,7 +951,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 }
@@ -771,20 +967,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 }
@@ -1196,8 +1391,8 @@ 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;
@@ -1205,22 +1400,87 @@ uses
         doconsume : boolean;
         constraintdata : tgenericconstraintdata;
         old_block_type : tblock_type;
+        is_const,last_is_const : boolean;
+        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;
+        is_const:=false;
+        last_is_const:=false;
+        last_token:=NOTOKEN;
         repeat
+          if try_to_consume(_CONST) then
+            begin
+              { last param was const without semicolon terminator }
+              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+              is_const := true;
+              const_list_index := result.count;
+            end;
           if token=_ID then
             begin
-              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
+              if is_const then
+                begin
+                  { last param was type without semicolon terminator }
+                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
+                end
+              else
+                begin
+                  { last param was const without semicolon terminator }
+                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
+                end;
               { type parameters need to be added as strict private }
               generictype.visibility:=vis_strictprivate;
               include(generictype.symoptions,sp_generic_para);
               result.add(orgpattern,generictype);
+              last_is_const:=is_const;
             end;
           consume(_ID);
-          if try_to_consume(_COLON) then
+          { const restriction }
+          if is_const then
+            begin
+              if 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:
+                              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;
+                          end;
+                          tconstsym(result[i]).constdef:=def;
+                        end
+                      else
+                        Message(type_e_mismatch);
+                    end;
+                  { after type restriction const list terminates }
+                  is_const:=false;
+                end;
+            end
+          { type restriction }
+          else if try_to_consume(_COLON) then
             begin
               if not allowconstraints then
                 { TODO }
@@ -1335,6 +1595,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
@@ -1360,21 +1621,27 @@ 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
+            is_const:=false;
+          last_token:=token;
+          last_type_pos:=current_filepos;
         until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
         { 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;
 
@@ -1382,7 +1649,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;
@@ -1407,10 +1676,22 @@ 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,true);
+                if generictype.typ=typesym then
+                  sym:=ctypesym.create(genericlist.nameofindex(i),ttypesym(generictype).typedef,true)
+                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);
+                  end
+                else
+                  internalerror(2019021602);
                 { type parameters need to be added as strict private }
                 sym.visibility:=vis_strictprivate;
                 st.insert(sym);
@@ -1418,13 +1699,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/ppu.pas b/compiler/ppu.pas
index 10c42e7eb8..31011be3e8 100644
--- a/compiler/ppu.pas
+++ b/compiler/ppu.pas
@@ -43,7 +43,7 @@ type
 {$endif Test_Double_checksum}
 
 const
-  CurrentPPUVersion = 201;
+  CurrentPPUVersion = 203;
 
 { unit flags }
   uf_init                = $000001; { unit has initialization section }
diff --git a/compiler/ptype.pas b/compiler/ptype.pas
index 38e2526e9f..28cd0f94f8 100644
--- a/compiler/ptype.pas
+++ b/compiler/ptype.pas
@@ -1436,7 +1436,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
diff --git a/compiler/symconst.pas b/compiler/symconst.pas
index a5ae7e0fb9..e02ce3a8ca 100644
--- a/compiler/symconst.pas
+++ b/compiler/symconst.pas
@@ -232,7 +232,10 @@ type
       because we have to access this information in the symtable unit }
     df_llvm_no_struct_packing,
     { internal def that's not for any export }
-    df_internal
+    df_internal,
+    { 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;
 
@@ -651,7 +654,7 @@ type
     arraydef,recorddef,pointerdef,orddef,
     stringdef,enumdef,procdef,objectdef,errordef,
     filedef,formaldef,setdef,procvardef,floatdef,
-    classrefdef,forwarddef,variantdef,undefineddef
+    classrefdef,forwarddef,variantdef,genericconstdef,undefineddef
   );
 
   { possible types for symtable entries }
@@ -692,7 +695,8 @@ type
   tconsttyp = (constnone,
     constord,conststring,constreal,
     constset,constpointer,constnil,
-    constresourcestring,constwstring,constguid
+    constresourcestring,constwstring,constguid,
+    constundefined
   );
 
   { RTTI information to store }
@@ -831,7 +835,7 @@ inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has
        'abstractdef','arraydef','recorddef','pointerdef','orddef',
        'stringdef','enumdef','procdef','objectdef','errordef',
        'filedef','formaldef','setdef','procvardef','floatdef',
-       'classrefdef','forwarddef','variantdef','undefineddef'
+       'classrefdef','forwarddef','variantdef','genconstdef','undefineddef'
      );
 
      EqualTypeName : array[tequaltype] of string[16] = (
diff --git a/compiler/symdef.pas b/compiler/symdef.pas
index 4a260c46b9..0f7a2e4c06 100644
--- a/compiler/symdef.pas
+++ b/compiler/symdef.pas
@@ -129,6 +129,9 @@ interface
           function is_generic:boolean;inline;
           { same as above for specializations }
           function is_specialization:boolean;inline;
+          { 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
@@ -2197,13 +2200,26 @@ 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);
            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
@@ -2220,12 +2236,12 @@ 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);
              end;
-           result:=false;
          end;
      end;
 
diff --git a/compiler/symsym.pas b/compiler/symsym.pas
index b21a5f9de9..04c07a5ec7 100644
--- a/compiler/symsym.pas
+++ b/compiler/symsym.pas
@@ -157,7 +157,7 @@ interface
           fprettyname : ansistring;
           constructor create(const n : string;def:tdef;doregister:boolean);virtual;
           destructor destroy;override;
-          constructor ppuload(ppufile:tcompilerppufile);
+          constructor ppuload(ppufile:tcompilerppufile);virtual;
           { do not override this routine in platform-specific subclasses,
             override ppuwrite_platform instead }
           procedure ppuwrite(ppufile:tcompilerppufile);override;final;
@@ -392,6 +392,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);
           constructor ppuload(ppufile:tcompilerppufile);
           destructor  destroy;override;
           procedure buildderef;override;
@@ -1581,7 +1582,6 @@ implementation
           tparasymtable(parast).ppuwrite(ppufile);
       end;
 
-
 {****************************************************************************
                             TABSTRACTVARSYM
 ****************************************************************************}
@@ -2344,6 +2344,13 @@ implementation
          value.len:=getlengthwidestring(pw);
       end;
 
+    constructor tconstsym.create_undefined(const n : string;def: tdef);
+      begin
+        inherited create(constsym,n,true);
+        fillchar(value, sizeof(value), #0);
+        consttyp:=constundefined;
+        constdef:=def;
+      end;
 
     constructor tconstsym.ppuload(ppufile:tcompilerppufile);
       var
@@ -2416,7 +2423,8 @@ implementation
                new(pguid(value.valueptr));
                ppufile.getdata(value.valueptr^,sizeof(tguid));
              end;
-           constnil :
+           constnil,
+           constundefined :
              ppufile.getderef(constdefderef);
            else
              Message1(unit_f_ppu_invalid_entry,tostr(ord(consttyp)));
@@ -2448,7 +2456,7 @@ implementation
       begin
         inherited;
         case consttyp  of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdefderef.build(constdef);
           constwstring:
             ;
@@ -2461,7 +2469,7 @@ implementation
     procedure tconstsym.deref;
       begin
         case consttyp of
-          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid:
+          constnil,constord,constreal,constpointer,constset,conststring,constresourcestring,constguid,constundefined:
             constdef:=tdef(constdefderef.resolve);
           constwstring:
             constdef:=carraydef.getreusable(cwidechartype,getlengthwidestring(pcompilerwidestring(value.valueptr)));
@@ -2476,7 +2484,8 @@ implementation
          inherited ppuwrite(ppufile);
          ppufile.putbyte(byte(consttyp));
          case consttyp of
-           constnil :
+           constnil,
+           constundefined :
              ppufile.putderef(constdefderef);
            constord :
              begin
@@ -2627,7 +2636,6 @@ implementation
           result:=inherited prettyname;
       end;
 
-
 {****************************************************************************
                                   TSYSSYM
 ****************************************************************************}
diff --git a/compiler/symtable.pas b/compiler/symtable.pas
index 796b2d6736..ae82024b03 100644
--- a/compiler/symtable.pas
+++ b/compiler/symtable.pas
@@ -2781,7 +2781,7 @@ implementation
 
     function generate_objectpascal_helper_key(def:tdef):string;
       begin
-        if not assigned(def) then
+        if not assigned(def) or (def.typ = errordef) then
           internalerror(2013020501);
         if def.typ in [recorddef,objectdef] then
           result:=make_mangledname('',tabstractrecorddef(def).symtable,'')
diff --git a/compiler/utils/ppuutils/ppudump.pp b/compiler/utils/ppuutils/ppudump.pp
index 74fde5c6c2..80d9d4df11 100644
--- a/compiler/utils/ppuutils/ppudump.pp
+++ b/compiler/utils/ppuutils/ppudump.pp
@@ -1552,7 +1552,8 @@ const
      { this should never happen for defs stored to a ppu file }
      (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_internal;       str:'Internal'),
+     (mask:df_has_generic_fields; str:'Has generic fields')
   );
   defstate : array[1..ord(high(tdefstate))] of tdefstateinfo=(
      (mask:ds_vmt_written;           str:'VMT Written'),
diff --git a/tests/test/tgenconst1.pp b/tests/test/tgenconst1.pp
new file mode 100644
index 0000000000..297b982b0f
--- /dev/null
+++ b/tests/test/tgenconst1.pp
@@ -0,0 +1,33 @@
+{$mode objfpc}
+program tgenconst1;
+
+type
+	kNames = set of (Blaise,Pascal);
+	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 TUndefined<const U> = 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 TUndefined<nil>;
+	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..f05a27718c
--- /dev/null
+++ b/tests/test/tgenconst10.pp
@@ -0,0 +1,13 @@
+{%FAIL}
+
+{$mode objfpc}
+
+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..ea409bec9b
--- /dev/null
+++ b/tests/test/tgenconst11.pp
@@ -0,0 +1,21 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst11;
+type
+	TEnum = (aaa,bbb,ccc,ddd);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<20>.Create;
+	b:=specialize TConst<10.1>.Create;
+	c:=specialize TConst<'_string'>.Create;
+	d:=specialize TConst<[1,2,3,4]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc,ddd]>.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..8f591f6867
--- /dev/null
+++ b/tests/test/tgenconst12.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+program tgenconst12;
+
+type
+  generic TTest<const U> = class
+  		class procedure DoThis;
+  end;
+
+class procedure TTest.DoThis;
+begin
+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..0d5f8b1813
--- /dev/null
+++ b/tests/test/tgenconst13.pp
@@ -0,0 +1,20 @@
+{$mode objfpc}
+program tgenconst13;
+type
+	TEnum = (aaa,bbb,ccc);
+type
+	generic TConst<const U> = class end;
+
+var
+	a:specialize TConst<10>;
+	b:specialize TConst<10.5>;
+	c:specialize TConst<'string'>;
+	d:specialize TConst<[1,2,3]>;
+	e:specialize TConst<[aaa,bbb,ccc]>;
+begin
+	a:=specialize TConst<10>.Create;
+	b:=specialize TConst<10.5>.Create;
+	c:=specialize TConst<'string'>.Create;
+	d:=specialize TConst<[1,2,3]>.Create;
+	e:=specialize TConst<[aaa,bbb,ccc]>.Create;
+end.
diff --git a/tests/test/tgenconst14.pp b/tests/test/tgenconst14.pp
new file mode 100644
index 0000000000..7f98086630
--- /dev/null
+++ b/tests/test/tgenconst14.pp
@@ -0,0 +1,29 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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;
+  end;
+
+var
+	op: specialize TBinaryOp<100>;
+begin
+	writeln(op.d0);
+	writeln(op.d1);
+	writeln(op.d2);
+	writeln(op.d3:1:1);
+	writeln(op.d4);
+	writeln(op.d5);
+	writeln(op.d6);
+	writeln(op.d7);
+end.
\ No newline at end of file
diff --git a/tests/test/tgenconst15.pp b/tests/test/tgenconst15.pp
new file mode 100644
index 0000000000..56744cd0a7
--- /dev/null
+++ b/tests/test/tgenconst15.pp
@@ -0,0 +1,30 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+program tgenconst15;
+
+type
+	kNames = set of (Blaise, Pascal);
+  generic TSet<const I: kNames> = record
+    const c = I;
+  end;
+  generic TString<const I: String> = record
+    const c = I;
+  end;
+  generic TWideString<const I: WideString> = record
+    const c = I;
+  end;
+  generic TSingle<const I: Single> = record
+    const c = I; 
+  end;
+  generic TDouble<const I: Double> = record
+    const c = I; 
+  end;
+  generic TReal<const I: Real> = record
+    const c = I; 
+  end;
+
+var
+	a0: specialize TReal<100>;
+begin
+	writeln(a0.c);
+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..275867ce25
--- /dev/null
+++ b/tests/test/tgenconst16.pp
@@ -0,0 +1,86 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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> = 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> = record
+    const
+      d0 = -I;
+      d1 = +I;
+      d2 = not I;
+  end;
+  generic TBinaryOp<const I> = 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> = record
+    procedure DoThis(param: integer = I);
+  end;
+
+procedure TOther.DoThis(param: integer = I);
+begin
+  writeln(param, ' default:', I);
+end;
+
+var
+  t0: specialize TBinaryOp<100>;
+  t1: specialize TOther<100>;
+begin
+  //writeln(op.d0);
+  //writeln(op.d1);
+  //writeln(op.d2);
+  //writeln(op.d3:1:1);
+  //writeln(op.d4);
+  //writeln(op.d5);
+  //writeln(op.d6);
+  //writeln(op.d7);
+end.
\ No newline at end of file
diff --git a/tests/test/tgenconst17.pp b/tests/test/tgenconst17.pp
new file mode 100644
index 0000000000..26dc2ee21f
--- /dev/null
+++ b/tests/test/tgenconst17.pp
@@ -0,0 +1,36 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+program tgenconst17;
+
+type
+  generic TUnaryOp<const I: integer> = record
+    const
+      d0 = -I;
+      d1 = +I;
+      d2 = not I;
+  end;
+  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 xor I;
+      d9 = I shl I;
+      d10 = I shr I;
+      d11 = I << I;
+      d12 = I >> I;
+      d13 = I <> I;
+      d14 = I < I;
+      d15 = I > I;
+      d16 = I <= I;
+      d17 = I >= I;
+      d18 = I = I;
+  end;
+
+begin
+end.
\ No newline at end of file
diff --git a/tests/test/tgenconst18.pp b/tests/test/tgenconst18.pp
new file mode 100644
index 0000000000..a4ba526803
--- /dev/null
+++ b/tests/test/tgenconst18.pp
@@ -0,0 +1,12 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedrecords}
+program tgenconst18;
+
+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/tgenconst2.pp b/tests/test/tgenconst2.pp
new file mode 100644
index 0000000000..aa3a960634
--- /dev/null
+++ b/tests/test/tgenconst2.pp
@@ -0,0 +1,12 @@
+{$mode objfpc}
+program tgenconst2;
+
+type
+	generic TStuff1<T1,T2;const U1,U2> = record end;
+	generic TStuff2<T1,T2;const U1,U2:integer> = record end;
+	
+var
+	a: specialize TStuff1<integer,string,10,'string'>;
+	b: specialize TStuff2<integer,string,10,10>;
+begin
+end.
diff --git a/tests/test/tgenconst3.pp b/tests/test/tgenconst3.pp
new file mode 100644
index 0000000000..aea0e307e2
--- /dev/null
+++ b/tests/test/tgenconst3.pp
@@ -0,0 +1,16 @@
+{$mode objfpc}
+{$modeswitch advancedrecords}
+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/tgenconst4.pp b/tests/test/tgenconst4.pp
new file mode 100644
index 0000000000..a1fae00c43
--- /dev/null
+++ b/tests/test/tgenconst4.pp
@@ -0,0 +1,11 @@
+{$mode objfpc}
+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..63514a976c
--- /dev/null
+++ b/tests/test/tgenconst5.pp
@@ -0,0 +1,24 @@
+{$mode objfpc}
+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..3ee3785423
--- /dev/null
+++ b/tests/test/tgenconst6.pp
@@ -0,0 +1,21 @@
+{$mode delphi}
+program tgenconst6;
+
+type
+	TList<T;const U> = 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..9d8e81ef05
--- /dev/null
+++ b/tests/test/tgenconst7.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..75844f7181
--- /dev/null
+++ b/tests/test/tgenconst8.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+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..939cb90302
--- /dev/null
+++ b/tests/test/tgenconst9.pp
@@ -0,0 +1,11 @@
+{%FAIL}
+{$mode objfpc}
+program tgenconst9;
+
+type
+	generic TByte<const U: Byte> = record end;
+	
+var
+	a: specialize TByte<string>;
+begin
+end.
-- 
2.17.2 (Apple Git-113)

gen-const-3-23.diff (96,712 bytes)

Ryan Joseph

2019-03-23 14:05

reporter  

bad-line-endings.png (89,530 bytes)
bad-line-endings.png (89,530 bytes)

Ryan Joseph

2019-03-23 14:15

reporter   ~0114987

I didn't get any feedback on the new bugs but I believe I fixed all of them and added some new tests which stress all the constant variations.

This time I didn't remove the no-op lines in the patch because I believe the old line endings may be incorrect actually. Look at screenshot I sent from "Sublime Merge". It shows there's something different about the old line endings and I haven't seen them anywhere else so I think it may be good to replace them actually.

Sven Barth

2019-03-24 17:13

manager   ~0115027

It doesn't matter whether they are incorrect or not. Your issue has nothing to do with incorrect line endings in unrelated code. If they annoy you, then report them in a separate issue. Here they only make reviewing the real changes harder.

Akira1364

2019-03-24 20:17

reporter   ~0115029

Sorry about that, I've been busy for the past few days. I'll update my local copy again and give it a test right now.

Akira1364

2019-03-25 14:26

reporter   ~0115039

Ok, I'm back. Seems good to me! I tried really hard to break it / get it to crash, but it seems to handle everything properly now.

Ryan Joseph

2019-03-25 15:42

reporter  

patch_3_25.diff (87,689 bytes)
From c80227ed7597385d240aa7bdc1556798aa15ec20 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Tue, 6 Nov 2018 13:58:49 +0700
Subject: [PATCH] constants in generics

---
 .gitignore                         |  23 +
 compiler/defcmp.pas                |   9 +-
 compiler/htypechk.pas              |   2 +-
 compiler/ncon.pas                  |  42 +-
 compiler/nmat.pas                  |   5 +-
 compiler/node.pas                  |  22 +-
 compiler/nset.pas                  |   7 +-
 compiler/pdecl.pas                 |  53 ++-
 compiler/pdecvar.pas               |   4 +
 compiler/pexpr.pas                 |  11 +-
 compiler/pgentype.pas              |   8 +-
 compiler/pgenutil.pas              | 693 ++++++++++++++++++++---------
 compiler/ppu.pas                   |   2 +-
 compiler/ptype.pas                 |   4 +-
 compiler/symconst.pas              |  12 +-
 compiler/symdef.pas                |  22 +-
 compiler/symsym.pas                |  22 +-
 compiler/symtable.pas              |   2 +-
 compiler/utils/ppuutils/ppudump.pp |   3 +-
 tests/test/tgenconst1.pp           |  33 ++
 tests/test/tgenconst10.pp          |  13 +
 tests/test/tgenconst11.pp          |  21 +
 tests/test/tgenconst12.pp          |  16 +
 tests/test/tgenconst13.pp          |  20 +
 tests/test/tgenconst14.pp          |  29 ++
 tests/test/tgenconst15.pp          |  30 ++
 tests/test/tgenconst16.pp          |  86 ++++
 tests/test/tgenconst17.pp          |  36 ++
 tests/test/tgenconst18.pp          |  12 +
 tests/test/tgenconst2.pp           |  12 +
 tests/test/tgenconst3.pp           |  16 +
 tests/test/tgenconst4.pp           |  11 +
 tests/test/tgenconst5.pp           |  24 +
 tests/test/tgenconst6.pp           |  21 +
 tests/test/tgenconst7.pp           |  11 +
 tests/test/tgenconst8.pp           |  11 +
 tests/test/tgenconst9.pp           |  11 +
 37 files changed, 1097 insertions(+), 262 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 tests/test/tgenconst1.pp
 create mode 100644 tests/test/tgenconst10.pp
 create mode 100644 tests/test/tgenconst11.pp
 create mode 100644 tests/test/tgenconst12.pp
 create mode 100644 tests/test/tgenconst13.pp
 create mode 100644 tests/test/tgenconst14.pp
 create mode 100644 tests/test/tgenconst15.pp
 create mode 100644 tests/test/tgenconst16.pp
 create mode 100644 tests/test/tgenconst17.pp
 create mode 100644 tests/test/tgenconst18.pp
 create mode 100644 tests/test/tgenconst2.pp
 create mode 100644 tests/test/tgenconst3.pp
 create mode 100644 tests/test/tgenconst4.pp
 create mode 100644 tests/test/tgenconst5.pp
 create mode 100644 tests/test/tgenconst6.pp
 create mode 100644 tests/test/tgenconst7.pp
 create mode 100644 tests/test/tgenconst8.pp
 create mode 100644 tests/test/tgenconst9.pp

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..64fdb156d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+# files
+pp
+fpmake
+rtl/darwin/fpcmade.x86_64-darwin
+fpmake_proc1 copy.inc
+tests/*.x86_64-darwin
+rtl/Package.fpc
+tests/createlst
+tests/gparmake
+
+# directories
+lazbuild/
+x86_64-darwin/
+tests/tstunits/
+tests/utils
+
+# patterns
+*.app
+*.o
+*.ppu
+*.fpm
+*.rsj
+*.lst
\ No newline at end of file
diff --git a/compiler/defcmp.pas b/compiler/defcmp.pas
index 3f5882f762..793dbbbe76 100644
--- a/compiler/defcmp.pas
+++ b/compiler/defcmp.pas
@@ -175,7 +175,6 @@ implementation
       symtable,symsym,symcpu,
       defutil,symutil;
 
-
     function compare_defs_ext(def_from,def_to : tdef;
                               fromtreetype : tnodetype;
                               var doconv : tconverttype;
@@ -337,9 +336,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 07c035dc26..2358ea4b6d 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2697,7 +2697,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/ncon.pas b/compiler/ncon.pas
index ae94637c28..1e203f74d6 100644
--- a/compiler/ncon.pas
+++ b/compiler/ncon.pas
@@ -279,6 +279,7 @@ implementation
         p1  : tnode;
         len : longint;
         pc  : pchar;
+        value_set : pconstset;
       begin
         p1:=nil;
         case p.consttyp of
@@ -304,18 +305,51 @@ 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 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_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;
+          { constundefined is a placeholder for unrestricted generic const params
+            so we just treat it as a nil node. }
+          constundefined :
+            begin
+              p1:=cnilnode.create;
+              p1.resultdef:=p.constdef;
+            end;
           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 355b493da4..d10dff6128 100644
--- a/compiler/nmat.pas
+++ b/compiler/nmat.pas
@@ -129,7 +129,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 b8600000bf..33a85b1493 100644
--- a/compiler/node.pas
+++ b/compiler/node.pas
@@ -194,7 +194,8 @@ interface
           'loadparentfpn',
           'objcselectorn',
           'objcprotocoln',
-          'specializen');
+          'specializen'
+          );
 
       { a set containing all const nodes }
       nodetype_const = [ordconstn,
@@ -272,10 +273,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
          }
@@ -983,6 +987,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;
 
@@ -1078,7 +1085,12 @@ implementation
     constructor tbinarynode.create(t:tnodetype;l,r : tnode);
       begin
          inherited create(t,l);
-         right:=r
+         { transfer generic paramater flag }
+         if assigned(l) and (nf_generic_para in l.flags) then
+           include(flags,nf_generic_para)
+         else if assigned(r) and (nf_generic_para in r.flags) then
+           include(flags,nf_generic_para);
+         right:=r;
       end;
 
 
diff --git a/compiler/nset.pas b/compiler/nset.pas
index 6270ec582e..bd031e6a86 100644
--- a/compiler/nset.pas
+++ b/compiler/nset.pas
@@ -239,7 +239,7 @@ implementation
            internalerror(20021126);
 
          t:=self;
-         if isbinaryoverloaded(t,[]) then
+         if isbinaryoverloaded(t,[]) then
            begin
              result:=t;
              exit;
@@ -392,8 +392,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/pdecl.pas b/compiler/pdecl.pas
index c5b5bcc921..d7e80b928f 100644
--- a/compiler/pdecl.pas
+++ b/compiler/pdecl.pas
@@ -126,9 +126,14 @@ implementation
              end;
            setconstn :
              begin
-               new(ps);
-               ps^:=tsetconstnode(p).value_set^;
-               hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+               if nf_generic_para in p.flags then
+                 hp:=cconstsym.create_ptr(orgname,constset,nil,p.resultdef)
+               else
+                 begin
+                   new(ps);
+                   ps^:=tsetconstnode(p).value_set^;
+                   hp:=cconstsym.create_ptr(orgname,constset,ps,p.resultdef);
+                 end;
              end;
            pointerconstn :
              begin
@@ -141,18 +146,18 @@ implementation
            typen :
              begin
                if is_interface(p.resultdef) then
-                begin
-                  if assigned(tobjectdef(p.resultdef).iidguid) then
-                   begin
-                     new(pg);
-                     pg^:=tobjectdef(p.resultdef).iidguid^;
-                     hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
-                   end
-                  else
-                   Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
-                end
-               else
-                Message(parser_e_illegal_expression);
+                 begin
+                   if assigned(tobjectdef(p.resultdef).iidguid) then
+                     begin
+                       new(pg);
+                       pg^:=tobjectdef(p.resultdef).iidguid^;
+                       hp:=cconstsym.create_ptr(orgname,constguid,pg,p.resultdef);
+                     end
+                    else
+                      Message1(parser_e_interface_has_no_guid,tobjectdef(p.resultdef).objrealname^);
+                 end
+               else 
+                 Message(parser_e_illegal_expression);
              end;
            inlinen:
              begin
@@ -177,8 +182,19 @@ 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
+          include(hp.symoptions,sp_generic_para);
         current_tokenpos:=storetokenpos;
         p.free;
         readconstant:=hp;
@@ -507,8 +523,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/pdecvar.pas b/compiler/pdecvar.pas
index 4d39397e46..8121d87853 100644
--- a/compiler/pdecvar.pas
+++ b/compiler/pdecvar.pas
@@ -1675,6 +1675,10 @@ implementation
                    end;
                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 bc0606ed4b..e6d9633ebd 100644
--- a/compiler/pexpr.pas
+++ b/compiler/pexpr.pas
@@ -446,6 +446,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,sinttype,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
@@ -466,6 +469,9 @@ implementation
                    end
                  else
                    statement_syssym:=cordconstnode.create(p1.resultdef.packedbitsize,sinttype,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;
@@ -4078,7 +4084,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
diff --git a/compiler/pgentype.pas b/compiler/pgentype.pas
index b2847c78f6..85270df256 100644
--- a/compiler/pgentype.pas
+++ b/compiler/pgentype.pas
@@ -28,7 +28,7 @@ interface
 uses
   cclasses,
   globtype,
-  symtype,symbase;
+  symconst,symtype,symbase;
 
 const
   inline_specialization_block_types = [bt_type,bt_var_type,bt_const_type,bt_body];
@@ -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 7760a4e134..ac6e59ce98 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;
@@ -63,18 +63,163 @@ implementation
 
 uses
   { common }
-  cutils,fpccrc,
+  sysutils,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;
 
+  type
+    tdeftypeset = set of tdeftyp;
+  const
+    tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,arraydef,floatdef,setdef,pointerdef,undefineddef];
+    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 is_generic_param_const(sym:tsym):boolean;
+      begin
+        if sym.typ=constsym then
+          result:=tconstsym(sym).consttyp<>constundefined
+        else
+          result:=false;
+      end;
+
+    function compare_orddef_by_range(param1,param2:torddef;value:tconstvalue):boolean;
+      begin
+        if (value.len<param2.low) or (value.len>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
+          begin
+            sym:=cconstsym.create_undefined(undefinedname,fromdef);
+            sym.owner:=fromdef.owner;
+            prettyname:='';
+            result:=sym;
+            exit;
+          end;
+        case node.nodetype of
+          ordconstn:
+            begin
+              sym:=cconstsym.create_ord(undefinedname,constord,tordconstnode(node).value,fromdef);
+              prettyname:=inttostr(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:=floattostr(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 us the typeparam owner }
+        sym.owner:=fromdef.owner;
+        result:=sym;
+      end;
 
     procedure maybe_add_waiting_unit(tt:tdef);
       var
@@ -104,203 +249,232 @@ 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;
+        //paratype : tconsttyp;
+        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:=is_generic_param_const(tsym(paramlist[i]));
+            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
-                                intffound:=assigned(
-                                             find_implemented_interface(objdef,
-                                               timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef
-                                             )
-                                           );
+                                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
+                              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 +484,12 @@ uses
         namepart : string;
         prettynamepart : ansistring;
         module : tmodule;
+        //paramdef : tgenericparamdef;
+        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 +501,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);
@@ -351,7 +528,9 @@ uses
             block_type:=bt_type;
             tmpparampos:=current_filepos;
             typeparam:=factor(false,[ef_type_only]);
-            if typeparam.nodetype=typen then
+            { 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 +546,47 @@ 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
+                    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
-                          begin
-                            { special handling for specializations inside generic function declarations }
-                            prettynamepart:=tdef(symtablestack.top.defowner).fullownerhierarchyname(true)+tprocdef(symtablestack.top.defowner).procsym.prettyname;
-                          end
-                        else
+                        if typeparam.nodetype = typen then
                           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 +606,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;
 
 
@@ -578,7 +773,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
@@ -627,7 +822,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);
@@ -656,7 +851,7 @@ uses
             result:=generrordef;
             exit;
           end;
-
+        
         { we've found the correct def }
         if context.sym.typ=typesym then
           result:=tstoreddef(ttypesym(context.sym).typedef)
@@ -747,6 +942,7 @@ uses
         hintsprocessed : boolean;
         pd : tprocdef;
         pdflags : tpdflags;
+        typedef : tstoreddef;
       begin
         if not assigned(context) then
           internalerror(2015052203);
@@ -755,7 +951,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 }
@@ -771,20 +967,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 }
@@ -1196,8 +1391,8 @@ 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;
@@ -1205,22 +1400,87 @@ uses
         doconsume : boolean;
         constraintdata : tgenericconstraintdata;
         old_block_type : tblock_type;
+        is_const,last_is_const : boolean;
+        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;
+        is_const:=false;
+        last_is_const:=false;
+        last_token:=NOTOKEN;
         repeat
+          if try_to_consume(_CONST) then
+            begin
+              { last param was const without semicolon terminator }
+              if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+              is_const := true;
+              const_list_index := result.count;
+            end;
           if token=_ID then
             begin
-              generictype:=ctypesym.create(orgpattern,cundefinedtype,false);
+              if is_const then
+                begin
+                  { last param was type without semicolon terminator }
+                  if (result.count>0) and not last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=tconstsym.create_undefined(orgpattern,cundefinedtype);
+                end
+              else
+                begin
+                  { last param was const without semicolon terminator }
+                  if (result.count>0) and last_is_const and (last_token<>_SEMICOLON) then
+                    MessagePos2(last_type_pos,scan_f_syn_expected,arraytokeninfo[_SEMICOLON].str,arraytokeninfo[last_token].str);
+                  generictype:=ttypesym.create(orgpattern,cundefinedtype,false);
+                end;
               { type parameters need to be added as strict private }
               generictype.visibility:=vis_strictprivate;
               include(generictype.symoptions,sp_generic_para);
               result.add(orgpattern,generictype);
+              last_is_const:=is_const;
             end;
           consume(_ID);
-          if try_to_consume(_COLON) then
+          { const restriction }
+          if is_const then
+            begin
+              if 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:
+                              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;
+                          end;
+                          tconstsym(result[i]).constdef:=def;
+                        end
+                      else
+                        Message(type_e_mismatch);
+                    end;
+                  { after type restriction const list terminates }
+                  is_const:=false;
+                end;
+            end
+          { type restriction }
+          else if try_to_consume(_COLON) then
             begin
               if not allowconstraints then
                 { TODO }
@@ -1335,6 +1595,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
@@ -1360,21 +1621,27 @@ 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
+            is_const:=false;
+          last_token:=token;
+          last_type_pos:=current_filepos;
         until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
         { 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;
 
@@ -1382,7 +1649,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;
@@ -1407,10 +1676,22 @@ 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(generi