Browse Source

* fix #35261: apply slightly adjusted changes by Ryan Joseph to implement support for implicit generic function specializations

The main adjustments were as follows:
  - fixing coding style and identation
  - fixing some typos
  - using a better name for the property in tcallcandidates which holds the symbols created for anonymous parameter values
Sven/Sarah Barth 3 years ago
parent
commit
90844c2027
45 changed files with 1657 additions and 76 deletions
  1. 4 1
      compiler/defutil.pas
  2. 5 3
      compiler/globtype.pas
  3. 42 31
      compiler/htypechk.pas
  4. 1 1
      compiler/ncal.pas
  5. 8 0
      compiler/nld.pas
  6. 672 36
      compiler/pgenutil.pas
  7. 50 0
      compiler/symdef.pas
  8. 9 2
      compiler/symsym.pas
  9. 11 1
      compiler/symtable.pas
  10. 2 1
      compiler/utils/ppuutils/ppudump.pp
  11. 31 0
      tests/test/timpfuncspez1.pp
  12. 22 0
      tests/test/timpfuncspez10.pp
  13. 20 0
      tests/test/timpfuncspez11.pp
  14. 27 0
      tests/test/timpfuncspez12.pp
  15. 25 0
      tests/test/timpfuncspez13.pp
  16. 25 0
      tests/test/timpfuncspez14.pp
  17. 25 0
      tests/test/timpfuncspez15.pp
  18. 17 0
      tests/test/timpfuncspez16.pp
  19. 17 0
      tests/test/timpfuncspez17.pp
  20. 17 0
      tests/test/timpfuncspez18.pp
  21. 26 0
      tests/test/timpfuncspez19.pp
  22. 24 0
      tests/test/timpfuncspez2.pp
  23. 28 0
      tests/test/timpfuncspez20.pp
  24. 23 0
      tests/test/timpfuncspez21.pp
  25. 22 0
      tests/test/timpfuncspez22.pp
  26. 19 0
      tests/test/timpfuncspez23.pp
  27. 19 0
      tests/test/timpfuncspez24.pp
  28. 41 0
      tests/test/timpfuncspez25.pp
  29. 17 0
      tests/test/timpfuncspez26.pp
  30. 16 0
      tests/test/timpfuncspez27.pp
  31. 16 0
      tests/test/timpfuncspez28.pp
  32. 19 0
      tests/test/timpfuncspez29.pp
  33. 18 0
      tests/test/timpfuncspez3.pp
  34. 24 0
      tests/test/timpfuncspez30.pp
  35. 25 0
      tests/test/timpfuncspez31.pp
  36. 34 0
      tests/test/timpfuncspez32.pp
  37. 20 0
      tests/test/timpfuncspez33.pp
  38. 30 0
      tests/test/timpfuncspez34.pp
  39. 54 0
      tests/test/timpfuncspez35.pp
  40. 19 0
      tests/test/timpfuncspez4.pp
  41. 22 0
      tests/test/timpfuncspez5.pp
  42. 22 0
      tests/test/timpfuncspez6.pp
  43. 30 0
      tests/test/timpfuncspez7.pp
  44. 30 0
      tests/test/timpfuncspez8.pp
  45. 29 0
      tests/test/timpfuncspez9.pp

+ 4 - 1
compiler/defutil.pas

@@ -821,7 +821,10 @@ implementation
     function is_array_of_const(p : tdef) : boolean;
       begin
          result:=(p.typ=arraydef) and
-                 (ado_IsArrayOfConst in tarraydef(p).arrayoptions);
+                 (ado_IsArrayOfConst in tarraydef(p).arrayoptions) and
+                 { consider it an array-of-const in the strict sense only if it
+                   isn't an array constructor }
+                 not (ado_IsConstructor in tarraydef(p).arrayoptions);
       end;
 
     function is_conststring_array(p: tdef): boolean;

+ 5 - 3
compiler/globtype.pas

@@ -528,7 +528,8 @@ interface
          m_multi_helpers,       { helpers can appear in multiple scopes simultaneously }
          m_array2dynarray,      { regular arrays can be implicitly converted to dynamic arrays }
          m_prefixed_attributes, { enable attributes that are defined before the type they belong to }
-         m_underscoreisseparator{ _ can be used as separator to group digits in numbers }
+         m_underscoreisseparator,{ _ can be used as separator to group digits in numbers }
+         m_implicit_function_specialization    { attempt to specialize generic function by inferring types from parameters }
        );
        tmodeswitches = set of tmodeswitch;
 
@@ -679,7 +680,7 @@ interface
 
        cstylearrayofconst = [pocall_cdecl,pocall_cppdecl,pocall_mwpascal,pocall_sysv_abi_cdecl,pocall_ms_abi_cdecl];
 
-       modeswitchstr : array[tmodeswitch] of string[21] = ('',
+       modeswitchstr : array[tmodeswitch] of string[30] = ('',
          '','','','','','','',
          {$ifdef gpc_mode}'',{$endif}
          { more specific }
@@ -721,7 +722,8 @@ interface
          'MULTIHELPERS',
          'ARRAYTODYNARRAY',
          'PREFIXEDATTRIBUTES',
-         'UNDERSCOREISSEPARATOR'
+         'UNDERSCOREISSEPARATOR',
+         'IMPLICITFUNCTIONSPECIALIZATION'
          );
 
 

+ 42 - 31
compiler/htypechk.pas

@@ -80,12 +80,12 @@ interface
         FParaNode   : tnode;
         FParaLength : smallint;
         FAllowVariant : boolean;
+        FParaAnonSyms : tfplist;
         procedure collect_overloads_in_struct(structdef:tabstractrecorddef;ProcdefOverloadList:TFPObjectList;searchhelpers,anoninherited:boolean;spezcontext:tspecializationcontext);
         procedure collect_overloads_in_units(ProcdefOverloadList:TFPObjectList; objcidcall,explicitunit: boolean;spezcontext:tspecializationcontext);
         procedure create_candidate_list(ignorevisibility,allowdefaultparas,objcidcall,explicitunit,searchhelpers,anoninherited:boolean;spezcontext:tspecializationcontext);
         procedure calc_distance(st_root:tsymtable;objcidcall: boolean);
         function  proc_add(st:tsymtable;pd:tprocdef;objcidcall: boolean):pcandidate;
-        function  maybe_specialize(var pd:tprocdef;spezcontext:tspecializationcontext):boolean;
       public
         constructor create(sym:tprocsym;st:TSymtable;ppn:tnode;ignorevisibility,allowdefaultparas,objcidcall,explicitunit,searchhelpers,anoninherited:boolean;spezcontext:tspecializationcontext);
         constructor create_operator(op:ttoken;ppn:tnode);
@@ -98,6 +98,10 @@ interface
         function  choose_best(var bestpd:tabstractprocdef; singlevariant: boolean):integer;
         procedure find_wrong_para;
         property  Count:integer read FProcCnt;
+        { list of symbols for anonymous types required for implicit specializations;
+          these should be handed over to the picked procdef, otherwise they'll be
+          freed by tcallcandidates }
+        property para_anon_syms:tfplist read FParaAnonSyms;
       end;
 
     type
@@ -2205,8 +2209,17 @@ implementation
         hp : pcandidate;
         psym : tprocsym;
         i : longint;
+        sym : tsym;
       begin
         FIgnoredCandidateProcs.free;
+        { free any symbols for anonymous parameter types that we're used for
+          specialization when no specialization was picked }
+        if assigned(FParaAnonSyms) then
+          begin
+            for i := 0 to FParaAnonSyms.count-1 do
+              tsym(FParaAnonSyms[i]).free;
+            FParaAnonSyms.free;
+          end;
         hp:=FCandidateProcs;
         while assigned(hp) do
          begin
@@ -2245,10 +2258,14 @@ implementation
           { add all definitions }
           result:=false;
           foundanything:=false;
+          { try to specialize the procsym }
+          if srsym.could_be_implicitly_specialized and
+            try_implicit_specialization(srsym,FParaNode,ProcdefOverloadList,FParaAnonSyms,tsym(FProcsym),result) then
+            foundanything:=true;
           for j:=0 to srsym.ProcdefList.Count-1 do
             begin
               pd:=tprocdef(srsym.ProcdefList[j]);
-              if not maybe_specialize(pd,spezcontext) then
+              if not finalize_specialization(pd,spezcontext) then
                 continue;
               if (po_ignore_for_overload_resolution in pd.procoptions) then
                 begin
@@ -2467,14 +2484,19 @@ implementation
                 srsym:=tsym(srsymtable.FindWithHash(hashedid));
                 if assigned(srsym) and
                    (srsym.typ=procsym) and
-                   (tprocsym(srsym).procdeflist.count>0) then
+                   (
+                     (tprocsym(srsym).procdeflist.count>0) or 
+                     (sp_generic_dummy in srsym.symoptions)
+                   ) then
                   begin
                     { add all definitions }
                     hasoverload:=false;
+                    if tprocsym(srsym).could_be_implicitly_specialized then
+                      try_implicit_specialization(srsym,FParaNode,ProcdefOverloadList,FParaAnonSyms,tsym(FProcsym),hasoverload);
                     for j:=0 to tprocsym(srsym).ProcdefList.Count-1 do
                       begin
                         pd:=tprocdef(tprocsym(srsym).ProcdefList[j]);
-                        if not maybe_specialize(pd,spezcontext) then
+                        if not finalize_specialization(pd,spezcontext) then
                           continue;
                         if (po_ignore_for_overload_resolution in pd.procoptions) then
                           begin
@@ -2784,33 +2806,6 @@ implementation
       end;
 
 
-    function tcallcandidates.maybe_specialize(var pd:tprocdef;spezcontext:tspecializationcontext):boolean;
-      var
-        def : tdef;
-      begin
-        result:=false;
-        if assigned(spezcontext) then
-          begin
-            if not (df_generic in pd.defoptions) then
-              internalerror(2015060301);
-            { check whether the given parameters are compatible
-              to the def's constraints }
-            if not check_generic_constraints(pd,spezcontext.paramlist,spezcontext.poslist) then
-              exit;
-            def:=generate_specialization_phase2(spezcontext,pd,false,'');
-            case def.typ of
-              errordef:
-                { do nothing }
-                ;
-              procdef:
-                pd:=tprocdef(def);
-              else
-                internalerror(2015070303);
-            end;
-          end;
-        result:=true;
-      end;
-
     procedure tcallcandidates.list(all:boolean);
       var
         hp : pcandidate;
@@ -3331,6 +3326,15 @@ implementation
                                      res:=-1
                                    else
                                     res:=0;
+                                   { if a specialization is better than a non-specialization then
+                                     the non-generic always wins }
+                                   if m_implicit_function_specialization in current_settings.modeswitches then
+                                     begin
+                                       if (currpd^.data.is_specialization and not bestpd^.data.is_specialization) then
+                                         res:=-1
+                                       else if (not currpd^.data.is_specialization and bestpd^.data.is_specialization) then
+                                         res:=1;
+                                     end;
                                  end;
                               end;
                            end;
@@ -3611,6 +3615,13 @@ implementation
         if (compare_paras(pd^.data.paras,bestpd^.data.paras,cp_value_equal_const,cpoptions)>=te_equal) and
           (not(po_objc in bestpd^.data.procoptions) or (bestpd^.data.messageinf.str^=pd^.data.messageinf.str^)) then
           compare_by_old_sortout_check := 1; // bestpd was sorted out before patch
+
+        { for implicit specializations non-generics should take precedence so
+          when comparing a specialization to a non-specialization mark as undecided
+          and it will be re-evaluated in is_better_candidate }
+        if (m_implicit_function_specialization in current_settings.modeswitches)
+          and (pd^.data.is_specialization <> bestpd^.data.is_specialization) then
+          compare_by_old_sortout_check:=0;
      end;
 
     function decide_restart(pd,bestpd:pcandidate) : boolean;

+ 1 - 1
compiler/ncal.pas

@@ -3867,7 +3867,7 @@ implementation
                      ensure that it is }
                    procdefinition.register_def;
                    if procdefinition.is_specialization and (procdefinition.typ=procdef) then
-                     maybe_add_pending_specialization(procdefinition);
+                     maybe_add_pending_specialization(procdefinition,candidates.para_anon_syms);
 
                    candidates.free;
                  end; { end of procedure to call determination }

+ 8 - 0
compiler/nld.pas

@@ -1121,6 +1121,7 @@ implementation
         hdef  : tdef;
         hp    : tarrayconstructornode;
         len   : longint;
+        diff,
         varia : boolean;
         eq    : tequaltype;
         hnodetype : tnodetype;
@@ -1144,6 +1145,7 @@ implementation
         hnodetype:=errorn;
         len:=0;
         varia:=false;
+        diff:=false;
         if assigned(left) then
          begin
            hp:=self;
@@ -1172,6 +1174,10 @@ implementation
                    end
                  else
                    eq:=compare_defs(hdef,hp.left.resultdef,hp.left.nodetype);
+                 { the element is not compatible with the previous element
+                   which means the constructor is array of const }
+                 if eq=te_incompatible then
+                   diff:=true;
                  if (not varia) and (eq<te_equal) then
                    begin
                      { If both are integers we need to take the type that can hold both
@@ -1202,6 +1208,8 @@ implementation
          include(tarraydef(resultdef).arrayoptions,ado_IsConstructor);
          if varia then
            include(tarraydef(resultdef).arrayoptions,ado_IsVariant);
+         if diff then
+           include(tarraydef(resultdef).arrayoptions,ado_IsArrayOfConst);
          tarraydef(resultdef).elementdef:=hdef;
       end;
 

+ 672 - 36
compiler/pgenutil.pas

@@ -33,6 +33,8 @@ uses
   globtype,
   { parser }
   pgentype,
+  { node }
+  node,
   { symtable }
   symtype,symdef,symbase;
 
@@ -52,10 +54,12 @@ uses
     procedure add_generic_dummysym(sym:tsym);
     function resolve_generic_dummysym(const name:tidstring):tsym;
     function could_be_generic(const name:tidstring):boolean;inline;
+    function try_implicit_specialization(sym:tsym;para:tnode;pdoverloadlist:tfpobjectlist;var unnamed_syms:tfplist;var first_procsym:tsym;var hasoverload:boolean):boolean;
+    function finalize_specialization(var pd:tprocdef;spezcontext:tspecializationcontext):boolean;
 
     procedure generate_specialization_procs;
     procedure generate_specializations_for_forwarddef(def:tdef);
-    procedure maybe_add_pending_specialization(def:tdef);
+    procedure maybe_add_pending_specialization(def:tdef;unnamed_syms:tfplist);
     function determine_generic_def(const name:tidstring):tstoreddef;
 
     procedure specialization_init(genericdef:tdef;var state:tspecializationstate);
@@ -72,7 +76,8 @@ uses
   symconst,symsym,symtable,defcmp,defutil,procinfo,
   { modules }
   fmodule,
-  node,nobj,ncon,
+  { node }
+  nobj,ncon,ncal,
   { parser }
   scanner,
   pbase,pexpr,pdecsub,ptype,psub,pparautl,pdecl;
@@ -83,6 +88,37 @@ uses
     tgeneric_param_const_types : tdeftypeset = [orddef,stringdef,floatdef,setdef,pointerdef,enumdef];
     tgeneric_param_nodes : tnodetypeset = [typen,ordconstn,stringconstn,realconstn,setconstn,niln];
 
+    procedure make_prettystring(paramtype:tdef;first:boolean;constprettyname:ansistring;var prettyname,specializename:ansistring);
+      var
+        namepart : string;
+        prettynamepart : ansistring;
+        module : tmodule;
+      begin
+        module:=find_module_from_symtable(paramtype.owner);
+        if not assigned(module) then
+          internalerror(2016112802);
+        namepart:='_$'+hexstr(module.moduleid,8)+'$$'+paramtype.unique_id_str;
+        { we use the full name of the type to uniquely identify it }
+        if (symtablestack.top.symtabletype=parasymtable) and
+            (symtablestack.top.defowner.typ=procdef) and
+            (paramtype.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:=paramtype.fullownerhierarchyname(true);
+          end;
+        specializename:=specializename+namepart;
+        if not first then
+          prettyname:=prettyname+',';
+        if constprettyname<>'' then
+          prettyname:=prettyname+constprettyname
+        else
+          prettyname:=prettyname+prettynamepart+paramtype.typesym.prettyname;
+      end;
+
     function get_generic_param_def(sym:tsym):tdef;
       begin
         if sym.typ=constsym then
@@ -497,14 +533,13 @@ uses
         parampos : pfileposinfo;
         tmpparampos : tfileposinfo;
         namepart : string;
-        prettynamepart : ansistring;
         module : tmodule;
         constprettyname : string;
         validparam : boolean;
       begin
         result:=true;
         prettyname:='';
-        prettynamepart:='';
+        constprettyname:='';
         if paramlist=nil then
           internalerror(2012061401);
         { set the block type to type, so that the parsed type are returned as
@@ -571,34 +606,7 @@ uses
                             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 typeparam.nodetype=typen then
-                          begin
-                            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+',';
-                        if constprettyname<>'' then
-                          prettyname:=prettyname+constprettyname
-                        else
-                          prettyname:=prettyname+prettynamepart+typeparam.resultdef.typesym.prettyname;
+                        make_prettystring(typeparam.resultdef,first,constprettyname,prettyname,specializename);
                       end;
                   end
                 else
@@ -636,6 +644,633 @@ uses
       end;
 
 
+    function finalize_specialization(var pd:tprocdef;spezcontext:tspecializationcontext):boolean;
+      var
+        def : tdef;
+      begin
+        result:=false;
+        if assigned(spezcontext) then
+          begin
+            if not (df_generic in pd.defoptions) then
+              internalerror(2015060301);
+            { check whether the given parameters are compatible
+              to the def's constraints }
+            if not check_generic_constraints(pd,spezcontext.paramlist,spezcontext.poslist) then
+              exit;
+            def:=generate_specialization_phase2(spezcontext,pd,false,'');
+            case def.typ of
+              errordef:
+                { do nothing }
+                ;
+              procdef:
+                pd:=tprocdef(def);
+              else
+                internalerror(2015070303);
+            end;
+          end;
+        result:=true;
+      end;
+
+
+    procedure transfer_unnamed_symbols(owner:tsymtable;unnamed_syms:tfplist);
+      var
+        i : integer;
+        sym : tsym;
+      begin
+        for i:=0 to unnamed_syms.count-1 do
+          begin
+            sym:=tsym(unnamed_syms[i]);
+            sym.ChangeOwnerAndName(owner.symlist,sym.realname);
+          end;
+        unnamed_syms.clear;
+      end;
+
+
+    function try_implicit_specialization(sym:tsym;para:tnode;pdoverloadlist:tfpobjectlist;var unnamed_syms:tfplist;var first_procsym:tsym;var hasoverload:boolean):boolean;
+
+      { hash key for generic parameter lookups }
+      function generic_param_hash(def:tdef):string;inline;
+        begin
+          result:=def.typename;
+        end;
+
+      { returns true if the def a literal array such as [1,2,3] and not a shortstring }
+      function is_array_literal(def:tdef):boolean;
+        begin
+          result:=(def.typ=arraydef) and not is_conststring_array(def);
+        end;
+
+      { makes the specialization context from the generic proc def and generic params }
+      procedure generate_implicit_specialization(out context:tspecializationcontext;genericdef:tprocdef;genericparams:tfphashlist);
+        var
+          parsedpos:tfileposinfo;
+          poslist:tfplist;
+          i: longint;
+          paramtype: ttypesym;
+          parampos : pfileposinfo;
+          tmpparampos : tfileposinfo;
+          paramname: string;
+        begin
+          context:=tspecializationcontext.create;
+          fillchar(parsedpos,sizeof(parsedpos),0);
+          poslist:=context.poslist;
+          tmpparampos:=current_filepos;
+          if genericparams.count<>genericdef.genericparas.count then
+            internalerror(2021020901);
+          for i:=0 to genericparams.count-1 do
+            begin
+              paramname:=generic_param_hash(ttypesym(genericdef.genericparas[i]).typedef);
+              paramtype:=ttypesym(genericparams.find(paramname));
+              if not assigned(paramtype) then
+                internalerror(2021020902);
+              new(parampos);
+              parampos^:=tmpparampos;
+              poslist.add(parampos);
+              context.paramlist.Add(paramtype);
+              make_prettystring(paramtype.typedef,i=0,'',context.prettyname,context.specializename);
+            end;
+          context.genname:=genericdef.procsym.realname;
+        end;
+
+      { specialization context parameter lists require a typesym so we need
+        to generate a placeholder for unnamed constant types like
+        short strings, open arrays, function pointers etc... }
+      function create_unnamed_typesym(def:tdef):tsym;
+        var
+          newtype: tsym;
+        begin
+          newtype:=nil;
+          if is_conststring_array(def) then
+            begin
+              { for constant strings we need to respect various modeswitches }
+              if (cs_refcountedstrings in current_settings.localswitches) then
+                begin
+                  if m_default_unicodestring in current_settings.modeswitches then
+                    newtype:=cunicodestringtype.typesym
+                  else
+                    newtype:=cansistringtype.typesym;
+                end
+              else
+                newtype:=cshortstringtype.typesym;
+            end
+          else if def.typ=stringdef then
+            newtype:=tstringdef(def).get_default_string_type.typesym
+          else
+            begin
+              newtype:=ctypesym.create(def.fullownerhierarchyname(false)+typName[def.typ]+'$'+def.unique_id_str,def);
+              newtype.owner:=def.owner;
+            end;
+          if not assigned(newtype) then
+            internalerror(2021020904);
+          result:=newtype;
+        end;
+
+      { searches for the generic param in specializations }
+      function find_param_in_specialization(owner:tprocdef;genericparam:ttypesym;def:tstoreddef):boolean;
+        var
+          parasym: ttypesym;
+          k, i: integer;
+        begin
+          result:=false;
+          for i:=0 to def.genericparas.count-1 do
+            begin
+              parasym:=ttypesym(def.genericparas[i]);
+              { the generic param must have a named typesym }
+              if not assigned(parasym.typedef.typesym) then
+                internalerror(2021020907);
+              { recurse into inline specialization }
+              if tstoreddef(parasym.typedef).is_specialization then
+                begin
+                  result:=find_param_in_specialization(owner,genericparam,tstoreddef(parasym.typedef));
+                  if result then
+                    exit;
+                end
+              else if (genericparam=parasym.typedef.typesym) and owner.is_generic_param(parasym.typedef) then
+                exit(true);
+            end;
+        end;
+
+      { searches for the generic param in arrays }
+      function find_param_in_array(owner:tprocdef;genericparam:ttypesym;def:tarraydef):boolean;
+        var
+          elementdef:tstoreddef;
+        begin
+          elementdef:=tstoreddef(def.elementdef);
+          { recurse into multi-dimensional array }
+          if elementdef.typ=arraydef then
+            result:=find_param_in_array(owner,genericparam,tarraydef(elementdef))
+          { something went wrong during parsing and the element is invalid }
+          else if elementdef.typ=errordef then
+            result:=false
+          else
+            begin
+              { the element must have a named typesym }
+              if not assigned(elementdef.typesym) then
+                internalerror(2021020906);
+              result:=(genericparam=elementdef.typesym) and owner.is_generic_param(elementdef);
+            end;
+        end;
+
+      { tests if the generic param is used in the parameter list }
+      function is_generic_param_used(owner:tprocdef;genericparam:ttypesym;paras:tfplist):boolean;
+        var
+          paravar:tparavarsym;
+          i: integer;
+        begin
+          result:=false;
+          for i:=0 to paras.count-1 do
+            begin
+              paravar:=tparavarsym(paras[i]);
+
+              { handle array types by using element types (for example: array of T) }
+              if paravar.vardef.typ=arraydef then
+                result:=find_param_in_array(owner,genericparam,tarraydef(paravar.vardef))
+              { for specializations check search in generic params }
+              else if tstoreddef(paravar.vardef).is_specialization then
+                result:=find_param_in_specialization(owner,genericparam,tstoreddef(paravar.vardef))
+              { something went wrong during parsing and the parameter is invalid }
+              else if paravar.vardef.typ=errordef then
+                exit(false)
+              else
+                begin
+                  if not assigned(paravar.vardef.typesym) then
+                    internalerror(2021020905);
+                  result:=(genericparam=paravar.vardef.typesym) and owner.is_generic_param(paravar.vardef)
+                end;
+
+              { exit if we find a used parameter }
+              if result then
+                exit;
+            end;
+        end;
+
+      { handle generic specializations by using generic params from caller
+        to specialize the target. for example "TRec<Integer>" can use "Integer"
+        to specialize "TRec<T>" with "Integer" for "T". }
+      procedure handle_specializations(genericparams:tfphashlist;target_def,caller_def:tstoreddef);
+        var
+          i,
+          index : integer;
+          key : string;
+          target_param,
+          caller_param : ttypesym;
+        begin
+          { the target and the caller must the same generic def 
+            with the same set of generic parameters }
+          if target_def.genericdef<>caller_def.genericdef then
+            internalerror(2021020909);
+
+          for i:=0 to target_def.genericparas.count-1 do
+            begin
+              target_param:=ttypesym(target_def.genericparas[i]);
+              caller_param:=ttypesym(caller_def.genericparas[i]);
+
+              { reject generics with constants }
+              if (target_param.typ=constsym) or (caller_param.typ=constsym) then
+                exit;
+
+              key:=generic_param_hash(target_param.typedef);
+
+              { the generic param is already used }
+              index:=genericparams.findindexof(key);
+              if index>=0 then
+                continue;
+
+              { add the type to the generic params }
+              genericparams.add(key,caller_param);
+            end;
+        end;
+
+      { specialize arrays by using element types but arrays may be multi-dimensional 
+        so we need to examine the caller/target pairs recursively in order to
+        verify the dimensionality is equal }
+      function handle_arrays(owner:tprocdef;target_def,caller_def:tarraydef;out target_element,caller_element:tdef):boolean;
+        begin
+          { the target and the caller are both arrays and the target is a 
+            specialization so we can recurse into the targets element def }
+          if is_array_literal(target_def.elementdef) and 
+            is_array_literal(caller_def.elementdef) and 
+            target_def.is_specialization then
+            result:=handle_arrays(owner,tarraydef(target_def.elementdef),tarraydef(caller_def.elementdef),target_element,caller_element)
+          else
+            begin
+              { the caller is an array which means the dimensionality is unbalanced
+                and thus the arrays are compatible }
+              if is_array_literal(caller_def.elementdef) then
+                exit(false);
+              { if the element is a generic param then return this type
+                along with the caller element type at the same level }
+              result:=owner.is_generic_param(target_def.elementdef);
+              if result then
+                begin
+                  target_element:=target_def.elementdef;
+                  caller_element:=caller_def.elementdef;
+                end;
+            end;
+        end;
+
+      { handle procvars by using the parameters from the caller to specialize
+        the parameters of the target generic procedure specialization. for example:
+
+          type generic TProc<S> = procedure(value: S);
+          generic procedure Run<T>(proc: specialize TProc<T>);
+          procedure DoCallback(value: integer);
+          Run(@DoCallback);
+
+        will specialize as Run<integer> because the signature
+        of DoCallback() matches TProc<S> so we can specialize "S"
+        with "integer", as they are both parameter #1
+      }
+
+      function handle_procvars(genericparams:tfphashlist;callerparams:tfplist;target_def:tdef;caller_def:tdef):boolean;
+        var
+          i,j : integer;
+          paravar : tparavarsym;
+          target_proc,
+          caller_proc : tprocvardef;
+          target_proc_para,
+          caller_proc_para : tparavarsym;
+          newparams : tfphashlist;
+          key : string;
+          index : integer;
+          valid_params : integer;
+        begin
+          result := false;
+
+          target_proc:=tprocvardef(target_def);
+          caller_proc:=tprocvardef(caller_def);
+
+          { parameter count must match exactly
+            currently default values are not considered }
+          if target_proc.paras.count<>caller_proc.paras.count then
+            exit;
+
+          { reject generics with constants }
+          for i:=0 to target_proc.genericdef.genericparas.count-1 do
+            if tsym(target_proc.genericdef.genericparas[i]).typ=constsym then
+              exit;
+
+          newparams:=tfphashlist.create;
+          valid_params:=0;
+
+          for i:=0 to target_proc.paras.count-1 do
+            begin
+              target_proc_para:=tparavarsym(target_proc.paras[i]);
+              caller_proc_para:=tparavarsym(caller_proc.paras[i]);
+
+              { the parameters are not compatible }
+              if compare_defs(caller_proc_para.vardef,target_proc_para.vardef,nothingn)=te_incompatible then
+                begin
+                  newparams.free;
+                  exit(false);
+                end;
+
+              if sp_generic_para in target_proc_para.vardef.typesym.symoptions then
+                begin
+                  paravar:=tparavarsym(tprocvardef(target_proc.genericdef).paras[i]);
+
+                  { find the generic param name in the generic def parameters }
+                  j:=target_proc.genericdef.genericparas.findindexof(paravar.vardef.typesym.name);
+
+                  target_def:=tparavarsym(target_proc.paras[j]).vardef;
+                  caller_def:=caller_proc_para.vardef;
+
+                  if not assigned(caller_def.typesym) then
+                    internalerror(2021020908);
+
+                  key:=generic_param_hash(target_def);
+
+                  { the generic param must not already be used }
+                  index:=genericparams.findindexof(key);
+                  if index<0 then
+                    begin
+                      { add the type to the list }
+                      index:=newparams.findindexof(key);
+                      if index<0 then
+                        newparams.add(key,caller_def.typesym);
+                    end;
+                end;
+
+              inc(valid_params);
+            end;
+
+          { if the count of valid params matches the target then
+            transfer the temporary params to the actual params }
+          result:=valid_params=target_proc.paras.count;
+          if result then
+            for i := 0 to newparams.count-1 do
+              genericparams.add(newparams.nameofindex(i),newparams[i]);
+
+          newparams.free;
+        end;
+
+      { compare generic parameters <T> with call node parameters. }
+      function is_possible_specialization(callerparams:tfplist;genericdef:tprocdef;out unnamed_syms:tfplist;out genericparams:tfphashlist):boolean;
+        var
+          i,j,
+          count : integer;
+          paravar : tparavarsym;
+          target_def,
+          caller_def : tdef;
+          target_key : string;
+          index : integer;
+          paras : tfplist;
+          target_element,
+          caller_element : tdef;
+          required_param_count : integer;
+          adef : tarraydef;
+        begin
+          result:=false;
+          paras:=nil;
+          genericparams:=nil;
+          required_param_count:=0;
+          unnamed_syms:=nil;
+
+          { first perform a check to reject generics with constants }
+          for i:=0 to genericdef.genericparas.count-1 do
+            if tsym(genericdef.genericparas[i]).typ=constsym then
+              exit;
+
+          { build list of visible target function parameters }
+          paras:=tfplist.create;
+          for i:=0 to genericdef.paras.count-1 do
+            begin
+              paravar:=tparavarsym(genericdef.paras[i]);
+              { ignore hidden parameters }
+              if vo_is_hidden_para in paravar.varoptions then
+                continue;
+              paras.add(paravar);
+
+              { const non-default parameters are required }
+              if not assigned(paravar.defaultconstsym) then
+                inc(required_param_count);
+            end;
+
+          { not enough parameters were supplied }
+          if callerparams.count<required_param_count then
+            begin
+              paras.free;
+              exit;
+            end;
+
+          { check to make sure the generic parameters are all used 
+            at least once in the  caller parameters. }
+          count:=0;
+          for i:=0 to genericdef.genericparas.count-1 do
+            if is_generic_param_used(genericdef,ttypesym(genericdef.genericparas[i]),paras) then
+              inc(count);
+
+          if count<genericdef.genericparas.count then
+            begin
+              paras.free;
+              exit;
+            end;
+
+          genericparams:=tfphashlist.create;
+          for i:=0 to callerparams.count-1 do
+            begin
+              caller_def:=ttypesym(callerparams[i]).typedef;
+
+              { caller parameter exceeded the possible parameters }
+              if i=paras.count then
+                begin
+                  genericparams.free;
+                  paras.free;
+                  exit;
+                end;
+
+              target_def:=tparavarsym(paras[i]).vardef;
+              target_key:='';
+
+              { strings are compatible with "array of T" so we 
+                need to use the element type for specialization }
+              if is_stringlike(caller_def) and
+                is_array_literal(target_def) and
+                genericdef.is_generic_param(tarraydef(target_def).elementdef) then
+                begin
+                  target_def:=tarraydef(target_def).elementdef;
+                  target_key:=generic_param_hash(target_def);
+                  caller_def:=tstringdef(caller_def).get_default_char_type;
+                end
+              { non-uniform array constructors (i.e. array of const) are not compatible 
+                with normal arrays like "array of T" so we reject them }
+              else if is_array_literal(target_def) and
+                (caller_def.typ=arraydef) and 
+                (ado_IsConstructor in tarraydef(caller_def).arrayoptions) and
+                (ado_IsArrayOfConst in tarraydef(caller_def).arrayoptions) then
+                begin
+                  continue;
+                end
+              { handle generic arrays }
+              else if is_array_literal(caller_def) and
+                is_array_literal(target_def) and
+                handle_arrays(genericdef,tarraydef(target_def),tarraydef(caller_def),target_element,caller_element) then
+                begin
+                  target_def:=target_element;
+                  caller_def:=caller_element;
+                  target_key:=generic_param_hash(target_def);
+                end
+              { handle generic procvars }
+              else if (caller_def.typ=procvardef) and 
+                (target_def.typ=procvardef) and 
+                tprocvardef(target_def).is_specialization and
+                handle_procvars(genericparams,callerparams,target_def,caller_def) then
+                begin
+                  continue;
+                end
+              { handle specialized objects by taking the base class as the type to specialize }    
+              else if is_class_or_object(caller_def) and 
+                is_class_or_object(target_def) and
+                genericdef.is_generic_param(target_def) then
+                begin
+                  target_key:=generic_param_hash(target_def);
+                  target_def:=tobjectdef(target_def).childof;
+                end
+              { handle generic specializations }
+              else if tstoreddef(caller_def).is_specialization and 
+                tstoreddef(target_def).is_specialization and
+                (tstoreddef(caller_def).genericdef=tstoreddef(target_def).genericdef) then
+                begin
+                  handle_specializations(genericparams,tstoreddef(target_def),tstoreddef(caller_def));
+                  continue;
+                end
+              { handle all other generic params }
+              else if target_def.typ=undefineddef then
+                target_key:=generic_param_hash(target_def);
+
+              { the param doesn't have a generic key which means we don't need to consider it }
+              if target_key='' then
+                continue;
+
+              { the generic param is already used }
+              index:=genericparams.findindexof(target_key);
+              if index>=0 then
+                continue;
+
+              { the caller type may not have a typesym so we need to create an unnamed one }
+              if not assigned(caller_def.typesym) then
+                begin
+                  sym:=create_unnamed_typesym(caller_def);
+                  { add the unnamed sym to the list but only it was allocated manually }
+                  if sym.owner=caller_def.owner then
+                    begin
+                      if not assigned(unnamed_syms) then
+                        unnamed_syms:=tfplist.create;
+                      unnamed_syms.add(sym);
+                    end;
+                  genericparams.add(target_key,sym);
+                end
+              else
+                genericparams.add(target_key,caller_def.typesym);
+            end;
+
+          { if the parameter counts match then the specialization is possible }
+          result:=genericparams.count=genericdef.genericparas.count;
+
+          { cleanup }
+          paras.free;
+          if not result then
+            genericparams.free;
+        end;
+
+      { make an ordered list of parameters from the caller }
+      function make_param_list(dummysym:tsym;para:tnode;var unnamed_syms:tfplist):tfplist;
+        var
+          pt : tcallparanode;
+          paradef : tdef;
+          sym : tsym;
+          i : integer;
+        begin
+          result:=tfplist.create;
+          pt:=tcallparanode(para);
+          while assigned(pt) do
+            begin
+              paradef:=pt.paravalue.resultdef;
+              { unnamed parameter types can not be specialized }
+              if not assigned(paradef.typesym) then
+                begin
+                  sym:=create_unnamed_typesym(paradef);
+                  result.insert(0,sym);
+                  { add the unnamed sym to the list but only if it was allocated manually }
+                  if sym.owner=paradef.owner then
+                    begin
+                      if not assigned(unnamed_syms) then
+                        unnamed_syms:=tfplist.create;
+                      unnamed_syms.add(sym);
+                    end;
+                end
+              else
+                result.insert(0,paradef.typesym);
+              pt:=tcallparanode(pt.nextpara);
+            end;
+        end;
+
+      var
+        i,j,k : integer;
+        srsym : tprocsym;
+        callerparams : tfplist;
+        pd : tprocdef;
+        dummysym : tprocsym;
+        genericparams : tfphashlist;
+        spezcontext : tspecializationcontext;
+        pd_unnamed_syms : tfplist;
+      begin
+        result:=false;
+        spezcontext:=nil;
+        genericparams:=nil;
+        dummysym:=tprocsym(sym);
+        callerparams:=make_param_list(dummysym,para,unnamed_syms);
+
+        { failed to build the parameter list }
+        if not assigned(callerparams) then
+          exit;
+
+        for i:=0 to dummysym.genprocsymovlds.count-1 do
+          begin
+            srsym:=tprocsym(dummysym.genprocsymovlds[i]);
+            for j:=0 to srsym.ProcdefList.Count-1 do
+              begin
+                pd:=tprocdef(srsym.ProcdefList[j]);
+                if is_possible_specialization(callerparams,pd,pd_unnamed_syms,genericparams) then
+                  begin
+                    generate_implicit_specialization(spezcontext,pd,genericparams);
+                    genericparams.free;
+                    { finalize the specialization so it can be added to the list of overloads }
+                    if not finalize_specialization(pd,spezcontext) then
+                      begin
+                        spezcontext.free;
+                        continue;
+                      end;
+                    { handle unnamed syms used by the specialization }
+                    if pd_unnamed_syms<>nil then
+                      begin
+                        transfer_unnamed_symbols(pd.owner,pd_unnamed_syms);
+                        pd_unnamed_syms.free;
+                      end;
+                    pdoverloadlist.add(pd);
+                    spezcontext.free;
+                    if po_overload in pd.procoptions then
+                      hasoverload:=true;
+                    { store first procsym found }
+                    if not assigned(first_procsym) then
+                      first_procsym:=srsym;
+                    result:=true;
+                  end
+                else
+                  begin
+                    { the specialization was not chosen so clean up any unnamed syms }
+                    if pd_unnamed_syms<>nil then
+                      begin
+                        for k:=0 to pd_unnamed_syms.count-1 do
+                          tsym(pd_unnamed_syms[k]).free;
+                        pd_unnamed_syms.free;
+                      end;
+                  end;
+              end;
+          end;
+        callerparams.free;
+      end;
+
     function generate_specialization_phase1(out context:tspecializationcontext;genericdef:tdef):tdef;
       var
         dummypos : tfileposinfo;
@@ -850,9 +1485,6 @@ uses
             st : TSymtable;
             i : longint;
           begin
-            { since commit 48986 deflist might have NIL entries }
-            if not assigned(def) then
-              exit;
             case def.typ of
               procdef:
                 tprocdef(def).forwarddef:=false;
@@ -2192,13 +2824,17 @@ uses
       end;
 
 
-    procedure maybe_add_pending_specialization(def:tdef);
+    procedure maybe_add_pending_specialization(def:tdef;unnamed_syms: tfplist);
       var
         hmodule : tmodule;
         st : tsymtable;
+        i : integer;
       begin
         if parse_generic then
           exit;
+        { transfer ownership of any unnamed syms to be the specialization }
+        if unnamed_syms<>nil then
+          transfer_unnamed_symbols(tprocdef(def).parast,unnamed_syms);
         st:=def.owner;
         while st.symtabletype in [localsymtable] do
           st:=st.defowner.owner;

+ 50 - 0
compiler/symdef.pas

@@ -721,6 +721,8 @@ interface
           procedure declared_far;virtual;
           procedure declared_near;virtual;
           function generate_safecall_wrapper: boolean; virtual;
+          { returns true if the def is a generic param of the procdef }
+          function is_generic_param(def:tdef): boolean;
        private
           procedure count_para(p:TObject;arg:pointer);
           procedure insert_para(p:TObject;arg:pointer);
@@ -991,6 +993,10 @@ interface
           function alignment : shortint;override;
           function  needs_inittable : boolean;override;
           function  getvardef:longint;override;
+          { returns the default char type def for the string }
+          function get_default_char_type: tdef;
+          { returns the default string type }
+          function get_default_string_type: tdef;
        end;
        tstringdefclass = class of tstringdef;
 
@@ -2735,6 +2741,39 @@ implementation
       end;
 
 
+    function tstringdef.get_default_char_type: tdef;
+      begin
+        case stringtype of
+          st_shortstring,
+          st_longstring,
+          st_ansistring:
+            result:=cansichartype;
+          st_unicodestring,
+          st_widestring:
+            result:=cwidechartype;
+        end;
+      end;
+
+
+    function tstringdef.get_default_string_type: tdef;
+      begin
+        case stringtype of
+          st_shortstring:
+            result:=cshortstringtype;
+          { st_longstring is currently not supported but 
+            when it is this case will need to be supplied }
+          st_longstring:
+            internalerror(2021040801);
+          st_ansistring:
+            result:=cansistringtype;
+          st_widestring:
+            result:=cwidestringtype;
+          st_unicodestring:
+            result:=cunicodestringtype;
+        end
+      end;
+
+
     function tstringdef.alignment : shortint;
       begin
         case stringtype of
@@ -5974,6 +6013,17 @@ implementation
       end;
 
 
+    function tabstractprocdef.is_generic_param(def:tdef): boolean;
+      begin
+        if def.typ=undefineddef then
+          result:=def.owner=self.parast
+        else if def.typ=objectdef then
+          result:=(def.owner=self.parast) and (tstoreddef(def).genconstraintdata<>nil)
+        else
+          result:=false;
+      end;
+
+
 {***************************************************************************
                                   TPROCDEF
 ***************************************************************************}

+ 9 - 2
compiler/symsym.pas

@@ -156,6 +156,7 @@ interface
           function find_procdef_assignment_operator(fromdef,todef:tdef;var besteq:tequaltype;isexplicit:boolean):Tprocdef;
           function find_procdef_enumerator_operator(fromdef,todef:tdef;var besteq:tequaltype):Tprocdef;
           procedure add_generic_overload(sym:tprocsym);
+          function could_be_implicitly_specialized:boolean;inline;
           property ProcdefList:TFPObjectList read FProcdefList;
           { only valid if sp_generic_dummy is set and either an overload was
             added using add_generic_overload or this was loaded from a ppu }
@@ -1098,7 +1099,6 @@ implementation
           end;
       end;
 
-
     function Tprocsym.Find_procdef_bytype(pt:Tproctypeoption):Tprocdef;
       var
         i  : longint;
@@ -1477,6 +1477,13 @@ implementation
       end;
 
 
+    function tprocsym.could_be_implicitly_specialized:boolean;
+      begin
+        result:=(m_implicit_function_specialization in current_settings.modeswitches) and 
+                (sp_generic_dummy in symoptions) and
+                assigned(genprocsymovlds);          
+      end;
+
 {****************************************************************************
                                   TERRORSYM
 ****************************************************************************}
@@ -1862,7 +1869,7 @@ implementation
                   (varregable <> vr_none)) or
                  (not refpara and
                   not(varregable in [vr_none,vr_addr])))
-{$if not defined(powerpc) and not defined(powerpc64) and not defined(aarch64)}
+{$if not defined(powerpc) and not defined(powerpc64)}
                 and ((vardef.typ <> recorddef) or
                      (varregable = vr_addr) or
                      tabstractrecordsymtable(tabstractrecorddef(vardef).symtable).has_single_field(tempdef) or

+ 11 - 1
compiler/symtable.pas

@@ -3368,8 +3368,8 @@ implementation
       begin
         if sym.typ=procsym then
           begin
-            { A procsym is visible, when there is at least one of the procdefs visible }
             result:=false;
+            { A procsym is visible, when there is at least one of the procdefs visible }
             for i:=0 to tprocsym(sym).ProcdefList.Count-1 do
               begin
                 pd:=tprocdef(tprocsym(sym).ProcdefList[i]);
@@ -3380,6 +3380,16 @@ implementation
                     exit;
                   end;
               end;
+            { check dummy sym visbility by following associated procsyms }
+            if tprocsym(sym).could_be_implicitly_specialized then
+              begin
+                for i:=0 to tprocsym(sym).genprocsymovlds.count-1 do
+                  if is_visible_for_object(tsym(tprocsym(sym).genprocsymovlds[i]),contextobjdef) then
+                    begin
+                      result:=true;
+                      exit;
+                    end;
+              end;
             if (tprocsym(sym).procdeflist.count=0) and (sp_generic_dummy in tprocsym(sym).symoptions) then
               result:=is_visible_for_object(sym.owner,sym.visibility,contextobjdef);
           end

+ 2 - 1
compiler/utils/ppuutils/ppudump.pp

@@ -2434,7 +2434,8 @@ const
          'm_multi_helpers',       { helpers can appear in multiple scopes simultaneously }
          'm_array2dynarray',      { regular arrays can be implicitly converted to dynamic arrays }
          'm_prefixed_attributes', { enable attributes that are defined before the type they belong to }
-         'm_underscoreisseparator'{ _ can be used as separator to group digits in numbers }
+         'm_underscoreisseparator',{ _ can be used as separator to group digits in numbers }
+         'm_implicit_function_specialization' { attempt to specialize generic function by inferring types from parameters }
        );
        { optimizer }
        optimizerswitchname : array[toptimizerswitch] of string[50] =

+ 31 - 0
tests/test/timpfuncspez1.pp

@@ -0,0 +1,31 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test precedence
+}
+
+program timpfuncspez1;
+
+function Test(const aStr: String): LongInt;
+begin
+  Result := 1;
+end;
+
+generic function Test<T>(aT: T): LongInt;
+begin
+  Result := 2;
+end;
+
+operator := (aArg: LongInt): String;
+begin
+  Result := '';
+end;
+
+begin
+	if Test('Hello World')<>1 then
+		Halt(-1);
+	if Test(42)<>2 then
+		Halt(-1);
+	if Test(String(42))<>1 then
+		Halt(-1);
+end.

+ 22 - 0
tests/test/timpfuncspez10.pp

@@ -0,0 +1,22 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test generic parameter order
+}
+
+program timpfuncspez10;
+uses
+  typinfo;
+
+generic function DoThis<S, T>(p1: T; p2: S; t1, t2: TTypeKind): boolean;
+begin
+	result := (GetTypeKind(S) = t1) and (GetTypeKind(T) = t2);
+	writeln('S:',GetTypeKind(S), ' T:',GetTypeKind(T), ' = ', result);
+end;
+
+begin
+	if not DoThis(1, 'a', tkChar, tkInteger) then
+		Halt(-1);
+	if not DoThis('a', 1, tkInteger, tkChar) then
+		Halt(-1);
+end.

+ 20 - 0
tests/test/timpfuncspez11.pp

@@ -0,0 +1,20 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Testing ambiguous specializations
+}
+
+program timpfuncspez11;
+
+generic procedure DoThis<T>(a:T; b: word);
+begin
+end;
+
+generic procedure DoThis<T>(a: word; b: T);
+begin
+end;
+
+begin
+	DoThis(1,1);
+end.

+ 27 - 0
tests/test/timpfuncspez12.pp

@@ -0,0 +1,27 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test specialized arrays and array constructors
+}
+
+program timpfuncspez12;
+uses
+	typinfo;
+
+type
+	generic TMyArray<T> = array of T;
+
+generic function DoThis<T>(a: specialize TMyArray<T>): TTypeKind;
+begin
+	writeln(GetTypeKind(T), ': ', Length(a));
+  result := GetTypeKind(T);
+end;
+
+begin
+	if DoThis(['a','b','c']) <> tkChar then
+		Halt(-1);
+	if DoThis(['aaa','bbb','ccc']) <> tkString then
+		Halt(-1);
+	if DoThis([1,2,3]) <> tkInteger then
+		Halt(-1);
+end.

+ 25 - 0
tests/test/timpfuncspez13.pp

@@ -0,0 +1,25 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test specializing proc vars by infering parameters from callback function
+}
+
+program timpfuncspez13;
+
+type
+  generic TProc<S, V> = procedure(name: S; context: V);
+
+generic procedure Run<U, T>(proc: specialize TProc<U, T>; context: T);
+begin
+  proc('TProc', context);
+end;
+
+procedure DoCallback(name: string; value: shortint);
+begin
+  writeln(name, ' called ', value);
+end;
+
+begin
+  Run(@DoCallback, 100);
+end.

+ 25 - 0
tests/test/timpfuncspez14.pp

@@ -0,0 +1,25 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test specializing proc vars by infering parameters from callback function
+}
+
+program timpfuncspez14;
+
+type
+  generic TProc<S> = procedure(name: S; value: integer);
+
+generic procedure Run<T, U>(proc: specialize TProc<T>; context: U);
+begin
+  proc('TProc', context);
+end;
+
+procedure DoCallback(name: string; value: integer);
+begin
+  writeln(name, ' called ', value);
+end;
+
+begin
+  Run(@DoCallback, 100);
+end.

+ 25 - 0
tests/test/timpfuncspez15.pp

@@ -0,0 +1,25 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test specializing proc vars with constant parameters
+  NOTE: currently not supported and therefore should fail
+}
+
+program timpfuncspez15;
+
+type
+  generic TProc<T; const U: integer> = procedure(value: T = U);
+
+generic procedure Run<T>(proc: specialize TProc<T, 10>);
+begin
+  proc();
+end;
+
+procedure DoCallback(value: integer);
+begin
+end;
+
+begin
+  Run(@DoCallback);
+end.

+ 17 - 0
tests/test/timpfuncspez16.pp

@@ -0,0 +1,17 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test default parameters
+  NOTE: currently default values are not considered for specialization so the test should fail
+}
+
+program timpfuncspez16;
+
+generic procedure DoThis<T>(a: integer; b: integer = 0; c: T = 0);
+begin
+end;
+
+begin
+  DoThis(1);
+end.

+ 17 - 0
tests/test/timpfuncspez17.pp

@@ -0,0 +1,17 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test default parameters
+  NOTE: currently default values are not considered for specialization so the test should fail
+}
+
+program timpfuncspez17;
+
+generic procedure DoThis<T>(a: integer; b: T = 0);
+begin
+end;
+
+begin
+  DoThis(1);
+end.

+ 17 - 0
tests/test/timpfuncspez18.pp

@@ -0,0 +1,17 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Constants with generics are excluded
+}
+
+program timpfuncspez18;
+
+generic procedure DoThis<T; const U: integer>(a: T);
+begin
+  writeln(U);
+end;
+
+begin
+  DoThis(1);
+end.

+ 26 - 0
tests/test/timpfuncspez19.pp

@@ -0,0 +1,26 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test specializing related object types
+
+  DoThis<T> can be specialized as DoThis<Integer>
+  because TSomeClass is specialized from TAnyClass<Integer>
+  and therefore we can infer "Integer" as the correct parameter
+}
+program timpfuncspez19;
+
+type
+  generic TAnyClass<U> = class
+  end;
+
+type
+  TSomeClass = specialize TAnyClass<Integer>;
+
+generic procedure DoThis<T>(aClass: specialize TAnyClass<T>);
+begin
+end;
+
+begin
+  DoThis(TSomeClass.Create);
+end.

+ 24 - 0
tests/test/timpfuncspez2.pp

@@ -0,0 +1,24 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test open arrays and array constructors
+}
+
+program timpfuncspez2;
+uses
+	typinfo;
+
+generic function DoThis<T>(a: array of T): TTypeKind;
+begin
+	writeln(GetTypeKind(T), ': ', Length(a));
+  result := GetTypeKind(T);
+end;
+
+begin
+	if DoThis(['a','b','c']) <> tkChar then
+		Halt(-1);
+	if DoThis(['aaa','bbb','ccc']) <> tkString then
+		Halt(-1);
+	if DoThis([1,2,3]) <> tkInteger then
+		Halt(-1);
+end.

+ 28 - 0
tests/test/timpfuncspez20.pp

@@ -0,0 +1,28 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test specializing related object types
+
+  DoThis<T> can be specialized as DoThis<Integer>
+  because TSomeRecord is specialized from TAnyRecord<Integer>
+  and therefore we can infer "Integer" as the correct parameter
+}
+program timpfuncspez20;
+
+type
+  generic TAnyRecord<U> = record
+  end;
+
+type
+  TSomeRecord = specialize TAnyRecord<Integer>;
+
+generic procedure DoThis<T>(aRecord: specialize TAnyRecord<T>);
+begin
+end;
+
+var
+  rec: TSomeRecord;
+begin
+  DoThis(rec);
+end.

+ 23 - 0
tests/test/timpfuncspez21.pp

@@ -0,0 +1,23 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test class constraints
+}
+
+program timpfuncspez21;
+
+type
+  TMyClass = class
+  end;
+  TMyChild = class(TMyClass)
+  end;
+
+generic procedure DoThis<T: TMyClass>(obj: T);
+begin
+end;
+
+begin
+  DoThis(TMyClass.Create);
+  DoThis(TMyChild.Create);
+end.

+ 22 - 0
tests/test/timpfuncspez22.pp

@@ -0,0 +1,22 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test class constraints
+}
+
+program timpfuncspez22;
+
+type
+  TMyClass = class
+  end;
+  TOtherClass = class
+  end;
+
+generic procedure DoThis<T: TMyClass>(obj: T);
+begin
+end;
+
+begin
+  DoThis(TOtherClass.Create);
+end.

+ 19 - 0
tests/test/timpfuncspez23.pp

@@ -0,0 +1,19 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test multi-dimensional array specializations
+}
+
+program timpfuncspez23;
+
+type
+  generic TArray<T> = array of T;
+
+generic procedure DoThis<T>(param: specialize TArray<specialize TArray<T>>);
+begin
+end;
+
+begin
+  DoThis([[1,2,3],[2,3,4],[3,4,5]]);
+end.

+ 19 - 0
tests/test/timpfuncspez24.pp

@@ -0,0 +1,19 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test multi-dimensional array specializations
+}
+
+program timpfuncspez24;
+
+type
+  generic TArray<T> = array of T;
+
+generic procedure DoThis<T>(param: specialize TArray<specialize TArray<T>>);
+begin
+end;
+
+begin
+  DoThis([[1,2,3],[2,3,4],[3,4,5]]);
+end.

+ 41 - 0
tests/test/timpfuncspez25.pp

@@ -0,0 +1,41 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test generic methods within generic method bodies
+}
+
+program timpfuncspez25;
+  
+type
+  TMyClass = class
+    generic procedure DoThis<T>(msg: T);
+    generic procedure DoThat<T>(msg: T);
+  end;
+  TMyChild = class(TMyClass)
+    generic procedure DoSomething<T>(msg: T);
+  end;
+
+generic procedure TMyClass.DoThis<T>(msg:T);
+begin
+  DoThat(msg);
+end;
+
+generic procedure TMyClass.DoThat<T>(msg: T);
+begin
+end;
+
+generic procedure TMyChild.DoSomething<T>(msg: T);
+begin
+  DoThis(msg);
+end;
+
+var
+  a: TMyClass;
+  b: TMyChild;
+begin
+  a := TMyClass.Create;
+  a.DoThis(1);
+  b := TMyChild.create;
+  b.DoSomething(1);
+end.

+ 17 - 0
tests/test/timpfuncspez26.pp

@@ -0,0 +1,17 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Parameter count must match in order for specialization to be considered
+}
+
+program timpfuncspez26;
+
+
+generic procedure Test<T>(aT: T);
+begin
+end;
+
+begin
+  Test(1, 1);
+end.

+ 16 - 0
tests/test/timpfuncspez27.pp

@@ -0,0 +1,16 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  All generic parameters must be used in the paramter list
+}
+
+program timpfuncspez27;
+
+generic procedure Test<T>(aT: Integer);
+begin
+end;
+
+begin
+  Test(1);
+end.

+ 16 - 0
tests/test/timpfuncspez28.pp

@@ -0,0 +1,16 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  All generic parameters must be used in the paramter list
+}
+
+program timpfuncspez28;
+
+generic procedure Test<T>(aT: array of integer);
+begin
+end;
+
+begin
+  Test([1,2,3]);
+end.

+ 19 - 0
tests/test/timpfuncspez29.pp

@@ -0,0 +1,19 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  All generic parameters must be used in the paramter list
+}
+
+program timpfuncspez29;
+
+type
+  generic TArray<T> = array of T;
+
+generic procedure Test<T>(aT: specialize TArray<integer>);
+begin
+end;
+
+begin
+  Test([1,2,3]);
+end.

+ 18 - 0
tests/test/timpfuncspez3.pp

@@ -0,0 +1,18 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test that sets are not compatible with open arrays
+}
+
+program timpfuncspez3;
+
+generic procedure DoThis<T>(a: array of T);
+begin
+end;
+
+const
+	c1 = [1,2,3];
+begin
+	DoThis(c1);
+end.

+ 24 - 0
tests/test/timpfuncspez30.pp

@@ -0,0 +1,24 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  All generic parameters must be used in the paramter list
+}
+program timpfuncspez30;
+
+type
+  generic TAnyRecord<U> = record
+  end;
+
+type
+  TSomeRecord = specialize TAnyRecord<Integer>;
+
+generic procedure DoThis<T>(aRecord: specialize TAnyRecord<Integer>);
+begin
+end;
+
+var
+  rec: TSomeRecord;
+begin
+  DoThis(rec);
+end.

+ 25 - 0
tests/test/timpfuncspez31.pp

@@ -0,0 +1,25 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  All generic parameters must be used in the paramter list
+}
+
+program timpfuncspez31;
+
+type
+  generic TProc<S> = procedure(name: S);
+
+generic procedure Run<T>(proc: specialize TProc<String>);
+begin
+  proc('TProc');
+end;
+
+procedure DoCallback(name: String);
+begin
+  writeln(name, ' called');
+end;
+
+begin
+  Run(@DoCallback);
+end.

+ 34 - 0
tests/test/timpfuncspez32.pp

@@ -0,0 +1,34 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test open arrays and array constructors
+}
+
+program timpfuncspez32;
+uses
+  typinfo;
+
+generic function DoThis<T>(a: array of T): TTypeKind;
+begin
+  writeln(GetTypeKind(T), ': ', Length(a));
+  result := GetTypeKind(T);
+end;
+
+type
+  TIntArray = array of integer;
+var
+  s1: array of integer;
+  s2: array[0..2] of integer;
+begin
+  s1 := [1,2,3];
+  if DoThis(s1) <> tkInteger then
+    Halt(-1);
+
+  s2 := [1,2,3];
+  if DoThis(s2) <> tkInteger then
+    Halt(-1);
+
+  s1 := TIntArray.Create(1,2,3);
+  if DoThis(s1) <> tkInteger then
+    Halt(-1);
+end.

+ 20 - 0
tests/test/timpfuncspez33.pp

@@ -0,0 +1,20 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Non-uniform array constructors are not compatible with "array of T"
+}
+
+program timpfuncspez33;
+
+generic procedure DoThis<T>(a: array of T);
+begin
+end;
+
+begin
+  DoThis([
+    1,
+    'a',
+    TObject.Create
+  ]);
+end.

+ 30 - 0
tests/test/timpfuncspez34.pp

@@ -0,0 +1,30 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test various overloads
+}
+
+program timpfuncspez34;
+
+generic procedure DoThis<A>(param1: A);
+begin
+end;
+ 
+generic procedure DoThis<A, B>(param1: A; param2: B);
+begin
+end;
+
+generic procedure DoThis<A, B, C>(param1: A; param2: B; param3: C);
+begin
+end;
+
+begin
+  DoThis(1);
+  DoThis(1,2);
+  DoThis(1,2,3);
+
+  DoThis(1);
+  DoThis(1,'aaa');
+  DoThis(1,'aaa',nil);
+end.

+ 54 - 0
tests/test/timpfuncspez35.pp

@@ -0,0 +1,54 @@
+{%NORUN}
+{$mode delphi}
+{$modeswitch implicitfunctionspecialization}
+{
+  Grab bag to test Delphi mode
+}
+
+program timpfuncspez35;
+
+{ Classes }
+
+type
+  TMyClass = class
+    class procedure Call<T>(msg: T);
+    procedure DoThis<T>(msg: T);
+  end;
+
+class procedure TMyClass.Call<T>(msg:T);
+begin
+end;
+
+procedure TMyClass.DoThis<T>(msg:T);
+begin
+end;
+
+{ Methods }
+
+procedure DoThis<A>(param1: A);
+begin
+end;
+ 
+procedure DoThis<A, B>(param1: A; param2: B);
+begin
+end;
+
+procedure DoThis<A, B, C>(param1: A; param2: B; param3: C);
+begin
+end;
+
+var
+  obj: TMyClass;
+begin
+  TMyClass.Call('Hello World');
+  obj := TMyClass.create;
+  obj.DoThis(1);
+
+  DoThis(1);
+  DoThis(1,2);
+  DoThis(1,2,3);
+
+  DoThis(1);
+  DoThis(1,'aaa');
+  DoThis(1,'aaa',nil);
+end.

+ 19 - 0
tests/test/timpfuncspez4.pp

@@ -0,0 +1,19 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test string literals (array of char) is compatible with open arrays
+}
+
+program timpfuncspez4;
+uses
+	typinfo;
+
+generic function DoThis<T>(a: array of T): TTypeKind;
+begin
+  result := GetTypeKind(T);
+end;
+
+begin
+	if DoThis('string') <> tkChar then
+		Halt(-1);
+end.

+ 22 - 0
tests/test/timpfuncspez5.pp

@@ -0,0 +1,22 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+  Test "array of enum"
+}
+
+program timpfuncspez5;
+uses
+  typinfo;
+
+type
+  TMyEnum = (_A, _B, _C);
+
+generic procedure DoThis<T>(value: T);
+begin
+  if GetTypeKind(value[0]) <> tkEnumeration then
+    Halt(-1);
+end;
+
+begin
+  DoThis([_A, _B]);
+end.

+ 22 - 0
tests/test/timpfuncspez6.pp

@@ -0,0 +1,22 @@
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test "array of enum" is compatible with open arrays
+}
+
+program timpfuncspez6;
+uses
+	typinfo;
+
+type
+	TMyEnum = (_A, _B, _C);
+
+generic procedure DoThis<T>(value: array of T);
+begin
+	if GetTypeKind(value[0]) <> tkEnumeration then
+	  Halt(-1);
+end;
+
+begin
+	DoThis([_A, _B]);
+end.

+ 30 - 0
tests/test/timpfuncspez7.pp

@@ -0,0 +1,30 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test generic methods
+}
+
+program timpfuncspez7;
+	
+type
+	TMyClass = class
+		generic class procedure Call<T>(msg: T);
+		generic procedure DoThis<T>(msg: T);
+	end;
+
+generic class procedure TMyClass.Call<T>(msg:T);
+begin
+end;
+
+generic procedure TMyClass.DoThis<T>(msg:T);
+begin
+end;
+
+var
+	obj:TMyClass;
+begin
+	TMyClass.Call('Hello World');
+	obj := TMyClass.create;
+	obj.DoThis(1);
+end.

+ 30 - 0
tests/test/timpfuncspez8.pp

@@ -0,0 +1,30 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{$modeswitch advancedrecords}
+{
+	Test generic record methods
+}
+
+program timpfuncspez8;
+	
+type
+	TMyRecord = record
+		generic class procedure Call<T>(msg: T); static;
+		generic procedure DoThis<T>(msg: T);
+	end;
+
+generic class procedure TMyRecord.Call<T>(msg:T);
+begin
+end;
+
+generic procedure TMyRecord.DoThis<T>(msg:T);
+begin
+end;
+
+var
+	rec: TMyRecord;
+begin
+	TMyRecord.Call('Hello World');
+	rec.DoThis(1);
+end.

+ 29 - 0
tests/test/timpfuncspez9.pp

@@ -0,0 +1,29 @@
+{%NORUN}
+{$mode objfpc}
+{$modeswitch implicitfunctionspecialization}
+{
+	Test generic object methods
+}
+
+program timpfuncspez9;
+	
+type
+	TMyObject = object
+		generic class procedure Call<T>(msg: T); static;
+		generic procedure DoThis<T>(msg: T);
+	end;
+
+generic class procedure TMyObject.Call<T>(msg:T);
+begin
+end;
+
+generic procedure TMyObject.DoThis<T>(msg:T);
+begin
+end;
+
+var
+	rec: TMyObject;
+begin
+	TMyObject.Call('Hello World');
+	rec.DoThis(1);
+end.