pas2js Generics

From Free Pascal wiki

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 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
    • specialize in ancestor type
    • forward generic class (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
    • Delphi: descendants cannot refer to type parameters of ancestors, pas2js allows it.
    • Delphi/FPC does not allows accessing local symbols. pas2js allows it.
    • class constructor
  • generic external class
  • generic 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
  • anonymous type in specialize: TBird<array of 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
  • 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
  • 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 refers to prior template name: type TBird<X, Y: TAnt<X>> = record a: X; b: Y; end;
  • statements:
    • specialize (cloning) all kinds of statements and expressions
    • 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 TWithConstraint
    • 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)
  • 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>

ToDos

  • nested generic type: allowed by Delphi, not allowed by FPC, not planned for 1.5
  • cascaded specialize: TBird<word>.TWing<byte>, not planned for 1.5
  • generic class:
    • Delphi type overloads A, A<T> and A<S,T>, not planned for 1.5
    • Delphi/FPC classname "TTest<System.Longint>", pas2js uses shorter classname "TTest$G1"
  • nicer error messages
    • instead of TList$G1 write TList<word>
    • write a hint where it was specialized
  • filer
  • optimization: reuse specialized functions, not planned for 1.5
  • constants as templates: Not planned