Browse Source

* check whether an anonymous function can be assigned to a global, method or nested function variable

Sven/Sarah Barth 4 years ago
parent
commit
8e2478e632
1 changed files with 60 additions and 9 deletions
  1. 60 9
      compiler/defcmp.pas

+ 60 - 9
compiler/defcmp.pas

@@ -2468,42 +2468,46 @@ implementation
         eq: tequaltype;
         eq: tequaltype;
         po_comp: tprocoptions;
         po_comp: tprocoptions;
         pa_comp: tcompare_paras_options;
         pa_comp: tcompare_paras_options;
+        captured : tfplist;
       begin
       begin
          proc_to_procvar_equal:=te_incompatible;
          proc_to_procvar_equal:=te_incompatible;
          if not(assigned(def1)) or not(assigned(def2)) then
          if not(assigned(def1)) or not(assigned(def2)) then
            exit;
            exit;
          { check for method pointer and local procedure pointer:
          { check for method pointer and local procedure pointer:
              a) anything but procvars can be assigned to blocks
              a) anything but procvars can be assigned to blocks
-             b) if one is a procedure of object, the other also has to be one
+             b) depending on their captured symbols anonymous functions can be
+                assigned to global, method or nested procvars
+             c) if one is a procedure of object, the other also has to be one
                 ("object static procedure" is equal to procedure as well)
                 ("object static procedure" is equal to procedure as well)
                 (except for block)
                 (except for block)
-             c) if one is a pure address, the other also has to be one
+             d) if one is a pure address, the other also has to be one
                 except if def1 is a global proc and def2 is a nested procdef
                 except if def1 is a global proc and def2 is a nested procdef
                 (global procedures can be converted into nested procvars)
                 (global procedures can be converted into nested procvars)
-             d) if def1 is a nested procedure, then def2 has to be a nested
+             e) if def1 is a nested procedure, then def2 has to be a nested
                 procvar and def1 has to have the po_delphi_nested_cc option
                 procvar and def1 has to have the po_delphi_nested_cc option
                 or does not use parentfp
                 or does not use parentfp
-             e) if def1 is a procvar, def1 and def2 both have to be nested or
+             f) if def1 is a procvar, def1 and def2 both have to be nested or
                 non-nested (we don't allow assignments from non-nested to
                 non-nested (we don't allow assignments from non-nested to
                 nested procvars to make sure that we can still implement
                 nested procvars to make sure that we can still implement
                 nested procvars using trampolines -- e.g., this would be
                 nested procvars using trampolines -- e.g., this would be
                 necessary for LLVM or CIL as long as they do not have support
                 necessary for LLVM or CIL as long as they do not have support
                 for Delphi-style frame pointer parameter passing) }
                 for Delphi-style frame pointer parameter passing) }
-         if is_block(def2) then                                     { a) }
+         if is_block(def2) or                                                           { a) }
+            (po_anonymous in def1.procoptions) then                                     { b) }
            { can't explicitly check against procvars here, because
            { can't explicitly check against procvars here, because
              def1 may already be a procvar due to a proc_to_procvar;
              def1 may already be a procvar due to a proc_to_procvar;
              this is checked in the type conversion node itself -> ok }
              this is checked in the type conversion node itself -> ok }
          else if
          else if
-            ((def1.is_methodpointer and not (po_staticmethod in def1.procoptions))<> { b) }
+            ((def1.is_methodpointer and not (po_staticmethod in def1.procoptions))<> { c) }
              (def2.is_methodpointer and not (po_staticmethod in def2.procoptions))) or
              (def2.is_methodpointer and not (po_staticmethod in def2.procoptions))) or
-            ((def1.is_addressonly<>def2.is_addressonly) and         { c) }
+            ((def1.is_addressonly<>def2.is_addressonly) and         { d) }
              (is_nested_pd(def1) or
              (is_nested_pd(def1) or
               not is_nested_pd(def2))) or
               not is_nested_pd(def2))) or
-            ((def1.typ=procdef) and                                 { d) }
+            ((def1.typ=procdef) and                                 { e) }
              is_nested_pd(def1) and
              is_nested_pd(def1) and
              (not(po_delphi_nested_cc in def1.procoptions) or
              (not(po_delphi_nested_cc in def1.procoptions) or
               not is_nested_pd(def2))) or
               not is_nested_pd(def2))) or
-            ((def1.typ=procvardef) and                              { e) }
+            ((def1.typ=procvardef) and                              { f) }
              (is_nested_pd(def1)<>is_nested_pd(def2))) then
              (is_nested_pd(def1)<>is_nested_pd(def2))) then
            exit;
            exit;
          pa_comp:=[cpo_ignoreframepointer];
          pa_comp:=[cpo_ignoreframepointer];
@@ -2539,6 +2543,53 @@ implementation
                 { in case of non-block to block, we need a type conversion }
                 { in case of non-block to block, we need a type conversion }
                 if (po_is_block in def1.procoptions) <> (po_is_block in def2.procoptions) then
                 if (po_is_block in def1.procoptions) <> (po_is_block in def2.procoptions) then
                   eq:=te_convert_l1;
                   eq:=te_convert_l1;
+                { for anonymous functions check whether their captured symbols are
+                  compatible with the target }
+                if po_anonymous in def1.procoptions then
+                  begin
+                    if def1.typ<>procdef then
+                      internalerror(2021052602);
+                    captured:=tprocdef(def1).capturedsyms;
+                    { if no symbol was captured an anonymous function is
+                      compatible to all three types of function pointers, but we
+                      might need to generate its code differently (e.g. get rid
+                      of parentfp parameter for global functions); the order for
+                      this is:
+                        - procedure variable
+                        - method variable
+                        - nested procvar }
+                    if not assigned(captured) or (captured.count=0) then
+                      begin
+                        if po_methodpointer in def2.procoptions then
+                          eq:=te_convert_l2
+                        else if po_delphi_nested_cc in def2.procoptions then
+                          eq:=te_convert_l3
+                        else
+                          eq:=te_convert_l1
+                      end
+                    { if only a Self was captured then the function is not
+                      compatible to normal function pointers; the order for this
+                      is:
+                        - method variable
+                        - nested function }
+                    else if (captured.count=1) and (vo_is_self in tabstractvarsym(pcapturedsyminfo(captured[0])^.sym).varoptions) then
+                      begin
+                        if po_methodpointer in def2.procoptions then
+                          eq:=te_convert_l1
+                        else if po_delphi_nested_cc in def2.procoptions then
+                          eq:=te_convert_l2
+                        else
+                          eq:=te_incompatible;
+                      end
+                    { otherwise it's compatible to nested function pointers only }
+                    else
+                      begin
+                        if po_delphi_nested_cc in def2.procoptions then
+                          eq:=te_convert_l1
+                        else
+                          eq:=te_incompatible;
+                      end;
+                  end;
               end;
               end;
             proc_to_procvar_equal:=eq;
             proc_to_procvar_equal:=eq;
           end;
           end;