pas2js Generics

From Lazarus wiki
Jump to navigationJump to search

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>
  • Delphi type overloads A, A<T> and A<S,T>
  • 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>
    • 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<integer>; FPC+pas2js: create i as string, Delphi: creates i as system.integer
  • optimization: reuse specialized functions
  • constants as templates
  • precompiled js files. At the moment all specializations are added to the unit where the generic is defined. That means using a new specialization require to recompile the js file.