Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-02

Operator overload resolution with arrays

13 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martok

Hi all,

I'm having some logical problems with fgl generic lists and operator overloads. This is essentially the same issue as discussed last year on the forum as <http://forum.lazarus.freepascal.org/index.php?topic=36089.0>. I know how to work around it, just... it doesn't seem very consistent.

Take the following simple declaration:
--------
type
  TTriangleIndex = array[0..2] of Integer;
  TFacetList = specialize TFPGList<TTriangleIndex>;
  operator = (A, B: TTriangleIndex): boolean;

--------
This will not work:
fgl.pp(948,50) Error: Operator is not overloaded: "TTriangleIndex" = "TTriangleIndex"

So, for arrays, the overloaded operator is not found, even though it is in the same interface section.

However, this arcane beast would work (although Codetools bail on the double
specialization):
--------
  TTriangleIndex = array[0..2] of Integer;
  generic TArrayContainer<T> = record
    x: T;
    class operator = (A, B: specialize TArrayContainer<T>): boolean;
    // mimic 'default' attribute on x
    class operator := (A: T): specialize TArrayContainer<T>; inline;
    class operator := (A: specialize TArrayContainer<T>): T; inline;
  end;
  TFacetList = specialize TFPGList<specialize TArrayContainer<TTriangleIndex>>;
--------

Class operators defined on the used type itself are taken into account. That feels a bit inconsistent, given that their scope is equivalent in any other use?

I know it's been like that forever, but... should it be?

Regards, Martok Ceterum censeo b32079 esse sanandam.

Reply by email

denisgolovan
to Martok
Show quoted text 11 lines
Take the following simple declaration: -------- type TTriangleIndex = array[0..2] of Integer; TFacetList = specialize TFPGList<TTriangleIndex>; operator = (A, B: TTriangleIndex): boolean; -------- This will not work: fgl.pp(948,50) Error: Operator is not overloaded: "TTriangleIndex" = "TTriangleIndex"

Hi, I believe https://bugs.freepascal.org/view.php?id=27690 is all about it.

Regards, Denis Golovan

Reply by email

Sven Barth
to Martok: I'm having some logical problems with fgl generic lists and operator overloads. This is…

Am 05.02.2018 13:02 schrieb "Martok" :

Hi all,

I'm having some logical problems with fgl generic lists and operator overloads.
This is essentially the same issue as discussed last year on the forum as <http://forum.lazarus.freepascal.org/index.php?topic=36089.0>. I know how to work around it, just... it doesn't seem very consistent.

While specializing the compiler only uses operator overloads (and helpers) that have been in scope during the declaration of the generic or those that are part of the types that are used for the specialization. This is definitely consistent.
While I do plan to test what happens if I allow units that were not part of the generic's scope during its declaration that has the potential to open a whole new can of bugs.

Regards,
Sven

Reply by email

Martok
to Maciej Izak: 2018-02-05 13:59 GMT+01:00 denisgolovan : IMHO this code shouldn't ever work. Any fix…

Looks like it.

IMHO this code shouldn't ever work. Any fix means really mess and hell and more problems.

Why? As the specialized interface gets "inserted" into the specializing unit at that point, so should the implementation be - and at that point, the operator *is* overloaded.
Instead, the overload has to be available in the implementation section of the generic type, /unless/ it is a class or record. That seems odd. You know I generally prefer consistency over obscure formal differences ;-)

And it's not like we don't already have absurd operator chains without that, see 31215.

* use Generics.Collections

I'd like to stick with fcl units, and rtl-generics seems pretty outdated compared to your repo?

Of course, the issue itself also occurs with any user code, not just containers. That kind of lookup means *no* overload on non-object types is available to generics declared in other units: arithmetic, Implicit, Explicit etc.

Regards, Martok Ceterum censeo b32079 esse sanandam.

Reply by email

Martok
to Sven Barth: I'm having some logical problems with fgl generic lists and operator overloads. This is…

Ah sorry, hadn't seen your message before my last.

While specializing the compiler only uses operator overloads (and helpers) that have been in scope during the declaration of the generic or those that are part of the types that are used for the specialization. This is definitely consistent.

Unintuitive, but consistent, okay.

Type helpers for the used type that are in scope at the time of specialization are also not applied. But I guess they don't count as "part of the type", so that's technically true.

I may have missed something important about how generics are specialized internally. That would explain what I wrote in my answer to Maciej.

Regards, Martok Ceterum censeo b32079 esse sanandam.

Reply by email

Martok
to Martok
Martok wrote 6 lines
Ah sorry, hadn't seen your message before my last. > While specializing the compiler only uses operator overloads (and helpers) that > have been in scope during the declaration of the generic or those that are part > of the types that are used for the specialization. This is definitely consistent. Unintuitive, but consistent, okay.

I only just noticed (and I read that page before writing the first message here...): the documentation says this is indeed not intentional?

<https://www.freepascal.org/docs-html/ref/refse57.html> """ Currently, due to an implementation restriction, it will not work using a global operator, i.e. the following does not work yet: ...
Support for this construct is expected in a future version of Free Pascal."""

Reply by email

Maciej Izak
to Martok: Looks like it. Why? As the specialized interface gets "inserted" into the specializing…

2018-02-05 15:00 GMT+01:00 Martok :

Why? ...

Default property / field or/and aspects is better solution. The way mentioned by Sven with extending generic's scope means almost no control over used operators (or really hard to predict), IMO bad design >.<. Now we have a lot of problems with type/class/record helpers scope, for extending generic's scope I can guarantee "exponentially" more non-fixable problems + destruction of consistency of language and problems with implementation of default property / field and aspects... Pandora's box is nothing in comparison to extending generic's scope in such way :P. Simple example:

specialization for the same generic type can behaves in different way!

for example final code for TFoo< TSomeArray> will be different than for TFoo<TSomeArray> declared in other module (!). What if TFoo<T> has some class var + class constructor ? What if inside is used some dictionary (declared as class var and handled in class constructor/destructor) to handle different specializations (where as key of that dictionary is used TypeInfo(T)) and some logic is hidden behind of type which behaves in different way for the same specialization?

Totally nuclear destruction and non-fixable problems for language and libraries. I will repeat it again : definitely non-consistent and destructive feature. This can't work in proper way.

* use Generics.Collections I'd like to stick with fcl units, and rtl-generics seems pretty outdated compared to your repo?

I'am working on Generics.Collections 2.0 it is not ready for trunk yet (anyway https://github.com/maciej-izak/generics.collections is full functional for 3.0.4 and trunk - just few tests are missing and part of interface for T*AVLTree* types).
Generics.Collections 2.0 is much improved, with many fixes, full of tests and ready/optimized for Smart Pointers / Nullable types. I found also many new compiler bugs related to generics types and I want to try to fix part of them before I decide to update rtl-generics.

Best regards, Maciej Izak

Reply by email

Sven Barth

Am 05.02.2018 15:49 schrieb "Maciej Izak" :

specialization for the same generic type can behaves in different way!

for example final code for TFoo< TSomeArray> will be different than for TFoo<TSomeArray> declared in other module (!). What if TFoo<T> has some class var + class constructor ? What if inside is used some dictionary (declared as class var and handled in class constructor/destructor) to handle different specializations (where as key of that dictionary is used TypeInfo(T)) and some logic is hidden behind of type which behaves in different way for the same specialization?

Totally nuclear destruction and non-fixable problems for language and libraries. I will repeat it again : definitely non-consistent and destructive feature. This can't work in proper way.

Which is exactly what I had meant with the whole new can of bugs :P

I found also many new compiler bugs related to generics types and I want to

try to fix part of them before I decide to update rtl-generics.

Would be good if you'd report them on Mantis nevertheless, cause generics are a bit of a sensitive area and I'd like to keep an overview there.

Regards,
Sven

Reply by email

Martok
to Maciej Izak: 2018-02-05 15:00 GMT+01:00 Martok : Default property / field or/and aspects is better…
Maciej Izak wrote
for example final code for TFoo< TSomeArray> will be different than for TFoo<TSomeArray> declared in other module (!).

Crap. True.

Now I see why it is how it is.

The maximum extent would be to accept overloads (and helpers) that are visible at the parameter type's declaration, because they would necessarily be visible in all specializations as well. That would be pretty much the same as how it currently accepts operators defined on the type, just one symtable up. It would also solve the "implementation restriction" from the manual - and only that. I think?

Regards, Martok Ceterum censeo b32079 esse sanandam.

Reply by email

Sven Barth
to Martok

Am 05.02.2018 17:50 schrieb "Martok" :

Maciej Izak wrote
for example final code for TFoo< TSomeArray> will be different than for TFoo<TSomeArray> declared in other module (!).

Crap. True.

Now I see why it is how it is.

The maximum extent would be to accept overloads (and helpers) that are visible
at the parameter type's declaration, because they would necessarily be visible
in all specializations as well. That would be pretty much the same as how it currently accepts operators defined on the type, just one symtable up. It would
also solve the "implementation restriction" from the manual - and only that. I
think?

That *could* be a possibility. I'd need to think about it a bit though to be sure that there are no caveats.

Regards,
Sven

Reply by email

Maciej Izak
to Sven Barth

2018-02-05 19:54 GMT+01:00 Sven Barth via fpc-pascal <

Show quoted text 20 lines
: Now I see why it is how it is. The maximum extent would be to accept overloads (and helpers) that are visible at the parameter type's declaration, because they would necessarily be visible in all specializations as well. That would be pretty much the same as how it currently accepts operators defined on the type, just one symtable up. It would also solve the "implementation restriction" from the manual - and only that. I think? That *could* be a possibility. I'd need to think about it a bit though to be sure that there are no caveats.

It may be good feature or more troubles ;) The only acceptable types where "one symtable up" should be used, are types with own RTTI info:

type
  MyInteger = type Integer;
   { .. some operators for MyInteger  ... }
  TMyHelper = type helper for MyInteger
     { something }
  end;

type alias declaration for our case is not an option:

type
  MyInteger = Integer;
  { .. some operators for MyInteger  ... }
  TMyHelper = type helper for MyInteger
     { something }
  end;

for such case TMyHelper (or operator) should be invisible in specializations, otherwise compiled program will explode :P.

Best regards, Maciej Izak

Reply by email

Maciej Izak
to Sven Barth: specialization for the same generic type can behaves in different way! for example final…

2018-02-05 17:12 GMT+01:00 Sven Barth via fpc-pascal <

Show quoted text 5 lines
: Would be good if you'd report them on Mantis nevertheless, cause generics are a bit of a sensitive area and I'd like to keep an overview there.

I was able to collect list of ~10 new bugs but part of them is probably related to 3.0.4, part is probably duplicated, part was not tested with trunk so final list might be shorter. Sometimes minimal test case, especially for generics is hard to expose. I will try to find enough motivation for new bug reports. :/ Compiler hates my code - internal errors, fatal errors, errors without source locations and false errors... New challenges/issues to fix :)

Best regards, Maciej Izak

Reply by email