Discussion:
[fpc-pascal] Constants in generics
Ryan Joseph
2018-11-06 07:13:01 UTC
Permalink
I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.

If I fix this up will it be considered as a patch? I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).

https://github.com/genericptr/freepascal/commit/ec518542b2da7d7f016702a82b2d05349a01a6fb

{$mode objfpc}
{$modeswitch advancedrecords}

program generic_constants;

type
generic TList<T, U> = record
list: array[0..U-1] of T;
function capacity: integer;
end;

function TList.capacity: integer;
begin
result := U;
end;

var
nums: specialize TList<integer,10>;
strs: specialize TList<integer,4>;
begin
writeln('sizeof:',sizeof(nums), ' capacity:',nums.capacity);
writeln('sizeof:',sizeof(strs), ' capacity:',strs.capacity);
end.



Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinf
Sven Barth via fpc-pascal
2018-11-06 13:21:57 UTC
Permalink
Post by Ryan Joseph
I implemented a first draft of constants (integers) in generics. My reason
was specifically that I wanted a way to add methods to static arrays and
generic records is the only way to accomplish this AFAIK.
If I fix this up will it be considered as a patch? I wanted to present the
idea first before I spent any more time. Here’s what I has so far (on
GitHub).
https://github.com/genericptr/freepascal/commit/ec518542b2da7d7f016702a82b2d05349a01a6fb
{$mode objfpc}
{$modeswitch advancedrecords}
program generic_constants;
type
generic TList<T, U> = record
list: array[0..U-1] of T;
function capacity: integer;
end;
function TList.capacity: integer;
begin
result := U;
end;
var
nums: specialize TList<integer,10>;
strs: specialize TList<integer,4>;
begin
writeln('sizeof:',sizeof(nums), ' capacity:',nums.capacity);
writeln('sizeof:',sizeof(strs), ' capacity:',strs.capacity);
end.
First of I'm not a fan of adding support for constants, mainly because it
will definitely not help parsing of inline specializations in mode Delphi
which are going to be annoying enough already.
That said: even if we do add it, then only if a generic constant parameter
is designated as such with "const N" in the generic declaration instead of
merely "N", so that the compiler does not assume it's a type.

Regards,
Sven
Ryan Joseph
2018-11-06 13:34:59 UTC
Permalink
First of I'm not a fan of adding support for constants, mainly because it will definitely not help parsing of inline specializations in mode Delphi which are going to be annoying enough already.
Can you give an example? There’s lots of ways generics can be used and I tested in only a few.

This is a very important improvement because it allows us to extend static arrays in ways we haven’t been able to do before. Unless I missed something huge it seems like a simple thing to implement (see the minor changes I made in a single unit).
That said: even if we do add it, then only if a generic constant parameter is designated as such with "const N" in the generic declaration instead of merely "N", so that the compiler does not assume it's a type.
Why is this preferable? If you tried to pass a type for a constant in a specialization then you’d probably just get a type error which is easy to catch. Generics in FPC are already very verbose (“specialize" keyword is exhaustingly long) but adding “const” is at least a small cosmetic change in a single location.


Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/list
Sven Barth via fpc-pascal
2018-11-06 16:19:43 UTC
Permalink
On Nov 6, 2018, at 8:21 PM, Sven Barth via fpc-pascal <
First of I'm not a fan of adding support for constants, mainly because
it will definitely not help parsing of inline specializations in mode
Delphi which are going to be annoying enough already.
Can you give an example? There’s lots of ways generics can be used and I
tested in only a few.
Complex inline specializations containing, for example a multiplication
with specializations on the left and the right side are currently not
possible in mode Delphi. In addition to that Delphi allows overloads of
generic types with variables and constants, so when the parser encounters
"Bla<N" and has not yet encountered the ">" it does not know whether it
needs to start a specialization or an expression. This is already annoying
enough to solve with merely types, but if the right side of the "<" can
also take constants for generics that gets downright proplematic.
That said: even if we do add it, then only if a generic constant
parameter is designated as such with "const N" in the generic declaration
instead of merely "N", so that the compiler does not assume it's a type.
Why is this preferable? If you tried to pass a type for a constant in a
specialization then you’d probably just get a type error which is easy to
catch. Generics in FPC are already very verbose (“specialize" keyword is
exhaustingly long) but adding “const” is at least a small cosmetic change
in a single location.
Specialization is an expensive operation. When the compiler can already
decide that "Something<SomeType, SomeOtherType>" can never be valid,
because it's declared as "Something<T, const N>", then this is preferable.
Otherwise the user might get some cryptic error message during the
specialization, because "SomeOtherType" is used where only a constant can
be used.
Pascal is considered a type safe language and without forcing users to
decorate such parameters as "const" you essentially allow users to mix
types and constants.
Not to mention that parsing the generic can already be done more correctly
then it could be if the parameter is a typesym (which it would be right
now).
You can't do e.g. "const SomeOtherConst = ConstParam * Pi" that way as it
would mean to ease up the compiler's restrictions for type checking even
more.

Regards,
Sven
Ryan Joseph
2018-11-07 01:59:26 UTC
Permalink
Complex inline specializations containing, for example a multiplication with specializations on the left and the right side are currently not possible in mode Delphi. In addition to that Delphi allows overloads of generic types with variables and constants, so when the parser encounters "Bla<N" and has not yet encountered the ">" it does not know whether it needs to start a specialization or an expression. This is already annoying enough to solve with merely types, but if the right side of the "<" can also take constants for generics that gets downright proplematic.
Can you show full a code example? If it really is so problematic because of something Delphi does then maybe it’s best left to objfpc mode until it can be resolved.
Specialization is an expensive operation. When the compiler can already decide that "Something<SomeType, SomeOtherType>" can never be valid, because it's declared as "Something<T, const N>", then this is preferable. Otherwise the user might get some cryptic error message during the specialization, because "SomeOtherType" is used where only a constant can be used.
Pascal is considered a type safe language and without forcing users to decorate such parameters as "const" you essentially allow users to mix types and constants.
Not to mention that parsing the generic can already be done more correctly then it could be if the parameter is a typesym (which it would be right now).
You can't do e.g. "const SomeOtherConst = ConstParam * Pi" that way as it would mean to ease up the compiler's restrictions for type checking even more.
I guess if you include const it could save some compile time if you tried to specialize with the wrong type? It’s a pretty minor savings but that’s ok.

type
generic TList<T,const U> = record
list: array[0..U-1] of T;
function capacity: integer;
end;

Btw I only implemented this with integers (no strings or floats) because afaik this feature is only useful for arrays. I guess you could use non-ints for default parameters but this is probably a stupid idea because of how much compile time you’ll add to specialize a large amount of types.

Is there any reason to support strings and floats?

type
generic TCaller<T,const U> = record
procedure Say(message:T = U);
end;

var
caller: specialize TCaller<string,’hello’>;
begin
caller.Say;

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/m
Martin
2018-11-07 11:51:47 UTC
Permalink
Post by Ryan Joseph
I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.
If I fix this up will it be considered as a patch? I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).
Just for info, there already is a way to do this, though being able to
do it direct would be nice. You can wrap any constants into an object
(or advanced record).

program Project1;
{$mode objfpc}{$H+}
uses  Classes;

type
  generic Foo<A> = object
    class procedure test;
  end;

  Data = object
    const val = 1;
  end;

var
  bar: specialize Foo<data>;

class procedure Foo.test;
begin
  writeln(A.val);
end;

begin
  bar.test;
  readln;
end.

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bi
denisgolovan
2018-11-07 14:25:54 UTC
Permalink
Does this trick also work for declaring arrays using "val"?

BR,
Denis
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/
Ryan Joseph
2018-11-08 12:59:32 UTC
Permalink
I added const in declarations so you get syntax error. I also disabled the const’s in delphi mode because it does indeed break inline specializations. Not sure how difficult that will be to fix or if it’s even worth it since this feature isn’t in delphi afaik.

https://github.com/genericptr/freepascal/tree/generic_constants

Pointless test to show the syntax:

{$mode objfpc}
{$modeswitch advancedrecords}
{$interfaces CORBA}

program generic_constants_interfaces;

type
generic IMyInterface<T, const U> = interface
procedure DoThis (value: T);
end;

type
generic THelper<T, const U> = record
list: array[0..U-1] of T;
end;

type
generic TMyClass<T, const U> = class (specialize IMyInterface<T, U>)
type
// todo: second param in THelper must be const also!
THelperType = specialize THelper<T, U>;
public
helper: THelperType;
procedure DoThis (value: T);
end;
TListClass = specialize TMyClass<integer,10>;

procedure TMyClass.DoThis (value: T);
begin
writeln('DoThis:',value, ' default: ', U);
end;

var
c: TListClass;
begin
c := TListClass.Create;
c.helper.list[5] := 100;
writeln(c.helper.list[5]);
c.DoThis(100);
end.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepasca
Florian Klämpfl
2018-11-08 21:28:27 UTC
Permalink
Post by Ryan Joseph
I implemented a first draft of constants (integers) in generics. My reason was specifically that I wanted a way to add methods to static arrays and generic records is the only way to accomplish this AFAIK.
If I fix this up will it be considered as a patch?
I wanted to present the idea first before I spent any more time. Here’s what I has so far (on GitHub).
I like the idea of const in generics, but it needs serious cleanup when it's working:
- commit messages like "first commit" are useless
- do not commit meta files like .gitignore with a functional commit
- follow the indention style of the surrounding code
- the compiler contains already a type called tgenericdef
- do not change the lazarus project files best practice is, to make a local copy for your own work (like ppcx64_ryan.lpi)
- create tests: succeeding as well as failing one
- do not use c style operators in the compiler, use inc/dec instead of += and -=
- replace comments like // note: ryan by comments like: "check for const generic paramters"
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.fre
Ryan Joseph
2018-11-09 02:13:43 UTC
Permalink
Post by Florian Klämpfl
- commit messages like "first commit" are useless
Those are for github so I could share but I need to learn SVN (again) eventually.
Post by Florian Klämpfl
- do not commit meta files like .gitignore with a functional commit
- follow the indention style of the surrounding code
Is the compiler 2 spaces for indent? There’s already enough inconsistency but I’m trying to figure it out.
Post by Florian Klämpfl
- the compiler contains already a type called tgenericdef
I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
Post by Florian Klämpfl
- do not change the lazarus project files best practice is, to make a local copy for your own work (like ppcx64_ryan.lpi)
Good idea, thanks.
Post by Florian Klämpfl
- create tests: succeeding as well as failing one
ok.
Post by Florian Klämpfl
- do not use c style operators in the compiler, use inc/dec instead of += and -=
ok.
Post by Florian Klämpfl
- replace comments like // note: ryan by comments like: "check for const generic paramters”
those are temporary for my use. I’ll remove those of course.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepasc
Sven Barth via fpc-pascal
2018-11-09 06:59:36 UTC
Permalink
Post by Ryan Joseph
Post by Florian Klämpfl
I like the idea of const in generics, but it needs serious cleanup when
- commit messages like "first commit" are useless
Those are for github so I could share but I need to learn SVN (again) eventually.
You can also use git svn. At least I myself use that for my own work (I
think Florian does, too). Of course you need to set up syncing with your
GitHub Repo as well, but I haven't done that for a SVN Repo yet...
Post by Ryan Joseph
Post by Florian Klämpfl
- do not commit meta files like .gitignore with a functional commit
- follow the indention style of the surrounding code
Is the compiler 2 spaces for indent? There’s already enough inconsistency
but I’m trying to figure it out.
Yes, two spaces. And "begin... end" is indented and its content is indented
again.
Post by Ryan Joseph
Post by Florian Klämpfl
- the compiler contains already a type called tgenericdef
I think I changed that name in the last commit. Btw I made all those types
because I didn’t want to populate the main types like ttypesym with extra
bytes but I don’t know what’s best practice in the codebase.
When something belongs in a main type then it belongs there. But as I
already wrote it should not be a ttypesym for constant generic parameters
anyway.

Regards,
Sven
Florian Klämpfl
2018-11-10 12:16:27 UTC
Permalink
Post by Ryan Joseph
Post by Florian Klämpfl
- commit messages like "first commit" are useless
Those are for github so I could share but I need to learn SVN (again) eventually.
This does not prevent you from creating proper commits, especially as the patch series from github could be easily
applied later on to fpc's main repository.
Post by Ryan Joseph
Post by Florian Klämpfl
- the compiler contains already a type called tgenericdef
I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
Priority is:
1. maintainability
2. portability
3. speed

Keep also ppu storing/loading in mind.
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freep
Ryan Joseph
2018-11-10 12:20:51 UTC
Permalink
Post by Florian Klämpfl
This does not prevent you from creating proper commits, especially as the patch series from github could be easily
applied later on to fpc's main repository.
I’ll assume these commits could be public some day then. I originally thought I would have to trash them all and start over for svn anyways.
Post by Florian Klämpfl
Post by Ryan Joseph
Post by Florian Klämpfl
- the compiler contains already a type called tgenericdef
I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
1. maintainability
2. portability
3. speed
Keep also ppu storing/loading in mind.
Thus far on the compiler I haven’t encountered PPU's at all so I just assumed they were part of the code generator and I didn’t need to worry about them. How are they relevant here? Honestly I barely know what the PPU’s are and how the compiler uses them.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://list
Florian Klämpfl
2018-11-10 12:22:51 UTC
Permalink
Post by Ryan Joseph
Post by Florian Klämpfl
Post by Ryan Joseph
Post by Florian Klämpfl
- the compiler contains already a type called tgenericdef
I think I changed that name in the last commit. Btw I made all those types because I didn’t want to populate the main types like ttypesym with extra bytes but I don’t know what’s best practice in the codebase.
1. maintainability
2. portability
3. speed
Keep also ppu storing/loading in mind.
Thus far on the compiler I haven’t encountered PPU's at all so I just assumed they were part of the code generator and I didn’t need to worry about them. How are they relevant here? Honestly I barely know what the PPU’s are and how the compiler uses them.
If you "export" a generic taking a const from a unit, this info has to be stored in the ppu.
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listin
Ryan Joseph
2018-11-10 13:16:10 UTC
Permalink
Post by Florian Klämpfl
If you "export" a generic taking a const from a unit, this info has to be stored in the ppu.
Can you show a test case I could use to see where this is triggered in the compiler? I don’t even know where to begin looking.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freep
Sven Barth via fpc-pascal
2018-11-10 17:42:28 UTC
Permalink
Post by Florian Klämpfl
Post by Florian Klämpfl
If you "export" a generic taking a const from a unit, this info has to
be stored in the ppu.
Can you show a test case I could use to see where this is triggered in the
compiler? I don’t even know where to begin looking.
The important part are the ppuwrite and ppuload methods that are provided
by tstoreddef, tstoredsym and tstoredsymtable and overridden by most of
their descendants. E.g. tstoreddef itself is also dealing with the generic
parameters.

And for a test: declare the generic in one unit, specialize it in another
and compile twice. Usually things go boom if you messed up something.

By the way: When adding/changing data that is written to the PPU increase
the PPUVersion constant in the ppu.pas unit (there is no backwards
compatibility for PPUs) and also adjust the ppudump utility in
compiler/utils/ppudump.

Regards,
Sven
Ryan Joseph
2018-11-09 04:20:12 UTC
Permalink
Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-
Florian Klämpfl
2018-11-10 12:12:24 UTC
Permalink
Post by Ryan Joseph
Question: should other consts besides integers be allowed? I don’t think it makes sense personally to use strings, floats or sets but maybe I’m wrong.
I would allow them for orthogonality reasons.

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/
Ben Grasset
2018-11-11 01:34:30 UTC
Permalink
Question: should other consts besides integers be allowed? I don’t think
it makes sense personally to use strings, floats or sets but maybe I’m
wrong.
As a daily FPC user there's nothing I find more annoying than compiler
restrictions that are visibly artificial, so I'd say you should definitely
allow basically anything that's physically possible to allow in that
context.
*Someone* will find it useful, trust me. I can already think of various
uses for string constants as generic parameters that I certainly would have
already done in the past if I could have, for example.
Post by Florian Klämpfl
I like the idea of const in generics, but it needs serious cleanup when
Question: should other consts besides integers be allowed? I don’t think
it makes sense personally to use strings, floats or sets but maybe I’m
wrong.
Regards,
Ryan Joseph
_______________________________________________
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Ryan Joseph
2018-11-14 02:25:24 UTC
Permalink
I think I have this done except for cleanup. Here’s the status:

- Integer,string,real,set and nil constant for parameters.
- Consts can have type restrictions which correspond to the above types.
- Const can be assigned to from generic const params.
- PPU loading.

Technical compiler issues:

- I couldn’t figure out how to extend PPU loading for a ttypesym subclass so I put 2 fields into ttypesym. Those are probably best moved to a subclass later.
- We may need new error messages but I just left place holders in for now.

https://github.com/genericptr/freepascal/tree/generic_constants

program generic_constants_types;
type
generic TList<T, const U:integer> = record
const
ID = U;
public
list: array[0..U-1] of T;
procedure dothis (msg:string);
end;

procedure TList.dothis (msg:string);
begin
writeln('message:',msg, ' high:', high(ID), ' constsize:', sizeof(ID), ' structsize:', sizeof(self));
end;

type
TDay = (Mon, Tue, Wed);
TDays = set of TDay;
const
kSomeDays:TDays = [Mon, Wed];
var
a: specialize TList<integer,10>;
//b: specialize TList<integer,'foo'>;
//c: specialize TList<integer,0.1>;
//d: specialize TList<integer,kSomeDays>;
//e: specialize TList<integer,nil>;
begin
a.dothis('hello');
end.


Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo
Sven Barth via fpc-pascal
2018-11-25 17:09:37 UTC
Permalink
Post by Ryan Joseph
- Integer,string,real,set and nil constant for parameters.
- Consts can have type restrictions which correspond to the above types.
- Const can be assigned to from generic const params.
- PPU loading.
- I couldn’t figure out how to extend PPU loading for a ttypesym subclass so I put 2 fields into ttypesym. Those are probably best moved to a subclass later.
- We may need new error messages but I just left place holders in for now.
https://github.com/genericptr/freepascal/tree/generic_constants
In contrast to the multiple helper ones I have much more to complain
about here:

- your pretty name is wrong; the pretty name for a specialization with
constants should be "TSomeGeneric<TSomeType, 42>", not
"TSomeGeneric<SomeUnit.TSomeType, System.LongInt#42>" as it would be now
- remove those tgenericparasym and tgeneric_*_parasym types; use simply
tconstsym instead of ttypesym for const parameters, other code will have
to check that correctly
- even though you'll remove is_const and const_type from ttypesym again
due to the above it's wrong to use putsmallset/getsmallset for an enum;
use putbyte/getbyte or one of the "larger" put*/get* functions depending
on the number of entries contained in the enum (Note: not a runtime
check, just pick the correct one at compile time)
- get rid of tscannerfile.parsing_generic_type; it's not only at the
wrong location (the name "parsing_generic_type" should already tell you
that it has no place inside the *scanner*), but it's also used to solve
something in a completely wrong way: you must *not* use
current_structdef for the generic parameters as the only valid parameter
list inside parse_generic_specialization_types_internal *is* paradeflist
and genericdef.genericparas. If there is still a problem then you need
to handle that differently (Note: also don't Exit from
parse_generic_specialization_type_internal, because the idea is that all
incompatibilities are shown at once, so use Continue to check the next
parameter)
- remove the check for m_objfpc; yes it won't work with inline
specializations in mode Delphi for now, but it can still be used for
specializations in type or var sections; also one idea for an
improvement I have for parsing inline specializations in mode Delphi
would be if the compiler knows that the symbol left of the "<" can only
be a generic (cause let's be honest: in most code types aren't
overloaded with variables/constants and more often than not types begin
with "T", so they shouldn't conflict with the names of more local
variables/constants/fields either)

Also thinking about the feature a bit it would be better to enforce the
type of the constant during the generic's declaration. After all I can
do different things with a string const, an ordinal const or a set
const. So it would be better to use "const <NAME>: <TYPE>" where type
can be any type that results in a tconstsym. After all the type of the
constant inside the generic is usually fixed and those few cases were a
variadic constant would be necessary could be handled by "TMyGeneric<T;
const N: T>" (which would not work right now, but as we also need to
support "TMyGeneric<T; S: SomeIntf<T>>" due to Delphi compatibility that
would be handled by the same solution).
This way you can also get rid of the cconstundefined and you can create
a correct tconstsym right of the bat.

And as I had written for the helper extension: add tests. A feature like
this will need many tests (you can use "tgenconst" as test name prefix).
Also don't forget to test this with generic routines.

Don't forget to check the code formatting as well, I saw a few locations
where you had spaces where they don't belong. ;)

Regards,
Sven
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pas
Ryan Joseph
2018-11-26 09:14:29 UTC
Permalink
This post might be inappropriate. Click to display it.
Sven Barth via fpc-pascal
2018-11-26 10:16:07 UTC
Permalink
Post by Sven Barth via fpc-pascal
On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <
- your pretty name is wrong; the pretty name for a specialization with
constants should be "TSomeGeneric<TSomeType, 42>", not
"TSomeGeneric<SomeUnit.TSomeType, System.LongInt#42>" as it would be now
I’ll change the # prefix for const params but the unit prefix was not
prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);
in pgenutil.pas.
Yes, but that is only because the code currently only handles types. For
constants the addition of the type is not required, only the constant
value.
Post by Sven Barth via fpc-pascal
- remove those tgenericparasym and tgeneric_*_parasym types; use simply
tconstsym instead of ttypesym for const parameters, other code will have to
check that correctly
Ok, I was able to remove those and replace with tconstsym. Much cleaner now.
- even though you'll remove is_const and const_type from ttypesym again
due to the above it's wrong to use putsmallset/getsmallset for an enum; use
putbyte/getbyte or one of the "larger" put*/get* functions depending on the
number of entries contained in the enum (Note: not a runtime check, just
pick the correct one at compile time)
Are you sure those should be changed? There seems to be lots of
assumptions made that the generic params are ttypesym and I’m not confident
that mixing in tconstsyms will be safe. Having said that I never liked that
I mixed in those const_* members into the class.
Like you mention below if the const type was restricted in the definition
then it would make sense to change these to tconstsym. I guess I need to
try and see how much code this change blows up.
Those assumptions will "simply" have to be checked/fixed. In the end we'll
have cleaner code and thus it will be worth it. And with the help of the
testsuite you can avoid regressions. :)
Post by Sven Barth via fpc-pascal
- get rid of tscannerfile.parsing_generic_type; it's not only at the
wrong location (the name "parsing_generic_type" should already tell you
that it has no place inside the *scanner*), but it's also used to solve
something in a completely wrong way: you must *not* use current_structdef
for the generic parameters as the only valid parameter list inside
parse_generic_specialization_types_internal *is* paradeflist and
genericdef.genericparas. If there is still a problem then you need to
handle that differently (Note: also don't Exit from
parse_generic_specialization_type_internal, because the idea is that all
incompatibilities are shown at once, so use Continue to check the next
parameter)
Yes, that was a hack to access current_structdef safely but I guess that’s
not what I should be doing. Let me see if I can get that same information
from the 2 params you mentioned.
Maybe you can show a test case that is failing?
Post by Sven Barth via fpc-pascal
- remove the check for m_objfpc; yes it won't work with inline
specializations in mode Delphi for now, but it can still be used for
specializations in type or var sections; also one idea for an improvement I
have for parsing inline specializations in mode Delphi would be if the
compiler knows that the symbol left of the "<" can only be a generic (cause
let's be honest: in most code types aren't overloaded with
variables/constants and more often than not types begin with "T", so they
shouldn't conflict with the names of more local variables/constants/fields
either)
I got a crash in Delphi mode but I can change that to an error.
For now we should reject an inline specialization (aka block_type =
bt_general) in mode Delphi if the found generic contains any constant
parameter (declarations of such generics should be allowed). Later on we
can lift that restriction once I've implemented the checks I mentioned.
Post by Sven Barth via fpc-pascal
Also thinking about the feature a bit it would be better to enforce the
type of the constant during the generic's declaration. After all I can do
different things with a string const, an ordinal const or a set const. So
it would be better to use "const <NAME>: <TYPE>" where type can be any type
that results in a tconstsym. After all the type of the constant inside the
generic is usually fixed and those few cases were a variadic constant would
be necessary could be handled by "TMyGeneric<T; const N: T>" (which would
SomeIntf<T>>" due to Delphi compatibility that would be handled by the same
solution).
This way you can also get rid of the cconstundefined and you can create
a correct tconstsym right of the bat.
Sure but I need to see how many assumptions this undoes in other areas. I
seem to remember having problems getting the const to fit in as it was and
that’s when they were all the same class.
As I said above that code will have to become aware of constants. Might
lead to more crashes for you right now, but the end result would be
cleaner/better. As a help you could compile the compiler with -CR which
would ensure that typecasts of classes are always done with "as", thus
triggering an exception at the correct location.
Post by Sven Barth via fpc-pascal
And as I had written for the helper extension: add tests. A feature like
this will need many tests (you can use "tgenconst" as test name prefix).
Also don't forget to test this with generic routines.
Don't forget to check the code formatting as well, I saw a few locations
where you had spaces where they don't belong. ;)
Regards,
Sven
Ryan Joseph
2018-11-26 11:13:36 UTC
Permalink
Post by Ryan Joseph
- your pretty name is wrong; the pretty name for a specialization with constants should be "TSomeGeneric<TSomeType, 42>", not "TSomeGeneric<SomeUnit.TSomeType, System.LongInt#42>" as it would be now
prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);
in pgenutil.pas.
Yes, but that is only because the code currently only handles types. For constants the addition of the type is not required, only the constant value.
Then is it ok if type params have the unit prefix? Right now I’m doing TGeneric<Unit.Type,40> because the first param is a type so it uses the original code.
Post by Ryan Joseph
Like you mention below if the const type was restricted in the definition then it would make sense to change these to tconstsym. I guess I need to try and see how much code this change blows up.
Those assumptions will "simply" have to be checked/fixed. In the end we'll have cleaner code and thus it will be worth it. And with the help of the testsuite you can avoid regressions. :)
Ok, I’ll make it happen if it’s important.
Post by Ryan Joseph
- get rid of tscannerfile.parsing_generic_type; it's not only at the wrong location (the name "parsing_generic_type" should already tell you that it has no place inside the *scanner*), but it's also used to solve something in a completely wrong way: you must *not* use current_structdef for the generic parameters as the only valid parameter list inside parse_generic_specialization_types_internal *is* paradeflist and genericdef.genericparas. If there is still a problem then you need to handle that differently (Note: also don't Exit from parse_generic_specialization_type_internal, because the idea is that all incompatibilities are shown at once, so use Continue to check the next parameter)
Yes, that was a hack to access current_structdef safely but I guess that’s not what I should be doing. Let me see if I can get that same information from the 2 params you mentioned.
Maybe you can show a test case that is failing?
This is why I wanted to access current_structdef. When "specialize IMyInterface<T, U>” was being parsed I need a way to refer back to the generic so I know what “U” is.

type
generic TMyClass<T, const U> = class (specialize IMyInterface<T, U>)
type
THelperType = specialize THelper<T, U>;
public
helper: THelperType;
procedure DoThis (value: T);
end;
TListClass = specialize TMyClass<integer,10>;
Post by Ryan Joseph
- remove the check for m_objfpc; yes it won't work with inline specializations in mode Delphi for now, but it can still be used for specializations in type or var sections; also one idea for an improvement I have for parsing inline specializations in mode Delphi would be if the compiler knows that the symbol left of the "<" can only be a generic (cause let's be honest: in most code types aren't overloaded with variables/constants and more often than not types begin with "T", so they shouldn't conflict with the names of more local variables/constants/fields either)
I got a crash in Delphi mode but I can change that to an error.
For now we should reject an inline specialization (aka block_type = bt_general) in mode Delphi if the found generic contains any constant parameter (declarations of such generics should be allowed). Later on we can lift that restriction once I've implemented the checks I mentioned.
Ok, I can give an error is this one instance where it fails and allow all the others.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pas
Sven Barth via fpc-pascal
2018-11-26 13:18:08 UTC
Permalink
Post by Sven Barth via fpc-pascal
On Nov 26, 2018, at 5:16 PM, Sven Barth via fpc-pascal <
On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <
- your pretty name is wrong; the pretty name for a specialization with
constants should be "TSomeGeneric<TSomeType, 42>", not
"TSomeGeneric<SomeUnit.TSomeType, System.LongInt#42>" as it would be now
I’ll change the # prefix for const params but the unit prefix was not
prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);
in pgenutil.pas.
Yes, but that is only because the code currently only handles types. For
constants the addition of the type is not required, only the constant
value.
Then is it ok if type params have the unit prefix? Right now I’m doing
TGeneric<Unit.Type,40> because the first param is a type so it uses the
original code.
Yes. Type parameters have their full name so that one can differentiate
between Unit1.TType and Unit2.TType or Unit1.TSomeClass.TType and
Unit2.TSomeClass.TType in error messages.
Post by Sven Barth via fpc-pascal
Like you mention below if the const type was restricted in the
definition then it would make sense to change these to tconstsym. I guess I
need to try and see how much code this change blows up.
Those assumptions will "simply" have to be checked/fixed. In the end
we'll have cleaner code and thus it will be worth it. And with the help of
the testsuite you can avoid regressions. :)
Ok, I’ll make it happen if it’s important.
- get rid of tscannerfile.parsing_generic_type; it's not only at the
wrong location (the name "parsing_generic_type" should already tell you
that it has no place inside the *scanner*), but it's also used to solve
something in a completely wrong way: you must *not* use current_structdef
for the generic parameters as the only valid parameter list inside
parse_generic_specialization_types_internal *is* paradeflist and
genericdef.genericparas. If there is still a problem then you need to
handle that differently (Note: also don't Exit from
parse_generic_specialization_type_internal, because the idea is that all
incompatibilities are shown at once, so use Continue to check the next
parameter)
Yes, that was a hack to access current_structdef safely but I guess
that’s not what I should be doing. Let me see if I can get that same
information from the 2 params you mentioned.
Maybe you can show a test case that is failing?
This is why I wanted to access current_structdef. When "specialize
IMyInterface<T, U>” was being parsed I need a way to refer back to the
generic so I know what “U” is.
type
generic TMyClass<T, const U> = class (specialize IMyInterface<T, U>)
type
THelperType = specialize THelper<T, U>;
public
helper: THelperType;
procedure DoThis (value: T);
end;
TListClass = specialize TMyClass<integer,10>;
You don't need to manually check for U. The parser will find U in the
symbol table of the TMyClass generic and then a list containing the
parameters will be generated and passed to
parse_generic_specialization_type_internal.
Though we'll probably either have to adjust the type of the paradeflist to
carry the type/constant sym instead of the Def or an additional list that
contains the constant symbols (plus NILs for the type ones).
I currently favor the first one, which will mean a few more adjustments
inside pgenutil when parsing specialization parameters.

Regards,
Sven
Ryan Joseph
2018-11-26 14:47:19 UTC
Permalink
You don't need to manually check for U. The parser will find U in the symbol table of the TMyClass generic and then a list containing the parameters will be generated and passed to parse_generic_specialization_type_internal. Though we'll probably either have to adjust the type of the paradeflist to carry the type/constant sym instead of the Def or an additional list that contains the constant symbols (plus NILs for the type ones).
I currently favor the first one, which will mean a few more adjustments inside pgenutil when parsing specialization parameters.
I get a "class type expected, but got "<erroneous type>”” on the “U” in the nested specialization so I assumed this was because the hidden constant “U” was getting parsed instead of the generic param type “U”. I need to look at this more closely tomorrow because I don’t really understand what’s happening to be honest.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-
Sven Barth via fpc-pascal
2018-11-26 21:59:00 UTC
Permalink
Post by Sven Barth via fpc-pascal
On Nov 26, 2018, at 8:18 PM, Sven Barth via fpc-pascal <
You don't need to manually check for U. The parser will find U in the
symbol table of the TMyClass generic and then a list containing the
parameters will be generated and passed to
parse_generic_specialization_type_internal. Though we'll probably either
have to adjust the type of the paradeflist to carry the type/constant sym
instead of the Def or an additional list that contains the constant symbols
(plus NILs for the type ones).
I currently favor the first one, which will mean a few more adjustments
inside pgenutil when parsing specialization parameters.
I get a "class type expected, but got "<erroneous type>”” on the “U” in
the nested specialization so I assumed this was because the hidden constant
“U” was getting parsed instead of the generic param type “U”. I need to
look at this more closely tomorrow because I don’t really understand what’s
happening to be honest.
Best check again once you've done the switch to tconstsym for constants. :)

Regards,
Sven
Ryan Joseph
2018-11-27 07:18:06 UTC
Permalink
Post by Sven Barth via fpc-pascal
Best check again once you've done the switch to tconstsym for constants. :)
I made most of the changes you mentioned so please look. Including the constsyms didn’t break anything and helped to clean up the code. I would have done that from the start but I wasn’t sure you wanted me mixing in new types.

Some things remaining:

1) kSomeDays is a typed const so I get a load node instead of tsetconstnode, which I need to need to know in order to make the name string. See the TODO’s in tgenericparamdef.create.

type
TDay = (Mon, Tue, Wed);
TDays = set of TDay;
const
kSomeDays:TDays = [Mon, Wed];


var
d: specialize TMyRecord_Set<integer,kSomeDays>; // this doesn’t work
d: specialize TMyRecord_Set<integer,[Mon,Wed]>; // this works because I get a set node


2) Again in tgenericparamdef.create I don’t know how to make the pretty name string from a set (see the TODO)

3) You may be right about forcing the const type at declaration. I still kept the undefined const placeholder in there but if you have nested specializations you can get type errors because the placeholder uses a nil node. It’s too early to know if there’s any reason for undefined consts.

4) I’m not sure if changing the types to consts messed up PPU loading or it works out of the box. Seems to be ok from initial tests though.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-b
denisgolovan
2018-11-27 07:25:02 UTC
Permalink
Hi

Sorry for breaking in, but I'd like to know if this functionality supports specializing generic functions with const parameters?

BR,
Denis
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/m
denisgolovan
2018-11-27 07:51:58 UTC
Permalink
Here’s my test which seems to be working.
program gc_procs;
generic procedure DoThis<T,const U:string>(msg:string = U);
begin
writeln(msg, ' ',sizeof(T), ' ', U);
end;
begin
specialize DoThis<integer,'foo'>('hello world’); // prints "hello world 4 foo"
specialize DoThis<integer,'foo’>; // prints “foo 4 foo"
end.
Thanks.
That's definitely a nice feature.
--
Regards,
Denis Golovan
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman
Ryan Joseph
2018-11-28 08:10:43 UTC
Permalink
I just noticed I sent this to the wrong person and the list never saw it so I’m sending it again. I feel like I should try to fix it while I’ve got my eyes on the generics code before I forget.

Is there a reason it’s not implemented yet? In theory you should be able to specialize a function as a type and use the type name as the function name. This is basically the same syntax for class construction but without the .create.

====================

As a side node I haven’t been willing to use generic functions yet because the syntax is so verbose it kind of defeats the purpose.

Why can’t we specialize functions as a type? Maybe that’s on the todo list also?

generic procedure DoThis<T>(msg:string);
begin
writeln(msg, ' ',sizeof(T));
end;

type
DoInt = specialize DoThis<integer>;

begin
DoInt(‘hello');

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lis
Sven Barth via fpc-pascal
2018-11-28 12:26:19 UTC
Permalink
Post by Ryan Joseph
I just noticed I sent this to the wrong person and the list never saw it
so I’m sending it again. I feel like I should try to fix it while I’ve got
my eyes on the generics code before I forget.
Is there a reason it’s not implemented yet? In theory you should be able
to specialize a function as a type and use the type name as the function
name. This is basically the same syntax for class construction but without
the .create.
====================
As a side node I haven’t been willing to use generic functions yet because
the syntax is so verbose it kind of defeats the purpose.
Why can’t we specialize functions as a type? Maybe that’s on the todo list
also?
Because that is not supposed to work. Generic routines are *routines*, not
types. You can't define aliases for routines either, not to mention the
problems with scoping as generic methods exist as well.

Generic routines however have a different boon in Delphi that I plan to add
as well: inference of the type parameters based on the parameters the user
passed. In that case the type parameter clause as well as the "specialize"
in non-Delphi modes would not need to be used and it would look like a
normal, non-generic call.

Regards,
Sven
Ryan Joseph
2018-11-28 14:26:39 UTC
Permalink
Because that is not supposed to work. Generic routines are *routines*, not types. You can't define aliases for routines either, not to mention the problems with scoping as generic methods exist as well.
It seems natural you could specialize a generic procedure because you can do this for all other generic types. Maybe a type isn’t the right way but it feels like there should be some way to specialize the procedure header outside of a begin/end block. I know you can't make aliases to procedures but generics are more of “instantiating a template” which falls outside the scope the language in general.
Generic routines however have a different boon in Delphi that I plan to add as well: inference of the type parameters based on the parameters the user passed. In that case the type parameter clause as well as the "specialize" in non-Delphi modes would not need to be used and it would look like a normal, non-generic call.
That’s probably the best way (Swift does this and I always wondered why FPC doesn’t) but it won’t work unless the parameters contain the generic parameter, which is a minority of generics functions anyways.

Just to be sure, you mean like this?

generic procedure DoThis<T>(msg:T);
begin
end;

begin

DoThis(‘hello’); // compiles as “specialize DoThis<string>(‘hello’)” because param types match generic parameter types

If all that’s involved is comparing params types to generic types then that looks like a relatively simple solution. Const generic params may have complicated this some however.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal
Sven Barth via fpc-pascal
2018-11-28 16:25:33 UTC
Permalink
On Nov 28, 2018, at 7:26 PM, Sven Barth via fpc-pascal <
Because that is not supposed to work. Generic routines are *routines*,
not types. You can't define aliases for routines either, not to mention the
problems with scoping as generic methods exist as well.
It seems natural you could specialize a generic procedure because you can
do this for all other generic types. Maybe a type isn’t the right way but
it feels like there should be some way to specialize the procedure header
outside of a begin/end block. I know you can't make aliases to procedures
but generics are more of “instantiating a template” which falls outside the
scope the language in general.
You're not wrong, but a type alias is definitely the wrong way.
But that is a completely different topic, one that I'm not willing to
discuss right now, aside from "we don't support something like that".
Generic routines however have a different boon in Delphi that I plan to
add as well: inference of the type parameters based on the parameters the
user passed. In that case the type parameter clause as well as the
"specialize" in non-Delphi modes would not need to be used and it would
look like a normal, non-generic call.
That’s probably the best way (Swift does this and I always wondered why
FPC doesn’t) but it won’t work unless the parameters contain the generic
parameter, which is a minority of generics functions anyways.
Just to be sure, you mean like this?
generic procedure DoThis<T>(msg:T);
begin
end;
begin
DoThis(‘hello’); // compiles as “specialize
DoThis<string>(‘hello’)” because param types match generic parameter types
If all that’s involved is comparing params types to generic types then
that looks like a relatively simple solution. Const generic params may
have complicated this some however.
Yes, all generic parameters need to be part of the routine's parameters and
constant parameters indeed won't work in that case.

Regards,
Sven
Ryan Joseph
2018-11-29 02:34:44 UTC
Permalink
Post by Sven Barth via fpc-pascal
You're not wrong, but a type alias is definitely the wrong way.
But that is a completely different topic, one that I'm not willing to discuss right now, aside from "we don't support something like that”.
Ok, maybe something to think about later. Compile time performance could be a reason to pursue down the road.
Post by Sven Barth via fpc-pascal
If all that’s involved is comparing params types to generic types then that looks like a relatively simple solution. Const generic params may have complicated this some however.
Yes, all generic parameters need to be part of the routine's parameters and constant parameters indeed won't work in that case.
What are the performance implications of specializing all these types across a code base? I’ve heard c++ developers mention the terrible performance of templates and I think that’s because of the inferred types and inability to specialize once (like FPC does for all other generics except procs).

Looking at this now I think that each unit that uses an inferred specialization will produce a unit procedure for just that unit. Is that right?

Here’s an example which specializes 3 generics (they’re shared right?) across 5 procedure calls.

generic procedure DoThis<T>(msg:T);
begin
end;

generic procedure DoThis<T>(msg:T;param1:T);
begin
end;

generic procedure DoThis<T>(msg:T;param1:integer);
begin
end;

begin
DoThis('hello'); // specialize DoThis<string>('hello');
DoThis(1); // specialize DoThis<integer>(1);
DoThis('a','b'); // specialize DoThis<string>('a','b’);
DoThis(1,1); // specialize DoThis<integer>(1,1);
DoThis('hello',1); // specialize DoThis<string>(‘hello',1);

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pa

Sven Barth via fpc-pascal
2018-11-27 10:35:22 UTC
Permalink
Post by Ryan Joseph
On Nov 27, 2018, at 4:59 AM, Sven Barth via fpc-pascal <
Best check again once you've done the switch to tconstsym for constants.
:)
I made most of the changes you mentioned so please look. Including the
constsyms didn’t break anything and helped to clean up the code. I would
have done that from the start but I wasn’t sure you wanted me mixing in new
types.
Good, thank you. Hopefully I'll find the time this evening to look at your
changes again. :)
Post by Ryan Joseph
1) kSomeDays is a typed const so I get a load node instead of
tsetconstnode, which I need to need to know in order to make the name
string. See the TODO’s in tgenericparamdef.create.
type
TDay = (Mon, Tue, Wed);
TDays = set of TDay;
const
kSomeDays:TDays = [Mon, Wed];
var
d: specialize TMyRecord_Set<integer,kSomeDays>; // this doesn’t
work
d: specialize TMyRecord_Set<integer,[Mon,Wed]>; // this works
because I get a set node
The difference between typed and untyped constants are available everywhere
else in the language as well, so I have no qualms (at least for now?) to
error out on them.

For sets I believe that this should work however (see here:
https://freepascal.org/docs-html/current/ref/refse9.html#x21-200002.1 ):

=== code begin ===
const
kSomeDays = [Mon, Wed];
=== code end ===

2) Again in tgenericparamdef.create I don’t know how to make the pretty
Post by Ryan Joseph
name string from a set (see the TODO)
I'll need to take a look at that. Maybe we'll need to add a list of
elements to the set comet type if it isn't already there. 🀷‍♀

3) You may be right about forcing the const type at declaration. I still
Post by Ryan Joseph
kept the undefined const placeholder in there but if you have nested
specializations you can get type errors because the placeholder uses a nil
node. It’s too early to know if there’s any reason for undefined consts.
4) I’m not sure if changing the types to consts messed up PPU loading or
it works out of the box. Seems to be ok from initial tests though.
Did you try with the generic and the specialization in different units and
changing only one of the two? Though you might want to check whether
tstoreddef.ppuload (or wherever tdef.genericparas is initialized after a
unit load) correctly instantiated the tconstsym instances instead of
ttypesym ones. It might already work correctly, but better safe than sorry.
😅

Regards,
Sven
Mattias Gaertner via fpc-pascal
2018-11-27 11:03:24 UTC
Permalink
On Tue, 27 Nov 2018 11:35:22 +0100
Post by Sven Barth via fpc-pascal
[...]
Post by Ryan Joseph
const
kSomeDays:TDays = [Mon, Wed];
[...]
The difference between typed and untyped constants are available
everywhere else in the language as well, so I have no qualms (at
least for now?) to error out on them.
What about

{$WritableConst off}
const kSomeDays:TDays = [Mon, Wed];

?
BTW, how do you name this? "proper typed const"?

Mattias
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listi
Sven Barth via fpc-pascal
2018-11-27 13:08:56 UTC
Permalink
Am Di., 27. Nov. 2018, 12:03 hat Mattias Gaertner via fpc-pascal <
Post by Mattias Gaertner via fpc-pascal
On Tue, 27 Nov 2018 11:35:22 +0100
Post by Sven Barth via fpc-pascal
[...]
Post by Ryan Joseph
const
kSomeDays:TDays = [Mon, Wed];
[...]
The difference between typed and untyped constants are available
everywhere else in the language as well, so I have no qualms (at
least for now?) to error out on them.
What about
{$WritableConst off}
const kSomeDays:TDays = [Mon, Wed];
?
BTW, how do you name this? "proper typed const"?
For the compiler it makes no difference whether a typed const is writable
or not. A typed const is a typed const and can't be used for some things
that untyped consts can be used for.

One further difference: untyped consts might not even be part of the
binary's data section in case of ordinal, float or set consts as they
simply become part of the code section. Typed consts always reside in the
data section.

Regards,
Sven
Ryan Joseph
2018-11-27 13:00:27 UTC
Permalink
Post by Ryan Joseph
1) kSomeDays is a typed const so I get a load node instead of tsetconstnode, which I need to need to know in order to make the name string. See the TODO’s in tgenericparamdef.create.
type
TDay = (Mon, Tue, Wed);
TDays = set of TDay;
const
kSomeDays:TDays = [Mon, Wed];
var
d: specialize TMyRecord_Set<integer,kSomeDays>; // this doesn’t work
d: specialize TMyRecord_Set<integer,[Mon,Wed]>; // this works because I get a set node
The difference between typed and untyped constants are available everywhere else in the language as well, so I have no qualms (at least for now?) to error out on them.
You mean reject them? The default behavior is to get a type_e_type_id_expected error. They can be included easily but I don’t know how to get the set from the load node (I think it needs further parsing).
Post by Ryan Joseph
=== code begin ===
const
kSomeDays = [Mon, Wed];
=== code end ===
Yes, sets work because I get a setnode which I can operate on.
Post by Ryan Joseph
Did you try with the generic and the specialization in different units and changing only one of the two? Though you might want to check whether tstoreddef.ppuload (or wherever tdef.genericparas is initialized after a unit load) correctly instantiated the tconstsym instances instead of ttypesym ones. It might already work correctly, but better safe than sorry. 😅
That seems to work. Here is my test:

========================================

program gc_ppu;
uses
gc_types_unit;

var
a: TShortIntList;
begin
a.dothis('hello’);
GlobalShortIntList.dothis('world');
end.

========================================

unit gc_types_unit;
interface

type
generic TList<T, const U:integer> = record
const ID = U;
public
list: array[0..U-1] of T;
procedure dothis (msg:string);
end;

const
kShortListLength = 128;
type
TShortIntList = specialize TList<Shortint,kShortListLength>;

var
GlobalShortIntList: TShortIntList;

implementation

procedure TList.dothis (msg:string);
begin
writeln('message:',msg, ' high:', high(ID), ' constsize:', sizeof(ID), ' structsize:', sizeof(self));
end;

end.


Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo
Sven Barth via fpc-pascal
2018-11-27 13:11:35 UTC
Permalink
Post by Ryan Joseph
On Nov 27, 2018, at 5:35 PM, Sven Barth via fpc-pascal <
1) kSomeDays is a typed const so I get a load node instead of
tsetconstnode, which I need to need to know in order to make the name
string. See the TODO’s in tgenericparamdef.create.
type
TDay = (Mon, Tue, Wed);
TDays = set of TDay;
const
kSomeDays:TDays = [Mon, Wed];
var
d: specialize TMyRecord_Set<integer,kSomeDays>; // this doesn’t
work
d: specialize TMyRecord_Set<integer,[Mon,Wed]>; // this works
because I get a set node
The difference between typed and untyped constants are available
everywhere else in the language as well, so I have no qualms (at least for
now?) to error out on them.
You mean reject them? The default behavior is to get a
type_e_type_id_expected error. They can be included easily but I don’t know
how to get the set from the load node (I think it needs further parsing).
We can add a new message that says "type id or untyped constant expected".
But if it correctly errors right now, we can leave it be for now.
Post by Ryan Joseph
=== code begin ===
const
kSomeDays = [Mon, Wed];
=== code end ===
Yes, sets work because I get a setnode which I can operate on.
Did you try with the generic and the specialization in different units
and changing only one of the two? Though you might want to check whether
tstoreddef.ppuload (or wherever tdef.genericparas is initialized after a
unit load) correctly instantiated the tconstsym instances instead of
ttypesym ones. It might already work correctly, but better safe than sorry.
😅
========================================
program gc_ppu;
uses
gc_types_unit;
var
a: TShortIntList;
begin
a.dothis('hello’);
GlobalShortIntList.dothis('world');
end.
========================================
unit gc_types_unit;
interface
type
generic TList<T, const U:integer> = record
const ID = U;
public
list: array[0..U-1] of T;
procedure dothis (msg:string);
end;
const
kShortListLength = 128;
type
TShortIntList = specialize TList<Shortint,kShortListLength>;
var
GlobalShortIntList: TShortIntList;
implementation
procedure TList.dothis (msg:string);
begin
writeln('message:',msg, ' high:', high(ID), ' constsize:',
sizeof(ID), ' structsize:', sizeof(self));
end;
end.
You did read the part about the generic and the specialization being
located in two different units? 😉

Regards,
Sven
Ryan Joseph
2018-11-27 13:35:50 UTC
Permalink
We can add a new message that says "type id or untyped constant expected". But if it correctly errors right now, we can leave it be for now.
Fine by me.
You did read the part about the generic and the specialization being located in two different units? 😉
Oops. This still works so I guess it’s ok.

program gc_ppu;
uses
gc_types_unit;

var
a: specialize TList<Integer,10>;
begin
a.dothis('hello');
end.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cg
Sven Barth via fpc-pascal
2018-11-27 16:07:45 UTC
Permalink
Post by Sven Barth via fpc-pascal
Post by Sven Barth via fpc-pascal
You did read the part about the generic and the specialization being
located in two different units? 😉
Oops. This still works so I guess it’s ok.
program gc_ppu;
uses
gc_types_unit;
var
a: specialize TList<Integer,10>;
begin
a.dothis('hello');
end.
Does it also work correctly if you change the constant and then recompile?
The same if the specialization is inside another unit used by the program?

Regards,
Sven
Ryan Joseph
2018-11-28 08:11:27 UTC
Permalink
Does it also work correctly if you change the constant and then recompile? The same if the specialization is inside another unit used by the program?
It appears to work. I’ll make a proper test suite once we have the basics settled.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mai
Sven Barth via fpc-pascal
2018-11-28 22:15:14 UTC
Permalink
Am Di., 27. Nov. 2018, 08:18 hat Ryan Joseph
On Nov 27, 2018, at 4:59 AM, Sven Barth via fpc-pascal
Best check again once you've done the switch to tconstsym for
constants. :)
I made most of the changes you mentioned so please look. Including
the constsyms didn’t break anything and helped to clean up the
code. I would have done that from the start but I wasn’t sure you
wanted me mixing in new types.
Good, thank you. Hopefully I'll find the time this evening to look at
your changes again. :)
Looks better.
The next thing to nuke is the tgenericparamdef. The parameters stored in
tdef.genericparas are the parameter symbols, so there should be no need
for defs. If necessary some of the code in pgenutil (and related
functions) will need to be changed from handling a list of defs to a
list of syms.

Regards,
Sven
Ryan Joseph
2018-11-29 01:46:01 UTC
Permalink
Post by Sven Barth via fpc-pascal
Looks better.
The next thing to nuke is the tgenericparamdef. The parameters stored in tdef.genericparas are the parameter symbols, so there should be no need for defs. If necessary some of the code in pgenutil (and related functions) will need to be changed from handling a list of defs to a list of syms.
I added that extra type so that it would be compatible with genericdeflist. I don’t see how that can be removed. The const sym is pulled out in generate_specialization_phase2 and renamed (which I forget why already).

So the tgenericparamdef is just a vessel to hold the constsym until generate_specialization_phase2? Either way removing it means breaking genericdeflist right?

if typeparam.nodetype <> typen then
begin
{ the typesym from paramdef will be added to the list in generate_specialization_phase2 }
paramdef := tgenericparamdef.create(typeparam.resultdef,typeparam,constprettyname);
genericdeflist.Add(paramdef);
end
else
begin
constprettyname := '';
genericdeflist.Add(typeparam.resultdef);
end;

from generate_specialization_phase2:

for i:=0 to genericdef.genericparas.Count-1 do
begin
srsym:=tsym(genericdef.genericparas[i]);
if not (sp_generic_para in srsym.symoptions) then
internalerror(2013092602);

// note: ryan
{ set the generic param name of the constsym of tgenericparamdef }
typedef := tstoreddef(context.genericdeflist[i]);
if typedef.typ = genericconstdef then
tgenericparamdef(typedef).typesym.realname := srsym.realname;

generictypelist.add(srsym.realname,typedef.typesym);
end;

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listin
Continue reading on narkive:
Loading...