Difference between revisions of "pas2js Generics"

From Free Pascal wiki
Jump to navigationJump to search
(16 intermediate revisions by the same user not shown)
Line 7: Line 7:
 
==Working==
 
==Working==
  
* generic record
 
* specialize:
 
** ObjFPC: ''type TFoo = specialize TGen<word>;''
 
** Delphi: ''type TFoo = TGen<word>;''
 
** ObjFPC anonymous: ''var r: specialize TBird<word>;''
 
** Delphi: anonymous ''var r: TBird<word>;''
 
 
* generic class
 
* generic class
** specialize in ancestor type
+
** ClassName, TypeInfo Name
** forward generic class (FPC 3.3.1 does not support it yet)
+
*** Delphi, pas2js: full path with param names, e.g. 'TAnt<System.Word>' and 'TAnt<System.Word>.TLeg'
 +
*** FPC: instead of param names uses crc, e.g. 'TAnt$1$crcHexNumber', full paths for classnames, e.g. 'TAnt$1$crcHexNumber.TLeg', last name for TypeInfo: e.g. 'TLeg'
 +
** JS specialize code in unit of generic
 +
** forward generic class, e.g. ''type TAnt<T>=class; TBird=class a: TAnt<word>; end; TAnt<T>=class a:T; end;''
 +
*** Delphi/pas2js: supported
 +
*** FPC: 3.3.1 does not support it yet
 
** enumtype inside generic is not propagated
 
** enumtype inside generic is not propagated
** ObjFPC: members can refer to parent type without type parameters
+
** ObjFPC: members can refer to parent type without type parameters, e.g. ''type generic TAnt<T> = class Parent: TAnt; end;''
** Delphi: descendants cannot refer to type parameters of ancestors, pas2js allows it.
+
** Delphi/FPC: descendants cannot refer to type parameters of ancestors, pas2js allows it within same unit (strict private).
** Delphi/FPC does not allows accessing local symbols. pas2js allows it.
+
** Delphi/FPC does not allow accessing local symbols. pas2js allows it.
 
** class constructor
 
** class constructor
 
* generic external class
 
* generic external class
* generic array
+
* generic record
 +
* generic dynamic array
 
* generic static array: Note: delphi wiki says "no static arrays", but 10.3 compiles it, see http://docwiki.embarcadero.com/RADStudio/Rio/en/Declaring_Generics
 
* generic static array: Note: delphi wiki says "no static arrays", but 10.3 compiles it, see http://docwiki.embarcadero.com/RADStudio/Rio/en/Declaring_Generics
 
* generic interface
 
* generic interface
Line 28: Line 28:
 
** specialized proc types can assign normal proc
 
** specialized proc types can assign normal proc
 
** Procedure local scope:
 
** Procedure local scope:
*** FPC, pas2js: allowed
+
*** pas2js: allowed, except for typeinfo
 +
*** FPC: allowed, including typeinfo
 
*** Delphi: Error: parameterized type can not be declared in procedure local scope
 
*** Delphi: Error: parameterized type can not be declared in procedure local scope
 
* anonymous type in specialize: ''TBird<array of word>''
 
* anonymous type in specialize: ''TBird<array of word>''
 +
* specialize:
 +
** ObjFPC: ''type TFoo = specialize TGen<word>;''
 +
** Delphi: ''type TFoo = TGen<word>;''
 +
** ObjFPC anonymous: ''var r: specialize TBird<word>;''
 +
** Delphi: anonymous ''var r: TBird<word>;''
 
* nested specialize: TBird<TWing<byte>>
 
* nested specialize: TBird<TWing<byte>>
 
* record/class field
 
* record/class field
Line 37: Line 43:
 
** Delphi: implementation must repeat type params, must omit constraints
 
** Delphi: implementation must repeat type params, must omit constraints
 
** FPC: implementation must not repeat type params
 
** FPC: implementation must not repeat type params
* for-loop, if-then-else, repeat-until, while-do, try-finally, try-except, case-of
 
* assignments
 
* primitive expression like identifiers and constants
 
* operators: +, -, *, /
 
 
* helper for generic type
 
* helper for generic type
 
* constraints:
 
* constraints:
Line 55: Line 57:
 
** forward class must repeat constraints
 
** forward class must repeat constraints
 
** Delphi does not allow ''TObject'' as constraint. pas2js allows it, because it has other root classes.
 
** Delphi does not allow ''TObject'' as constraint. pas2js allows it, because it has other root classes.
** constraint refers to prior template name: ''type TBird<X, Y: TAnt<X>> = record a: X; b: Y; end;''. This is Delphi compatible. FPC 3.3.1 does not support this (23th Aug 2020).
+
** constraint can refer prior templates: ''type TBird<X, Y: TAnt<X>> = record a: X; b: Y; end;''. This is Delphi compatible. FPC 3.3.1 does not support this (23th Aug 2020).
 
* statements:
 
* statements:
 
** specialize (cloning) all kinds of statements and expressions
 
** specialize (cloning) all kinds of statements and expressions
 +
** for-loop, if-then-else, repeat-until, while-do, try-finally, try-except, case-of
 +
** assignments
 +
** primitive expression like identifiers and constants
 +
** operators: +, -, *, /
 
** inline specialize expression, e.g. ''TList<word>.Create''
 
** inline specialize expression, e.g. ''TList<word>.Create''
 
*** Delphi: TFoo<Integer>.Create
 
*** Delphi: TFoo<Integer>.Create
Line 70: Line 76:
 
*** Delphi: always Error: Incompatible types: 'Integer' and 'T'
 
*** Delphi: always Error: Incompatible types: 'Integer' and 'T'
 
*** FPC/pas2js: allowed for valid combinations -> operators are checked on specialization, Note: because of implementation cross uses, check is delayed until used unit implementation is complete
 
*** FPC/pas2js: allowed for valid combinations -> operators are checked on specialization, Note: because of implementation cross uses, check is delayed until used unit implementation is complete
** for-in TWithConstraint
+
** for-in T do -> T must have constraints
 
** typeinfo(T)
 
** typeinfo(T)
 
** T is TFoo<Integer>
 
** T is TFoo<Integer>
Line 108: Line 114:
 
* function GetTypeKind(aType): TTypeKind
 
* function GetTypeKind(aType): TTypeKind
 
* RTTI
 
* RTTI
 +
** RTTI names use FPC/Delphi like ''TList<word>'' instead of the JS name ''TList$G1''
  
 
= RTL Units =
 
= RTL Units =
Line 146: Line 153:
 
* anonymous record, e.g. ''type TAnt<T> = class a: record end; end;''
 
* anonymous record, e.g. ''type TAnt<T> = class a: record end; end;''
 
* anonymous set, e.g. ''type TAnt<T> = class a: (red, blue); end;''
 
* anonymous set, e.g. ''type TAnt<T> = class a: (red, blue); end;''
* type alias type as parameter: create separate specialization, also needed by RTTI
+
* type alias type as parameter: create separate specialization, at the moment ''TList<integer>'' and ''TList<TColor>'' are the same specialization in pas2js
 
* generic class:
 
* generic class:
** nested generic type: i.e. generic inside a generic. Allowed by Delphi, not allowed by FPC, not planned for 1.5
+
** nested generic type: i.e. generic inside a generic. Allowed by Delphi, not allowed by FPC
** cascaded specialize: TBird<word>.TWing<byte>, not planned for 1.5
+
** cascaded specialize: TBird<word>.TWing<byte>
** Delphi type overloads A, A<T> and A<S,T>, not planned for 1.5
+
** Delphi type overloads A, A<T> and A<S,T>
** Delphi/FPC classname "TTest<System.Longint>", pas2js uses shorter classname "TTest$G1"
+
** generic ancestor, allowed in FPC, not allowed in Delphi: ''TExample<T: class> = class(T) end;''
** generic ancestor, allowed in FPC, not allowed in Delphi: ''TExample<T: class> = class(T) end;'', not planned for 1.5
 
 
* nicer error messages
 
* nicer error messages
** instead of ''TList$G1'' write ''TList<word>''
 
 
** write a hint where it was specialized
 
** write a hint where it was specialized
 
* Redefinition. Example: ''type TBird<T> = class i: integer; end;  integer = string; TEagle = TBird<word>;''  FPC+pas2js: create i as ''string'', Delphi: creates i as ''system.integer''
 
* Redefinition. Example: ''type TBird<T> = class i: integer; end;  integer = string; TEagle = TBird<word>;''  FPC+pas2js: create i as ''string'', Delphi: creates i as ''system.integer''
 
* filer
 
* filer
* optimization: reuse specialized functions, not planned for 1.5
+
* optimization: reuse specialized functions
* constants as templates: Not planned
+
* constants as templates
  
  
 
[[Category:Pas2js]]
 
[[Category:Pas2js]]

Revision as of 19:22, 17 February 2021

Overview

Pas2js 1.5 supports generic types and functions in either the ObjFPC or Delphi like way.

Next goal is to test and fix bugs.

Working

  • generic class
    • ClassName, TypeInfo Name
      • Delphi, pas2js: full path with param names, e.g. 'TAnt<System.Word>' and 'TAnt<System.Word>.TLeg'
      • FPC: instead of param names uses crc, e.g. 'TAnt$1$crcHexNumber', full paths for classnames, e.g. 'TAnt$1$crcHexNumber.TLeg', last name for TypeInfo: e.g. 'TLeg'
    • JS specialize code in unit of generic
    • forward generic class, e.g. type TAnt<T>=class; TBird=class a: TAnt<word>; end; TAnt<T>=class a:T; end;
      • Delphi/pas2js: supported
      • FPC: 3.3.1 does not support it yet
    • enumtype inside generic is not propagated
    • ObjFPC: members can refer to parent type without type parameters, e.g. type generic TAnt<T> = class Parent: TAnt; end;
    • Delphi/FPC: descendants cannot refer to type parameters of ancestors, pas2js allows it within same unit (strict private).
    • Delphi/FPC does not allow accessing local symbols. pas2js allows it.
    • class constructor
  • generic external class
  • generic record
  • generic dynamic array
  • generic static array: Note: delphi wiki says "no static arrays", but 10.3 compiles it, see http://docwiki.embarcadero.com/RADStudio/Rio/en/Declaring_Generics
  • generic interface
  • generic procedural type
    • specialized proc types can assign normal proc
    • Procedure local scope:
      • pas2js: allowed, except for typeinfo
      • FPC: allowed, including typeinfo
      • Delphi: Error: parameterized type can not be declared in procedure local scope
  • anonymous type in specialize: TBird<array of word>
  • specialize:
    • ObjFPC: type TFoo = specialize TGen<word>;
    • Delphi: type TFoo = TGen<word>;
    • ObjFPC anonymous: var r: specialize TBird<word>;
    • Delphi: anonymous var r: TBird<word>;
  • nested specialize: TBird<TWing<byte>>
  • record/class field
  • property
  • method (not confused with generic method)
    • Delphi: implementation must repeat type params, must omit constraints
    • FPC: implementation must not repeat type params
  • helper for generic type
  • constraints:
    • keyword record, class, constructor checked at specialization
    • class type checked at specialization
  • constraints:
    • "class":
      • Delphi/FPC: T is TObject
      • pas2js: test if T is a class, either TObject or an external class
    • "constructor": test if T is TObject and Create resolves to TObject.create
    • "record": test if T is a record type
    • class type: test if param fits
    • list of interface types: test if param fits
    • forward class must repeat constraints
    • Delphi does not allow TObject as constraint. pas2js allows it, because it has other root classes.
    • constraint can refer prior templates: type TBird<X, Y: TAnt<X>> = record a: X; b: Y; end;. This is Delphi compatible. FPC 3.3.1 does not support this (23th Aug 2020).
  • statements:
    • specialize (cloning) all kinds of statements and expressions
    • for-loop, if-then-else, repeat-until, while-do, try-finally, try-except, case-of
    • assignments
    • primitive expression like identifiers and constants
    • operators: +, -, *, /
    • inline specialize expression, e.g. TList<word>.Create
      • Delphi: TFoo<Integer>.Create
      • FPC: specialize TFoo<Integer>.Create
    • "obj is T": allowed with constraint "class" or class type
    • typecast T(): allowed with constraint "class" or class type
    • using implementation function in generic function:
      • Delphi: Error: Method of parameterized type declared in interface section must not use local symbol '%s'
      • FPC: Error: Global Generic template references static symtable
      • Pas2js: allowed
    • anInt:=GenericVar
      • Delphi: always Error: Incompatible types: 'Integer' and 'T'
      • FPC/pas2js: allowed for valid combinations -> operators are checked on specialization, Note: because of implementation cross uses, check is delayed until used unit implementation is complete
    • for-in T do -> T must have constraints
    • typeinfo(T)
    • T is TFoo<Integer>
    • T as TFoo<Integer>
    • TFoo<Integer>(expr)
    • call
      • pas2js: Later checked by specialization.
      • Delphi: must fit the constraint. For example without constraint it only fits untyped args.
      • FPC: if there is only one function in scope select it, overloads are selected by constraints alone. Later checked by specialization.
  • generic function (aka parametrized method):
    • ObjFPC: generic procedure Fly<T>(a: T)
    • Delphi: procedure Fly<T>(a: T) Note: Delphi 10.3 only supports parametrized method, not global procedure, pas2js allows it
    • forward
    • unit interface/implementation
    • overloads
    • generic procedure inside a procedure is forbidden, same as FPC/Delphi
    • local procedures inside a generic function: supported by FPC and pas2js, not supported by Delphi
    • calling self
    • constraints, see above, same as type constraints
    • ObjFPC explicitly spezialize: specialize Fly<word>(aWord)
    • Delphi explicitly spezialize: Fly<word>(aWord)
    • parameter type inline specialization: procedure Run<T>(List: TList<T>);
  • generic method (aka parametrized method):
    • methods: virtual, message, constructor and destructors cannot have type parameters
    • class interface methods cannot have type parameters
    • proc types
    • overloads
  • automatically inferring types of generic method/function:
    • MyProc(1) calls MyProc<T>(a: T)
    • $modeswitch implicitfunctionspecialization: mode Delphi: default enabled, ObjFPC: disabled
    • array of T: procedure Run<T>(a: array of T)
    • any order: procedure Run<S,T>(a:T; b:S)
    • infer types: widen types, e.g. Run(1,1000000) specializes Run<T>(a,b:T) with T as longint. Not supported by Delphi/FPC, supported by pas2js to reduce number of specializations
  • typecast between specializations gives a warning, not an error
  • typecast TGenType<jsvalue> from/to TGenType<SomeType> without warning
  • allowing ATExtClass<jsvalue> := ATExtClass<SomeType>
  • function GetTypeKind(aType): TTypeKind
  • RTTI
    • RTTI names use FPC/Delphi like TList<word> instead of the JS name TList$G1

RTL Units

  • generics.defaults.pas
  • generics.strings.pas
  • generics.collections.pas

RTL Classes

  • TArray<T>
  • IComparer<T>
  • TOnComparison<T>
  • TComparer<T>
  • TDefaultComparer<T>
  • IEnumerator<T>
  • IEnumerable<T>
  • TCollectionNotifyEvent<T>
  • TCustomArrayHelper<T>
  • TArrayHelper<T>
  • TEnumerator<T>
  • TEnumerable<T>
  • TCustomList<T>
  • TCustomListEnumerator<T>
  • TCustomInvertedListEnumerator<T>
  • TList<T>
  • TObjectList<T: class>
  • TThreadList<T>
  • TQueue<T>
  • TObjectQueue<T: class>
  • TStack<T>
  • TObjectStack<T: class>
  • TPair<TKey,TValue>
  • TDictionary<TKey,TValue>

ToDos

  • anonymous record, e.g. type TAnt<T> = class a: record end; end;
  • anonymous set, e.g. type TAnt<T> = class a: (red, blue); end;
  • type alias type as parameter: create separate specialization, at the moment TList<integer> and TList<TColor> are the same specialization in pas2js
  • generic class:
    • nested generic type: i.e. generic inside a generic. Allowed by Delphi, not allowed by FPC
    • cascaded specialize: TBird<word>.TWing<byte>
    • Delphi type overloads A, A<T> and A<S,T>
    • generic ancestor, allowed in FPC, not allowed in Delphi: TExample<T: class> = class(T) end;
  • nicer error messages
    • write a hint where it was specialized
  • Redefinition. Example: type TBird<T> = class i: integer; end; integer = string; TEagle = TBird<word>; FPC+pas2js: create i as string, Delphi: creates i as system.integer
  • filer
  • optimization: reuse specialized functions
  • constants as templates