Constants in generics

classic Classic list List threaded Threaded
96 messages Options
12345
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am 14.11.2018 um 03:25 schrieb Ryan Joseph:

> 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

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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> - 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 added by me. See the line:

prettynamepart:=typeparam.resultdef.fullownerhierarchyname(true);

in pgenutil.pas.

> - 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.

> - 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.

> - 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.

>
> 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.

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.

>
> 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,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Mo., 26. Nov. 2018, 10:46 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> - 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 added by me. See the line:

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. 


> - 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. :) 


> - 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? 


> - 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. 


>
> 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.

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. 


>
> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 26, 2018, at 5:16 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> Am Mo., 26. Nov. 2018, 10:46 hat Ryan Joseph <[hidden email]> geschrieben:
>
>
> > On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
> >
> > - 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 added by me. See the line:
>
> 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.

> 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>;


>
>
> > - 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Mo., 26. Nov. 2018, 12:14 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 26, 2018, at 5:16 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> Am Mo., 26. Nov. 2018, 10:46 hat Ryan Joseph <[hidden email]> geschrieben:
>
>
> > On Nov 26, 2018, at 12:09 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
> >
> > - 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 added by me. See the line:
>
> 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.


> 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 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 26, 2018, at 8:18 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Mo., 26. Nov. 2018, 15:47 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 26, 2018, at 8:18 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 27, 2018, at 4:59 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

denisgolovan
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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 27, 2018, at 2:25 PM, denisgolovan <[hidden email]> wrote:
>
> Hi
>
> Sorry for breaking in, but I'd like to know if this functionality supports specializing generic functions with const parameters?
>
> BR,
> Denis

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.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

denisgolovan

> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
Am Di., 27. Nov. 2018, 08:18 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 27, 2018, at 4:59 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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. :) 


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

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 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 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 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
On Tue, 27 Nov 2018 11:35:22 +0100
Sven Barth via fpc-pascal <[hidden email]> wrote:

>[...]
> > 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
In reply to this post by Free Pascal - General mailing list


> On Nov 27, 2018, at 5:35 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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).

>
> 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 ===

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. 😅

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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list
Am Di., 27. Nov. 2018, 12:03 hat Mattias Gaertner via fpc-pascal <[hidden email]> geschrieben:
On Tue, 27 Nov 2018 11:35:22 +0100
Sven Barth via fpc-pascal <[hidden email]> wrote:

>[...]
> > 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 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
Am Di., 27. Nov. 2018, 14:00 hat Ryan Joseph <[hidden email]> geschrieben:


> On Nov 27, 2018, at 5:35 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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. 


>
> 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 ===

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. 😅

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.

You did read the part about the generic and the specialization being located in two different units? 😉

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph


> On Nov 27, 2018, at 8:11 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Free Pascal - General mailing list
Am Di., 27. Nov. 2018, 15:08 hat Ryan Joseph <[hidden email]> geschrieben:
> 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 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
In reply to this post by denisgolovan
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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Constants in generics

Ryan Joseph
In reply to this post by Free Pascal - General mailing list


> On Nov 27, 2018, at 11:07 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> 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  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12345