mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 04:22:07 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1220 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1220 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{
 | 
						|
    Copyright (c) 2011
 | 
						|
 | 
						|
    Contains different functions that are used in the context of
 | 
						|
    parsing generics.
 | 
						|
 | 
						|
    This program is free software; you can redistribute it and/or modify
 | 
						|
    it under the terms of the GNU General Public License as published by
 | 
						|
    the Free Software Foundation; either version 2 of the License, or
 | 
						|
    (at your option) any later version.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public License
 | 
						|
    along with this program; if not, write to the Free Software
 | 
						|
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
 | 
						|
 ****************************************************************************
 | 
						|
}
 | 
						|
unit pgenutil;
 | 
						|
 | 
						|
{$i fpcdefs.inc}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  { common }
 | 
						|
  cclasses,
 | 
						|
  { global }
 | 
						|
  globtype,
 | 
						|
  { symtable }
 | 
						|
  symtype,symdef,symbase;
 | 
						|
 | 
						|
    procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string;parsedpos:tfileposinfo);
 | 
						|
    procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string);
 | 
						|
    function parse_generic_parameters(allowconstraints:boolean):TFPObjectList;
 | 
						|
    function parse_generic_specialization_types(genericdeflist:tfpobjectlist;poslist:tfplist;out prettyname,specializename:ansistring):boolean;
 | 
						|
    procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
 | 
						|
    procedure maybe_insert_generic_rename_symbol(const name:tidstring;genericlist:tfpobjectlist);
 | 
						|
    function generate_generic_name(const name:tidstring;specializename:ansistring):tidstring;
 | 
						|
    procedure split_generic_name(const name:tidstring;out nongeneric:string;out count:longint);
 | 
						|
    function resolve_generic_dummysym(const name:tidstring):tsym;
 | 
						|
    function could_be_generic(const name:tidstring):boolean;inline;
 | 
						|
 | 
						|
    type
 | 
						|
      tspecializationstate = record
 | 
						|
        oldsymtablestack   : tsymtablestack;
 | 
						|
        oldextendeddefs    : TFPHashObjectList;
 | 
						|
        oldgenericdummysyms: tfphashobjectlist;
 | 
						|
      end;
 | 
						|
 | 
						|
    procedure specialization_init(genericdef:tdef;var state:tspecializationstate);
 | 
						|
    procedure specialization_done(var state:tspecializationstate);
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
uses
 | 
						|
  { common }
 | 
						|
  cutils,fpccrc,
 | 
						|
  { global }
 | 
						|
  globals,tokens,verbose,finput,
 | 
						|
  { symtable }
 | 
						|
  symconst,symsym,symtable,defcmp,
 | 
						|
  { modules }
 | 
						|
  fmodule,
 | 
						|
  { pass 1 }
 | 
						|
  htypechk,
 | 
						|
  node,nobj,nmem,
 | 
						|
  { parser }
 | 
						|
  scanner,
 | 
						|
  pbase,pexpr,pdecsub,ptype;
 | 
						|
 | 
						|
 | 
						|
    procedure maybe_add_waiting_unit(tt:tdef);
 | 
						|
      var
 | 
						|
        hmodule : tmodule;
 | 
						|
      begin
 | 
						|
        if not assigned(tt) or
 | 
						|
            not (df_generic in tt.defoptions) then
 | 
						|
          exit;
 | 
						|
 | 
						|
        hmodule:=find_module_from_symtable(tt.owner);
 | 
						|
        if not assigned(hmodule) then
 | 
						|
          internalerror(2012092401);
 | 
						|
 | 
						|
        if hmodule=current_module then
 | 
						|
          exit;
 | 
						|
 | 
						|
        if hmodule.state<>ms_compiled then
 | 
						|
          begin
 | 
						|
{$ifdef DEBUG_UNITWAITING}
 | 
						|
            Writeln('Unit ', current_module.modulename^,
 | 
						|
              ' waiting for ', hmodule.modulename^);
 | 
						|
{$endif DEBUG_UNITWAITING}
 | 
						|
            if current_module.waitingforunit.indexof(hmodule)<0 then
 | 
						|
              current_module.waitingforunit.add(hmodule);
 | 
						|
            if hmodule.waitingunits.indexof(current_module)<0 then
 | 
						|
              hmodule.waitingunits.add(current_module);
 | 
						|
          end;
 | 
						|
      end;
 | 
						|
 | 
						|
    function check_generic_constraints(genericdef:tstoreddef;paradeflist:tfpobjectlist;poslist:tfplist):boolean;
 | 
						|
      var
 | 
						|
        i,j,
 | 
						|
        intfcount : longint;
 | 
						|
        formaldef,
 | 
						|
        paradef : tstoreddef;
 | 
						|
        objdef,
 | 
						|
        paraobjdef,
 | 
						|
        formalobjdef : tobjectdef;
 | 
						|
        intffound : boolean;
 | 
						|
        filepos : tfileposinfo;
 | 
						|
      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
 | 
						|
          internalerror(2012101002);
 | 
						|
        if paradeflist.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
 | 
						|
              begin
 | 
						|
                case formaldef.typ of
 | 
						|
                  recorddef:
 | 
						|
                    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);
 | 
						|
                      else
 | 
						|
                        internalerror(2012101003);
 | 
						|
                    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
 | 
						|
                      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:
 | 
						|
                            if not def_is_related(paraobjdef,formalobjdef.childof) then
 | 
						|
                              begin
 | 
						|
                                MessagePos2(filepos,type_e_incompatible_types,paraobjdef.typename,formalobjdef.childof.typename);
 | 
						|
                                result:=false;
 | 
						|
                              end;
 | 
						|
                          odt_class,
 | 
						|
                          odt_javaclass:
 | 
						|
                            begin
 | 
						|
                              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;
 | 
						|
                                  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;
 | 
						|
                        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;
 | 
						|
                        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
 | 
						|
                              inc(intfcount)
 | 
						|
                            else
 | 
						|
                              MessagePos2(filepos,parser_e_class_doesnt_implement_interface,paraobjdef.typename,timplementedinterface(formalobjdef.implementedinterfaces[j]).intfdef.typename);
 | 
						|
                          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;
 | 
						|
      var
 | 
						|
        old_block_type : tblock_type;
 | 
						|
        first : boolean;
 | 
						|
        typeparam : tnode;
 | 
						|
        parampos : pfileposinfo;
 | 
						|
        tmpparampos : tfileposinfo;
 | 
						|
      begin
 | 
						|
        result:=true;
 | 
						|
        if genericdeflist=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
 | 
						|
          tloadvmtaddrnode) }
 | 
						|
        old_block_type:=block_type;
 | 
						|
        { if parsedtype is set, then the first type identifer was already parsed
 | 
						|
          (happens in inline specializations) and thus we only need to parse
 | 
						|
          the remaining types and do as if the first one was already given }
 | 
						|
        first:=not assigned(parsedtype);
 | 
						|
        if assigned(parsedtype) then
 | 
						|
          begin
 | 
						|
            genericdeflist.Add(parsedtype);
 | 
						|
            specializename:='$'+parsedtype.fulltypename;
 | 
						|
            prettyname:=parsedtype.typesym.prettyname;
 | 
						|
            if assigned(poslist) then
 | 
						|
              begin
 | 
						|
                New(parampos);
 | 
						|
                parampos^:=parsedpos;
 | 
						|
                poslist.add(parampos);
 | 
						|
              end;
 | 
						|
          end
 | 
						|
        else
 | 
						|
          begin
 | 
						|
            specializename:='';
 | 
						|
            prettyname:='';
 | 
						|
          end;
 | 
						|
        while not (token in [_GT,_RSHARPBRACKET]) do
 | 
						|
          begin
 | 
						|
            { "first" is set to false at the end of the loop! }
 | 
						|
            if not first then
 | 
						|
              consume(_COMMA);
 | 
						|
            block_type:=bt_type;
 | 
						|
            tmpparampos:=current_filepos;
 | 
						|
            typeparam:=factor(false,true);
 | 
						|
            if typeparam.nodetype=typen then
 | 
						|
              begin
 | 
						|
                if df_generic in typeparam.resultdef.defoptions then
 | 
						|
                  Message(parser_e_no_generics_as_params);
 | 
						|
                if assigned(poslist) then
 | 
						|
                  begin
 | 
						|
                    New(parampos);
 | 
						|
                    parampos^:=tmpparampos;
 | 
						|
                    poslist.add(parampos);
 | 
						|
                  end;
 | 
						|
                genericdeflist.Add(typeparam.resultdef);
 | 
						|
                if not assigned(typeparam.resultdef.typesym) then
 | 
						|
                  message(type_e_generics_cannot_reference_itself)
 | 
						|
                else
 | 
						|
                  begin
 | 
						|
                    { we use the full name of the type to uniquely identify it }
 | 
						|
                    specializename:=specializename+'$'+typeparam.resultdef.fulltypename;
 | 
						|
                    if not first then
 | 
						|
                      prettyname:=prettyname+',';
 | 
						|
                    prettyname:=prettyname+typeparam.resultdef.fullownerhierarchyname+typeparam.resultdef.typesym.prettyname;
 | 
						|
                  end;
 | 
						|
              end
 | 
						|
            else
 | 
						|
              begin
 | 
						|
                Message(type_e_type_id_expected);
 | 
						|
                result:=false;
 | 
						|
              end;
 | 
						|
            typeparam.free;
 | 
						|
            first:=false;
 | 
						|
          end;
 | 
						|
        block_type:=old_block_type;
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    function parse_generic_specialization_types(genericdeflist: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);
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string);
 | 
						|
      var
 | 
						|
        dummypos : tfileposinfo;
 | 
						|
      begin
 | 
						|
        FillChar(dummypos, SizeOf(tfileposinfo), 0);
 | 
						|
        generate_specialization(tt,parse_class_parent,_prettyname,nil,'',dummypos);
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string;parsedpos:tfileposinfo);
 | 
						|
      var
 | 
						|
        st  : TSymtable;
 | 
						|
        srsym : tsym;
 | 
						|
        pt2 : tnode;
 | 
						|
        errorrecovery,
 | 
						|
        found,
 | 
						|
        first,
 | 
						|
        err : boolean;
 | 
						|
        errval,
 | 
						|
        i,
 | 
						|
        gencount : longint;
 | 
						|
        genericdef,def : tstoreddef;
 | 
						|
        generictype : ttypesym;
 | 
						|
        genericdeflist : TFPObjectList;
 | 
						|
        generictypelist : TFPObjectList;
 | 
						|
        prettyname,specializename : ansistring;
 | 
						|
        ufinalspecializename,
 | 
						|
        countstr,genname,ugenname,finalspecializename : string;
 | 
						|
        vmtbuilder : TVMTBuilder;
 | 
						|
        specializest : tsymtable;
 | 
						|
        item : tobject;
 | 
						|
        old_current_structdef : tabstractrecorddef;
 | 
						|
        old_current_genericdef,old_current_specializedef : tstoreddef;
 | 
						|
        tempst : tglobalsymtable;
 | 
						|
        old_block_type: tblock_type;
 | 
						|
        hashedid: thashedidstring;
 | 
						|
        state : tspecializationstate;
 | 
						|
        hmodule : tmodule;
 | 
						|
        oldcurrent_filepos : tfileposinfo;
 | 
						|
        poslist : tfplist;
 | 
						|
      begin
 | 
						|
        { retrieve generic def that we are going to replace }
 | 
						|
        genericdef:=tstoreddef(tt);
 | 
						|
        tt:=nil;
 | 
						|
 | 
						|
        { either symname must be given or genericdef needs to be valid }
 | 
						|
        errorrecovery:=false;
 | 
						|
        if (symname='') and
 | 
						|
            (not assigned(genericdef) or
 | 
						|
            not assigned(genericdef.typesym) or
 | 
						|
            (genericdef.typesym.typ<>typesym)) then
 | 
						|
          begin
 | 
						|
            errorrecovery:=true;
 | 
						|
            tt:=generrordef;
 | 
						|
          end;
 | 
						|
 | 
						|
        { Only parse the parameters for recovery or
 | 
						|
          for recording in genericbuf }
 | 
						|
        if parse_generic or errorrecovery then
 | 
						|
          begin
 | 
						|
            first:=assigned(parsedtype);
 | 
						|
            if not first and not try_to_consume(_LT) then
 | 
						|
              consume(_LSHARPBRACKET);
 | 
						|
            gencount:=0;
 | 
						|
            { handle "<>" }
 | 
						|
            if not first and ((token=_RSHARPBRACKET) or (token=_GT)) then
 | 
						|
              Message(type_e_type_id_expected)
 | 
						|
            else
 | 
						|
              repeat
 | 
						|
                if not first then
 | 
						|
                  begin
 | 
						|
                    pt2:=factor(false,true);
 | 
						|
                    pt2.free;
 | 
						|
                  end;
 | 
						|
                first:=false;
 | 
						|
                inc(gencount);
 | 
						|
              until not try_to_consume(_COMMA);
 | 
						|
            if not try_to_consume(_GT) then
 | 
						|
              consume(_RSHARPBRACKET);
 | 
						|
            { we need to return a def that can later pass some checks like
 | 
						|
              whether it's an interface or not }
 | 
						|
            if not errorrecovery and
 | 
						|
                (not assigned(tt) or (tt.typ=undefineddef)) then
 | 
						|
              begin
 | 
						|
                if (symname='') and genericdef.is_generic then
 | 
						|
                  { this happens in non-Delphi modes }
 | 
						|
                  tt:=genericdef
 | 
						|
                else
 | 
						|
                  begin
 | 
						|
                    { find the corresponding generic symbol so that any checks
 | 
						|
                      done on the returned def will be handled correctly }
 | 
						|
                    str(gencount,countstr);
 | 
						|
                    if symname='' then
 | 
						|
                      genname:=ttypesym(genericdef.typesym).realname
 | 
						|
                    else
 | 
						|
                      genname:=symname;
 | 
						|
                    genname:=genname+'$'+countstr;
 | 
						|
                    ugenname:=upper(genname);
 | 
						|
                    { first check whether the found name is the same as that of
 | 
						|
                      the current def or one of its (generic) surrounding defs;
 | 
						|
                      this is necessary as the symbol of the generic can not yet
 | 
						|
                      be used for lookup as it still contains a reference to an
 | 
						|
                      errordef) }
 | 
						|
                    def:=current_genericdef;
 | 
						|
                    repeat
 | 
						|
                      if def.typ in [objectdef,recorddef] then
 | 
						|
                        if tabstractrecorddef(def).objname^=ugenname then
 | 
						|
                          begin
 | 
						|
                            tt:=def;
 | 
						|
                            break;
 | 
						|
                          end;
 | 
						|
                      def:=tstoreddef(def.owner.defowner);
 | 
						|
                    until not assigned(def) or not (df_generic in def.defoptions);
 | 
						|
                    { it's not part of the current object hierarchy, so search
 | 
						|
                      for the symbol }
 | 
						|
                    if not assigned(tt) then
 | 
						|
                      begin
 | 
						|
                      srsym:=nil;
 | 
						|
                      if not searchsym(ugenname,srsym,st) or
 | 
						|
                          (srsym.typ<>typesym) then
 | 
						|
                        begin
 | 
						|
                          identifier_not_found(genname);
 | 
						|
                          tt:=generrordef;
 | 
						|
                          exit;
 | 
						|
                        end;
 | 
						|
                      tt:=ttypesym(srsym).typedef;
 | 
						|
                      { this happens in non-Delphi modes if we encounter a
 | 
						|
                        specialization of the generic class or record we're
 | 
						|
                        currently parsing }
 | 
						|
                      if (tt.typ=errordef) and assigned(current_structdef) and
 | 
						|
                          (current_structdef.objname^=ugenname) then
 | 
						|
                        tt:=current_structdef;
 | 
						|
                    end;
 | 
						|
                  end;
 | 
						|
              end;
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
 | 
						|
        if not assigned(parsedtype) and not try_to_consume(_LT) then
 | 
						|
          begin
 | 
						|
            consume(_LSHARPBRACKET);
 | 
						|
            { handle "<>" }
 | 
						|
            if (token=_GT) or (token=_RSHARPBRACKET) then
 | 
						|
              begin
 | 
						|
                Message(type_e_type_id_expected);
 | 
						|
                if not try_to_consume(_GT) then
 | 
						|
                  try_to_consume(_RSHARPBRACKET);
 | 
						|
                tt:=generrordef;
 | 
						|
                exit;
 | 
						|
              end;
 | 
						|
          end;
 | 
						|
 | 
						|
        genericdeflist:=TFPObjectList.Create(false);
 | 
						|
        poslist:=tfplist.create;
 | 
						|
 | 
						|
        { Parse type parameters }
 | 
						|
        err:=not parse_generic_specialization_types_internal(genericdeflist,poslist,prettyname,specializename,parsedtype,parsedpos);
 | 
						|
        if err then
 | 
						|
          begin
 | 
						|
            if not try_to_consume(_GT) then
 | 
						|
              try_to_consume(_RSHARPBRACKET);
 | 
						|
            genericdeflist.free;
 | 
						|
            for i:=0 to poslist.count-1 do
 | 
						|
              dispose(pfileposinfo(poslist[i]));
 | 
						|
            poslist.free;
 | 
						|
            tt:=generrordef;
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
 | 
						|
        { use the name of the symbol as procvars return a user friendly version
 | 
						|
          of the name }
 | 
						|
        if symname='' then
 | 
						|
          genname:=ttypesym(genericdef.typesym).realname
 | 
						|
        else
 | 
						|
          genname:=symname;
 | 
						|
 | 
						|
        { in case of non-Delphi mode the type name could already be a generic
 | 
						|
          def (but maybe the wrong one) }
 | 
						|
        if assigned(genericdef) and
 | 
						|
            ([df_generic,df_specialization]*genericdef.defoptions<>[]) then
 | 
						|
          begin
 | 
						|
            { remove the type count suffix from the generic's name }
 | 
						|
            for i:=Length(genname) downto 1 do
 | 
						|
              if genname[i]='$' then
 | 
						|
                begin
 | 
						|
                  genname:=copy(genname,1,i-1);
 | 
						|
                  break;
 | 
						|
                end;
 | 
						|
            { in case of a specialization we've only reached the specialization
 | 
						|
              checksum yet }
 | 
						|
            if df_specialization in genericdef.defoptions then
 | 
						|
              for i:=length(genname) downto 1 do
 | 
						|
                if genname[i]='$' then
 | 
						|
                  begin
 | 
						|
                    genname:=copy(genname,1,i-1);
 | 
						|
                    break;
 | 
						|
                  end;
 | 
						|
          end
 | 
						|
        else
 | 
						|
          begin
 | 
						|
            split_generic_name(genname,ugenname,gencount);
 | 
						|
            if genname<>ugenname then
 | 
						|
              genname:=ugenname;
 | 
						|
          end;
 | 
						|
 | 
						|
        { search a generic with the given count of params }
 | 
						|
        countstr:='';
 | 
						|
        str(genericdeflist.Count,countstr);
 | 
						|
 | 
						|
        genname:=genname+'$'+countstr;
 | 
						|
        ugenname:=upper(genname);
 | 
						|
 | 
						|
        if assigned(genericdef) and (genericdef.owner.symtabletype in [objectsymtable,recordsymtable]) then
 | 
						|
          begin
 | 
						|
            if genericdef.owner.symtabletype = objectsymtable then
 | 
						|
              found:=searchsym_in_class(tobjectdef(genericdef.owner.defowner),tobjectdef(genericdef.owner.defowner),ugenname,srsym,st,[])
 | 
						|
            else
 | 
						|
              found:=searchsym_in_record(tabstractrecorddef(genericdef.owner.defowner),ugenname,srsym,st);
 | 
						|
          end
 | 
						|
        else
 | 
						|
          found:=searchsym(ugenname,srsym,st);
 | 
						|
 | 
						|
        if not found or (srsym.typ<>typesym) then
 | 
						|
          begin
 | 
						|
            identifier_not_found(genname);
 | 
						|
            if not try_to_consume(_GT) then
 | 
						|
              try_to_consume(_RSHARPBRACKET);
 | 
						|
            for i:=0 to poslist.count-1 do
 | 
						|
              dispose(pfileposinfo(poslist[i]));
 | 
						|
            poslist.free;
 | 
						|
            genericdeflist.Free;
 | 
						|
            tt:=generrordef;
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
 | 
						|
        { we've found the correct def }
 | 
						|
        genericdef:=tstoreddef(ttypesym(srsym).typedef);
 | 
						|
 | 
						|
        if not check_generic_constraints(genericdef,genericdeflist,poslist) then
 | 
						|
          begin
 | 
						|
            { the parameters didn't fit the constraints, so don't continue with the
 | 
						|
              specialization }
 | 
						|
            genericdeflist.free;
 | 
						|
            for i:=0 to poslist.count-1 do
 | 
						|
              dispose(pfileposinfo(poslist[i]));
 | 
						|
            poslist.free;
 | 
						|
            tt:=generrordef;
 | 
						|
            if not try_to_consume(_GT) then
 | 
						|
              try_to_consume(_RSHARPBRACKET);
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
 | 
						|
        { build the new type's name }
 | 
						|
        finalspecializename:=generate_generic_name(genname,specializename);
 | 
						|
        ufinalspecializename:=upper(finalspecializename);
 | 
						|
        prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
 | 
						|
 | 
						|
        { select the symtable containing the params }
 | 
						|
        case genericdef.typ of
 | 
						|
          procdef:
 | 
						|
            st:=genericdef.GetSymtable(gs_para);
 | 
						|
          objectdef,
 | 
						|
          recorddef:
 | 
						|
            st:=genericdef.GetSymtable(gs_record);
 | 
						|
          arraydef:
 | 
						|
            st:=tarraydef(genericdef).symtable;
 | 
						|
          procvardef:
 | 
						|
            st:=genericdef.GetSymtable(gs_para);
 | 
						|
          else
 | 
						|
            internalerror(200511182);
 | 
						|
        end;
 | 
						|
 | 
						|
        generictypelist:=tfpobjectlist.create(false);
 | 
						|
 | 
						|
        { build the list containing the types for the generic params }
 | 
						|
        if not assigned(genericdef.genericparas) then
 | 
						|
          internalerror(2013092601);
 | 
						|
        if genericdeflist.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);
 | 
						|
            generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[i]));
 | 
						|
            generictypelist.add(generictype);
 | 
						|
          end;
 | 
						|
 | 
						|
        { Special case if we are referencing the current defined object }
 | 
						|
        if assigned(current_structdef) and
 | 
						|
           (current_structdef.objname^=ufinalspecializename) then
 | 
						|
          tt:=current_structdef;
 | 
						|
 | 
						|
        { decide in which symtable to put the specialization }
 | 
						|
        if current_module.is_unit and current_module.in_interface then
 | 
						|
          specializest:=current_module.globalsymtable
 | 
						|
        else
 | 
						|
          specializest:=current_module.localsymtable;
 | 
						|
 | 
						|
        { Can we reuse an already specialized type? }
 | 
						|
 | 
						|
        { for this first check whether we are currently specializing a nested
 | 
						|
          type of the current (main) specialization (this is necessary, because
 | 
						|
          during that time the symbol of the main specialization will still
 | 
						|
          contain a reference to an errordef) }
 | 
						|
        if not assigned(tt) and assigned(current_specializedef) then
 | 
						|
          begin
 | 
						|
            def:=current_specializedef;
 | 
						|
            repeat
 | 
						|
              if def.typ in [objectdef,recorddef] then
 | 
						|
                if tabstractrecorddef(def).objname^=ufinalspecializename then begin
 | 
						|
                  tt:=def;
 | 
						|
                  break;
 | 
						|
                end;
 | 
						|
              def:=tstoreddef(def.owner.defowner);
 | 
						|
            until not assigned(def) or not (df_specialization in def.defoptions);
 | 
						|
          end;
 | 
						|
 | 
						|
        { now check whether there is a specialization somewhere else }
 | 
						|
        if not assigned(tt) then
 | 
						|
          begin
 | 
						|
            hashedid.id:=ufinalspecializename;
 | 
						|
 | 
						|
            srsym:=tsym(specializest.findwithhash(hashedid));
 | 
						|
            if assigned(srsym) then
 | 
						|
              begin
 | 
						|
                if srsym.typ<>typesym then
 | 
						|
                  internalerror(200710171);
 | 
						|
                tt:=ttypesym(srsym).typedef;
 | 
						|
              end
 | 
						|
            else
 | 
						|
              { the generic could have been specialized in the globalsymtable
 | 
						|
                already, so search there as well }
 | 
						|
              if (specializest<>current_module.globalsymtable) and assigned(current_module.globalsymtable) then
 | 
						|
                begin
 | 
						|
                  srsym:=tsym(current_module.globalsymtable.findwithhash(hashedid));
 | 
						|
                  if assigned(srsym) then
 | 
						|
                    begin
 | 
						|
                      if srsym.typ<>typesym then
 | 
						|
                        internalerror(2011121101);
 | 
						|
                      tt:=ttypesym(srsym).typedef;
 | 
						|
                    end;
 | 
						|
                end;
 | 
						|
          end;
 | 
						|
 | 
						|
        if not assigned(tt) then
 | 
						|
          begin
 | 
						|
            specialization_init(genericdef,state);
 | 
						|
 | 
						|
            { push a temporary global symtable so that the specialization is
 | 
						|
              added to the correct symtable; this symtable does not contain
 | 
						|
              any other symbols, so that the type resolution can not be
 | 
						|
              influenced by symbols in the current unit }
 | 
						|
            tempst:=tspecializesymtable.create(current_module.modulename^,current_module.moduleid);
 | 
						|
            symtablestack.push(tempst);
 | 
						|
 | 
						|
            { Reparse the original type definition }
 | 
						|
            if not err then
 | 
						|
              begin
 | 
						|
                old_current_specializedef:=nil;
 | 
						|
                old_current_genericdef:=nil;
 | 
						|
                old_current_structdef:=nil;
 | 
						|
 | 
						|
                if parse_class_parent then
 | 
						|
                  begin
 | 
						|
                    old_current_structdef:=current_structdef;
 | 
						|
                    old_current_genericdef:=current_genericdef;
 | 
						|
                    old_current_specializedef:=current_specializedef;
 | 
						|
 | 
						|
                    if genericdef.owner.symtabletype in [recordsymtable,objectsymtable] then
 | 
						|
                      current_structdef:=tabstractrecorddef(genericdef.owner.defowner)
 | 
						|
                    else
 | 
						|
                      current_structdef:=nil;
 | 
						|
                    current_genericdef:=nil;
 | 
						|
                    current_specializedef:=nil;
 | 
						|
                  end;
 | 
						|
 | 
						|
                maybe_add_waiting_unit(genericdef);
 | 
						|
 | 
						|
                { First a new typesym so we can reuse this specialization and
 | 
						|
                  references to this specialization can be handled }
 | 
						|
                srsym:=ttypesym.create(finalspecializename,generrordef);
 | 
						|
                specializest.insert(srsym);
 | 
						|
 | 
						|
                { specializations are declarations as such it is the wisest to
 | 
						|
                  declare set the blocktype to "type"; otherwise we'll
 | 
						|
                  experience unexpected side effects like the addition of
 | 
						|
                  classrefdefs if we have a generic that's derived from another
 | 
						|
                  generic }
 | 
						|
                old_block_type:=block_type;
 | 
						|
                block_type:=bt_type;
 | 
						|
 | 
						|
                if not assigned(genericdef.generictokenbuf) then
 | 
						|
                  internalerror(200511171);
 | 
						|
                hmodule:=find_module_from_symtable(genericdef.owner);
 | 
						|
                if hmodule=nil then
 | 
						|
                  internalerror(2012051202);
 | 
						|
                oldcurrent_filepos:=current_filepos;
 | 
						|
                { use the index the module got from the current compilation process }
 | 
						|
                current_filepos.moduleindex:=hmodule.unit_index;
 | 
						|
                current_tokenpos:=current_filepos;
 | 
						|
                current_scanner.startreplaytokens(genericdef.generictokenbuf);
 | 
						|
                read_named_type(tt,srsym,genericdef,generictypelist,false,false);
 | 
						|
                current_filepos:=oldcurrent_filepos;
 | 
						|
                ttypesym(srsym).typedef:=tt;
 | 
						|
                tt.typesym:=srsym;
 | 
						|
 | 
						|
                if _prettyname<>'' then
 | 
						|
                  ttypesym(tt.typesym).fprettyname:=_prettyname
 | 
						|
                else
 | 
						|
                  ttypesym(tt.typesym).fprettyname:=prettyname;
 | 
						|
 | 
						|
                { Note regarding hint directives:
 | 
						|
                  There is no need to remove the flags for them from the
 | 
						|
                  specialized generic symbol, because hint directives that
 | 
						|
                  follow the specialization are handled by the code in
 | 
						|
                  pdecl.types_dec and added to the type symbol.
 | 
						|
                  E.g.: TFoo = TBar<Blubb> deprecated;
 | 
						|
                  Here the symbol TBar$1$Blubb will contain the
 | 
						|
                  "sp_hint_deprecated" flag while the TFoo symbol won't.}
 | 
						|
 | 
						|
                case tt.typ of
 | 
						|
                  { Build VMT indexes for classes and read hint directives }
 | 
						|
                  objectdef:
 | 
						|
                    begin
 | 
						|
                      try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
 | 
						|
                      consume(_SEMICOLON);
 | 
						|
 | 
						|
                      vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
 | 
						|
                      vmtbuilder.generate_vmt;
 | 
						|
                      vmtbuilder.free;
 | 
						|
                    end;
 | 
						|
                  { handle params, calling convention, etc }
 | 
						|
                  procvardef:
 | 
						|
                    begin
 | 
						|
                      if not check_proc_directive(true) then
 | 
						|
                        begin
 | 
						|
                          try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
 | 
						|
                          consume(_SEMICOLON);
 | 
						|
                        end;
 | 
						|
                      parse_var_proc_directives(ttypesym(srsym));
 | 
						|
                      handle_calling_convention(tprocvardef(tt));
 | 
						|
                      if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
 | 
						|
                        consume(_SEMICOLON);
 | 
						|
                    end;
 | 
						|
                  else
 | 
						|
                    { parse hint directives for records and arrays }
 | 
						|
                    begin
 | 
						|
                      try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
 | 
						|
                      consume(_SEMICOLON);
 | 
						|
                    end;
 | 
						|
                end;
 | 
						|
                { Consume the semicolon if it is also recorded }
 | 
						|
                try_to_consume(_SEMICOLON);
 | 
						|
 | 
						|
                block_type:=old_block_type;
 | 
						|
                if parse_class_parent then
 | 
						|
                  begin
 | 
						|
                    current_structdef:=old_current_structdef;
 | 
						|
                    current_genericdef:=old_current_genericdef;
 | 
						|
                    current_specializedef:=old_current_specializedef;
 | 
						|
                  end;
 | 
						|
              end;
 | 
						|
 | 
						|
            { extract all created symbols and defs from the temporary symtable
 | 
						|
              and add them to the specializest }
 | 
						|
            for i:=tempst.SymList.Count-1 downto 0 do
 | 
						|
              begin
 | 
						|
                item:=tempst.SymList.Items[i];
 | 
						|
                { using changeowner the symbol is automatically added to the
 | 
						|
                  new symtable }
 | 
						|
                tsym(item).ChangeOwner(specializest);
 | 
						|
              end;
 | 
						|
 | 
						|
            for i:=tempst.DefList.Count-1 downto 0 do
 | 
						|
              begin
 | 
						|
                item:=tempst.DefList.Items[i];
 | 
						|
                { using changeowner the def is automatically added to the new
 | 
						|
                  symtable }
 | 
						|
                tdef(item).ChangeOwner(specializest);
 | 
						|
              end;
 | 
						|
 | 
						|
            { if a generic was declared during the specialization we need to
 | 
						|
              flag the specialize symtable accordingly }
 | 
						|
            if sto_has_generic in tempst.tableoptions then
 | 
						|
              specializest.includeoption(sto_has_generic);
 | 
						|
 | 
						|
            tempst.free;
 | 
						|
 | 
						|
            specialization_done(state);
 | 
						|
          end;
 | 
						|
 | 
						|
        if not (token in [_GT, _RSHARPBRACKET]) then
 | 
						|
          begin
 | 
						|
            consume(_RSHARPBRACKET);
 | 
						|
            exit;
 | 
						|
          end
 | 
						|
        else
 | 
						|
          consume(token);
 | 
						|
 | 
						|
        genericdeflist.free;
 | 
						|
        generictypelist.free;
 | 
						|
        if assigned(genericdef) then
 | 
						|
          begin
 | 
						|
            { check the hints of the found generic symbol }
 | 
						|
            srsym:=genericdef.typesym;
 | 
						|
            check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
 | 
						|
          end;
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    function parse_generic_parameters(allowconstraints:boolean):TFPObjectList;
 | 
						|
      var
 | 
						|
        generictype : ttypesym;
 | 
						|
        i,firstidx : longint;
 | 
						|
        srsymtable : tsymtable;
 | 
						|
        basedef,def : tdef;
 | 
						|
        defname : tidstring;
 | 
						|
        allowconstructor,
 | 
						|
        doconsume : boolean;
 | 
						|
        constraintdata : tgenericconstraintdata;
 | 
						|
        old_block_type : tblock_type;
 | 
						|
      begin
 | 
						|
        result:=TFPObjectList.Create(false);
 | 
						|
        firstidx:=0;
 | 
						|
        old_block_type:=block_type;
 | 
						|
        block_type:=bt_type;
 | 
						|
        repeat
 | 
						|
          if token=_ID then
 | 
						|
            begin
 | 
						|
              generictype:=ttypesym.create(orgpattern,cundefinedtype);
 | 
						|
              include(generictype.symoptions,sp_generic_para);
 | 
						|
              result.add(generictype);
 | 
						|
            end;
 | 
						|
          consume(_ID);
 | 
						|
          if try_to_consume(_COLON) then
 | 
						|
            begin
 | 
						|
              if not allowconstraints then
 | 
						|
                { TODO }
 | 
						|
                Message(parser_e_illegal_expression{ parser_e_generic_constraints_not_allowed_here});
 | 
						|
              { construct a name which can be used for a type specification }
 | 
						|
              constraintdata:=tgenericconstraintdata.create;
 | 
						|
              defname:='';
 | 
						|
              str(current_module.deflist.count,defname);
 | 
						|
              defname:='$gendef'+defname;
 | 
						|
 | 
						|
              allowconstructor:=m_delphi in current_settings.modeswitches;
 | 
						|
 | 
						|
              basedef:=generrordef;
 | 
						|
              repeat
 | 
						|
                doconsume:=true;
 | 
						|
 | 
						|
                case token of
 | 
						|
                  _CONSTRUCTOR:
 | 
						|
                    begin
 | 
						|
                      if not allowconstructor or (gcf_constructor in constraintdata.flags) then
 | 
						|
                        Message(parser_e_illegal_expression);
 | 
						|
                      include(constraintdata.flags,gcf_constructor);
 | 
						|
                      allowconstructor:=false;
 | 
						|
                    end;
 | 
						|
                  _CLASS:
 | 
						|
                    begin
 | 
						|
                      if gcf_class in constraintdata.flags then
 | 
						|
                        Message(parser_e_illegal_expression);
 | 
						|
                      if basedef=generrordef then
 | 
						|
                        include(constraintdata.flags,gcf_class)
 | 
						|
                      else
 | 
						|
                        Message(parser_e_illegal_expression);
 | 
						|
                    end;
 | 
						|
                  _RECORD:
 | 
						|
                    begin
 | 
						|
                      if ([gcf_constructor,gcf_class]*constraintdata.flags<>[])
 | 
						|
                          or (constraintdata.interfaces.count>0) then
 | 
						|
                        Message(parser_e_illegal_expression)
 | 
						|
                      else
 | 
						|
                        begin
 | 
						|
                          srsymtable:=trecordsymtable.create(defname,0);
 | 
						|
                          basedef:=trecorddef.create(defname,srsymtable);
 | 
						|
                          include(constraintdata.flags,gcf_record);
 | 
						|
                          allowconstructor:=false;
 | 
						|
                        end;
 | 
						|
                    end;
 | 
						|
                  else
 | 
						|
                    begin
 | 
						|
                      { after single_type "token" is the trailing ",", ";" or
 | 
						|
                        ">"! }
 | 
						|
                      doconsume:=false;
 | 
						|
                      { def is already set to a class or record }
 | 
						|
                      if gcf_record in constraintdata.flags then
 | 
						|
                        Message(parser_e_illegal_expression);
 | 
						|
                      single_type(def, [stoAllowSpecialization]);
 | 
						|
                      { only types that are inheritable are allowed }
 | 
						|
                      if (def.typ<>objectdef) or
 | 
						|
                          not (tobjectdef(def).objecttype in [odt_class,odt_interfacecom,odt_interfacecorba,odt_interfacejava,odt_javaclass]) then
 | 
						|
                        Message1(type_e_class_or_interface_type_expected,def.typename)
 | 
						|
                      else
 | 
						|
                        case tobjectdef(def).objecttype of
 | 
						|
                          odt_class,
 | 
						|
                          odt_javaclass:
 | 
						|
                            begin
 | 
						|
                              if gcf_class in constraintdata.flags then
 | 
						|
                                { "class" + concrete class is not allowed }
 | 
						|
                                Message(parser_e_illegal_expression)
 | 
						|
                              else
 | 
						|
                                { do we already have a concrete class? }
 | 
						|
                                if basedef<>generrordef then
 | 
						|
                                  Message(parser_e_illegal_expression)
 | 
						|
                                else
 | 
						|
                                  basedef:=def;
 | 
						|
                            end;
 | 
						|
                          odt_interfacecom,
 | 
						|
                          odt_interfacecorba,
 | 
						|
                          odt_interfacejava,
 | 
						|
                          odt_dispinterface:
 | 
						|
                            constraintdata.interfaces.add(def);
 | 
						|
                        end;
 | 
						|
                    end;
 | 
						|
                end;
 | 
						|
                if doconsume then
 | 
						|
                  consume(token);
 | 
						|
              until not try_to_consume(_COMMA);
 | 
						|
 | 
						|
              if ([gcf_class,gcf_constructor]*constraintdata.flags<>[]) or
 | 
						|
                  (constraintdata.interfaces.count>1) or
 | 
						|
                  (
 | 
						|
                    (basedef.typ=objectdef) and
 | 
						|
                    (tobjectdef(basedef).objecttype in [odt_javaclass,odt_class])
 | 
						|
                  ) then
 | 
						|
                begin
 | 
						|
                  if basedef.typ=errordef then
 | 
						|
                    { don't pass an errordef as a parent to a tobjectdef }
 | 
						|
                    basedef:=class_tobject
 | 
						|
                  else
 | 
						|
                    if (basedef.typ<>objectdef) or
 | 
						|
                        not (tobjectdef(basedef).objecttype in [odt_javaclass,odt_class]) then
 | 
						|
                      internalerror(2012101101);
 | 
						|
                  basedef:=tobjectdef.create(tobjectdef(basedef).objecttype,defname,tobjectdef(basedef));
 | 
						|
                  for i:=0 to constraintdata.interfaces.count-1 do
 | 
						|
                    tobjectdef(basedef).implementedinterfaces.add(
 | 
						|
                      timplementedinterface.create(tobjectdef(constraintdata.interfaces[i])));
 | 
						|
                end
 | 
						|
              else
 | 
						|
                if constraintdata.interfaces.count=1 then
 | 
						|
                  begin
 | 
						|
                    if basedef.typ<>errordef then
 | 
						|
                      internalerror(2013021601);
 | 
						|
                    def:=tdef(constraintdata.interfaces[0]);
 | 
						|
                    basedef:=tobjectdef.create(tobjectdef(def).objecttype,defname,tobjectdef(def));
 | 
						|
                    constraintdata.interfaces.delete(0);
 | 
						|
                  end;
 | 
						|
              if basedef.typ<>errordef then
 | 
						|
                with tstoreddef(basedef) do
 | 
						|
                  begin
 | 
						|
                    genconstraintdata:=tgenericconstraintdata.create;
 | 
						|
                    genconstraintdata.flags:=constraintdata.flags;
 | 
						|
                    genconstraintdata.interfaces.assign(constraintdata.interfaces);
 | 
						|
                    include(defoptions,df_genconstraint);
 | 
						|
                  end;
 | 
						|
 | 
						|
              for i:=firstidx to result.count-1 do
 | 
						|
                ttypesym(result[i]).typedef:=basedef;
 | 
						|
              { we need a typesym in case we do a Delphi-mode inline
 | 
						|
                specialization with this parameter; so just use the first sym }
 | 
						|
              if not assigned(basedef.typesym) then
 | 
						|
                basedef.typesym:=ttypesym(result[firstidx]);
 | 
						|
              firstidx:=result.count;
 | 
						|
 | 
						|
              constraintdata.free;
 | 
						|
            end;
 | 
						|
        until not (try_to_consume(_COMMA) or try_to_consume(_SEMICOLON));
 | 
						|
        block_type:=old_block_type;
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
 | 
						|
      var
 | 
						|
        i: longint;
 | 
						|
        generictype: ttypesym;
 | 
						|
        st: tsymtable;
 | 
						|
      begin
 | 
						|
        def.genericdef:=genericdef;
 | 
						|
        if not assigned(genericlist) then
 | 
						|
          exit;
 | 
						|
 | 
						|
        if assigned(genericdef) then
 | 
						|
          include(def.defoptions,df_specialization)
 | 
						|
        else
 | 
						|
          if genericlist.count>0 then
 | 
						|
            include(def.defoptions,df_generic);
 | 
						|
 | 
						|
        case def.typ of
 | 
						|
          recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
 | 
						|
          arraydef: st:=tarraydef(def).symtable;
 | 
						|
          procvardef,procdef: st:=tabstractprocdef(def).parast;
 | 
						|
          else
 | 
						|
            internalerror(201101020);
 | 
						|
        end;
 | 
						|
 | 
						|
        if (genericlist.count>0) and not assigned(def.genericparas) then
 | 
						|
          def.genericparas:=tfphashobjectlist.create(false);
 | 
						|
        for i:=0 to genericlist.count-1 do
 | 
						|
          begin
 | 
						|
            generictype:=ttypesym(genericlist[i]);
 | 
						|
            st.insert(generictype);
 | 
						|
            include(generictype.symoptions,sp_generic_para);
 | 
						|
            def.genericparas.add(generictype.name,generictype);
 | 
						|
          end;
 | 
						|
       end;
 | 
						|
 | 
						|
    procedure maybe_insert_generic_rename_symbol(const name:tidstring;genericlist:tfpobjectlist);
 | 
						|
      var
 | 
						|
        gensym : ttypesym;
 | 
						|
      begin
 | 
						|
        { for generics in non-Delphi modes we insert a private type symbol
 | 
						|
          that has the same base name as the currently parsed generic and
 | 
						|
          that references this defs }
 | 
						|
        if not (m_delphi in current_settings.modeswitches) and
 | 
						|
            (
 | 
						|
              (
 | 
						|
                parse_generic and
 | 
						|
                assigned(genericlist) and
 | 
						|
                (genericlist.count>0)
 | 
						|
              ) or
 | 
						|
              (
 | 
						|
                assigned(current_specializedef) and
 | 
						|
                assigned(current_structdef.genericdef) and
 | 
						|
                (current_structdef.genericdef.typ in [objectdef,recorddef]) and
 | 
						|
                (pos('$',name)>0)
 | 
						|
              )
 | 
						|
            ) then
 | 
						|
          begin
 | 
						|
            { we need to pass nil as def here, because the constructor wants
 | 
						|
              to set the typesym of the def which is not what we want }
 | 
						|
            gensym:=ttypesym.create(copy(name,1,pos('$',name)-1),nil);
 | 
						|
            gensym.typedef:=current_structdef;
 | 
						|
            include(gensym.symoptions,sp_internal);
 | 
						|
            { the symbol should be only visible to the generic class
 | 
						|
              itself }
 | 
						|
            gensym.visibility:=vis_strictprivate;
 | 
						|
            symtablestack.top.insert(gensym);
 | 
						|
          end;
 | 
						|
      end;
 | 
						|
 | 
						|
    function generate_generic_name(const name:tidstring;specializename:ansistring):tidstring;
 | 
						|
    var
 | 
						|
      crc : cardinal;
 | 
						|
    begin
 | 
						|
      if specializename='' then
 | 
						|
        internalerror(2012061901);
 | 
						|
      { build the new type's name }
 | 
						|
      crc:=UpdateCrc32(0,specializename[1],length(specializename));
 | 
						|
      result:=name+'$crc'+hexstr(crc,8);
 | 
						|
    end;
 | 
						|
 | 
						|
    procedure split_generic_name(const name:tidstring;out nongeneric:string;out count:longint);
 | 
						|
      var
 | 
						|
        i,code : longint;
 | 
						|
        countstr : string;
 | 
						|
      begin
 | 
						|
        for i:=length(name) downto 1 do
 | 
						|
          if name[i]='$' then
 | 
						|
            begin
 | 
						|
              nongeneric:=copy(name,1,i-1);
 | 
						|
              countstr:=copy(name,i+1,length(name)-i);
 | 
						|
              val(countstr,count,code);
 | 
						|
              if code<>0 then
 | 
						|
                break;
 | 
						|
              exit;
 | 
						|
            end;
 | 
						|
        nongeneric:=name;
 | 
						|
        count:=0;
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    function resolve_generic_dummysym(const name:tidstring):tsym;
 | 
						|
      var
 | 
						|
        list : tfpobjectlist;
 | 
						|
      begin
 | 
						|
        list:=tfpobjectlist(current_module.genericdummysyms.find(name));
 | 
						|
        if assigned(list) and (list.count>0) then
 | 
						|
          result:=tgenericdummyentry(list.last).resolvedsym
 | 
						|
        else
 | 
						|
          result:=nil;
 | 
						|
      end;
 | 
						|
 | 
						|
 | 
						|
    function could_be_generic(const name:tidstring):boolean;
 | 
						|
      begin
 | 
						|
        result:=(name<>'') and
 | 
						|
                  (current_module.genericdummysyms.findindexof(name)>=0);
 | 
						|
      end;
 | 
						|
 | 
						|
    procedure specialization_init(genericdef:tdef;var state: tspecializationstate);
 | 
						|
    var
 | 
						|
      pu : tused_unit;
 | 
						|
      hmodule : tmodule;
 | 
						|
      unitsyms : TFPHashObjectList;
 | 
						|
      sym : tsym;
 | 
						|
      i : Integer;
 | 
						|
    begin
 | 
						|
      if not assigned(genericdef) then
 | 
						|
        internalerror(200705151);
 | 
						|
      { Setup symtablestack at definition time
 | 
						|
        to get types right, however this is not perfect, we should probably record
 | 
						|
        the resolved symbols }
 | 
						|
      state.oldsymtablestack:=symtablestack;
 | 
						|
      state.oldextendeddefs:=current_module.extendeddefs;
 | 
						|
      state.oldgenericdummysyms:=current_module.genericdummysyms;
 | 
						|
      current_module.extendeddefs:=TFPHashObjectList.create(true);
 | 
						|
      current_module.genericdummysyms:=tfphashobjectlist.create(true);
 | 
						|
      symtablestack:=tdefawaresymtablestack.create;
 | 
						|
      hmodule:=find_module_from_symtable(genericdef.owner);
 | 
						|
      if hmodule=nil then
 | 
						|
        internalerror(200705152);
 | 
						|
      { collect all unit syms in the generic's unit as we need to establish
 | 
						|
        their unitsym.module link again so that unit identifiers can be used }
 | 
						|
      unitsyms:=tfphashobjectlist.create(false);
 | 
						|
      if (hmodule<>current_module) and assigned(hmodule.globalsymtable) then
 | 
						|
        for i:=0 to hmodule.globalsymtable.symlist.count-1 do
 | 
						|
          begin
 | 
						|
            sym:=tsym(hmodule.globalsymtable.symlist[i]);
 | 
						|
            if sym.typ=unitsym then
 | 
						|
              unitsyms.add(upper(sym.realname),sym);
 | 
						|
          end;
 | 
						|
      { add all units if we are specializing inside the current unit (as the
 | 
						|
        generic could have been declared in the implementation part), but load
 | 
						|
        only interface units, if we are in a different unit as then the generic
 | 
						|
        needs to be in the interface section }
 | 
						|
      pu:=tused_unit(hmodule.used_units.first);
 | 
						|
      while assigned(pu) do
 | 
						|
        begin
 | 
						|
          if not assigned(pu.u.globalsymtable) then
 | 
						|
            { in certain circular, but valid unit constellations it can happen
 | 
						|
              that we specialize a generic in a different unit that was used
 | 
						|
              in the implementation section of the generic's unit and were the
 | 
						|
              interface is still being parsed and thus the localsymtable is in
 | 
						|
              reality the global symtable }
 | 
						|
            if pu.u.in_interface then
 | 
						|
              symtablestack.push(pu.u.localsymtable)
 | 
						|
            else
 | 
						|
              internalerror(200705153)
 | 
						|
          else
 | 
						|
            symtablestack.push(pu.u.globalsymtable);
 | 
						|
          sym:=tsym(unitsyms.find(pu.u.modulename^));
 | 
						|
          if assigned(sym) and not assigned(tunitsym(sym).module) then
 | 
						|
            tunitsym(sym).module:=pu.u;
 | 
						|
          pu:=tused_unit(pu.next);
 | 
						|
        end;
 | 
						|
      unitsyms.free;
 | 
						|
      if assigned(hmodule.globalsymtable) then
 | 
						|
        symtablestack.push(hmodule.globalsymtable);
 | 
						|
      { push the localsymtable if needed }
 | 
						|
      if (hmodule<>current_module) or not current_module.in_interface then
 | 
						|
        symtablestack.push(hmodule.localsymtable);
 | 
						|
    end;
 | 
						|
 | 
						|
    procedure specialization_done(var state: tspecializationstate);
 | 
						|
    begin
 | 
						|
      { Restore symtablestack }
 | 
						|
      current_module.extendeddefs.free;
 | 
						|
      current_module.extendeddefs:=state.oldextendeddefs;
 | 
						|
      current_module.genericdummysyms.free;
 | 
						|
      current_module.genericdummysyms:=state.oldgenericdummysyms;
 | 
						|
      symtablestack.free;
 | 
						|
      symtablestack:=state.oldsymtablestack;
 | 
						|
      { clear the state record to be on the safe side }
 | 
						|
      fillchar(state, sizeof(state), 0);
 | 
						|
    end;
 | 
						|
 | 
						|
end.
 |