View Issue Details

IDProjectCategoryView StatusLast Update
0028911FPCCompilerpublic2020-11-16 15:21
ReporterMaciej Izak Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version3.1.1 
Summary0028911: "class var" is broken for generics
Description"class var" for generics type (record/class) is broken.

Attached example should print (like in equivalent in C# or for Delphi XE2 and newer):
42 21 84

Current output:
42 42 42

Very important point for whole generics system.

Related bug report with more comments: 0024848

Current implementation is broken, don't work even like buggy Delphi 2009-XE implementation.

Java implementation works as follow:
-single static variable for all specializations
-single static constructor for all specializations

C# implementation:
-static variable per specialization
-static constructor per specialization

Delphi buggy implementation (2009-XE):
-static variable is broken (unclear behavior)
-static constructor per specialization

Delphi correct implementation: (XE2 and newer):
-static variable per specialization
-static constructor per specialization

FPC:
-static variable instance per module and per specialization (?!)
-static constructor per specialization

Please adjust this behavior. We need to keep single standard/approach like in C#/Delphi (preferred) or like in Java.
Tagsgenerics
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Relationships

related to 0024848 resolvedSven Barth Class constructor undefined symbol in generic class 
related to 0030433 new rtl-generics THashMapCaseInsensitive example crashes on armhf 

Activities

Maciej Izak

2015-10-26 16:41

reporter  

genclassconstr.zip (2,603 bytes)

Thaddy de Koning

2015-10-28 15:08

reporter   ~0086944

Last edited: 2015-10-28 15:10

View 2 revisions

If I read this it seems that this is according to specs:
http://alex.ciobanu.org/?p=258

He actually describes Delphi's behavior like:
FPC:
-static variable instance per module and per specialization (?!)
-static constructor per specialization

Maciej Izak

2015-10-28 15:41

reporter   ~0086946

@Thaddy thanks for link. Nope. Look at date - 2009 (!). They fixed buggy class var/class constructor implementation for generics since XE2. Anyway output from Delphi 2009-XE is different than in FPC : 42 42 0 . Delphi 2009-XE implementation is defected (for example http://qc.embarcadero.com/wc/qcmain.aspx?d=77378). Basing on 2009-XE generics implementation is bad decision.

IMO The most logical output for attached code is 42 21 84 (like in C# equivalent and like for Delphi XE2 and newer).

This bug is blocking me in many areas of using generics :\.

Sven Barth

2015-10-30 14:07

manager   ~0087024

@Maciej: would you please test what happens if you use runtime packages? Namely a specialization of the same generic (with a class var) inside a package and inside the main program?

Regards,
Sven

Thaddy de Koning

2015-10-30 14:58

reporter   ~0087032

Last edited: 2015-10-30 15:00

View 3 revisions

@Maciej:

With all due respect and respect for your work: the main issues in Delphi are NOT fixed. Half the QC's regarding generics are still open, including 2009 ones (ref.2012, btw and acknowledged).

Although I agree with you that C# like results would maybe be preferable.

It simply hasn't matured in both flavors. Neither Delphi nor FPC.
I think it is still a usable feature in its current state.

Can you point me to a fix for these?:
-static variable instance per module and per specialization (?!)
-static constructor per specialization

AfaiT (T=tested up to XE5) they are not there.

Thaddy de Koning

2015-10-30 16:52

reporter   ~0087036

Last edited: 2015-10-30 16:53

View 2 revisions

BTW:
-static constructor per specialization


Is of course not a bug.

The matter is about per unit, as I understand. That should not be the case, but as Sven wrote: I wonder what happens with packages.

Maciej Izak

2015-11-05 21:27

reporter   ~0087141

@Sven: Result from XE2 is: 0 0 42 . Tomorrow I will test with XE7 and XE8. Modified example is attached.

@Thaddy: Delphi is not ideal ;), but all new things in Delphi are copied from C# - so in few scenarios we can use C# behaviors as main point (especially generics).

Maciej Izak

2015-11-05 21:27

reporter  

Maciej Izak

2015-11-06 08:36

reporter   ~0087144

@Sven: final results :)

Without packages. Tested for XE2, XE7, XE8: 42 21 84
With packages. Tested for XE2, XE7, XE8: 0 0 42

Thaddy de Koning

2015-11-06 11:28

reporter   ~0087147

Last edited: 2015-11-06 11:30

View 3 revisions

Note that it is common for *some* code to be documented as having special requirements. (Like memory managers)

I would not object to such a solution - and for the time being - for the issue at hand.

The (my) problem is I wouldn't know where to start, but if you have patches in mind let me test them, @Maciej.

Thaddy de Koning

2015-11-06 13:52

reporter   ~0087151

Does unit order change things?

Maciej Izak

2015-11-07 20:16

reporter   ~0087190

@Thaddy:
The order doesn't matter.

The patch does not exist for now. I'm currently working on GTK, Qt, Carbon and Windows compatibility for SpartaBasic (docked form designer) patch for Lazarus. My work on FPC compiler and libraries is a little delayed... :\

Martok

2015-11-07 23:42

reporter   ~0087191

Last edited: 2015-11-07 23:49

View 2 revisions

This appears to only be weird (but not a bug) in {$mode delphi}, in {$mode objfpc} the behaviour is more obvious: every specialization gets its own copy of the variable.
If you declare a specialization like
    TLongintTest = specialize TTest<LongInt>;
and use this same type from all 3 test locations, everything works as expected.
Declaring the specialization in each module is a different type (cf. type assignment rules; they are correctly identified as different types if you try to assign ugenclassconstr2.TLongintTest to ugenclassconstr3.TLongintTest), and thus we have different static variables.

Delphi-Style inline specializations using TTest<Longint> without declaring a type first count as a separate type for every module where they are used (which makes sense, as the compiler cannot know what other modules define the same specialization), so each module declares a different TTest<Longint> (can be verified by some error messages, where the mangled type identifier is different).

To me, that looks like what I would expect. And like a case for explicit specialization...

Edit: which was already stated by Sven in 0024848...

Maciej Izak

2015-11-08 10:50

reporter   ~0087195

For me, current solution is a crime. Singleton pattern is unusable both for Delphi mode and ObjFpc mode.

Pre declaring:

TB = specialize TB<Integer>;

is not a case, as library developer I can not predict all possible specializations.
Singletons are unusable in fpcobj too. Approach "class var" per module where is
used specialization is worst thing ever. For that purpose should be introduced keyword "type" like:

type
  TFoo = type specialization TA<Integer>;

Current approach is precedent in all generics/templates that I know (comparison to C++, C#, Delphi and Java).

Maybe current solution is good in fpcobj theory but it is terrible in practical singleton usage :\. Even solution from Java is better than current mess. At this moment the use of singletons is complicated (and different than in any other language) and current solution is using more memory.

Sorry if the statement is slightly emotional but I use a lot of class var with generics and that matter is very important for any generics library with class var sections.

Martok

2015-11-08 12:33

reporter   ~0087197

Hm, I can see your point now. Just posted because we have code using exactly this, and it behaved as expected so I wasn't sure if this really can be a conceptual issue.

Consider this:
    generic TutlEnumHelper<T> = class ...
Which provides To/FromString and iterators for enums (yes, even sparse enums). It uses class vars to cache some values, initialized in a class constructor. The usage pattern for that is to declare a helper instance once, usually next to where the enum is declared. Note that this is not the library developer's job, but the user's, and it happens in user code. The user then uses that single type everywhere. Although it wouldn't really matter here if we had multiple instances, as they all are write-once from static data...

And, to be honest, I still consider Singleton an antipattern, but we're not opening that can of worms now ;-)

Sven Barth

2015-11-08 20:59

manager   ~0087198

I won't change the current implementation for now, because once I'm going to fix this I'm going to fix this correctly for all cases, including packages, everything else is merely a workaround. But for that I first need to finish my work on packages.

Regards,
Sven

Maciej Izak

2015-11-09 20:19

reporter   ~0087210

@Sven for me that sounds like good deal :)

Martok

2015-11-19 19:00

reporter   ~0087407

As a side note, this behaviour is actually documented in section 8.4 "A word about type compatibility".

Strangely, the examples there work as documented even translated to Delphi syntax, contrary to what I wrote above. Now I'm confused.

Thaddy de Koning

2016-08-17 21:21

reporter   ~0094162

Since the subject was touched today:
@Maciej "but all new things in Delphi are copied from C# " is of course the other way around: C# is essentially object pascal with curly brackets and designed by the same person (even the same team).

Sven Barth

2016-08-19 15:01

manager   ~0094188

But the features that were added in Delphi after C# had been introduced are definitely inspired by the latter. However that is not something that should be discussed /here/, we have fpc-other for these kind of discussions...

NoName

2020-02-07 20:45

reporter   ~0120928

Any news on that one?

Thaddy de Koning

2020-02-07 22:41

reporter   ~0120930

Last edited: 2020-02-07 22:43

View 2 revisions

No. But I haven't test this particular case yet. You should test against trunk of course.

Sven Barth

2020-02-07 23:19

manager   ~0120932

Test against trunk won't help, cause then this bug report would have been resolved.

Issue History

Date Modified Username Field Change
2015-10-26 16:41 Maciej Izak New Issue
2015-10-26 16:41 Maciej Izak File Added: genclassconstr.zip
2015-10-26 19:22 Cyrax Tag Attached: generics
2015-10-28 15:08 Thaddy de Koning Note Added: 0086944
2015-10-28 15:10 Thaddy de Koning Note Edited: 0086944 View Revisions
2015-10-28 15:41 Maciej Izak Note Added: 0086946
2015-10-30 14:07 Sven Barth Note Added: 0087024
2015-10-30 14:08 Sven Barth Relationship added related to 0024848
2015-10-30 14:58 Thaddy de Koning Note Added: 0087032
2015-10-30 14:59 Thaddy de Koning Note Edited: 0087032 View Revisions
2015-10-30 15:00 Thaddy de Koning Note Edited: 0087032 View Revisions
2015-10-30 16:52 Thaddy de Koning Note Added: 0087036
2015-10-30 16:53 Thaddy de Koning Note Edited: 0087036 View Revisions
2015-11-05 21:27 Maciej Izak Note Added: 0087141
2015-11-05 21:27 Maciej Izak File Added: genclassconstr_with_dpk.zip
2015-11-06 08:36 Maciej Izak Note Added: 0087144
2015-11-06 11:28 Thaddy de Koning Note Added: 0087147
2015-11-06 11:28 Thaddy de Koning Note Edited: 0087147 View Revisions
2015-11-06 11:30 Thaddy de Koning Note Edited: 0087147 View Revisions
2015-11-06 13:52 Thaddy de Koning Note Added: 0087151
2015-11-07 20:16 Maciej Izak Note Added: 0087190
2015-11-07 23:42 Martok Note Added: 0087191
2015-11-07 23:49 Martok Note Edited: 0087191 View Revisions
2015-11-08 10:50 Maciej Izak Note Added: 0087195
2015-11-08 12:33 Martok Note Added: 0087197
2015-11-08 20:59 Sven Barth Note Added: 0087198
2015-11-09 20:19 Maciej Izak Note Added: 0087210
2015-11-19 19:00 Martok Note Added: 0087407
2016-08-17 21:21 Thaddy de Koning Note Added: 0094162
2016-08-19 15:01 Sven Barth Note Added: 0094188
2017-04-07 09:31 Maciej Izak Relationship added related to 0030433
2020-02-07 20:45 NoName Note Added: 0120928
2020-02-07 22:41 Thaddy de Koning Note Added: 0120930
2020-02-07 22:43 Thaddy de Koning Note Edited: 0120930 View Revisions
2020-02-07 23:19 Sven Barth Note Added: 0120932