View Issue Details

IDProjectCategoryView StatusLast Update
0036350FPCCompilerpublic2019-11-24 15:29
ReporterRyan JosephAssigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status acknowledgedResolutionopen 
Product VersionProduct Build 
Target VersionFixed in Version 
Summary0036350: [PATCH] advanced objects mode switch
DescriptionFor consideration a tiny patch for "advanced objects" mode switch which currently only allows class operators in objects.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget-
Attached Files
  • patch.diff (14,837 bytes)
    From 57c8106175cb1328d71c75bf50392d08084a0312 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Sat, 20 Apr 2019 09:58:41 -0400
    Subject: [PATCH] advanced objects mode switch
    
    ---
     .gitignore             |  23 +++++
     compiler/globtype.pas  |   8 +-
     compiler/htypechk.pas  |   7 +-
     compiler/pdecobj.pas   |  26 ++++-
     compiler/pdecsub.pas   |   5 +
     tests/test/tadvobj1.pp | 227 +++++++++++++++++++++++++++++++++++++++++
     tests/test/tadvobj2.pp |  14 +++
     tests/test/tadvobj3.pp |  33 ++++++
     tests/test/tadvobj4.pp |  42 ++++++++
     9 files changed, 375 insertions(+), 10 deletions(-)
     create mode 100644 .gitignore
     create mode 100644 tests/test/tadvobj1.pp
     create mode 100644 tests/test/tadvobj2.pp
     create mode 100644 tests/test/tadvobj3.pp
     create mode 100644 tests/test/tadvobj4.pp
    
    diff --git a/.gitignore b/.gitignore
    new file mode 100644
    index 0000000000..64fdb156d0
    --- /dev/null
    +++ b/.gitignore
    @@ -0,0 +1,23 @@
    +# files
    +pp
    +fpmake
    +rtl/darwin/fpcmade.x86_64-darwin
    +fpmake_proc1 copy.inc
    +tests/*.x86_64-darwin
    +rtl/Package.fpc
    +tests/createlst
    +tests/gparmake
    +
    +# directories
    +lazbuild/
    +x86_64-darwin/
    +tests/tstunits/
    +tests/utils
    +
    +# patterns
    +*.app
    +*.o
    +*.ppu
    +*.fpm
    +*.rsj
    +*.lst
    \ No newline at end of file
    diff --git a/compiler/globtype.pas b/compiler/globtype.pas
    index 7d23464d57..c4050d9615 100644
    --- a/compiler/globtype.pas
    +++ b/compiler/globtype.pas
    @@ -446,7 +446,8 @@ interface
              m_isolike_io,          { I/O as it required by an ISO compatible compiler }
              m_isolike_program_para, { program parameters as it required by an ISO compatible compiler }
              m_isolike_mod,         { mod operation as it is required by an iso compatible compiler }
    -         m_array_operators      { use Delphi compatible array operators instead of custom ones ("+") }
    +         m_array_operators,     { use Delphi compatible array operators instead of custom ones ("+") }
    +         m_advanced_objects     { adds additional features to objects }
            );
            tmodeswitches = set of tmodeswitch;
     
    @@ -597,7 +598,7 @@ interface
     
            cstylearrayofconst = [pocall_cdecl,pocall_cppdecl,pocall_mwpascal,pocall_sysv_abi_cdecl,pocall_ms_abi_cdecl];
     
    -       modeswitchstr : array[tmodeswitch] of string[18] = ('',
    +       modeswitchstr : array[tmodeswitch] of string[19] = ('',
              '','','','','','','',
              {$ifdef gpc_mode}'',{$endif}
              { more specific }
    @@ -635,7 +636,8 @@ interface
              'ISOIO',
              'ISOPROGRAMPARAS',
              'ISOMOD',
    -         'ARRAYOPERATORS'
    +         'ARRAYOPERATORS',
    +         'ADVANCEDOBJECTS'
              );
     
     
    diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
    index 07c035dc26..16d52235eb 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2443,13 +2443,14 @@ implementation
             else
             if (FOperator<>NOTOKEN) then
               begin
    -            { check operands and if they contain records then search in records,
    +            { check operands and if they contain structs then search in structs,
                   then search in unit }
                 pt:=tcallparanode(FParaNode);
                 while assigned(pt) do
                   begin
    -                if (pt.resultdef.typ=recorddef) and
    -                    (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
    +                if ((pt.resultdef.typ=recorddef) or 
    +                   ((m_advanced_objects in current_settings.modeswitches) and is_object(pt.resultdef))) and
    +                   (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
                       collect_overloads_in_struct(tabstractrecorddef(pt.resultdef),ProcdefOverloadList,searchhelpers,anoninherited,spezcontext);
                     pt:=tcallparanode(pt.right);
                   end;
    diff --git a/compiler/pdecobj.pas b/compiler/pdecobj.pas
    index 1e41f98a70..6d8b045684 100644
    --- a/compiler/pdecobj.pas
    +++ b/compiler/pdecobj.pas
    @@ -883,7 +883,8 @@ implementation
           begin
             case token of
               _PROCEDURE,
    -          _FUNCTION:
    +          _FUNCTION,
    +          _OPERATOR:
                 begin
                   if (astruct.symtable.currentvisibility=vis_published) and
                      not(oo_can_have_published in astruct.objectoptions) then
    @@ -1086,9 +1087,11 @@ implementation
               is_classdef:=false;
               { read class method/field/property }
               consume(_CLASS);
    -          { class modifier is only allowed for procedures, functions, }
    -          { constructors, destructors, fields and properties          }
    -          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or (token=_CONSTRUCTOR)) then
    +          { class modifier is only allowed for procedures, functions,
    +            constructors, destructors, fields, properties and operators (if m_class_operators is enabled) }
    +          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or 
    +             (token=_CONSTRUCTOR) or
    +             ((token=_OPERATOR) and is_object(current_structdef) and (m_advanced_objects in current_settings.modeswitches))) then
                 Message(parser_e_procedure_or_function_expected);
     
               { Java interfaces can contain final class vars }
    @@ -1342,6 +1345,21 @@ implementation
                     is_classdef:=false;
                     hadgeneric:=false;
                   end;
    +            _OPERATOR :
    +              { operators must be class methods and enabled via mode switch.
    +                class helpers like record helpers are not allowed to contain operators }
    +              if (m_advanced_objects in current_settings.modeswitches) and 
    +                 is_classdef and 
    +                 is_object(current_structdef) and
    +                 not is_classhelper(current_structdef) then
    +                begin                    
    +                  method_dec(current_structdef,is_classdef,hadgeneric);
    +                  fields_allowed:=false;
    +                  is_classdef:=false;
    +                  hadgeneric:=false;
    +                end
    +              else
    +                consume(_ID);
                 _END :
                   begin
                     consume(_END);
    diff --git a/compiler/pdecsub.pas b/compiler/pdecsub.pas
    index e2b0c1712a..eefa4f6360 100644
    --- a/compiler/pdecsub.pas
    +++ b/compiler/pdecsub.pas
    @@ -1480,6 +1480,11 @@ implementation
                           (tparavarsym(pd.parast.SymList[1]).varspez<>vs_var)
                          ) then
                         Message(parser_e_overload_impossible);
    +                  
    +                  { management operators are only possible in records }
    +                  if (m_advanced_objects in current_settings.modeswitches) 
    +                     and (assigned(pd.struct) and not is_record(pd.struct)) then
    +                    Message(parser_e_overload_impossible);
     
                       trecordsymtable(pd.procsym.Owner).includemanagementoperator(
                         token2managementoperator(optoken));
    diff --git a/tests/test/tadvobj1.pp b/tests/test/tadvobj1.pp
    new file mode 100644
    index 0000000000..2210550ba7
    --- /dev/null
    +++ b/tests/test/tadvobj1.pp
    @@ -0,0 +1,227 @@
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj1;
    +
    +type
    +  TMyObject = object
    +    public type
    +      TRight = integer;
    +      TSelf = TMyObject;
    +    public
    +
    +      // Assignment operators
    +      class operator := (right: TRight): TSelf;
    +      class operator explicit (right: TRight): TSelf;
    +
    +      // Arithmetic operators
    +      class operator + (left: TSelf; right: TRight): TSelf;
    +      class operator + (left: TSelf; right: TSelf): TSelf;
    +      class operator - (left: TSelf; right: TRight): TSelf;
    +      class operator * (left: TSelf; right: TRight): TSelf;
    +      class operator / (left: TSelf; right: TRight): TSelf;
    +      class operator ** (left: TSelf; right: TRight): TSelf;
    +      class operator div (left: TSelf; right: TRight): TSelf;
    +      class operator mod (left: TSelf; right: TRight): TSelf;
    +
    +      // Logical operators
    +      class operator and (left: TSelf; right: TRight): TSelf;
    +      class operator or (left: TSelf; right: TRight): TSelf;
    +      class operator xor (left: TSelf; right: TRight): TSelf;
    +      class operator shl (left: TSelf; right: TRight): TSelf;
    +      class operator shr (left: TSelf; right: TRight): TSelf;
    +      //class operator << (left: TSelf; right: TRight): TSelf;
    +      //class operator >> (left: TSelf; right: TRight): TSelf;
    +
    +      // Unary operators
    +      class operator + (left: TSelf): TSelf;
    +      class operator - (left: TSelf): TSelf;
    +      class operator not (left: TSelf): boolean;
    +
    +      // Relational operators
    +      class operator <> (left: TSelf; right: TRight): boolean;
    +      class operator < (left: TSelf; right: TRight): boolean;
    +      class operator > (left: TSelf; right: TRight): boolean;
    +      class operator <= (left: TSelf; right: TRight): boolean;
    +      class operator >= (left: TSelf; right: TRight): boolean;
    +      class operator = (left: TSelf; right: TRight): boolean;
    +
    +      // Set operators
    +      class operator >< (left: TSelf; right: TRight): boolean;
    +      class operator in (left: TSelf; right: TRight): boolean;
    +  end;
    +
    +class operator TMyObject.:= (right: TRight): TSelf;
    +begin
    +  writeln('TMyObject.:=');
    +end;
    +
    +class operator TMyObject.explicit (right: TRight): TSelf;
    +begin
    +  writeln('TMyObject.explicit');
    +end;  
    +
    +class operator TMyObject.+ (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.+ (left: TSelf; right: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.- (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.* (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject./ (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.** (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.div (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.mod (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.and (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.or (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.xor (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.shl (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.shr (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +//class operator TSelf.<< (left: TSelf; right: TRight): TSelf;
    +//begin
    +//end;
    +
    +//class operator TSelf.>> (left: TSelf; right: TRight): TSelf;
    +//begin
    +//end;
    +
    +class operator TMyObject.not (left: TSelf): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.+ (left: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.- (left: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.<> (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.< (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.> (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.<= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.>= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.>< (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.in (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +var
    +  c: TMyObject;
    +  b: boolean;
    +begin
    +  // Assignment operators
    +  c := TMyObject(100);
    +  c := 100;
    +
    +  // Arithmetic operators
    +  c := c + 1;
    +  c := c - 1;
    +  c := c * 1;
    +  c := c / 1;
    +  c += 1;
    +  c -= 1;
    +  c *= 1;
    +  c /= 1;
    +  c := c ** 1;
    +  c := c div 1;
    +  c := c mod 1;
    +
    +  // Logical operators
    +  c := c and 1;
    +  c := c or 1;
    +  c := c xor 1;
    +  c := c shl 1;
    +  c := c shr 1;
    +
    +  // Unary operators
    +  c := +c;
    +  c := -c;
    +  b := not c;
    +
    +  // Relational operators
    +  b := c <> 1;
    +  b := c > 1;
    +  b := c > 1;
    +  b := c <= 1;
    +  b := c >= 1;
    +  b := c = 1;
    +
    +  // Set operators
    +  b := c >< 1;
    +  b := c in 1;
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tadvobj2.pp b/tests/test/tadvobj2.pp
    new file mode 100644
    index 0000000000..a90349c0c3
    --- /dev/null
    +++ b/tests/test/tadvobj2.pp
    @@ -0,0 +1,14 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj2;
    +
    +{ classes don't support operators }
    +type
    +  TMyClass = class
    +     class operator := (right: integer): TMyClass;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tadvobj3.pp b/tests/test/tadvobj3.pp
    new file mode 100644
    index 0000000000..246909e419
    --- /dev/null
    +++ b/tests/test/tadvobj3.pp
    @@ -0,0 +1,33 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj3;
    +
    +{ advanced objects don't support management operators }
    +type
    +  TMyObject = object
    +    class operator Initialize(var A: TMyObject);
    +    class operator Finalize(var A: TMyObject);
    +    class operator Copy(constref Source: TMyObject; var Dest: TMyObject);
    +    class operator AddRef(var Dest: TMyObject);
    +  end;
    +
    +class operator TMyObject.Initialize(var A: TMyObject);
    +begin
    +end;
    +
    +class operator TMyObject.Finalize(var A: TMyObject);
    +begin
    +end;
    +
    +class operator TMyObject.Copy(constref Source: TMyObject; var Dest: TMyObject);
    +begin
    +end;
    +
    +class operator TMyObject.AddRef(var Dest: TMyObject);
    +begin
    +end;
    +
    +begin
    +end.
    diff --git a/tests/test/tadvobj4.pp b/tests/test/tadvobj4.pp
    new file mode 100644
    index 0000000000..78238b8e09
    --- /dev/null
    +++ b/tests/test/tadvobj4.pp
    @@ -0,0 +1,42 @@
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj4;
    +
    +type
    +  TVec2 = object
    +    x, y: integer;
    +    class operator + (left: TVec2; right: TVec2): TVec2;
    +  end;
    +
    +class operator TVec2.+ (left: TVec2; right: TVec2): TVec2;
    +begin
    +  result.x := left.x + right.x;
    +  result.y := left.y + right.y;
    +end;
    +
    +type
    +  TVec3 = object(TVec2)
    +    z: integer;
    +    class operator + (left: TVec3; right: TVec3): TVec3;
    +  end;
    +
    +class operator TVec3.+ (left: TVec3; right: TVec3): TVec3;
    +begin
    +  result.x := left.x + right.x;
    +  result.y := left.y + right.y;
    +  result.z := left.z + right.z;
    +end;
    +
    +var
    +  a0: TVec3 = (x: 1; y: 1; z: 1);
    +  b0: TVec3 = (x: 1; y: 1; z: 1);
    +  c0: TVec3;
    +
    +  a1: TVec2 = (x: 1; y: 1);
    +  b1: TVec2 = (x: 1; y: 1);
    +  c1: TVec2;
    +begin
    +  c0 := a0 + b0;
    +  c1 := a1 + b1;
    +end.
    \ No newline at end of file
    -- 
    2.21.0 (Apple Git-122)
    
    
    patch.diff (14,837 bytes)
  • patch2.diff (15,763 bytes)
    From d27ccbead29d6218a29e2cbe3c45b5437b1778f2 Mon Sep 17 00:00:00 2001
    From: Ryan Joseph <genericptr@gmail.com>
    Date: Sat, 20 Apr 2019 09:58:41 -0400
    Subject: [PATCH] advanced objects mode switch
    
    ---
     compiler/globtype.pas  |   8 +-
     compiler/htypechk.pas  |   6 +-
     compiler/pdecobj.pas   |  26 ++++-
     compiler/pdecsub.pas   |   5 +
     tests/test/tadvobj1.pp | 215 +++++++++++++++++++++++++++++++++++++++++
     tests/test/tadvobj2.pp |  14 +++
     tests/test/tadvobj3.pp |  42 ++++++++
     tests/test/tadvobj4.pp |  18 ++++
     tests/test/tadvobj5.pp |  18 ++++
     tests/test/tadvobj6.pp |  18 ++++
     tests/test/tadvobj7.pp |  18 ++++
     tests/test/tadvobj8.pp |  14 +++
     12 files changed, 392 insertions(+), 10 deletions(-)
     create mode 100644 tests/test/tadvobj1.pp
     create mode 100644 tests/test/tadvobj2.pp
     create mode 100644 tests/test/tadvobj3.pp
     create mode 100644 tests/test/tadvobj4.pp
     create mode 100644 tests/test/tadvobj5.pp
     create mode 100644 tests/test/tadvobj6.pp
     create mode 100644 tests/test/tadvobj7.pp
     create mode 100644 tests/test/tadvobj8.pp
    
    diff --git a/compiler/globtype.pas b/compiler/globtype.pas
    index 7d23464d57..c4050d9615 100644
    --- a/compiler/globtype.pas
    +++ b/compiler/globtype.pas
    @@ -446,7 +446,8 @@ interface
              m_isolike_io,          { I/O as it required by an ISO compatible compiler }
              m_isolike_program_para, { program parameters as it required by an ISO compatible compiler }
              m_isolike_mod,         { mod operation as it is required by an iso compatible compiler }
    -         m_array_operators      { use Delphi compatible array operators instead of custom ones ("+") }
    +         m_array_operators,     { use Delphi compatible array operators instead of custom ones ("+") }
    +         m_advanced_objects     { adds additional features to objects }
            );
            tmodeswitches = set of tmodeswitch;
     
    @@ -597,7 +598,7 @@ interface
     
            cstylearrayofconst = [pocall_cdecl,pocall_cppdecl,pocall_mwpascal,pocall_sysv_abi_cdecl,pocall_ms_abi_cdecl];
     
    -       modeswitchstr : array[tmodeswitch] of string[18] = ('',
    +       modeswitchstr : array[tmodeswitch] of string[19] = ('',
              '','','','','','','',
              {$ifdef gpc_mode}'',{$endif}
              { more specific }
    @@ -635,7 +636,8 @@ interface
              'ISOIO',
              'ISOPROGRAMPARAS',
              'ISOMOD',
    -         'ARRAYOPERATORS'
    +         'ARRAYOPERATORS',
    +         'ADVANCEDOBJECTS'
              );
     
     
    diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
    index 07c035dc26..e7b44edc38 100644
    --- a/compiler/htypechk.pas
    +++ b/compiler/htypechk.pas
    @@ -2443,13 +2443,13 @@ implementation
             else
             if (FOperator<>NOTOKEN) then
               begin
    -            { check operands and if they contain records then search in records,
    +            { check operands and if they contain structs then search in structs,
                   then search in unit }
                 pt:=tcallparanode(FParaNode);
                 while assigned(pt) do
                   begin
    -                if (pt.resultdef.typ=recorddef) and
    -                    (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
    +                if ((pt.resultdef.typ=recorddef) or is_object(pt.resultdef)) and
    +                   (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
                       collect_overloads_in_struct(tabstractrecorddef(pt.resultdef),ProcdefOverloadList,searchhelpers,anoninherited,spezcontext);
                     pt:=tcallparanode(pt.right);
                   end;
    diff --git a/compiler/pdecobj.pas b/compiler/pdecobj.pas
    index 1e41f98a70..6d8b045684 100644
    --- a/compiler/pdecobj.pas
    +++ b/compiler/pdecobj.pas
    @@ -883,7 +883,8 @@ implementation
           begin
             case token of
               _PROCEDURE,
    -          _FUNCTION:
    +          _FUNCTION,
    +          _OPERATOR:
                 begin
                   if (astruct.symtable.currentvisibility=vis_published) and
                      not(oo_can_have_published in astruct.objectoptions) then
    @@ -1086,9 +1087,11 @@ implementation
               is_classdef:=false;
               { read class method/field/property }
               consume(_CLASS);
    -          { class modifier is only allowed for procedures, functions, }
    -          { constructors, destructors, fields and properties          }
    -          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or (token=_CONSTRUCTOR)) then
    +          { class modifier is only allowed for procedures, functions,
    +            constructors, destructors, fields, properties and operators (if m_class_operators is enabled) }
    +          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or 
    +             (token=_CONSTRUCTOR) or
    +             ((token=_OPERATOR) and is_object(current_structdef) and (m_advanced_objects in current_settings.modeswitches))) then
                 Message(parser_e_procedure_or_function_expected);
     
               { Java interfaces can contain final class vars }
    @@ -1342,6 +1345,21 @@ implementation
                     is_classdef:=false;
                     hadgeneric:=false;
                   end;
    +            _OPERATOR :
    +              { operators must be class methods and enabled via mode switch.
    +                class helpers like record helpers are not allowed to contain operators }
    +              if (m_advanced_objects in current_settings.modeswitches) and 
    +                 is_classdef and 
    +                 is_object(current_structdef) and
    +                 not is_classhelper(current_structdef) then
    +                begin                    
    +                  method_dec(current_structdef,is_classdef,hadgeneric);
    +                  fields_allowed:=false;
    +                  is_classdef:=false;
    +                  hadgeneric:=false;
    +                end
    +              else
    +                consume(_ID);
                 _END :
                   begin
                     consume(_END);
    diff --git a/compiler/pdecsub.pas b/compiler/pdecsub.pas
    index e2b0c1712a..eefa4f6360 100644
    --- a/compiler/pdecsub.pas
    +++ b/compiler/pdecsub.pas
    @@ -1480,6 +1480,11 @@ implementation
                           (tparavarsym(pd.parast.SymList[1]).varspez<>vs_var)
                          ) then
                         Message(parser_e_overload_impossible);
    +                  
    +                  { management operators are only possible in records }
    +                  if (m_advanced_objects in current_settings.modeswitches) 
    +                     and (assigned(pd.struct) and not is_record(pd.struct)) then
    +                    Message(parser_e_overload_impossible);
     
                       trecordsymtable(pd.procsym.Owner).includemanagementoperator(
                         token2managementoperator(optoken));
    diff --git a/tests/test/tadvobj1.pp b/tests/test/tadvobj1.pp
    new file mode 100644
    index 0000000000..9024231aeb
    --- /dev/null
    +++ b/tests/test/tadvobj1.pp
    @@ -0,0 +1,215 @@
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj1;
    +
    +type
    +  TMyObject = object
    +    public type
    +      TRight = integer;
    +      TSelf = TMyObject;
    +    public
    +
    +      // Assignment operators
    +      class operator := (right: TRight): TSelf;
    +      class operator explicit (right: TRight): TSelf;
    +
    +      // Arithmetic operators
    +      class operator + (left: TSelf; right: TRight): TSelf;
    +      class operator + (left: TSelf; right: TSelf): TSelf;
    +      class operator - (left: TSelf; right: TRight): TSelf;
    +      class operator * (left: TSelf; right: TRight): TSelf;
    +      class operator / (left: TSelf; right: TRight): TSelf;
    +      class operator ** (left: TSelf; right: TRight): TSelf;
    +      class operator div (left: TSelf; right: TRight): TSelf;
    +      class operator mod (left: TSelf; right: TRight): TSelf;
    +
    +      // Logical operators
    +      class operator and (left: TSelf; right: TRight): TSelf;
    +      class operator or (left: TSelf; right: TRight): TSelf;
    +      class operator xor (left: TSelf; right: TRight): TSelf;
    +      class operator shl (left: TSelf; right: TRight): TSelf;
    +      class operator shr (left: TSelf; right: TRight): TSelf;
    +
    +      // Unary operators
    +      class operator + (left: TSelf): TSelf;
    +      class operator - (left: TSelf): TSelf;
    +      class operator not (left: TSelf): boolean;
    +
    +      // Relational operators
    +      class operator <> (left: TSelf; right: TRight): boolean;
    +      class operator < (left: TSelf; right: TRight): boolean;
    +      class operator > (left: TSelf; right: TRight): boolean;
    +      class operator <= (left: TSelf; right: TRight): boolean;
    +      class operator >= (left: TSelf; right: TRight): boolean;
    +      class operator = (left: TSelf; right: TRight): boolean;
    +
    +      // Set operators
    +      class operator >< (left: TSelf; right: TRight): boolean;
    +      class operator in (left: TSelf; right: TRight): boolean;
    +  end;
    +
    +class operator TMyObject.:= (right: TRight): TSelf;
    +begin
    +end;
    +
    +class operator TMyObject.explicit (right: TRight): TSelf;
    +begin
    +end;  
    +
    +class operator TMyObject.+ (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.+ (left: TSelf; right: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.- (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.* (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject./ (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.** (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.div (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.mod (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.and (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.or (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.xor (left: TSelf; right: TRight): TSelf;
    +begin 
    +  result := left;
    +end;
    +
    +class operator TMyObject.shl (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.shr (left: TSelf; right: TRight): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.not (left: TSelf): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.+ (left: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.- (left: TSelf): TSelf;
    +begin
    +  result := left;
    +end;
    +
    +class operator TMyObject.<> (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.< (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.> (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.<= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.>= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.= (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.>< (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +class operator TMyObject.in (left: TSelf; right: TRight): boolean;
    +begin
    +end;
    +
    +var
    +  c: TMyObject;
    +  b: boolean;
    +begin
    +  // Assignment operators
    +  c := TMyObject(100);
    +  c := 100;
    +
    +  // Arithmetic operators
    +  c := c + 1;
    +  c := c - 1;
    +  c := c * 1;
    +  c := c / 1;
    +  c += 1;
    +  c -= 1;
    +  c *= 1;
    +  c /= 1;
    +  c := c ** 1;
    +  c := c div 1;
    +  c := c mod 1;
    +
    +  // Logical operators
    +  c := c and 1;
    +  c := c or 1;
    +  c := c xor 1;
    +  c := c shl 1;
    +  c := c shr 1;
    +
    +  // Unary operators
    +  c := +c;
    +  c := -c;
    +  b := not c;
    +
    +  // Relational operators
    +  b := c <> 1;
    +  b := c > 1;
    +  b := c > 1;
    +  b := c <= 1;
    +  b := c >= 1;
    +  b := c = 1;
    +
    +  // Set operators
    +  b := c >< 1;
    +  b := c in 1;
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tadvobj2.pp b/tests/test/tadvobj2.pp
    new file mode 100644
    index 0000000000..b0397f5b6f
    --- /dev/null
    +++ b/tests/test/tadvobj2.pp
    @@ -0,0 +1,14 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj2;
    +
    +{ test to verify classes don't accidentally support operators }
    +type
    +  TMyClass = class
    +     class operator := (right: integer): TMyClass;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tadvobj3.pp b/tests/test/tadvobj3.pp
    new file mode 100644
    index 0000000000..6d25fd1c3c
    --- /dev/null
    +++ b/tests/test/tadvobj3.pp
    @@ -0,0 +1,42 @@
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj3;
    +
    +type
    +  TVec2 = object
    +    x, y: integer;
    +    class operator + (left: TVec2; right: TVec2): TVec2;
    +  end;
    +
    +class operator TVec2.+ (left: TVec2; right: TVec2): TVec2;
    +begin
    +  result.x := left.x + right.x;
    +  result.y := left.y + right.y;
    +end;
    +
    +type
    +  TVec3 = object(TVec2)
    +    z: integer;
    +    class operator + (left: TVec3; right: TVec3): TVec3;
    +  end;
    +
    +class operator TVec3.+ (left: TVec3; right: TVec3): TVec3;
    +begin
    +  result.x := left.x + right.x;
    +  result.y := left.y + right.y;
    +  result.z := left.z + right.z;
    +end;
    +
    +var
    +  a0: TVec3 = (x: 1; y: 1; z: 1);
    +  b0: TVec3 = (x: 1; y: 1; z: 1);
    +  c0: TVec3;
    +
    +  a1: TVec2 = (x: 1; y: 1);
    +  b1: TVec2 = (x: 1; y: 1);
    +  c1: TVec2;
    +begin
    +  c0 := a0 + b0;
    +  c1 := a1 + b1;
    +end.
    \ No newline at end of file
    diff --git a/tests/test/tadvobj4.pp b/tests/test/tadvobj4.pp
    new file mode 100644
    index 0000000000..17bba6399a
    --- /dev/null
    +++ b/tests/test/tadvobj4.pp
    @@ -0,0 +1,18 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj4;
    +
    +{ advanced objects don't support management operators }
    +type
    +  TMyObject = object
    +    class operator Initialize(var A: TMyObject);
    +  end;
    +
    +class operator TMyObject.Initialize(var A: TMyObject);
    +begin
    +end;
    +
    +begin
    +end.
    diff --git a/tests/test/tadvobj5.pp b/tests/test/tadvobj5.pp
    new file mode 100644
    index 0000000000..f87b10d4e2
    --- /dev/null
    +++ b/tests/test/tadvobj5.pp
    @@ -0,0 +1,18 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj5;
    +
    +{ advanced objects don't support management operators }
    +type
    +  TMyObject = object
    +    class operator Finalize(var A: TMyObject);
    +  end;
    +
    +class operator TMyObject.Finalize(var A: TMyObject);
    +begin
    +end;
    +
    +begin
    +end.
    diff --git a/tests/test/tadvobj6.pp b/tests/test/tadvobj6.pp
    new file mode 100644
    index 0000000000..a38b80d5e1
    --- /dev/null
    +++ b/tests/test/tadvobj6.pp
    @@ -0,0 +1,18 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj6;
    +
    +{ advanced objects don't support management operators }
    +type
    +  TMyObject = object
    +    class operator Copy(constref Source: TMyObject; var Dest: TMyObject);
    +  end;
    +
    +class operator TMyObject.Copy(constref Source: TMyObject; var Dest: TMyObject);
    +begin
    +end;
    +
    +begin
    +end.
    diff --git a/tests/test/tadvobj7.pp b/tests/test/tadvobj7.pp
    new file mode 100644
    index 0000000000..2b75f2581d
    --- /dev/null
    +++ b/tests/test/tadvobj7.pp
    @@ -0,0 +1,18 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedobjects}
    +
    +program tadvobj7;
    +
    +{ advanced objects don't support management operators }
    +type
    +  TMyObject = object
    +    class operator AddRef(var Dest: TMyObject);
    +  end;
    +
    +class operator TMyObject.AddRef(var Dest: TMyObject);
    +begin
    +end;
    +
    +begin
    +end.
    diff --git a/tests/test/tadvobj8.pp b/tests/test/tadvobj8.pp
    new file mode 100644
    index 0000000000..db9795a09c
    --- /dev/null
    +++ b/tests/test/tadvobj8.pp
    @@ -0,0 +1,14 @@
    +{%FAIL}
    +{$mode objfpc}
    +{$modeswitch advancedrecords}
    +
    +program tadvobj8;
    +
    +{ test to make sure advanced records don't enable advanced objects features }
    +type
    +  TMyObject = object
    +    class operator := (right: integer): TMyObject;
    +  end;
    +
    +begin
    +end.
    \ No newline at end of file
    -- 
    2.21.0 (Apple Git-122)
    
    
    patch2.diff (15,763 bytes)

Activities

Ryan Joseph

2019-11-23 16:23

reporter  

patch.diff (14,837 bytes)
From 57c8106175cb1328d71c75bf50392d08084a0312 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Sat, 20 Apr 2019 09:58:41 -0400
Subject: [PATCH] advanced objects mode switch

---
 .gitignore             |  23 +++++
 compiler/globtype.pas  |   8 +-
 compiler/htypechk.pas  |   7 +-
 compiler/pdecobj.pas   |  26 ++++-
 compiler/pdecsub.pas   |   5 +
 tests/test/tadvobj1.pp | 227 +++++++++++++++++++++++++++++++++++++++++
 tests/test/tadvobj2.pp |  14 +++
 tests/test/tadvobj3.pp |  33 ++++++
 tests/test/tadvobj4.pp |  42 ++++++++
 9 files changed, 375 insertions(+), 10 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 tests/test/tadvobj1.pp
 create mode 100644 tests/test/tadvobj2.pp
 create mode 100644 tests/test/tadvobj3.pp
 create mode 100644 tests/test/tadvobj4.pp

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..64fdb156d0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+# files
+pp
+fpmake
+rtl/darwin/fpcmade.x86_64-darwin
+fpmake_proc1 copy.inc
+tests/*.x86_64-darwin
+rtl/Package.fpc
+tests/createlst
+tests/gparmake
+
+# directories
+lazbuild/
+x86_64-darwin/
+tests/tstunits/
+tests/utils
+
+# patterns
+*.app
+*.o
+*.ppu
+*.fpm
+*.rsj
+*.lst
\ No newline at end of file
diff --git a/compiler/globtype.pas b/compiler/globtype.pas
index 7d23464d57..c4050d9615 100644
--- a/compiler/globtype.pas
+++ b/compiler/globtype.pas
@@ -446,7 +446,8 @@ interface
          m_isolike_io,          { I/O as it required by an ISO compatible compiler }
          m_isolike_program_para, { program parameters as it required by an ISO compatible compiler }
          m_isolike_mod,         { mod operation as it is required by an iso compatible compiler }
-         m_array_operators      { use Delphi compatible array operators instead of custom ones ("+") }
+         m_array_operators,     { use Delphi compatible array operators instead of custom ones ("+") }
+         m_advanced_objects     { adds additional features to objects }
        );
        tmodeswitches = set of tmodeswitch;
 
@@ -597,7 +598,7 @@ interface
 
        cstylearrayofconst = [pocall_cdecl,pocall_cppdecl,pocall_mwpascal,pocall_sysv_abi_cdecl,pocall_ms_abi_cdecl];
 
-       modeswitchstr : array[tmodeswitch] of string[18] = ('',
+       modeswitchstr : array[tmodeswitch] of string[19] = ('',
          '','','','','','','',
          {$ifdef gpc_mode}'',{$endif}
          { more specific }
@@ -635,7 +636,8 @@ interface
          'ISOIO',
          'ISOPROGRAMPARAS',
          'ISOMOD',
-         'ARRAYOPERATORS'
+         'ARRAYOPERATORS',
+         'ADVANCEDOBJECTS'
          );
 
 
diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
index 07c035dc26..16d52235eb 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2443,13 +2443,14 @@ implementation
         else
         if (FOperator<>NOTOKEN) then
           begin
-            { check operands and if they contain records then search in records,
+            { check operands and if they contain structs then search in structs,
               then search in unit }
             pt:=tcallparanode(FParaNode);
             while assigned(pt) do
               begin
-                if (pt.resultdef.typ=recorddef) and
-                    (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
+                if ((pt.resultdef.typ=recorddef) or 
+                   ((m_advanced_objects in current_settings.modeswitches) and is_object(pt.resultdef))) and
+                   (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
                   collect_overloads_in_struct(tabstractrecorddef(pt.resultdef),ProcdefOverloadList,searchhelpers,anoninherited,spezcontext);
                 pt:=tcallparanode(pt.right);
               end;
diff --git a/compiler/pdecobj.pas b/compiler/pdecobj.pas
index 1e41f98a70..6d8b045684 100644
--- a/compiler/pdecobj.pas
+++ b/compiler/pdecobj.pas
@@ -883,7 +883,8 @@ implementation
       begin
         case token of
           _PROCEDURE,
-          _FUNCTION:
+          _FUNCTION,
+          _OPERATOR:
             begin
               if (astruct.symtable.currentvisibility=vis_published) and
                  not(oo_can_have_published in astruct.objectoptions) then
@@ -1086,9 +1087,11 @@ implementation
           is_classdef:=false;
           { read class method/field/property }
           consume(_CLASS);
-          { class modifier is only allowed for procedures, functions, }
-          { constructors, destructors, fields and properties          }
-          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or (token=_CONSTRUCTOR)) then
+          { class modifier is only allowed for procedures, functions,
+            constructors, destructors, fields, properties and operators (if m_class_operators is enabled) }
+          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or 
+             (token=_CONSTRUCTOR) or
+             ((token=_OPERATOR) and is_object(current_structdef) and (m_advanced_objects in current_settings.modeswitches))) then
             Message(parser_e_procedure_or_function_expected);
 
           { Java interfaces can contain final class vars }
@@ -1342,6 +1345,21 @@ implementation
                 is_classdef:=false;
                 hadgeneric:=false;
               end;
+            _OPERATOR :
+              { operators must be class methods and enabled via mode switch.
+                class helpers like record helpers are not allowed to contain operators }
+              if (m_advanced_objects in current_settings.modeswitches) and 
+                 is_classdef and 
+                 is_object(current_structdef) and
+                 not is_classhelper(current_structdef) then
+                begin                    
+                  method_dec(current_structdef,is_classdef,hadgeneric);
+                  fields_allowed:=false;
+                  is_classdef:=false;
+                  hadgeneric:=false;
+                end
+              else
+                consume(_ID);
             _END :
               begin
                 consume(_END);
diff --git a/compiler/pdecsub.pas b/compiler/pdecsub.pas
index e2b0c1712a..eefa4f6360 100644
--- a/compiler/pdecsub.pas
+++ b/compiler/pdecsub.pas
@@ -1480,6 +1480,11 @@ implementation
                       (tparavarsym(pd.parast.SymList[1]).varspez<>vs_var)
                      ) then
                     Message(parser_e_overload_impossible);
+                  
+                  { management operators are only possible in records }
+                  if (m_advanced_objects in current_settings.modeswitches) 
+                     and (assigned(pd.struct) and not is_record(pd.struct)) then
+                    Message(parser_e_overload_impossible);
 
                   trecordsymtable(pd.procsym.Owner).includemanagementoperator(
                     token2managementoperator(optoken));
diff --git a/tests/test/tadvobj1.pp b/tests/test/tadvobj1.pp
new file mode 100644
index 0000000000..2210550ba7
--- /dev/null
+++ b/tests/test/tadvobj1.pp
@@ -0,0 +1,227 @@
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj1;
+
+type
+  TMyObject = object
+    public type
+      TRight = integer;
+      TSelf = TMyObject;
+    public
+
+      // Assignment operators
+      class operator := (right: TRight): TSelf;
+      class operator explicit (right: TRight): TSelf;
+
+      // Arithmetic operators
+      class operator + (left: TSelf; right: TRight): TSelf;
+      class operator + (left: TSelf; right: TSelf): TSelf;
+      class operator - (left: TSelf; right: TRight): TSelf;
+      class operator * (left: TSelf; right: TRight): TSelf;
+      class operator / (left: TSelf; right: TRight): TSelf;
+      class operator ** (left: TSelf; right: TRight): TSelf;
+      class operator div (left: TSelf; right: TRight): TSelf;
+      class operator mod (left: TSelf; right: TRight): TSelf;
+
+      // Logical operators
+      class operator and (left: TSelf; right: TRight): TSelf;
+      class operator or (left: TSelf; right: TRight): TSelf;
+      class operator xor (left: TSelf; right: TRight): TSelf;
+      class operator shl (left: TSelf; right: TRight): TSelf;
+      class operator shr (left: TSelf; right: TRight): TSelf;
+      //class operator << (left: TSelf; right: TRight): TSelf;
+      //class operator >> (left: TSelf; right: TRight): TSelf;
+
+      // Unary operators
+      class operator + (left: TSelf): TSelf;
+      class operator - (left: TSelf): TSelf;
+      class operator not (left: TSelf): boolean;
+
+      // Relational operators
+      class operator <> (left: TSelf; right: TRight): boolean;
+      class operator < (left: TSelf; right: TRight): boolean;
+      class operator > (left: TSelf; right: TRight): boolean;
+      class operator <= (left: TSelf; right: TRight): boolean;
+      class operator >= (left: TSelf; right: TRight): boolean;
+      class operator = (left: TSelf; right: TRight): boolean;
+
+      // Set operators
+      class operator >< (left: TSelf; right: TRight): boolean;
+      class operator in (left: TSelf; right: TRight): boolean;
+  end;
+
+class operator TMyObject.:= (right: TRight): TSelf;
+begin
+  writeln('TMyObject.:=');
+end;
+
+class operator TMyObject.explicit (right: TRight): TSelf;
+begin
+  writeln('TMyObject.explicit');
+end;  
+
+class operator TMyObject.+ (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.+ (left: TSelf; right: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.- (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.* (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject./ (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.** (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.div (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.mod (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.and (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.or (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.xor (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.shl (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.shr (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+//class operator TSelf.<< (left: TSelf; right: TRight): TSelf;
+//begin
+//end;
+
+//class operator TSelf.>> (left: TSelf; right: TRight): TSelf;
+//begin
+//end;
+
+class operator TMyObject.not (left: TSelf): boolean;
+begin
+end;
+
+class operator TMyObject.+ (left: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.- (left: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.<> (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.< (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.> (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.<= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.>= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.>< (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.in (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+var
+  c: TMyObject;
+  b: boolean;
+begin
+  // Assignment operators
+  c := TMyObject(100);
+  c := 100;
+
+  // Arithmetic operators
+  c := c + 1;
+  c := c - 1;
+  c := c * 1;
+  c := c / 1;
+  c += 1;
+  c -= 1;
+  c *= 1;
+  c /= 1;
+  c := c ** 1;
+  c := c div 1;
+  c := c mod 1;
+
+  // Logical operators
+  c := c and 1;
+  c := c or 1;
+  c := c xor 1;
+  c := c shl 1;
+  c := c shr 1;
+
+  // Unary operators
+  c := +c;
+  c := -c;
+  b := not c;
+
+  // Relational operators
+  b := c <> 1;
+  b := c > 1;
+  b := c > 1;
+  b := c <= 1;
+  b := c >= 1;
+  b := c = 1;
+
+  // Set operators
+  b := c >< 1;
+  b := c in 1;
+end.
\ No newline at end of file
diff --git a/tests/test/tadvobj2.pp b/tests/test/tadvobj2.pp
new file mode 100644
index 0000000000..a90349c0c3
--- /dev/null
+++ b/tests/test/tadvobj2.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj2;
+
+{ classes don't support operators }
+type
+  TMyClass = class
+     class operator := (right: integer): TMyClass;
+  end;
+
+begin
+end.
\ No newline at end of file
diff --git a/tests/test/tadvobj3.pp b/tests/test/tadvobj3.pp
new file mode 100644
index 0000000000..246909e419
--- /dev/null
+++ b/tests/test/tadvobj3.pp
@@ -0,0 +1,33 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj3;
+
+{ advanced objects don't support management operators }
+type
+  TMyObject = object
+    class operator Initialize(var A: TMyObject);
+    class operator Finalize(var A: TMyObject);
+    class operator Copy(constref Source: TMyObject; var Dest: TMyObject);
+    class operator AddRef(var Dest: TMyObject);
+  end;
+
+class operator TMyObject.Initialize(var A: TMyObject);
+begin
+end;
+
+class operator TMyObject.Finalize(var A: TMyObject);
+begin
+end;
+
+class operator TMyObject.Copy(constref Source: TMyObject; var Dest: TMyObject);
+begin
+end;
+
+class operator TMyObject.AddRef(var Dest: TMyObject);
+begin
+end;
+
+begin
+end.
diff --git a/tests/test/tadvobj4.pp b/tests/test/tadvobj4.pp
new file mode 100644
index 0000000000..78238b8e09
--- /dev/null
+++ b/tests/test/tadvobj4.pp
@@ -0,0 +1,42 @@
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj4;
+
+type
+  TVec2 = object
+    x, y: integer;
+    class operator + (left: TVec2; right: TVec2): TVec2;
+  end;
+
+class operator TVec2.+ (left: TVec2; right: TVec2): TVec2;
+begin
+  result.x := left.x + right.x;
+  result.y := left.y + right.y;
+end;
+
+type
+  TVec3 = object(TVec2)
+    z: integer;
+    class operator + (left: TVec3; right: TVec3): TVec3;
+  end;
+
+class operator TVec3.+ (left: TVec3; right: TVec3): TVec3;
+begin
+  result.x := left.x + right.x;
+  result.y := left.y + right.y;
+  result.z := left.z + right.z;
+end;
+
+var
+  a0: TVec3 = (x: 1; y: 1; z: 1);
+  b0: TVec3 = (x: 1; y: 1; z: 1);
+  c0: TVec3;
+
+  a1: TVec2 = (x: 1; y: 1);
+  b1: TVec2 = (x: 1; y: 1);
+  c1: TVec2;
+begin
+  c0 := a0 + b0;
+  c1 := a1 + b1;
+end.
\ No newline at end of file
-- 
2.21.0 (Apple Git-122)

patch.diff (14,837 bytes)

Ryan Joseph

2019-11-23 16:31

reporter   ~0119459

https://github.com/genericptr/freepascal/tree/advanced_objects.

Sven Barth

2019-11-23 22:44

manager   ~0119470

- don't include .gitignore in your patch
- your change in htypechk.pas is not consistent with the behavior of records: if a record has operators (aka modeswitch advancedrecords was active) then they are also used in units *without* that modeswitch; your code behaves differently for objects
- please stop using "<<" and ">>"; they are simply aliases for "shl" and "shr" done by the scanner
- in a %FAIL test you can only test for a single error; thus declaring all four management operators is rather useless; instead you need to create a test for each of the management operators

Ryan Joseph

2019-11-23 23:27

reporter   ~0119472

Not sure I understand your second comment. Do you mean that because m_advanced_objects was set that sto_has_operator will also be set and thus I don't need the check for the mode switch? I wrote this code months ago and I don't remember exactly why I did what I did.

Sven Barth

2019-11-24 14:14

manager   ~0119474

sto_has_operator is set if the def's symtable contains an operator. It does not matter whether it came there thanks to a modeswitch or something else. The important point is there is an operator in there (or one of the nested symtables) and if it matches it should be used.
The modeswitch should not be used to decide whether an operator is used, only whether it can be declared at all.

Ryan Joseph

2019-11-24 15:13

reporter   ~0119476

Here are there requested changes.

patch2.diff (15,763 bytes)
From d27ccbead29d6218a29e2cbe3c45b5437b1778f2 Mon Sep 17 00:00:00 2001
From: Ryan Joseph <genericptr@gmail.com>
Date: Sat, 20 Apr 2019 09:58:41 -0400
Subject: [PATCH] advanced objects mode switch

---
 compiler/globtype.pas  |   8 +-
 compiler/htypechk.pas  |   6 +-
 compiler/pdecobj.pas   |  26 ++++-
 compiler/pdecsub.pas   |   5 +
 tests/test/tadvobj1.pp | 215 +++++++++++++++++++++++++++++++++++++++++
 tests/test/tadvobj2.pp |  14 +++
 tests/test/tadvobj3.pp |  42 ++++++++
 tests/test/tadvobj4.pp |  18 ++++
 tests/test/tadvobj5.pp |  18 ++++
 tests/test/tadvobj6.pp |  18 ++++
 tests/test/tadvobj7.pp |  18 ++++
 tests/test/tadvobj8.pp |  14 +++
 12 files changed, 392 insertions(+), 10 deletions(-)
 create mode 100644 tests/test/tadvobj1.pp
 create mode 100644 tests/test/tadvobj2.pp
 create mode 100644 tests/test/tadvobj3.pp
 create mode 100644 tests/test/tadvobj4.pp
 create mode 100644 tests/test/tadvobj5.pp
 create mode 100644 tests/test/tadvobj6.pp
 create mode 100644 tests/test/tadvobj7.pp
 create mode 100644 tests/test/tadvobj8.pp

diff --git a/compiler/globtype.pas b/compiler/globtype.pas
index 7d23464d57..c4050d9615 100644
--- a/compiler/globtype.pas
+++ b/compiler/globtype.pas
@@ -446,7 +446,8 @@ interface
          m_isolike_io,          { I/O as it required by an ISO compatible compiler }
          m_isolike_program_para, { program parameters as it required by an ISO compatible compiler }
          m_isolike_mod,         { mod operation as it is required by an iso compatible compiler }
-         m_array_operators      { use Delphi compatible array operators instead of custom ones ("+") }
+         m_array_operators,     { use Delphi compatible array operators instead of custom ones ("+") }
+         m_advanced_objects     { adds additional features to objects }
        );
        tmodeswitches = set of tmodeswitch;
 
@@ -597,7 +598,7 @@ interface
 
        cstylearrayofconst = [pocall_cdecl,pocall_cppdecl,pocall_mwpascal,pocall_sysv_abi_cdecl,pocall_ms_abi_cdecl];
 
-       modeswitchstr : array[tmodeswitch] of string[18] = ('',
+       modeswitchstr : array[tmodeswitch] of string[19] = ('',
          '','','','','','','',
          {$ifdef gpc_mode}'',{$endif}
          { more specific }
@@ -635,7 +636,8 @@ interface
          'ISOIO',
          'ISOPROGRAMPARAS',
          'ISOMOD',
-         'ARRAYOPERATORS'
+         'ARRAYOPERATORS',
+         'ADVANCEDOBJECTS'
          );
 
 
diff --git a/compiler/htypechk.pas b/compiler/htypechk.pas
index 07c035dc26..e7b44edc38 100644
--- a/compiler/htypechk.pas
+++ b/compiler/htypechk.pas
@@ -2443,13 +2443,13 @@ implementation
         else
         if (FOperator<>NOTOKEN) then
           begin
-            { check operands and if they contain records then search in records,
+            { check operands and if they contain structs then search in structs,
               then search in unit }
             pt:=tcallparanode(FParaNode);
             while assigned(pt) do
               begin
-                if (pt.resultdef.typ=recorddef) and
-                    (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
+                if ((pt.resultdef.typ=recorddef) or is_object(pt.resultdef)) and
+                   (sto_has_operator in tabstractrecorddef(pt.resultdef).symtable.tableoptions) then
                   collect_overloads_in_struct(tabstractrecorddef(pt.resultdef),ProcdefOverloadList,searchhelpers,anoninherited,spezcontext);
                 pt:=tcallparanode(pt.right);
               end;
diff --git a/compiler/pdecobj.pas b/compiler/pdecobj.pas
index 1e41f98a70..6d8b045684 100644
--- a/compiler/pdecobj.pas
+++ b/compiler/pdecobj.pas
@@ -883,7 +883,8 @@ implementation
       begin
         case token of
           _PROCEDURE,
-          _FUNCTION:
+          _FUNCTION,
+          _OPERATOR:
             begin
               if (astruct.symtable.currentvisibility=vis_published) and
                  not(oo_can_have_published in astruct.objectoptions) then
@@ -1086,9 +1087,11 @@ implementation
           is_classdef:=false;
           { read class method/field/property }
           consume(_CLASS);
-          { class modifier is only allowed for procedures, functions, }
-          { constructors, destructors, fields and properties          }
-          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or (token=_CONSTRUCTOR)) then
+          { class modifier is only allowed for procedures, functions,
+            constructors, destructors, fields, properties and operators (if m_class_operators is enabled) }
+          if not((token in [_FUNCTION,_PROCEDURE,_PROPERTY,_VAR,_DESTRUCTOR,_THREADVAR]) or 
+             (token=_CONSTRUCTOR) or
+             ((token=_OPERATOR) and is_object(current_structdef) and (m_advanced_objects in current_settings.modeswitches))) then
             Message(parser_e_procedure_or_function_expected);
 
           { Java interfaces can contain final class vars }
@@ -1342,6 +1345,21 @@ implementation
                 is_classdef:=false;
                 hadgeneric:=false;
               end;
+            _OPERATOR :
+              { operators must be class methods and enabled via mode switch.
+                class helpers like record helpers are not allowed to contain operators }
+              if (m_advanced_objects in current_settings.modeswitches) and 
+                 is_classdef and 
+                 is_object(current_structdef) and
+                 not is_classhelper(current_structdef) then
+                begin                    
+                  method_dec(current_structdef,is_classdef,hadgeneric);
+                  fields_allowed:=false;
+                  is_classdef:=false;
+                  hadgeneric:=false;
+                end
+              else
+                consume(_ID);
             _END :
               begin
                 consume(_END);
diff --git a/compiler/pdecsub.pas b/compiler/pdecsub.pas
index e2b0c1712a..eefa4f6360 100644
--- a/compiler/pdecsub.pas
+++ b/compiler/pdecsub.pas
@@ -1480,6 +1480,11 @@ implementation
                       (tparavarsym(pd.parast.SymList[1]).varspez<>vs_var)
                      ) then
                     Message(parser_e_overload_impossible);
+                  
+                  { management operators are only possible in records }
+                  if (m_advanced_objects in current_settings.modeswitches) 
+                     and (assigned(pd.struct) and not is_record(pd.struct)) then
+                    Message(parser_e_overload_impossible);
 
                   trecordsymtable(pd.procsym.Owner).includemanagementoperator(
                     token2managementoperator(optoken));
diff --git a/tests/test/tadvobj1.pp b/tests/test/tadvobj1.pp
new file mode 100644
index 0000000000..9024231aeb
--- /dev/null
+++ b/tests/test/tadvobj1.pp
@@ -0,0 +1,215 @@
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj1;
+
+type
+  TMyObject = object
+    public type
+      TRight = integer;
+      TSelf = TMyObject;
+    public
+
+      // Assignment operators
+      class operator := (right: TRight): TSelf;
+      class operator explicit (right: TRight): TSelf;
+
+      // Arithmetic operators
+      class operator + (left: TSelf; right: TRight): TSelf;
+      class operator + (left: TSelf; right: TSelf): TSelf;
+      class operator - (left: TSelf; right: TRight): TSelf;
+      class operator * (left: TSelf; right: TRight): TSelf;
+      class operator / (left: TSelf; right: TRight): TSelf;
+      class operator ** (left: TSelf; right: TRight): TSelf;
+      class operator div (left: TSelf; right: TRight): TSelf;
+      class operator mod (left: TSelf; right: TRight): TSelf;
+
+      // Logical operators
+      class operator and (left: TSelf; right: TRight): TSelf;
+      class operator or (left: TSelf; right: TRight): TSelf;
+      class operator xor (left: TSelf; right: TRight): TSelf;
+      class operator shl (left: TSelf; right: TRight): TSelf;
+      class operator shr (left: TSelf; right: TRight): TSelf;
+
+      // Unary operators
+      class operator + (left: TSelf): TSelf;
+      class operator - (left: TSelf): TSelf;
+      class operator not (left: TSelf): boolean;
+
+      // Relational operators
+      class operator <> (left: TSelf; right: TRight): boolean;
+      class operator < (left: TSelf; right: TRight): boolean;
+      class operator > (left: TSelf; right: TRight): boolean;
+      class operator <= (left: TSelf; right: TRight): boolean;
+      class operator >= (left: TSelf; right: TRight): boolean;
+      class operator = (left: TSelf; right: TRight): boolean;
+
+      // Set operators
+      class operator >< (left: TSelf; right: TRight): boolean;
+      class operator in (left: TSelf; right: TRight): boolean;
+  end;
+
+class operator TMyObject.:= (right: TRight): TSelf;
+begin
+end;
+
+class operator TMyObject.explicit (right: TRight): TSelf;
+begin
+end;  
+
+class operator TMyObject.+ (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.+ (left: TSelf; right: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.- (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.* (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject./ (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.** (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.div (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.mod (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.and (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.or (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.xor (left: TSelf; right: TRight): TSelf;
+begin 
+  result := left;
+end;
+
+class operator TMyObject.shl (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.shr (left: TSelf; right: TRight): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.not (left: TSelf): boolean;
+begin
+end;
+
+class operator TMyObject.+ (left: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.- (left: TSelf): TSelf;
+begin
+  result := left;
+end;
+
+class operator TMyObject.<> (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.< (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.> (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.<= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.>= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.= (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.>< (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+class operator TMyObject.in (left: TSelf; right: TRight): boolean;
+begin
+end;
+
+var
+  c: TMyObject;
+  b: boolean;
+begin
+  // Assignment operators
+  c := TMyObject(100);
+  c := 100;
+
+  // Arithmetic operators
+  c := c + 1;
+  c := c - 1;
+  c := c * 1;
+  c := c / 1;
+  c += 1;
+  c -= 1;
+  c *= 1;
+  c /= 1;
+  c := c ** 1;
+  c := c div 1;
+  c := c mod 1;
+
+  // Logical operators
+  c := c and 1;
+  c := c or 1;
+  c := c xor 1;
+  c := c shl 1;
+  c := c shr 1;
+
+  // Unary operators
+  c := +c;
+  c := -c;
+  b := not c;
+
+  // Relational operators
+  b := c <> 1;
+  b := c > 1;
+  b := c > 1;
+  b := c <= 1;
+  b := c >= 1;
+  b := c = 1;
+
+  // Set operators
+  b := c >< 1;
+  b := c in 1;
+end.
\ No newline at end of file
diff --git a/tests/test/tadvobj2.pp b/tests/test/tadvobj2.pp
new file mode 100644
index 0000000000..b0397f5b6f
--- /dev/null
+++ b/tests/test/tadvobj2.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj2;
+
+{ test to verify classes don't accidentally support operators }
+type
+  TMyClass = class
+     class operator := (right: integer): TMyClass;
+  end;
+
+begin
+end.
\ No newline at end of file
diff --git a/tests/test/tadvobj3.pp b/tests/test/tadvobj3.pp
new file mode 100644
index 0000000000..6d25fd1c3c
--- /dev/null
+++ b/tests/test/tadvobj3.pp
@@ -0,0 +1,42 @@
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj3;
+
+type
+  TVec2 = object
+    x, y: integer;
+    class operator + (left: TVec2; right: TVec2): TVec2;
+  end;
+
+class operator TVec2.+ (left: TVec2; right: TVec2): TVec2;
+begin
+  result.x := left.x + right.x;
+  result.y := left.y + right.y;
+end;
+
+type
+  TVec3 = object(TVec2)
+    z: integer;
+    class operator + (left: TVec3; right: TVec3): TVec3;
+  end;
+
+class operator TVec3.+ (left: TVec3; right: TVec3): TVec3;
+begin
+  result.x := left.x + right.x;
+  result.y := left.y + right.y;
+  result.z := left.z + right.z;
+end;
+
+var
+  a0: TVec3 = (x: 1; y: 1; z: 1);
+  b0: TVec3 = (x: 1; y: 1; z: 1);
+  c0: TVec3;
+
+  a1: TVec2 = (x: 1; y: 1);
+  b1: TVec2 = (x: 1; y: 1);
+  c1: TVec2;
+begin
+  c0 := a0 + b0;
+  c1 := a1 + b1;
+end.
\ No newline at end of file
diff --git a/tests/test/tadvobj4.pp b/tests/test/tadvobj4.pp
new file mode 100644
index 0000000000..17bba6399a
--- /dev/null
+++ b/tests/test/tadvobj4.pp
@@ -0,0 +1,18 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj4;
+
+{ advanced objects don't support management operators }
+type
+  TMyObject = object
+    class operator Initialize(var A: TMyObject);
+  end;
+
+class operator TMyObject.Initialize(var A: TMyObject);
+begin
+end;
+
+begin
+end.
diff --git a/tests/test/tadvobj5.pp b/tests/test/tadvobj5.pp
new file mode 100644
index 0000000000..f87b10d4e2
--- /dev/null
+++ b/tests/test/tadvobj5.pp
@@ -0,0 +1,18 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj5;
+
+{ advanced objects don't support management operators }
+type
+  TMyObject = object
+    class operator Finalize(var A: TMyObject);
+  end;
+
+class operator TMyObject.Finalize(var A: TMyObject);
+begin
+end;
+
+begin
+end.
diff --git a/tests/test/tadvobj6.pp b/tests/test/tadvobj6.pp
new file mode 100644
index 0000000000..a38b80d5e1
--- /dev/null
+++ b/tests/test/tadvobj6.pp
@@ -0,0 +1,18 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj6;
+
+{ advanced objects don't support management operators }
+type
+  TMyObject = object
+    class operator Copy(constref Source: TMyObject; var Dest: TMyObject);
+  end;
+
+class operator TMyObject.Copy(constref Source: TMyObject; var Dest: TMyObject);
+begin
+end;
+
+begin
+end.
diff --git a/tests/test/tadvobj7.pp b/tests/test/tadvobj7.pp
new file mode 100644
index 0000000000..2b75f2581d
--- /dev/null
+++ b/tests/test/tadvobj7.pp
@@ -0,0 +1,18 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedobjects}
+
+program tadvobj7;
+
+{ advanced objects don't support management operators }
+type
+  TMyObject = object
+    class operator AddRef(var Dest: TMyObject);
+  end;
+
+class operator TMyObject.AddRef(var Dest: TMyObject);
+begin
+end;
+
+begin
+end.
diff --git a/tests/test/tadvobj8.pp b/tests/test/tadvobj8.pp
new file mode 100644
index 0000000000..db9795a09c
--- /dev/null
+++ b/tests/test/tadvobj8.pp
@@ -0,0 +1,14 @@
+{%FAIL}
+{$mode objfpc}
+{$modeswitch advancedrecords}
+
+program tadvobj8;
+
+{ test to make sure advanced records don't enable advanced objects features }
+type
+  TMyObject = object
+    class operator := (right: integer): TMyObject;
+  end;
+
+begin
+end.
\ No newline at end of file
-- 
2.21.0 (Apple Git-122)

patch2.diff (15,763 bytes)

Sven Barth

2019-11-24 15:23

manager   ~0119477

Better. I'll ask on core what the others think about it.

Ryan Joseph

2019-11-24 15:29

reporter   ~0119478

Thank you Sven. This is the definition of trivial so hopefully it's smooth sailing. It was discussed on the mailing list and I was told that it should be behind a mode switch (which makes sense) but then I discovered that properties were already allowed in objects. The question is why are properties not part of the mode switch also then?

Issue History

Date Modified Username Field Change
2019-11-23 16:23 Ryan Joseph New Issue
2019-11-23 16:23 Ryan Joseph File Added: patch.diff
2019-11-23 16:31 Ryan Joseph Note Added: 0119459
2019-11-23 22:44 Sven Barth Note Added: 0119470
2019-11-23 23:27 Ryan Joseph Note Added: 0119472
2019-11-24 14:14 Sven Barth Note Added: 0119474
2019-11-24 15:13 Ryan Joseph File Added: patch2.diff
2019-11-24 15:13 Ryan Joseph Note Added: 0119476
2019-11-24 15:23 Sven Barth Note Added: 0119477
2019-11-24 15:24 Sven Barth Severity minor => feature
2019-11-24 15:24 Sven Barth Status new => acknowledged
2019-11-24 15:24 Sven Barth FPCTarget => -
2019-11-24 15:29 Ryan Joseph Note Added: 0119478