Sign in Join
Wiki copytemporary

Generics proposals: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Generics proposals

OldNewLine
1+## Already implemented!
2+
3+> **Note:** Generic classes are already implemented in the 2.3.1 version of FPC. See the [FPC reference](http://www.freepascal.org/docs-html/ref/ref.html).
4+
5+This page contains proposals and ideas, which have lead to the current implementation and for further features.
6+
7+**Note, April 8th 2019: almost everything on this page is highly outdated and does not reflect the current state of FPC features.**
8+
9+## Why
10+
11+- Typesafety !
12+- Speed
13+- Readability
14+
15+See "Already existing stuff" for possible but lacking ways right now.
16+
17+## Interested Parties
18+
19+\- Almindor - dannym - fpk - giantm - oliebol - plugwash - Synopsis - tom - PublicJoke - neli - dr_evil
20+
21+## Usage example (uncategorized)
22+
23+TODO: combine the below stuff into one example using the \<\> syntax
24+
25+(tom proposal)
26+
27+``` pascal
28+ type
29+ // explicit addition of "generic" keyword. Maybe something like "uses generic <...>"
30+ // better use the <>-brackets because otherwise it may cause confusion with arrays
31+ // in the actual instantiation
32+ abc = class(..) generic <a, b>
33+ end;
34+ var
35+ // because they're still unused ;)
36+ xyz : abc<String, Integer>;
37+ xyz2 : abc<AObj1, AObj2>;
38+ begin
39+ // don't repeat generics in instance creation (if not necessary)
40+ xyz := abc.Create;
41+ xyz2 := abc<descendant_of_Aobj1, descendant_of_Aobj2>.Create;
42+ end.
43+```
44+
45+(dannym proposal new)
46+
47+``` pascal
48+ type
49+ GList = generic class of T
50+ public
51+ procedure Add(const stuff: T);
52+ end;
53+
54+ procedure GList.Add(const stuff: T); // note that here no 'of T'
55+ begin
56+ ...
57+ end;
58+
59+ var
60+ intlist: GList of Integer;
61+ begin
62+ intlist := GList.Create; // note that here the detail type is missing and
63+ // needs to be deducted from the var declaration
64+ //or intlist := typeof(intlist).Create;
65+ //intlist := GList of Integer.Create; <-- this is unclear if that works
66+ end;
67+```
68+
69+(dannym proposal - old)
70+
71+``` pascal
72+ type
73+ GList<T> = class
74+ public
75+ procedure Add(const stuff: T);
76+ end;
77+
78+ procedure GList<T>.Add(const stuff: T);
79+ begin
80+ ...
81+ end;
82+
83+ var
84+ intlist: GList<Integer>;
85+ begin
86+ intlist := GList<Integer>.Create;
87+ end;
88+```
89+
90+plugwash:
91+
92+``` pascal
93+ tmycollection=template(tobject)[x]
94+ tintegercollection=tmycollection[integer]
95+```
96+
97+oliebol:
98+
99+``` pascal
100+ template blaat<t:integer>(a:t);
101+ var x : t;
102+ begin
103+ do something with a, and use t with it
104+ end;
105+```
106+
107+note that we should have a generics section (like interface and implementation sections) containing the generics:
108+
109+``` pascal
110+ unit xyz;
111+ generics
112+ type
113+ GList......
114+ procedure GList.Add(const value: T);
115+ ....
116+ end.
117+```
118+
119+(eiffel)
120+
121+<http://paste.lisp.org/display/6236>
122+
123+(PublicJoke proposal)
124+
125+``` pascal
126+ unit Unit1(TMathType);
127+
128+ interface
129+
130+ type
131+ TMathClass = class
132+ public
133+ class function Add(a1, a2: TMathType): TMathType;
134+ end;
135+
136+ implementation
137+
138+ class function TMathClass.Add(a1, a2: TMathType): TMathType;
139+ begin
140+ Result:=a1+a2;
141+ end;
142+
143+ end.
144+```
145+
146+``` pascal
147+ unit Unit2;
148+
149+ type
150+ TMathType = Longword;
151+
152+ interface
153+
154+ implementation
155+
156+ end.
157+```
158+
159+``` pascal
160+ program prog;
161+
162+ uses
163+ Unit2,
164+ Unit1(Byte) as ByteMath in 'Unit1.pas',
165+ Unit1(TMathType) as PolyMath in 'Unit1.pas';
166+
167+ type
168+ TByteMathClass = ByteMath.TMathClass;
169+ TPolyMathClass = PolyMath.TMathClass;
170+
171+ var
172+ b: Byte;
173+ p: TMathType;
174+
175+ begin
176+ b:=TByteMathClass.Add(1, 2);
177+ p:=TPolyMathClass.Add(3, 4);
178+ end.
179+```
180+
181+(Gadnio proposal) I personally think the C++ way of doing this is clear enough AND will be the easiest to parse/understand:
182+
183+``` pascal
184+ template <T>
185+ TGenericObject< T > = record
186+ Data : T;
187+ OtherStuff : Pointer;
188+ end;{TGenericObject}
189+ template <TKey, TData = TGenericObject< boolean > >
190+ TGenericList = class( TWhee< TKey > )
191+ public
192+ function Lookup( const Key : TKey ): TData;
193+ template <THihi>
194+ class function DataToHihi( Data : TData ): THihi;
195+ end;{TGenericList}
196+```
197+
198+Also, note that template specialization is a very, very important thing (for me especially). In C++ they've got vector\< bool \> that behaves different than the standard vector\< T \>. An example:
199+
200+``` pascal
201+ template <T, TT>
202+ TSame = class
203+ class function Same: Boolean;
204+ end;{TSame}
205+ template <T>
206+ TSame = class
207+ class function Same: Boolean;
208+ end;{TSame}
209+```
210+
211+``` pascal
212+ template <T, TT>
213+ class function TSame.Same: Boolean;
214+ begin
215+ Result := false;
216+ end;{TSame<T, TT>.Same}
217+ template <T>
218+ class function TSame.Same: Boolean;
219+ begin
220+ Result := true;
221+ end;{TSame<T>.Same}
222+```
223+
224+This allows to make a compile-time check for same types that are not TObject, e.g.
225+
226+``` pascal
227+ TFoo = class;
228+ TBar = class( TFoo );
229+ var
230+ S : Boolean;
231+ begin
232+ S := TSame< String, Integer >.Same; //s = false
233+ S := TSame< Int64, Integer >.Same; //s = false
234+ S := TSame< String, String >.Same; //s = true
235+ S := TSame< TBar, TFoo >.Same;//s = false (not the same as "var x: TFoo; begin x := TBar.Create; end;" )
236+ end;
237+```
238+
239+Another thing to note: It would be very good if something like this (c++ syntax) would be implemented:
240+
241+``` pascal
242+ template <bool b>
243+ class h
244+ {
245+ public:
246+ static const int i = 0;
247+ };//h
248+```
249+
250+``` c
251+ template <>
252+ class h< true >
253+ {
254+ public:
255+ static const int i = 1;
256+ };
257+```
258+
259+``` c
260+ template <>
261+ class h< false >
262+ {
263+ public:
264+ static const int i = 2;
265+ };
266+```
267+
268+Here
269+
270+``` c
271+ h<false>.i = 2
272+ h<true>.i = 1
273+```
274+
275+ALWAYS.
276+
277+## Usage example
278+
279+If you add more syntax examples, please add equivalent to both. Two main syntaxes seem to be popular, "\<\>" which looks more or less like C++, c#, etc.. and adding a "generic" keyword which is more along the Pascal style of code. Go to [[Generics Vote]] to vote for one or the other syntax.
280+
281+### Using brackets: \<\>
282+
283+``` pascal
284+ type
285+ TGenericCollection<T: TCollectionItem> = class(TComponent)
286+ ...implement TCollection and use T
287+ end;
288+
289+ TCollection = TGenericCollection<TCollectionItem>;
290+ TFieldDefs = TGenericCollection<TFieldDef>;
291+
292+ TGenericList<T: PtrInt> = class(TObject)
293+ ...implement TList and use PtrInt size for code generation
294+ end;
295+
296+ TList = TGenericList<Pointer>;
297+```
298+
299+Implementation of TGenericCollection can be compiled as if (T: TCollectionItem) were used.
300+
301+Would this solve the circular dependency ? It seems so to me, because one knows at least as much as in current implementation of these classes, but I'm no compiler engineer.
302+
303+For procedures:
304+
305+``` pascal
306+ function MyFunc<T: TTreeItem>(A: T): T;
307+ begin
308+ // do something with the tree item
309+ end;
310+
311+ function Max<T>(A, B: T): T;
312+ begin
313+ if A < B then
314+ Result := B
315+ else
316+ Result := A;
317+ end;
318+```
319+
320+My restrictions won't allow implementing generic Max and Min, I guess. That really needs macro-alike stuff (for the compiler implementation).
321+
322+Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit)
323+
324+``` pascal
325+ type
326+ TListItem<T> = record
327+ Data: T;
328+ Next: TListItem;
329+ end;
330+ PListItem = ^TListItem;
331+
332+ TList<T> = class
333+ private
334+ fHead: PListItem<T>;
335+ fTail: PListItem<T>;
336+ published
337+ procedure Add(Item: T);
338+ end;
339+
340+ procedure TList<T>.Add(Item: T);
341+ var
342+ node: PListItem<T>;
343+ begin
344+ New(node);
345+ node^.Data := Item;
346+ node^.Next := nil;
347+
348+ if Assigned(fTail) then begin
349+ fTail^.Next := node;
350+ end else begin
351+ fHead := node;
352+ end;
353+
354+ fTail := node;
355+ end;
356+
357+ type
358+ TApple = class
359+ end;
360+ TOrange = class
361+ end;
362+
363+ TAppleList = TList<TApple>;
364+
365+ var
366+ apples: TAppleList;
367+ apple: TApple;
368+ begin
369+ apples.Add(TApple.Create); // works
370+ apples.Add(TOrange.Create); // compile error
371+
372+ apple := apples[0]; // works
373+ apple := apples[1]; // not applicable
374+ apple := apples[0] as TApple; // works, but unneccessary
375+ apple := apples[1] as TApple; // not applicable
376+ end;
377+```
378+
379+### Using the "generic" keyword
380+
381+``` pascal
382+ type
383+ TGenericCollection = generic(T: TCollectionItem) class(TComponent)
384+ ...implement TCollection and use T
385+ end;
386+
387+ TCollection = specialize TGenericCollection(TCollectionItem);
388+ TFieldDefs = specialize TGenericCollection(TFieldDef);
389+
390+ TGenericList = generic(T: PtrInt) class(TObject)
391+ ...implement TList and use PtrInt size for code generation
392+ end;
393+
394+ TList = specialize TGenericList(Pointer);
395+```
396+
397+For procedures:
398+
399+``` pascal
400+ function generic(T: TTreeItem) MyFunc(A: T): T;
401+ begin
402+ // do something with the tree item
403+ end;
404+
405+ function generic(T) Max(A, B: T): T;
406+ begin
407+ if A < B then
408+ Result := B
409+ else
410+ Result := A;
411+ end;
412+```
413+
414+Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit)
415+
416+``` pascal
417+ type
418+ TListItem = generic(T) record
419+ Data: T;
420+ Next: TListItem;
421+ end;
422+ PListItem = ^TListItem;
423+
424+ TList = generic(T) class
425+ private
426+ fHead: specialize PListItem(T);
427+ fTail: specialize PListItem(T);
428+ published
429+ procedure Add(Item: T);
430+ end;
431+
432+ procedure generic(T) TList.Add(Item: T);
433+ var
434+ node: specialize PListItem(T);
435+ begin
436+ New(node);
437+ node^.Data := Item;
438+ node^.Next := nil;
439+
440+ if Assigned(fTail) then begin
441+ fTail^.Next := node;
442+ end else begin
443+ fHead := node;
444+ end;
445+
446+ fTail := node;
447+ end;
448+
449+ type
450+ TApple = class
451+ end;
452+ TOrange = class
453+ end;
454+
455+ TAppleList = specialize TList(TApple);
456+
457+ var
458+ apples: TAppleList;
459+ apple: TApple;
460+ begin
461+ apples.Add(TApple.Create); // works
462+ apples.Add(TOrange.Create); // compile error
463+
464+ apple := apples[0]; // works
465+ apple := apples[1]; // not applicable
466+ apple := apples[0] as TApple; // works, but unneccessary
467+ apple := apples[1] as TApple; // not applicable
468+ end;
469+```
470+
471+Ugly things in need to be resolved:
472+
473+``` pascal
474+ { a generic type "TPointer" which is a pointer to type instantiated with }
475+ type TPointer = generic(Type) ^Type;
476+ { a generic type that instantiates all the enums for Type }
477+ type TAllEnums = generic(Type)(low(Type),high(Type));
478+```
479+
480+So, before a keyword can be real "nice" the following things need addressing:
481+
482+1. Double brackets ()(), looks C-ish again
483+2. In math, one usually defines functions with the parameters on the left of the equal sign, not on the right
484+
485+#### Suggestion 2
486+
487+Inspired by idea from oliebol in combination with ugly examples, in list of const, var, type sections, add "template" section:
488+
489+``` pascal
490+ const
491+ ThisIsAConst = 1;
492+
493+ type
494+ TMyEnum = (ab, ac);
495+
496+ template(T: TCollectionItem)
497+ TGenericCollection = class(TComponent)
498+ ...implement TCollection and use T
499+ end;
500+
501+ type
502+ TCollection = specialize TGenericCollection(TCollectionItem);
503+ TFieldDefs = specialize TGenericCollection(TFieldDef);
504+
505+ template(T: PtrInt)
506+ TGenericList = class(TObject)
507+ ...implement TList and use PtrInt size for code generation
508+ end;
509+
510+ type
511+ TList = specialize TGenericList(Pointer);
512+```
513+
514+For procedures:
515+
516+``` pascal
517+ function generic(T: TTreeItem) MyFunc(A: T): T;
518+ begin
519+ // do something with the tree item
520+ end;
521+
522+ function generic(T) Max(A, B: T): T;
523+ begin
524+ if A < B then
525+ Result := B
526+ else
527+ Result := A;
528+ end;
529+```
530+
531+Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit)
532+
533+``` pascal
534+ template(T)
535+ TListItem = record
536+ Data: T;
537+ Next: TListItem;
538+ end;
539+ PListItem = ^specialize TListItem(T);
540+
541+ TList = class
542+ type
543+ PItem = specialize PListItem(T);
544+ private
545+ fHead: PItem;
546+ fTail: PItem;
547+ published
548+ procedure Add(Item: T);
549+ end;
550+
551+ ...implementation...
552+
553+ procedure generic(T) TList.Add(Item: T);
554+ var
555+ node: PItem;
556+ begin
557+ New(node);
558+ node^.Data := Item;
559+ node^.Next := nil;
560+
561+ if Assigned(fTail) then begin
562+ fTail^.Next := node;
563+ end else begin
564+ fHead := node;
565+ end;
566+
567+ fTail := node;
568+ end;
569+
570+ type
571+ TApple = class
572+ end;
573+ TOrange = class
574+ end;
575+
576+ TAppleList = specialize TList(TApple);
577+
578+ var
579+ apples: TAppleList;
580+ apple: TApple;
581+ begin
582+ apples.Add(TApple.Create); // works
583+ apples.Add(TOrange.Create); // compile error
584+
585+ apple := apples[0]; // works
586+ apple := apples[1]; // not applicable
587+ apple := apples[0] as TApple; // works, but unneccessary
588+ apple := apples[1] as TApple; // not applicable
589+ end;
590+```
591+
592+#### Suggestion 3
593+
594+``` pascal
595+ { Chu Jetcheng, 2008-07-24 }
596+
597+ program Generics;
598+
599+ type
600+ { The compiler should maintain a global table storing each specialized type for each generic. The
601+ `of' operator is used to create a specialization for a specified generic type. Specialized types
602+ of the same `type layout' (no other words) point to the same entry in the table of specializations
603+ of the specialized generic type, and thus are compatible. Generic objects and records should also
604+ be allowed. }
605+
606+ TGeneric = generic class(TAncestorClass) of T1, T2
607+ private
608+ FData1: T1;
609+ FData2: T2;
610+ public
611+ property Data1: T1 read FData1 write FData1;
612+ property Data2: T2 read FData2 write FData2;
613+ constructor Create(const A: T1; const B: T2);
614+ procedure WriteData; virtual;
615+ end;
616+
617+ TGenericDescendent = generic class(TGeneric of Integer, T3) of T3
618+ private
619+ FData3: T3;
620+ FObjectOfSameType: TGenericDescendent of T3;
621+ public
622+ property Data3: T3 read FData3 write FData3;
623+ property ObjectOfSameType: TGenericDescendent of T3 read FObjectOfSameType write FObjectOfSameType;
624+ constructor Create(const C: T3);
625+ procedure WriteData; override;
626+ end;
627+
628+ TGenericIR = TGeneric of Integer, Real;
629+ TGenericIRClass = class of TGenericIR;
630+
631+ TGenericISClass = class of (TGeneric of Integer, string);
632+
633+ PGenericObj = generic ^TGenericObj;
634+ TGenericObj = generic object of T
635+ MyField: T;
636+ procedure WriteData;
637+ end;
638+
639+ { TGeneric }
640+
641+ { Use the same syntax for method implementations as with immediate classes. }
642+
643+ constructor TGeneric.Create(const A: T1; const B: T2);
644+ begin
645+ FData1 := A;
646+ FData2 := B;
647+ end;
648+
649+ procedure TGeneric.WriteData;
650+ begin
651+ Writeln(FData1);
652+ Writeln(FData2);
653+ end;
654+
655+ { TGenericDescendent }
656+
657+ constructor TGenericDescendent.Create(const C: T3);
658+ begin
659+ inherited Create(16, C);
660+ FData3 := C;
661+ end;
662+
663+ procedure TGenericDescendent.WriteData;
664+ begin
665+ inherited;
666+ Writeln(FData3);
667+ if Assigned(FObjectOfSameType) then FObjectOfSameType.WriteData;
668+ end;
669+
670+ { TGenericObj }
671+
672+ procedure TGenericObj.WriteData;
673+ begin
674+ Writeln(MyField);
675+ end;
676+
677+ var
678+ Obj1: TGeneric of Integer, Real;
679+ Obj2, Obj3: TGenericDescendent of string;
680+ OldStyleObj: TGenericObj of Integer;
681+ OldStyleObjPtr: PGenericObj of Integer;
682+
683+ begin
684+ Obj1 := TGeneric.Create(32, 3.14);
685+ Obj1.WriteData;
686+ Writeln(#9, Obj1 is TGenericIR);
687+ Writeln(#9, Obj1 is TGenericIS);
688+
689+ Obj2 := TGenericDescendent.Create('Generic test.');
690+ Obj2.WriteData;
691+ Writeln(#9, Obj2 is TGenericIR);
692+ Writeln(#9, Obj2 is TGenericIS);
693+
694+ Obj3 := TGenericDescendent.Create('Another generic test.');
695+ Obj3.ObjectOfSameType := Obj2;
696+ Obj3.WriteData;
697+
698+ Obj3.Free;
699+ Obj2.Free;
700+ Obj1.Free;
701+
702+ OldStyleObj.MyField := 100;
703+ OldStyleObj.WriteData;
704+
705+ OldStyleObjPtr := New(PGenericObj of Integer);
706+ with OldStyleObjPtr^ do begin
707+ MyField := 202;
708+ WriteData;
709+ end;
710+ Dispose(ObjStyleObjPtr);
711+ end.
712+```
713+
714+## Terms
715+
716+type user:
717+
718+- the function in the compiler that defines a variable or return type and thus 'uses a (already defined) type'.
719+
720+immediate type:
721+
722+- not generic and not specialized, i.e. 'normal type'
723+
724+generic type:
725+
726+- template for a class with some unspecified types, never to be filled in into this class type. Only by specialisation.
727+
728+specialization:
729+
730+- copy generic type class to new specialized type class
731+
732+type parameters:
733+
734+- T and Q, the unknown types for the generic TSomeGeneric\<T,Q\>
735+
736+specialized type:
737+
738+- a generic type with all type parameters know, written into a real class type (tree) and reused if possible
739+
740+## Changes
741+
742+### Changes in class/record definition representation
743+
744+Each class definition representation has added fields for:
745+
746+` - class instantiation mode`\
747+`    0  immediate type`\
748+`    1  generic type, no instantiation, just generate specialized type`\
749+`    2  specialized type`
750+
751+` - when mode 2:`\
752+`     generic type uplink (XXXX, what, unitname, typename?)`\
753+`   when mode 1:`\
754+`     list of specialized types known so far (and where),`\
755+`     XXXX list items`\
756+`   when mode 0: `\
757+`     nothing`
758+
759+### Changes in 'type user' compilation (for class/record types only)
760+
761+Each class type user will have to check mode of the class type.
762+
763+For the used class type, If mode is immediate
764+
765+` - proceed as always till now.`
766+
767+If mode is specialized
768+
769+` - proceed as always till now `\
770+` - keep in mind the generic type for some checks later (and debugging).`
771+
772+If mode is generic, this:
773+
774+` - check 'list of specialized types' for the type parameter to use.`\
775+`   If there is already a specialized type, use that.`\
776+`   (given that it is compatible with 'compile parameters' XXXX)`
777+
778+` - If not available, clone the generic type in the tree, and fill in the`\
779+`   type params with the actual types to use.`\
780+`   Generate machine code as normal.`\
781+`   Remember the new specialized type for later in the list by the `\
782+`   generic type and type params used.`
783+
784+(the generic type is best written to some ppu as parse tree so that it can be refetched for cloning somewhen. To keep it easy, initially it should be limited to having its .pas file around when wanting to use a generic)
785+
786+### type params
787+
788+Generics store a list of the type params (names).
789+
790+` ('T', 'Q')`
791+
792+And Specializations store a list of type mappings from the real types to the type params, in the way of
793+
794+``` pascal
795+ type
796+ T = Integer;
797+ Q = Boolean;
798+```
799+
800+of course these are local to this class/record specialization.
801+
802+## Other things to note (for *later*)
803+
804+### What if a method implementation depends on the type used to know how the implementation should look like ?
805+
806+``` pascal
807+ procedure GList<T is TObject>.Add(const value: T);
808+ procedure GList<T is Integer>.Add(const value: T);
809+```
810+
811+?
812+
813+or more cryptic like
814+
815+``` pascal
816+ procedure GList<TObject>.Add(const value: T);
817+ procedure GList<Integer>.Add(const value: T);
818+```
819+
820+?
821+
822+or like
823+
824+``` pascal
825+ procedure GList<T>.Add(const value: Integer);
826+ procedure GList<T>.Add(const value: TObject);
827+```
828+
829+the latter would need extra compiler support but would be nicer, overall. Potentially clashing with normal overloads though.
830+
831+or as an alternative like:
832+
833+``` pascal
834+ {$IF T is TObject}
835+ procedure GList.Add(const value:TObject);
836+ {$ELSE}
837+ procedure GList.Add(const value:Integer);
838+ {$ENDIF}
839+```
840+
841+This would also allow to define constants or records in dependency of the generic type. This could be useful for example if you can specialize a class with static or dynamic array types (like array of double) and you need a static or dynamic array of integer having the same length as the generic. Currently we can play a bit with sizeOf(T), but this has its limitations (see <http://www.lazarusforum.de/viewtopic.php?f=10&t=11915#p106921>)...
842+
843+### How to do internal storage classes
844+
845+``` pascal
846+ type
847+ PNode<T> = ^TNode<T>;
848+ TNode<T> = record
849+ prev,next: PNode<T>;
850+ data: T;
851+ end;
852+
853+ type
854+ GList<T> = class
855+ private
856+ head, tail: PNode<T>;
857+ cnt: Integer;
858+ public
859+ procedure Add(const stuff: T);
860+ end;
861+```
862+
863+?
864+
865+This implies that generics are best implemented both for records and for classes alike! Also implies that pointer types need to support generic too.
866+
867+Also one specialization should chain other specializations (probably also when deriving from a generic).
868+
869+### How to derive from the generic type
870+
871+It would be good if generic types could be derived from.
872+
873+``` pascal
874+ type
875+ TGenericEventyList<T> = class(TGenericList<T>)
876+ public
877+ procedure Add(value: T); override;
878+ end;
879+ ...
880+```
881+
882+### Interfaces should support generics too
883+
884+``` pascal
885+ type
886+ IBa<T> = interface
887+ procedure Add(value: T);
888+ end;
889+ TBa<T> = class(...,IBla<T>,IInterface)
890+ end;
891+```
892+
893+(what to do about the guids of the specializations? probably just generate whatever, they dont have any real meaning after all)
894+
895+Of course this has limitations to check, like
896+
897+- the generic interface cannot be used, just its specializations
898+- the specialized interface cannot be used in a generic class interface list
899+- (the generic interface can be used in a generic class interface list)
900+- the generic interface itself is invalid to use anywhere (just as generic classes are) except for specializing and deriving from
901+- normal interfaces cannot derive from generic interfaces
902+- (generic interfaces can derive from generic interfaces) - given the type params are the same
903+- specialized interfaces cannot be derived from
904+
905+### Limitation of possible specialisations
906+
907+``` pascal
908+ type
909+ TGenericList<T: TObject or Integer> = ...
910+ end;
911+```
912+
913+Probably useful...
914+
915+### What to do with class functions
916+
917+I dont know a whole lot of how class functions are stored internally. What happens to class functions in generics?
918+
919+### 'class of'
920+
921+\<Almindor\> don't forget class of, we need class of compatibility for dynamic class factories
922+
923+\<Almindor\> There are possible problems with 'class of'
924+
925+## Implementation details
926+
927+### Sanity checks
928+
929+- Generic types cannot be instantiated from
930+- (Generic types can be derived from)
931+- Specialized types cannot be derived from
932+- Normal types cannot use generic types in its definition
933+- (Normal types can use specialized types in its definition)
934+- There are no half specialized types (one type param specified, the other not)
935+- prevent loops while 'unpacking' specializations (loop counter field in every specialization)
936+
937+### Things to note and not forgot
938+
939+- A class and record generic can use itself as type within its definition.
940+
941+example:
942+
943+``` pascal
944+ type
945+ TBla<T> = class
946+ parent: TBla<T>;
947+ end;
948+```
949+
950+- A class and record generic can use other generics as types within its definition.
951+
952+``` pascal
953+ type
954+ TBla<T> = class
955+ parent: TBlo<T>;
956+ end;
957+```
958+
959+## Implementation steps
960+
961+### Extend parser to support '\<T\>' and '\<T: bla or bla or bla\>' and '\<T,Q\>' notation
962+
963+Easy to do. Done.
964+
965+## Different ways to do it, one excluding the other
966+
967+### Way 1
968+
969+#### Change class/record/object/pointertype/interface parser to mark if it is a generic, a specialization or normal
970+
971+Mediate to do. Have done it for class/object and pointertype. Needs all kinds of clone functions for the defs and syms. fpk says he has patches. waiting.
972+
973+#### insert dummy symbols (typesym + typedef) for the type params in the generic
974+
975+Cannot be inserted into the object because parsing of types within objects has been disabled to make that possible:
976+
977+``` pascal
978+ type
979+ Row = Cardinal;
980+ TBla = class
981+ Row: Integer;
982+ end;
983+```
984+
985+weird.. (function searchsym_type, is not possible to have type definitions in records, objects, parameters)
986+
987+#### parse implementation section methods *for the generic*
988+
989+Is harder than it sounds, because the class/object the method is member of has to be searched for the type symbol for the type params (and these *not* derefed but just used as is).
990+
991+#### when parsing generics, use a dummy type in place of the type params
992+
993+For that, defer the type checking of the compiler in that case to 'as late as possible' or 'never for the generic' \<--- TODO... hard...
994+
995+#### Change derivation handlers to do the sanity checks
996+
997+Should pose no problems other than finding all the places.
998+
999+#### Add error messages for it
1000+
1001+#### Change pexpr (tnode creating)
1002+
1003+\<Synopsis+\> see the do_resulttypepass() calls in pexpr.pas But the compiler needs to known for example if it allows a . after the type
1004+
1005+- add a new tnode type for postfix operators (instead of resolving the operators into the fields/methods they reference) and defer type checking until later (just before binary generation - which is not done for the generic type).
1006+
1007+Examples for postfix operators are:
1008+
1009+- '\[\]' array member access (normal array, class default array property?)
1010+
1011+- '.' record/class member method/variable access
1012+
1013+- '^' pointer dereference
1014+
1015+- (automatic pointer dereference)
1016+
1017+\<Synopsis\> grep for m_auto and you'll find the places where it is used \<Synopsis\> only in pexpr.pas
1018+
1019+#### Change ppu streaming routines to save the tree of generics to the ppu
1020+
1021+#### Change type user to instantiate specialized types for generics when used
1022+
1023+Somewhat hard to do. (main work here :))
1024+
1025+For the instantiation
1026+
1027+- there is a dummy type sym needed (ok),
1028+- a new objectdef
1029+- clone of all the syms and defs (at least proc and property) of the original class/object
1030+- and replacing all the type params by the correct types.
1031+- Store that into the module as def (not on disk).
1032+- (\<Synopsis+\> an additional symtable shall be added to tmodule: tempsymtable; all temporary tdef's shall be stored in that symtable)
1033+- Then resume compiling for the class/object.
1034+
1035+### Way 2
1036+
1037+just keep the source code of the generic around and do blind replacing of the type params and then compile a specialization from that as 'source'
1038+
1039+#### Notes
1040+
1041+\<Synopsis+\> but the code to generate the specialization at runtime can be generated at the time the specialization is created. It doesn't need to be stored in ppu. all information is available to generate the tree at that time.
1042+
1043+\<Synopsis+\> In the 2.1.x development series a rewrite of the ppu loading is planned to make it more clean and more maintainable. Currently it is risky to fix bugs without introducing new bugs
1044+
1045+\<plugwash-\> It needs to be stored in some form so that the specialisations can be generated from it. \<Synopsis+\> but you can't store them in the ppu. Or with the same limitations as inlining. No references to the implementation symtable.
1046+
1047+fpk has patches to make cloning defs possible.
1048+
1049+01:28:24 \<Synopsis+\> users will complain; things like:
1050+
1051+``` pascal
1052+ interface
1053+ generictype declar
1054+ implementation
1055+ procedure helper;
1056+ begin
1057+ end
1058+ constructor generictype.create
1059+ begin
1060+ helper
1061+ end
1062+ end.
1063+```
1064+
1065+Will not be allowed.
1066+
1067+Another problem:
1068+
1069+``` pascal
1070+ unit a;
1071+ interface
1072+ generictypeA
1073+ implementation
1074+ uses B
1075+ end.
1076+
1077+ unit b;
1078+ interface
1079+ uses a
1080+ implemenation
1081+ begin
1082+ generictypeA<integer>.craete
1083+ end.
1084+```
1085+
1086+At the time that the implementation of B is parsed it needs already the code of generictypeA. but that is not yet parsed! The compiler doesn't know anything yet. It only knows that generictypeA exists.
1087+
1088+\<fpk-\> dannym, Synopsis: we can simply forbid such code in generics which cause the problem
1089+
1090+\<fpk-\> i.e. everything used by generics must be already included in the interface uses clause ... or forbid references to units used only in the implementation section :)
1091+
1092+\<Synopsis+\> fpk had a good idea that can solve some of the issues: unit bla; interface ... generics ... implementation ... initialisation ... finalizatrion end.
1093+
1094+\<Synopsis+\> The generics delcarations shall be put in a separate section before implementation is parsed
1095+
1096+\<dannym\> at 'procedure TTest.Add(x: T);', T needs to be known. but it doesnt seem that the object symtable is searched for the class method parameters. correct ?
1097+
1098+\<Synopsis+\> dannym: correct, object symtables can not contain types, because that created problems. there is special code for that: remove the objectsymtable before parsing type declarations.
1099+
1100+``` pascal
1101+ { for records, don't search the recordsymtable for the symbols of the types }
1102+ oldsymtablestack:=symtablestack;
1103+ symtablestack:=symtablestack.next;
1104+```
1105+
1106+Because you can have a field with the same name as a type
1107+
1108+``` pascal
1109+ type
1110+ Wrd = Word;
1111+ R = Record
1112+ Wrd : Wrd;
1113+ end
1114+```
1115+
1116+this is allowed. To support this the record or object symtable is remvoed from the symtablestack when the type is parsed.
1117+
1118+## Already existing stuff
1119+
1120+### metaclasses
1121+
1122+``` pascal
1123+ type
1124+ TObjectClass = class of TObject;
1125+ TTypedList = class
1126+ private
1127+ Fwanttype: TObjectClass;
1128+ ...
1129+ public
1130+ constructor Create(itemtype: TObjectClass);
1131+ procedure Add(item: TObject);
1132+ ...
1133+ end;
1134+ constructor TTypedList.Create(itemtype: TObjectClass);
1135+ begin
1136+ Fwanttype := itemtype;
1137+ ...
1138+ end;
1139+ procedure TTypedList.Add(item: TObject);
1140+ begin
1141+ assert(item.Class = Fwanttype);
1142+ ...
1143+ end;
1144+
1145+ ...
1146+ TSomeStuff = class
1147+ end;
1148+ ...
1149+ Flist := TTypedList.Create(TSomeStuff);
1150+```
1151+
1152+Advantages
1153+
1154+- already works
1155+
1156+Disadvantages
1157+
1158+- slow at runtime
1159+- cannot do simple types
1160+- need to cast all the time even for the supported types (i.e. when reading). eeek.
1161+
1162+## To be incorporated here
1163+
1164+### Chat logs
1165+
1166+\<Synopsis\> <http://www.eleforum.com/cgi-bin/eleweb_lift?action=3&script=wall&num=18&reversesort=true&type=today&starttime=00:00&endtime=23:59&startdate=01-02-05&enddate=01-02-05> \<Synopsis\> or \<Synopsis\> <http://neli.hopto.org:3980/~micha/irc-fpc/2005/01/%23fpc.20050102.log>
1167+
1168+## See also
1169+
1170+- [[Generics]]
1171+
1172+---
1173+*From the Free Pascal wiki, page "Generics proposals", as it stood on 2020-02-16 after a change by GAN, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
1174+Category: FPC
1175+Category: Proposals

Back to history