Merging of generics branch of Sven Barth

------------------------------------------------------------------------
r19731 | svenbarth | 2011-12-03 11:53:02 +0100 (Sa, 03 Dez 2011) | 2 lines

pexpr.pas, post_comp_expr_gendef:
This is not the result you are looking for: The result of "postfixoperators" is only set to true if either "again" was "true" once or the node was changed to an errornode. So using the result for deciding whether we overwrite the def or not is incorrect. So just call "postfixoperators" and process the returned node accordingly.
------------------------------------------------------------------------
r19723 | svenbarth | 2011-12-02 15:28:23 +0100 (Fr, 02 Dez 2011) | 1 line

Added a few more tests. All except tgeneric65.pp (object inside generic record) and tgeneric68.pp (object inside generic object) are successfully compiled.
------------------------------------------------------------------------
r19722 | svenbarth | 2011-12-02 15:12:42 +0100 (Fr, 02 Dez 2011) | 1 line

Fix a remaining artefact from the overloaded symbols approach (just a comment, but nevertheless a change...)
------------------------------------------------------------------------
r19721 | svenbarth | 2011-12-02 15:11:56 +0100 (Fr, 02 Dez 2011) | 3 lines

ptype.pas, read_named_type, expr_type:
* Adjust a comment.
* Add an additional check for the owning symtable of the dummy symbol and the current_structdef just to be on the save side (it's not needed inside specializations)
------------------------------------------------------------------------
r19720 | svenbarth | 2011-12-02 15:11:06 +0100 (Fr, 02 Dez 2011) | 1 line

Fix the test. It's mode Delphi, but does not compile in Delphi, because "TSomeRecord" and "TSomeRecord<T>" are different identifiers.
------------------------------------------------------------------------
r19719 | svenbarth | 2011-12-02 15:10:06 +0100 (Fr, 02 Dez 2011) | 18 lines

Fix introduced regressions.

pgenutil.pas, generate_specialization:
* If we are parsing the result type of a function or operator that belongs to a generic (parse_generic is true) we need to accept also "_LT" and "_GT" as for this the "block_type" is NOT set to one of "bt_type", "bt_var_type", "bt_const_type" and only there "_LSHARPBRACKET" and "_RSHARPBRACKET" are returned by the scanner. This is part of the fix for webtbs\tw18567.pp.
* In non—Delphi modes if we encounter a specialization of the currently parsed class/record (using "specialization"!) the given "tt" will be an errordef (because the def of the generic's symbol was not yet set to "current_structdef"). To solve this we check in this case whether the calculated generic name is equal to that of the "current_structdef" and simply return that as specialized def. This fixes test\tgeneric11.pp.
* When searching for the generic symbol search if the generic belongs to a class or record then we need to search for it in the class or record. This fixes webtbs\tw16090.pp.

ptype.pas
* parse_nested_types: We now return the generic defs for specializations inside generics instead of an undefined def, so we must also parse nested type usages correctly, so that type checks don't fail (undefined defs circumvent them mostly). This fixes webtbs\tw10247.pp.
* single_type: We need to allow _LT as an indication for a Delphi specialization as return types are parsed with block_type not in "bt_type", "bt_var_type", "bt_const_type". This is also a fix a part of the fix for webtbs\tw18567.pp.
* read_named_type, expr_type:
Fixes for test\tgeneric17.pp and test\tgeneric22.pp:
(a) In non-Delphi modes we might encounter usages of the generic dummy symbol inside the generic class belonging to it. This is basically a specialization, but as the reference from the dummy symbol to the "current_structdef" is not yet established (this is done after "read_named_type" in "types_dec" returns) we need to use other ways to check for the valid use of the dummy symbol (basically we check whether the name of the dummy symbol and the name of the current_structdef without the type count match)
(b) For specializations we can check whether the genericdef of the currently parsed structdef (the specialization) is the same as the typedef of the encountered dummy symbol.

pexpr.pas, factor, factor_read_id:
Fixes for test\tgeneric17.pp and test\tgeneric22.pp:
To allow the mentioned fixes in ptype for the same tests to be usable at all we must not return an "identifier not found" error if we encounter such a valid use of a generic dummy symbol.
------------------------------------------------------------------------
r19718 | svenbarth | 2011-12-02 15:08:46 +0100 (Fr, 02 Dez 2011) | 3 lines

generate_specialization:
* Remove some unused variables
* Fix a comment
------------------------------------------------------------------------
r19685 | svenbarth | 2011-11-25 16:25:10 +0100 (Fr, 25 Nov 2011) | 1 line

Incorporate the changes from trunk into "postfixoperators" and "handle_factor_typenode". The latter needed to be extended by a parameter "typeonly" which is "false" in almost all calls except the one inside "factor_read_id" where the "typeonly" parameter of "factor" is used.
------------------------------------------------------------------------
r19676 | svenbarth | 2011-11-24 17:48:47 +0100 (Do, 24 Nov 2011) | 4 lines

Rebase to revision 19673

pexpr.pas: Changes in postfixoperators and the base of handle_factor_typenode not yet incorporated (the code from trunk was simply commented for now)

------------------------------------------------------------------------
r19675 | svenbarth | 2011-11-24 15:42:42 +0100 (Do, 24 Nov 2011) | 11 lines

Somehow the changes regarding tf_methods_specialized weren't commited, thus here they are again:
* symconst.pas: remove tf_methods_specialized
* psub.pas: remove check for/inclusion of tf_methods_specialized as this isn't needed anymore since the generic is specialized in a temporary symtable

pgenutil.pas: remove merge artifacts

pdecl.pas: fix compilation ("s" was duplicate)

pexpr.pas:
* fix calling of generate_specialization
* disable the goto in sub_expr for now; this will be enabled again once right hand sides work as well
------------------------------------------------------------------------
r19674 | svenbarth | 2011-11-24 11:19:57 +0100 (Do, 24 Nov 2011) | 3 lines

Rebase to revision 19078 (directly before the merge of cpstrnew)

The changes regarding pretty names for generics and token buffer endianess were integrated into my changes. Not every call to generate_specialization is fixed though, so compilation will fail.
------------------------------------------------------------------------
r19671 | svenbarth | 2011-11-23 18:25:09 +0100 (Mi, 23 Nov 2011) | 79 lines

Merge branch 'unique-syms'

Conflicts:
	compiler/pdecl.pas
	compiler/pexpr.pas
	compiler/pgenutil.pas
	compiler/ptype.pas

The original log messages as git was a bit forgetting here :( (newest at the top):

?commit 7ef252de8023494ee6d39910e289f9e31658d47b
Author: Sven Barth <pascaldragon@minerva>
Date:   Mon Nov 21 17:13:36 2011 +0100

    Fix the compilation of inline specializations of which the generic is derived from another generic.
    
    pgenutil.pas, generate_specialization:
    * Set the "block_type" to "bt_type" when parsing the type parameters, so that the nodes are returned as "ttypenode" instead of e.g. "tloadvmtaddrnode" in case of classes outside of type sections.
    * Set the "block_type" to "bt_type" before calling "read_name_type", so that no unexpected sideeffects happen, because types like classes normally only are declared inside type sections (e.g. for the case a generic class is derived from another generic class a classrefdef for the specialized parent class will be created inside the derived specialized class if the block type is not a type one).

commit 1041a8f7a3a41f4fdf2975ce40055c698281ce71
Author: Sven Barth <pascaldragon@minerva>
Date:   Fri Nov 18 19:03:50 2011 +0100

    Improve inline specializations a bit, so now expressions like "TSomeGeneric<TSomeType>.SomeClassProc OP SomeNonGeneric" is possible. Using another class function of a generic as the right side is not yet working (that still needs some thinking).
    
    To achive this the generalization code must basically continue directly after the "factor" call, so that the operator and the right side are correctly parsed when walking up the call stack. This is done by jumping from the end of the specialization code in the "<"-case to the start of "sub_expr". The freshly generated node (in the above example a callnode) will be passed down the callstack through a new parameter "factornode". If that is set (currently only in the case of a specialization on the left side) "factor" won't be called and the right side will be parsed with the "factornode" as the left side. If it is not set (which is the case for all other calls to "sub_expr" in the unit) then the usual call to "factor" will be done and the result will be used as the left side.

commit a01ccd265f8d6cc5a2f3e88e23afbcd3d5960afb
Author: Sven Barth <pascaldragon@minerva>
Date:   Fri Nov 18 18:37:04 2011 +0100

    Fix compilation of ppudump.
    
    symconst.pas:
    * Remove sto_has_generic, which was the last remainer of my "overloaded type symbols" approach.
    * Remove df_methods_specialized, as it isn't needed anymore with the recent "temporary symtable" solution.
    
    psub.pas, specialize_objectdefs, process_abstractrecorddef:
    Remove the checks for/inclusion of df_methods_specialized.
    
    utils/ppudump.pp:
    Add "sp_generic_dummy" to the symbol options.

commit d16deac060e65d4b53e8fe9c27fe7e1f6d00a416
Author: Sven Barth <pascaldragon@minerva>
Date:   Wed Nov 16 16:34:51 2011 +0100

    Fix compilation of "gset.pp" from fcl-stl.
    
    nld.pas:
    Extend ttypenode by a reference to the type symbol. Normally this is simply the typesym of the given def, but for specializations in type sections of generics this is not the case, because generate_specialization will return a reference to the generic definition and not the new one (thus the symbol will be wrong).
    
    ppu.pas:
    Increase PPU version because of the extension of ttypenode.
    
    pexpr.pas:
    * handle_factor_typenode: Extend the function by a "sym" parameter which will normally be "nil". In that case it is set to the def's typesym. The "typesym" field of the created type node is then set to this sym.
    * For now pass nearly always "nil" for the above mentioned sym except inside factor_read_id when we've encountered a typesym.
    
    ptype.pas, read_named_type, expr_type:
    Exchange the "is_owned_by" check with a "sym_is_owned_by" check so that we can correctly detect that we are using a specialized type declaration inside a generic (once nested generic are allowed this condition needs to be checked).

commit 23668d2fc9070afc26b4288ed0db9a8eaf6f40e6
Author: Sven Barth <pascaldragon@minerva>
Date:   Wed Nov 16 07:51:12 2011 +0100

    psub.pas:
    * tcgprocinfo.parse_body: Methods of generic classes need to set "parse_generic" as well, so that variables for "stacked generics" (generic array => generic record) inside the method body are handled correctly.
    * specialize_objectdefs: Don't try to generate method bodies for abstract methods.
    
    pdecvar.pas, read_property_dec:
    Allow specializations for the return types of properties (should they be allowed for index types as well?).
    
    symtable.pas:
    Add a new class "tspecializesymtable" which is basically a globalsymtable but is always assuming to be the current unit. This symtable is used in "generate_specializations" (see below) and is needed to allow visibilty checks for "private", etc. to succeed.
    
    pgenutil.pas, generate_specializations:
    Instead of hackily pushing a symtable that may contain conflicting symbols onto the symtable stack for the specialization, a temporary global symtable using the above mentioned "tspecializesymtable" is created and pushed. After the specialization is done all symbols and defs that were added to the temporary symtable are moved to their final symtable (either the global- or localsymtable of the unit, depending on the current position of compilation). This way symbols are correctly added to a top level symtable, but without potential side effects like resolving the wrong symbol.
------------------------------------------------------------------------
r19435 | svenbarth | 2011-10-09 18:16:19 +0200 (So, 09 Okt 2011) | 1 line

Set "current_structdef", "current_genericdef" and "current_specializedef" to values that were valid during the declaration of the generic  when specializing it ("current_genericdef" and "current_specializedef" might need to still be corrected though)
------------------------------------------------------------------------
r19434 | svenbarth | 2011-10-09 18:15:26 +0200 (So, 09 Okt 2011) | 2 lines

Arrays and procvars inside a generic declaration are not declared as generic/specialization anymore (this partly reverts a previous commit). This reduces the problematic cases in the check whether a found def was specialized inside the class (the changed check in read_named_type.expr_type).
It's still not an ideal solution as the usage of generic classes/records (without specialization!) that are declared inside the current parsed class/record will compromise this check again.
------------------------------------------------------------------------
r19433 | svenbarth | 2011-10-09 18:14:33 +0200 (So, 09 Okt 2011) | 1 line

Extend the test with a usage of "TTestInteger" and correct the comments a bit.
------------------------------------------------------------------------
r19432 | svenbarth | 2011-10-09 18:13:30 +0200 (So, 09 Okt 2011) | 9 lines

We need to flag specializations of record-/objectdef once we have generated their methods otherwise an interesting situation might occur:
The classes in "fgl.pas" implement an enumerator in the generic class "TFPGListEnumerator" and "specialize" that inside themselves. If we now specialize one of the generic classes (e.g. "TFPGList") the "TFPGListEnumerator" is really specialized as well. That means a def is added to the global symtable (the local one in case of a program or library file). If we now use the enumerator class in the same file (e.g. by using a "for ... in", which has a temporary variable of that type) then the methods of the enumerator are specialized again (the def itself is not). To avoid this (and time consuming searches for existing method specializations) we flag the specialized def as "done" once we're finished.

symconst.pas
  * add a new flag "df_methods_specialized" to the "tdefoption" enumeration

psub.pas, process_abstractrecorddef
  * check the def for the "df_methods_specialized" flag and continue only if that is not set
  * set the "df_methods_specialized" flag before leaving the function
------------------------------------------------------------------------
r19431 | svenbarth | 2011-10-09 18:12:25 +0200 (So, 09 Okt 2011) | 1 line

This check was commited by accident; it was a remain from an experimental solution to the "fix compilation of fgl"-problem.
------------------------------------------------------------------------
r19430 | svenbarth | 2011-10-09 18:11:31 +0200 (So, 09 Okt 2011) | 19 lines

Fix compilation of unit "fgl.pp" and of test "tests/test/tgeneric29.pp".

symtable.pas:
  * reduce the "childdef" parameter of "is_owned_by" from "tabstractrecorddef" to "tdef", so that more primitive defs can be checked as well
  * add a new function "sym_is_owned_by" which is similar to "is_owned_by", but takes a symbol and a symtable as parameter; the owner chain of the symtable is checked until a non-object- and non-record-symtable is reached

ptype.pas:
  * extend "id_type", so that the symbol and the symtable that belongs to the returned def is returned as well
  * this is needed to check inside "single_type" whether a def that is a generic was specialized inside another generic, because in that case the genericdef is returned by "generate_specialization" and not a new specialized def, but the corresponding type symbol (which is different from "hdef.typesym") belongs to the class itself; I need to admit that this solution isn't very clean and one could try to circumvent some of the checks, so I need to find a better detection for such a case (concrete example: the enumerator specialization inside the classes of "fgl.pas")
  * in "read_named_type.expr_type" the check for "df_generic" is extended analogous to the previous change, but instead of relying on the symbol it uses the def. This is needed so that types like method pointers that are defined inside the current generic are not disallowed as they contain the "df_generic" flag as well; like the previous change this change isn't clean either and maybe it's better to remove the inclusion of the "df_generic" flag from everything except records and "objects" inside records/"objects" again. Such a solution will "only" reduce the problem to records and "objects" though...

pgenutil.pas:
  * only add a new undefined def if we're not parsing the parent class or interfaces ("parse_class_parent" is true), otherwise the InternalError regarding the "equal count of defs" will trigger
  * there are now two cases where we need to return a generic def instead of a undefined one when we're parsing a generic:
    a) we have the previously mentioned case that "parse_class_parent" is true
    b) an undefined def was added, but we need to return a generic def, so that checks can be passed
  * use the correct variable when building the generic name, otherwise we get errors like "identifier '$1' not found"
  * don't push the symtable if we're currently parsing the list of interfaces or the parent class, because then e.g. a generic interface will be included in the symtable of the implementing class which isn't what we want; the current solution is not clean though, so this needs to be investigated more
  * Note: In the current state of "generate_specialization" the function could be simplyfied a bit more; this will be done when the implementation is satisfactory enough
------------------------------------------------------------------------
r19429 | svenbarth | 2011-10-09 18:10:28 +0200 (So, 09 Okt 2011) | 20 lines

Allow generics to be overloaded by variables.

* symconst.pas:
   add an entry for the generic dummy symbol to the symbol options enumeration
* pgenutil.pas:
   - extend "generate_specialization" by the possibility to pass a symbol name instead of a def
   - if "symname" is given that is used; otherwise "genericdef" or "tt" is used
* pexpr.pas:
   - in case of "<" we are trying to receive a generic dummy symbol from the left node (new function "getgenericsym")
   - it's name is then passed to "generate_specialization" which in turn fills genericdef
   - adjust call to "generate_specialization"
* pdecl.pas:
   - we can now check for "sp_generic_dummy instead of "not sp_generic_para" to check whether we've found the dummy symbol of a previous generic declaration
   - if a new dummy symbol is created we need to include "sp_generic_dummy"
   - if we've found a non-generic symbol with the same name we need to include the "sp_generic_dummy" flag as well
* symtable.pas
   - add a new function "searchsym_with_symoption" that more or less works the same as "searchsym", but only returns successfully if the found symbol contains the given flag
   - "searchsym_with_symoption" and "searchsym" are based on the same function "maybe_searchsym_with_symoption" which is the extended implementation of "searchsym" (note: object symtables are not yet searched if a symoption is to be looked for)
   - add a function "handle_generic_dummysym" which can be used to hide the undefineddef symbol in a symtable
   - correctly handle generic dummy symbols in case of variables in "tstaticsymtable.checkduplicate"
------------------------------------------------------------------------
r19428 | svenbarth | 2011-10-09 18:09:09 +0200 (So, 09 Okt 2011) | 3 lines

types_dec:
   - fix a comment
   - the created undefineddef must not be freed, as the count of the list the def is contained in, is used to find other defs again
------------------------------------------------------------------------
r19427 | svenbarth | 2011-10-09 18:08:15 +0200 (So, 09 Okt 2011) | 14 lines

Corrected the handling of hint directives.

pgenutils.pas/generate_specialization:
- parse hint directives of the generic if they are recorded
- output hint messages of the generic after the ">" is successfully parsed

pexpr.pas:
- factor: don't display hints of a potential generic type if the next token is a "<"
- sub_expr:
 * added two inline methods which
    a) checks whether a node is a typenode or a loadvmtaddrnode with a typenode
    b) returns the typedef of such a node
 * check hint directives for the first parsed type argument of a specialization
 * in the case of parsing a non-generic type the hints of the left and right node of the resulting "<" node need to be checked (the right ones only if another "<" is following)
------------------------------------------------------------------------
r19426 | svenbarth | 2011-10-09 18:07:22 +0200 (So, 09 Okt 2011) | 5 lines

generate_specialization needs to return the correct generic def if the parent classes are parsed, so that that the usage of generic interfaces is allowed.

This fixes the compilation of test tests\test\tgeneric29.pp and the reminder in pdecobj.pas is not needed anymore.

Note: Perhaps this behavior should be enabled in general if "parse_generic" is true (and not only if parse_parent_class if true as well).
------------------------------------------------------------------------
r19425 | svenbarth | 2011-10-09 18:06:31 +0200 (So, 09 Okt 2011) | 1 line

Added two reminders for me
------------------------------------------------------------------------
r19424 | svenbarth | 2011-10-09 18:05:31 +0200 (So, 09 Okt 2011) | 32 lines

Switching from overloaded type symbol to unique symbol per generic.

Reasons for the "unique symbol" approach:
- no special search operations for cross unit search needed (which is supported by Delphi) => less performance impact
- no special care needed to really find the correct generic => less increase of parser complexity

Currently all generic tests except tgeneric29.pp compile and inline specializations work as well.

The changes in detail:
* pdecl.pas/types_dec:
- The variables used to hold the final name of the symbol are now prefixed with "gen". In case of non-generics the prefixed ones are equal to the non-prefixed ones (e.g. orgtypename=genorgtypename). In case of a generic symbol the "gen"-variants contain the type parameter count suffix (e.g. '$1' in case of 'TTest<T>') as well.
- The unmodified pattern is used to insert and detect a dummy symbol with that name, so that type declarations and - more important - inline specializations can find that symbol.
- In non-Delphi modes this symbol is also used to detect whether we have a type redefinition which is not allowed currently; its typedef points to the generic def.
- In mode Delphi the def of that dummy symbol (which contains an undefineddef) is modified when a corresponding non-generic type is parsed, so that it contains the def of the real type.

* pdecsub.pas/parse_proc_head
- consume_generic_type_parameter now only parses the type parameters and picks the generic with the correct amount of parameters. The verification of the order and names of the parameters needs to be added again.
- it also does not use "def" anymore, but it sets "srsym"
- in parse_proc_head the symbol (srsym) is only searched if the symbol isn't assigned already; in case of a generic in mode FPC it will find the dummy symbol that points to the generic def

* pexpr.pas
- in factor_read_id there are three cases to handle:
 + the symbol is not assigned => error
 + a possible generic symbol (either an undefined def or the non-generic variant) => no error and no hints
 + a non-generic symbol => hints
 Point 1 is handled correctly, point 2 and 3 aren't currently and also they might be needed to be moved somewhere else
- sub_expr:
 + a node can be a tloadvmtaddrnode as well if the non-generic variant of a generic symbol is a class
 + we can only check afterwards whether the specialization was successful

* pgenutil.pas/generate_specialization
using the count of the parsed types the correct symbol can be found easily
------------------------------------------------------------------------
r18005 | svenbarth | 2011-07-16 18:19:33 +0200 (Sa, 16 Jul 2011) | 1 line

Rebase to revision 18000
------------------------------------------------------------------------
r18004 | svenbarth | 2011-07-16 16:13:56 +0200 (Sa, 16 Jul 2011) | 1 line

pexpr.pas, sub_expr: Added support for "as" and "is" operators if the right hand side is an inline specialization (currently detected by the next token being a "<"). This could potentially introduce some problems if the right hand side isn't a specialization but a "<" comparison together with some overloaded operators (I still need to find a case for such a problem)...
------------------------------------------------------------------------
r18003 | svenbarth | 2011-07-16 16:13:11 +0200 (Sa, 16 Jul 2011) | 5 lines

factor_read_id:
don't accept the generic dummy symbol if the next token isn't a "<"

sub_expr:
generate an error if we had a normal "<" comparison containing the dummy symbol on the left side instead of a specialization
------------------------------------------------------------------------
r18002 | svenbarth | 2011-07-16 16:12:25 +0200 (Sa, 16 Jul 2011) | 17 lines

Implement support for nested non-generic types inside generic types. This is mostly for records, classes and objects ("structures") as those didn't work at all, but the others (arrays, procvars) weren't done cleanly either.

pobjdec.pas (object_dec) / ptype.pas (record_dec, array_dec, procvar_dec):
- enable "parse_generic" if a nested type is parsed and we're already inside a generic (this prevents code to be generated for the nested type's methods)
- set the "df_specialization" flag so that the code for generating the methods (and thus resolving the forwards declarations) is called for this symbol

pexpr.pas:
add "post_comp_expr_gendef" which basically calls "handle_factor_typenode" and "postfixoperators" as those aren't exported from the unit themselves

ptype.pas, read_named_type.expr_type:
- use "post_comp_expr_gendef" to parse the use of nested types (e.g. "var t: TTest<T>.TTestSub")

psub.pas, specialize_objectdefs:
implement the generation of the method bodies for nested structures (resolves the forward declarations)

pdecl.pas, types_dec:
when we encounter a nested structure inside a specialization of a structure, we need to find the corresponding generic definition so that the generic can be correctly parsed later on.
------------------------------------------------------------------------
r18001 | svenbarth | 2011-07-16 16:11:31 +0200 (Sa, 16 Jul 2011) | 1 line

Finally fixed the handling of hint directives and added a comment explaining the situation in the context of generics.
------------------------------------------------------------------------
r17999 | svenbarth | 2011-07-16 16:10:34 +0200 (Sa, 16 Jul 2011) | 2 lines

* Reordered the conditions for the inline spezialization as the "isgeneric" boolean is not needed
* As "handle_factor_typenode" is now available the classrefdef wrapper is not needed anymore
------------------------------------------------------------------------
r17998 | svenbarth | 2011-07-16 16:09:38 +0200 (Sa, 16 Jul 2011) | 1 line

Removed the remaining traces of the type overloads and increased PPU version to differ from trunk.
------------------------------------------------------------------------
r17997 | svenbarth | 2011-07-16 16:08:49 +0200 (Sa, 16 Jul 2011) | 1 line

Integrated the changes from trunks's postfixoperators into my own and removed the local version again.
------------------------------------------------------------------------
r17996 | svenbarth | 2011-07-16 16:08:03 +0200 (Sa, 16 Jul 2011) | 5 lines

generate_specialization needs to return the correct generic def if the parent classes are parsed, so that that the usage of generic interfaces is allowed.

This fixes the compilation of test tests\test\tgeneric29.pp and the reminder in pdecobj.pas is not needed anymore.

Note: Perhaps this behavior should be enabled in general if "parse_generic" is true (and not only if parse_parent_class if true as well).
------------------------------------------------------------------------
r17995 | svenbarth | 2011-07-16 16:07:20 +0200 (Sa, 16 Jul 2011) | 1 line

Added two reminders for me
------------------------------------------------------------------------
r17547 | svenbarth | 2011-05-23 22:52:51 +0200 (Mo, 23 Mai 2011) | 1 line

Rebase to revision 17533
------------------------------------------------------------------------
r17542 | svenbarth | 2011-05-23 21:47:09 +0200 (Mo, 23 Mai 2011) | 4 lines

Added some tests for:
- multiple symbols with a similar name
- hint directives
- inline specializations
------------------------------------------------------------------------
r17541 | svenbarth | 2011-05-23 21:19:12 +0200 (Mo, 23 Mai 2011) | 3 lines

Allow typecasts to inline specializations as well.

For this the code which handles this inside factor_read_id had to be moved to local unit scope and is named handle_factor_typenode.
------------------------------------------------------------------------
r17540 | svenbarth | 2011-05-23 21:17:53 +0200 (Mo, 23 Mai 2011) | 1 line

Remove the (now) non-functional check for inline specialization.
------------------------------------------------------------------------
r17539 | svenbarth | 2011-05-23 21:16:39 +0200 (Mo, 23 Mai 2011) | 14 lines

Corrected the handling of hint directives.

pgenutils.pas/generate_specialization:
- parse hint directives of the generic if they are recorded
- output hint messages of the generic after the ">" is successfully parsed

pexpr.pas:
- factor: don't display hints of a potential generic type if the next token is a "<"
- sub_expr:
 * added two inline methods which
    a) checks whether a node is a typenode or a loadvmtaddrnode with a typenode
    b) returns the typedef of such a node
 * check hint directives for the first parsed type argument of a specialization
 * in the case of parsing a non-generic type the hints of the left and right node of the resulting "<" node need to be checked (the right ones only if another "<" is following)
------------------------------------------------------------------------
r17538 | svenbarth | 2011-05-23 21:15:36 +0200 (Mo, 23 Mai 2011) | 5 lines

generate_specialization needs to return the correct generic def if the parent classes are parsed, so that that the usage of generic interfaces is allowed.

This fixes the compilation of test tests\test\tgeneric29.pp and the reminder in pdecobj.pas is not needed anymore.

Note: Perhaps this behavior should be enabled in general if "parse_generic" is true (and not only if parse_parent_class if true as well).
------------------------------------------------------------------------
r17537 | svenbarth | 2011-05-23 21:14:33 +0200 (Mo, 23 Mai 2011) | 1 line

Added two reminders for me
------------------------------------------------------------------------
r17536 | svenbarth | 2011-05-23 21:13:51 +0200 (Mo, 23 Mai 2011) | 1 line

This test does not need to be run
------------------------------------------------------------------------
r17535 | svenbarth | 2011-05-23 21:12:50 +0200 (Mo, 23 Mai 2011) | 32 lines

Switching from overloaded type symbol to unique symbol per generic.

Reasons for the "unique symbol" approach:
- no special search operations for cross unit search needed (which is supported by Delphi) => less performance impact
- no special care needed to really find the correct generic => less increase of parser complexity

Currently all generic tests except tgeneric29.pp compile and inline specializations work as well.

The changes in detail:
* pdecl.pas/types_dec:
- The variables used to hold the final name of the symbol are now prefixed with "gen". In case of non-generics the prefixed ones are equal to the non-prefixed ones (e.g. orgtypename=genorgtypename). In case of a generic symbol the "gen"-variants contain the type parameter count suffix (e.g. '$1' in case of 'TTest<T>') as well.
- The unmodified pattern is used to insert and detect a dummy symbol with that name, so that type declarations and - more important - inline specializations can find that symbol.
- In non-Delphi modes this symbol is also used to detect whether we have a type redefinition which is not allowed currently; its typedef points to the generic def.
- In mode Delphi the def of that dummy symbol (which contains an undefineddef) is modified when a corresponding non-generic type is parsed, so that it contains the def of the real type.

* pdecsub.pas/parse_proc_head
- consume_generic_type_parameter now only parses the type parameters and picks the generic with the correct amount of parameters. The verification of the order and names of the parameters needs to be added again.
- it also does not use "def" anymore, but it sets "srsym"
- in parse_proc_head the symbol (srsym) is only searched if the symbol isn't assigned already; in case of a generic in mode FPC it will find the dummy symbol that points to the generic def

* pexpr.pas
- in factor_read_id there are three cases to handle:
 + the symbol is not assigned => error
 + a possible generic symbol (either an undefined def or the non-generic variant) => no error and no hints
 + a non-generic symbol => hints
 Point 1 is handled correctly, point 2 and 3 aren't currently and also they might be needed to be moved somewhere else
- sub_expr:
 + a node can be a tloadvmtaddrnode as well if the non-generic variant of a generic symbol is a class
 + we can only check afterwards whether the specialization was successful

* pgenutil.pas/generate_specialization
using the count of the parsed types the correct symbol can be found easily
------------------------------------------------------------------------
r17534 | svenbarth | 2011-05-23 21:11:50 +0200 (Mo, 23 Mai 2011) | 1 line

This fixes an access violation when compiling tests\test\tgeneric30.pp
------------------------------------------------------------------------
r17405 | svenbarth | 2011-05-04 12:43:13 +0200 (Mi, 04 Mai 2011) | 11 lines

*pexpr.pas:
- moved "postfixoperators" from local declaration of "factor" to implementation declarations of the unit, so it can be used in "sub_expr"
- for this a parameter "getaddr:boolean" needed to be added, because it used the parameter that was defined by "factor"
=> adjustments inside "factor" for calls to "postfixoperators"
- extended the "_LT" ("<") case of "sub_expr" with handling of inline generic specializations. If a potential generic is detected (Delphi mode, left and right node are type nodes, next token is ">" or ",") it is tried to parse the generic declaration and generate a specialization. If this succeeds, potential postfix operators are parsed and a node <> caddnode is returned.

*pgenutil.pas:
"generate_specialization" was extended so that the first type identifer can already have been parsed (which is the case in inline specializations)

*ptype.pas
adjustments because of the extension of "generate_specialization"
------------------------------------------------------------------------
r17404 | svenbarth | 2011-05-04 12:40:07 +0200 (Mi, 04 Mai 2011) | 1 line

Moved "parse_generic_parameters" and "insert_generic_parameter_types" from "pdecl.pas" to "pgenutil.pas"
------------------------------------------------------------------------
r17403 | svenbarth | 2011-05-04 12:35:23 +0200 (Mi, 04 Mai 2011) | 1 line

Moved "generate_specialization" from "ptype.pas" to "pgenutil.pas"
------------------------------------------------------------------------
r17397 | svenbarth | 2011-05-02 22:22:41 +0200 (Mo, 02 Mai 2011) | 3 lines

Added a file which will hold the various functions related to generic parsing. The header copyright notice and the info comment might not yet be final.

Note: I've added this mostly empty, because I used SVN instead of GIT SVN, as I don't know whether it would handle the properties for this new file correctly.
------------------------------------------------------------------------
r17396 | svenbarth | 2011-05-02 21:47:53 +0200 (Mo, 02 Mai 2011) | 6 lines

consume_generic_type_parameter now parses the available parameters first before deciding which generic def is the correct one (this is stored in the "def" variable of the parent frame). The count of the parameters and the order is checked.

parse_proc_head itself uses the correct def (the def found by consume_generic_type_parameter in mode Delphi and the first generic def of the symbol in the other modes) which is available in the "def" variable.

Status of generics:
Non-Delphi generics now work as before and declarations of Delphi generics work as well. Inline specialisations don't work currently.
------------------------------------------------------------------------
r17395 | svenbarth | 2011-05-02 21:46:41 +0200 (Mo, 02 Mai 2011) | 1 line

Added two TODOs for places that I'll need to adjust for inline specializations.
------------------------------------------------------------------------
r17394 | svenbarth | 2011-05-02 21:45:34 +0200 (Mo, 02 Mai 2011) | 12 lines

* ptype.pas:
"generate_specialization" now parses the generic parameters without verifying them. The verification is done after their count is known and thus the correct generic def can be determined.

Note: It does currently only work with the first found symbol, the extended lookup needs to be implemented yet (including the unit name works though)

* pexpr.pas:
In "factor_read_id" an "identifer not found" error is generated if the undefined non-generic def is used (e.g. as a type for a variable)

Note: This check needs to be adjusted for the case "typeonly=false".

Status of generics:
Specializations can now be parsed, but declarations containing methods are still broken, because the correct def is not yet resolved (not even talking about inline specializations yet ;) )
------------------------------------------------------------------------
r17393 | svenbarth | 2011-05-02 21:44:14 +0200 (Mo, 02 Mai 2011) | 9 lines

*type symbol overloads are only allowed in mode Delphi
*a check for overloads with the same count of arguments is not yet in place
*in non-Delphi modes overloads need to be checked for non-generics as well, e.g. "TTest<T>" is already defined and now a "TTest" is declared
*when a generic is encountered and the symbol does not yet exist, a new symbol with an undefineddef is added and the generic def is added as an overload; if the symbol already exists, the generic is just added
*if a non-generic is parsed and the symbol is already defined (but the typedef is still an undefineddef) then the typedef is updated
*the symtable tree (up to the unit symtable (global or local)) gets the "sto_has_generic" flag which will be used when searching generics with the same name, but different parameter counts in different units

State of generics:
broken, because the generic defs are not yet searched/found
------------------------------------------------------------------------
r17392 | svenbarth | 2011-05-02 21:42:40 +0200 (Mo, 02 Mai 2011) | 1 line

Extend ttypesym by a list that will contain all generic "overloads" of this symbol.
------------------------------------------------------------------------
r17341 | svenbarth | 2011-04-18 23:15:52 +0200 (Mo, 18 Apr 2011) | 1 line

Rebase to revision 17340
------------------------------------------------------------------------
r17316 | svenbarth | 2011-04-14 09:11:07 +0200 (Do, 14 Apr 2011) | 1 line

Created a branch for working on various aspects of generics
------------------------------------------------------------------------

git-svn-id: trunk@19763 -
This commit is contained in:
florian 2011-12-06 21:29:42 +00:00
commit 20b1e3af78
52 changed files with 2898 additions and 1217 deletions

36
.gitattributes vendored
View File

@ -361,6 +361,7 @@ compiler/pdecsub.pas svneol=native#text/plain
compiler/pdecvar.pas svneol=native#text/plain
compiler/pexports.pas svneol=native#text/plain
compiler/pexpr.pas svneol=native#text/plain
compiler/pgenutil.pas svneol=native#text/pascal
compiler/pinline.pas svneol=native#text/plain
compiler/pmodules.pas svneol=native#text/plain
compiler/powerpc/agppcmpw.pas svneol=native#text/plain
@ -10103,9 +10104,42 @@ tests/test/tgeneric32.pp svneol=native#text/pascal
tests/test/tgeneric33.pp svneol=native#text/pascal
tests/test/tgeneric34.pp svneol=native#text/pascal
tests/test/tgeneric35.pp svneol=native#text/pascal
tests/test/tgeneric36.pp svneol=native#text/pascal
tests/test/tgeneric37.pp svneol=native#text/pascal
tests/test/tgeneric38.pp svneol=native#text/pascal
tests/test/tgeneric39.pp svneol=native#text/pascal
tests/test/tgeneric4.pp svneol=native#text/plain
tests/test/tgeneric40.pp svneol=native#text/pascal
tests/test/tgeneric41.pp svneol=native#text/pascal
tests/test/tgeneric42.pp svneol=native#text/pascal
tests/test/tgeneric43.pp svneol=native#text/pascal
tests/test/tgeneric44.pp svneol=native#text/pascal
tests/test/tgeneric45.pp svneol=native#text/pascal
tests/test/tgeneric46.pp svneol=native#text/pascal
tests/test/tgeneric47.pp svneol=native#text/pascal
tests/test/tgeneric48.pp svneol=native#text/pascal
tests/test/tgeneric49.pp svneol=native#text/pascal
tests/test/tgeneric5.pp svneol=native#text/plain
tests/test/tgeneric50.pp svneol=native#text/pascal
tests/test/tgeneric51.pp svneol=native#text/pascal
tests/test/tgeneric52.pp svneol=native#text/pascal
tests/test/tgeneric53.pp svneol=native#text/pascal
tests/test/tgeneric54.pp svneol=native#text/pascal
tests/test/tgeneric55.pp svneol=native#text/pascal
tests/test/tgeneric56.pp svneol=native#text/pascal
tests/test/tgeneric57.pp svneol=native#text/pascal
tests/test/tgeneric58.pp svneol=native#text/pascal
tests/test/tgeneric59.pp svneol=native#text/pascal
tests/test/tgeneric6.pp svneol=native#text/plain
tests/test/tgeneric60.pp svneol=native#text/pascal
tests/test/tgeneric61.pp svneol=native#text/pascal
tests/test/tgeneric62.pp svneol=native#text/pascal
tests/test/tgeneric63.pp svneol=native#text/pascal
tests/test/tgeneric64.pp svneol=native#text/pascal
tests/test/tgeneric65.pp svneol=native#text/pascal
tests/test/tgeneric66.pp svneol=native#text/pascal
tests/test/tgeneric67.pp svneol=native#text/pascal
tests/test/tgeneric68.pp svneol=native#text/pascal
tests/test/tgeneric7.pp svneol=native#text/plain
tests/test/tgeneric8.pp svneol=native#text/plain
tests/test/tgeneric9.pp svneol=native#text/plain
@ -10602,6 +10636,8 @@ tests/test/ugeneric10.pp svneol=native#text/plain
tests/test/ugeneric14.pp svneol=native#text/plain
tests/test/ugeneric3.pp svneol=native#text/plain
tests/test/ugeneric4.pp svneol=native#text/plain
tests/test/ugeneric59a.pp svneol=native#text/pascal
tests/test/ugeneric59b.pp svneol=native#text/pascal
tests/test/ugeneric7.pp svneol=native#text/plain
tests/test/uhintdir.pp svneol=native#text/plain
tests/test/uhlp3.pp svneol=native#text/pascal

View File

@ -115,6 +115,8 @@ interface
helperallowed : boolean;
typedef : tdef;
typedefderef : tderef;
typesym : tsym;
typesymderef : tderef;
constructor create(def:tdef);virtual;
constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override;
procedure ppuwrite(ppufile:tcompilerppufile);override;
@ -1066,6 +1068,7 @@ implementation
begin
inherited create(typen);
typedef:=def;
typesym:=def.typesym;
allowed:=false;
helperallowed:=false;
end;
@ -1075,6 +1078,7 @@ implementation
begin
inherited ppuload(t,ppufile);
ppufile.getderef(typedefderef);
ppufile.getderef(typesymderef);
allowed:=boolean(ppufile.getbyte);
helperallowed:=boolean(ppufile.getbyte);
end;
@ -1084,6 +1088,7 @@ implementation
begin
inherited ppuwrite(ppufile);
ppufile.putderef(typedefderef);
ppufile.putderef(typesymderef);
ppufile.putbyte(byte(allowed));
ppufile.putbyte(byte(helperallowed));
end;
@ -1093,6 +1098,7 @@ implementation
begin
inherited buildderefimpl;
typedefderef.build(typedef);
typesymderef.build(typesym);
end;
@ -1100,6 +1106,7 @@ implementation
begin
inherited derefimpl;
typedef:=tdef(typedefderef.resolve);
typesym:=tsym(typesymderef.resolve);
end;

View File

@ -47,10 +47,6 @@ interface
procedure property_dec(is_classpropery: boolean);
procedure resourcestring_dec;
{ generics support }
function parse_generic_parameters:TFPObjectList;
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
implementation
uses
@ -70,7 +66,7 @@ implementation
ncgutil,
{ parser }
scanner,
pbase,pexpr,ptype,ptconst,pdecsub,pdecvar,pdecobj,
pbase,pexpr,ptype,ptconst,pdecsub,pdecvar,pdecobj,pgenutil,
{ cpu-information }
cpuinfo
;
@ -336,51 +332,6 @@ implementation
consume(_SEMICOLON);
end;
function parse_generic_parameters:TFPObjectList;
var
generictype : ttypesym;
begin
result:=TFPObjectList.Create(false);
repeat
if token=_ID then
begin
generictype:=ttypesym.create(orgpattern,cundefinedtype);
include(generictype.symoptions,sp_generic_para);
result.add(generictype);
end;
consume(_ID);
until not try_to_consume(_COMMA) ;
end;
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
var
i: longint;
generictype: ttypesym;
st: tsymtable;
begin
def.genericdef:=genericdef;
if not assigned(genericlist) then
exit;
case def.typ of
recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
arraydef: st:=tarraydef(def).symtable;
procvardef,procdef: st:=tabstractprocdef(def).parast;
else
internalerror(201101020);
end;
for i:=0 to genericlist.count-1 do
begin
generictype:=ttypesym(genericlist[i]);
if generictype.typedef.typ=undefineddef then
include(def.defoptions,df_generic)
else
include(def.defoptions,df_specialization);
st.insert(generictype);
end;
end;
procedure types_dec(in_structure: boolean);
procedure finalize_objc_class_or_protocol_external_status(od: tobjectdef);
@ -394,7 +345,8 @@ implementation
end;
var
typename,orgtypename : TIDString;
typename,orgtypename,
gentypename,genorgtypename : TIDString;
newtype : ttypesym;
sym : tsym;
hdef : tdef;
@ -409,6 +361,8 @@ implementation
generictokenbuf : tdynamicarray;
vmtbuilder : TVMTBuilder;
p:tnode;
gendef : tstoreddef;
s : shortstring;
begin
old_block_type:=block_type;
{ save unit container of forward declarations -
@ -442,8 +396,18 @@ implementation
consume(_LSHARPBRACKET);
generictypelist:=parse_generic_parameters;
consume(_RSHARPBRACKET);
str(generictypelist.Count,s);
gentypename:=typename+'$'+s;
genorgtypename:=orgtypename+'$'+s;
end
else
begin
gentypename:=typename;
genorgtypename:=orgtypename;
end;
consume(_EQ);
{ support 'ttype=type word' syntax }
@ -465,12 +429,18 @@ implementation
{ is the type already defined? -- must be in the current symtable,
not in a nested symtable or one higher up the stack -> don't
use searchsym & frinds! }
sym:=tsym(symtablestack.top.find(typename));
sym:=tsym(symtablestack.top.find(gentypename));
newtype:=nil;
{ found a symbol with this name? }
if assigned(sym) then
begin
if (sym.typ=typesym) then
if (sym.typ=typesym) and
{ this should not be a symbol that was created by a generic
that was declared earlier }
not (
(ttypesym(sym).typedef.typ=undefineddef) and
(sp_generic_dummy in sym.symoptions)
) then
begin
if ((token=_CLASS) or
(token=_INTERFACE) or
@ -502,12 +472,12 @@ implementation
end;
consume(token);
{ we can ignore the result, the definition is modified }
object_dec(objecttype,orgtypename,nil,nil,tobjectdef(ttypesym(sym).typedef),ht_none);
object_dec(objecttype,genorgtypename,nil,nil,tobjectdef(ttypesym(sym).typedef),ht_none);
newtype:=ttypesym(sym);
hdef:=newtype.typedef;
end
else
message1(parser_h_type_redef,orgtypename);
message1(parser_h_type_redef,genorgtypename);
end;
end;
{ no old type reused ? Then insert this new type }
@ -517,14 +487,73 @@ implementation
referencing the type before it's really set it
will give an error (PFV) }
hdef:=generrordef;
gendef:=nil;
storetokenpos:=current_tokenpos;
newtype:=ttypesym.create(orgtypename,hdef);
newtype.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(newtype);
if isgeneric then
begin
{ for generics we need to check whether a non-generic type
already exists and if not we need to insert a symbol with
the non-generic name (available in (org)typename) that is a
undefineddef, so that inline specializations can be used }
sym:=tsym(symtablestack.top.Find(typename));
if not assigned(sym) then
begin
sym:=ttypesym.create(orgtypename,tundefineddef.create);
Include(sym.symoptions,sp_generic_dummy);
ttypesym(sym).typedef.typesym:=sym;
sym.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(sym);
ttypesym(sym).typedef.owner:=sym.owner;
end
else
{ this is not allowed in non-Delphi modes }
if not (m_delphi in current_settings.modeswitches) then
Message1(sym_e_duplicate_id,genorgtypename)
else
{ we need to find this symbol even if it's a variable or
something else when doing an inline specialization }
Include(sym.symoptions,sp_generic_dummy);
end
else
begin
if assigned(sym) and (sym.typ=typesym) and
(ttypesym(sym).typedef.typ=undefineddef) and
(sp_generic_dummy in sym.symoptions) then
begin
{ this is a symbol that was added by an earlier generic
declaration, reuse it }
newtype:=ttypesym(sym);
newtype.typedef:=hdef;
sym:=nil;
end;
{ check whether this is a declaration of a type inside a
specialization }
if assigned(current_structdef) and
(df_specialization in current_structdef.defoptions) then
begin
if not assigned(current_structdef.genericdef) or
not (current_structdef.genericdef.typ in [recorddef,objectdef]) then
internalerror(2011052301);
sym:=tsym(tabstractrecorddef(current_structdef.genericdef).symtable.Find(gentypename));
if not assigned(sym) or not (sym.typ=typesym) then
internalerror(2011052302);
{ use the corresponding type in the generic's symtable as
genericdef for the specialized type }
gendef:=tstoreddef(ttypesym(sym).typedef);
end;
end;
{ insert a new type if we don't reuse an existing symbol }
if not assigned(newtype) then
begin
newtype:=ttypesym.create(genorgtypename,hdef);
newtype.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(newtype);
end;
current_tokenpos:=defpos;
current_tokenpos:=storetokenpos;
{ read the type definition }
read_named_type(hdef,orgtypename,nil,generictypelist,false);
read_named_type(hdef,genorgtypename,gendef,generictypelist,false);
{ update the definition of the type }
if assigned(hdef) then
begin
@ -565,8 +594,8 @@ implementation
begin
stringdispose(objname);
stringdispose(objrealname);
objrealname:=stringdup(orgtypename);
objname:=stringdup(upper(orgtypename));
objrealname:=stringdup(genorgtypename);
objname:=stringdup(upper(genorgtypename));
end;
include(hdef.defoptions,df_unique);
@ -577,10 +606,19 @@ implementation
if not assigned(hdef.typesym) then
hdef.typesym:=newtype;
end;
{ in non-Delphi modes we need a reference to the generic def
without the generic suffix, so it can be found easily when
parsing method implementations }
if isgeneric and assigned(sym) and
not (m_delphi in current_settings.modeswitches) and
(ttypesym(sym).typedef.typ=undefineddef) then
{ don't free the undefineddef as the defids rely on the count
of the defs in the def list of the module}
ttypesym(sym).typedef:=hdef;
newtype.typedef:=hdef;
{ KAZ: handle TGUID declaration in system unit }
if (cs_compilesystem in current_settings.moduleswitches) and not assigned(rec_tguid) and
(typename='TGUID') and { name: TGUID and size=16 bytes that is 128 bits }
(gentypename='TGUID') and { name: TGUID and size=16 bytes that is 128 bits }
assigned(hdef) and (hdef.typ=recorddef) and (hdef.size=16) then
rec_tguid:=trecorddef(hdef);
end;

View File

@ -46,7 +46,7 @@ implementation
symbase,symsym,symtable,
node,nld,nmem,ncon,ncnv,ncal,
fmodule,scanner,
pbase,pexpr,pdecsub,pdecvar,ptype,pdecl,ppu
pbase,pexpr,pdecsub,pdecvar,ptype,pdecl,pgenutil,ppu
;
const
@ -1148,6 +1148,11 @@ implementation
else if assigned(genericlist) then
current_genericdef:=current_structdef;
{ nested types of specializations are specializations as well }
if assigned(old_current_structdef) and
(df_specialization in old_current_structdef.defoptions) then
include(current_structdef.defoptions,df_specialization);
{ set published flag in $M+ mode, it can also be inherited and will
be added when the parent class set with tobjectdef.set_parent (PFV) }
if (cs_generate_rtti in current_settings.localswitches) and
@ -1188,6 +1193,10 @@ implementation
symtablestack.push(current_structdef.symtable);
insert_generic_parameter_types(current_structdef,genericdef,genericlist);
{ when we are parsing a generic already then this is a generic as
well }
if old_parse_generic then
include(current_structdef.defoptions, df_generic);
parse_generic:=(df_generic in current_structdef.defoptions);
{ parse list of parent classes }

View File

@ -910,75 +910,61 @@ implementation
function consume_generic_type_parameter:boolean;
var
i:integer;
ok:boolean;
sym:tsym;
idx : integer;
genparalistdecl : TFPHashList;
genname : tidstring;
s : shortstring;
begin
result:=not assigned(astruct)and(m_delphi in current_settings.modeswitches);
if result then
begin
{ a generic type parameter? }
srsym:=search_object_name(sp,false);
if (srsym.typ=typesym) and
(ttypesym(srsym).typedef.typ in [objectdef,recorddef]) then
begin
astruct:=tabstractrecorddef(ttypesym(srsym).typedef);
if (df_generic in astruct.defoptions) and try_to_consume(_LT) then
begin
ok:=true;
i:=0;
repeat
if ok and (token=_ID) then
begin
ok:=false;
while i<astruct.symtable.SymList.Count-1 do
begin
sym:=tsym(astruct.symtable.SymList[i]);
if sp_generic_para in sym.symoptions then
begin
ok:=sym.Name=pattern;
inc(i);
break;
end;
inc(i);
end;
if not ok then
Message1(type_e_generic_declaration_does_not_match,astruct.RttiName);
end;
consume(_ID);
until not try_to_consume(_COMMA);
if ok then
while i<astruct.symtable.SymList.Count-1 do
begin
sym:=tsym(astruct.symtable.SymList[i]);
if sp_generic_para in sym.symoptions then
begin
Message1(type_e_generic_declaration_does_not_match,astruct.RttiName);
break;
end;
inc(i);
end;
consume(_GT);
end
else
if (df_generic in astruct.defoptions) and (token=_POINT) then
begin
Message1(type_e_generic_declaration_does_not_match,astruct.RttiName);
end
else
begin
{ not a method. routine name just accidentally match some structure name }
astruct:=nil;
if try_to_consume(_LT) then
{ parse all parameters first so we can check whether we have
the correct generic def available }
genparalistdecl:=TFPHashList.Create;
if try_to_consume(_LT) then
begin
{ start with 1, so Find can return Nil (= 0) }
idx:=1;
repeat
if token=_ID then
begin
Message(type_e_type_parameters_are_not_allowed_here);
repeat
consume(_ID);
until not try_to_consume(_COMMA);
consume(_GT);
genparalistdecl.Add(pattern, Pointer(PtrInt(idx)));
consume(_ID);
inc(idx);
end
else
begin
message2(scan_f_syn_expected,arraytokeninfo[_ID].str,arraytokeninfo[token].str);
if token<>_COMMA then
consume(token);
end;
end;
end;
until not try_to_consume(_COMMA);
if not try_to_consume(_GT) then
consume(_RSHARPBRACKET);
end
else
begin
{ no generic }
srsym:=nil;
exit;
end;
s:='';
str(genparalistdecl.count,s);
genname:=sp+'$'+s;
genparalistdecl.free;
srsym:=search_object_name(genname,false);
if not assigned(srsym) then
begin
{ TODO : print a nicer typename that contains the parsed
generic types }
Message1(type_e_generic_declaration_does_not_match,genname);
srsym:=nil;
exit;
end;
end;
end;
@ -1024,13 +1010,14 @@ implementation
end;
{ method ? }
srsym:=nil;
if (consume_generic_type_parameter or not assigned(astruct)) and
(symtablestack.top.symtablelevel=main_program_level) and
try_to_consume(_POINT) then
begin
repeat
searchagain:=false;
if not assigned(astruct) then
if not assigned(astruct) and not assigned(srsym) then
srsym:=search_object_name(sp,true);
{ consume proc name }
procstartfilepos:=current_tokenpos;

View File

@ -461,7 +461,7 @@ implementation
if (token=_COLON) or (paranr>0) or (astruct=nil) then
begin
consume(_COLON);
single_type(p.propdef,[]);
single_type(p.propdef,[stoAllowSpecialization]);
if is_dispinterface(astruct) and not is_automatable(p.propdef) then
Message1(type_e_not_automatable,p.propdef.typename);

File diff suppressed because it is too large Load Diff

540
compiler/pgenutil.pas Normal file
View File

@ -0,0 +1,540 @@
{
Copyright (c) 2011
Contains different functions that are used in the context of
parsing generics.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit pgenutil;
{$i fpcdefs.inc}
interface
uses
{ common }
cclasses,
{ symtable }
symtype,symdef;
procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string);
function parse_generic_parameters:TFPObjectList;
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
implementation
uses
{ common }
cutils,
{ global }
globals,globtype,tokens,verbose,
{ symtable }
symconst,symbase,symsym,symtable,
{ modules }
fmodule,
{ pass 1 }
htypechk,
node,nobj,nmem,
{ parser }
scanner,
pbase,pexpr,pdecsub,ptype;
procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string);
var
st : TSymtable;
srsym : tsym;
pt2 : tnode;
found,
first,
err : boolean;
i,
gencount : longint;
genericdef : tstoreddef;
generictype : ttypesym;
genericdeflist : TFPObjectList;
generictypelist : TFPObjectList;
oldsymtablestack : tsymtablestack;
oldextendeddefs : TFPHashObjectList;
hmodule : tmodule;
pu : tused_unit;
prettyname : ansistring;
uspecializename,
countstr,genname,ugenname,specializename : string;
vmtbuilder : TVMTBuilder;
specializest : tsymtable;
item : tobject;
old_current_structdef : tabstractrecorddef;
old_current_genericdef,old_current_specializedef : tstoreddef;
tempst : tglobalsymtable;
old_block_type: tblock_type;
begin
{ retrieve generic def that we are going to replace }
genericdef:=tstoreddef(tt);
tt:=nil;
{ either symname must be given or genericdef needs to be valid }
if (symname='') and
(not assigned(genericdef) or
not assigned(genericdef.typesym) or
(genericdef.typesym.typ<>typesym)) then
internalerror(2011042701);
{ Only parse the parameters for recovery or
for recording in genericbuf }
if parse_generic then
begin
if not try_to_consume(_LT) then
consume(_LSHARPBRACKET);
gencount:=0;
repeat
pt2:=factor(false,true);
pt2.free;
inc(gencount);
until not try_to_consume(_COMMA);
if not try_to_consume(_GT) then
consume(_RSHARPBRACKET);
{ we need to return a def that can later pass some checks like
whether it's an interface or not }
if parse_generic and (not assigned(tt) or (tt.typ=undefineddef)) then
begin
if (symname='') and (df_generic in genericdef.defoptions) then
{ this happens in non-Delphi modes }
tt:=genericdef
else
begin
{ find the corresponding generic symbol so that any checks
done on the returned def will be handled correctly }
str(gencount,countstr);
if symname='' then
genname:=ttypesym(genericdef.typesym).realname
else
genname:=symname;
genname:=genname+'$'+countstr;
ugenname:=upper(genname);
if not searchsym(ugenname,srsym,st) or
(srsym.typ<>typesym) then
begin
identifier_not_found(genname);
exit;
end;
tt:=ttypesym(srsym).typedef;
{ this happens in non-Delphi modes if we encounter a
specialization of the generic class or record we're
currently parsing }
if (tt.typ=errordef) and assigned(current_structdef) and
(current_structdef.objname^=ugenname) then
tt:=current_structdef;
end;
end;
exit;
end;
if not assigned(parsedtype) and not try_to_consume(_LT) then
consume(_LSHARPBRACKET);
generictypelist:=TFPObjectList.create(false);
genericdeflist:=TFPObjectList.Create(false);
{ Parse type parameters }
err:=false;
{ set the block type to type, so that the parsed type are returned as
ttypenode (e.g. classes are in non type-compatible blocks returned as
tloadvmtaddrnode) }
old_block_type:=block_type;
{ if parsedtype is set, then the first type identifer was already parsed
(happens in inline specializations) and thus we only need to parse
the remaining types and do as if the first one was already given }
first:=not assigned(parsedtype);
if assigned(parsedtype) then
begin
genericdeflist.Add(parsedtype);
specializename:='$'+parsedtype.typesym.realname;
prettyname:=parsedtype.typesym.prettyname;
end
else
begin
specializename:='';
prettyname:='';
end;
while not (token in [_GT,_RSHARPBRACKET]) do
begin
{ "first" is set to false at the end of the loop! }
if not first then
consume(_COMMA);
block_type:=bt_type;
pt2:=factor(false,true);
if pt2.nodetype=typen then
begin
if df_generic in pt2.resultdef.defoptions then
Message(parser_e_no_generics_as_params);
genericdeflist.Add(pt2.resultdef);
if not assigned(pt2.resultdef.typesym) then
message(type_e_generics_cannot_reference_itself)
else
begin
specializename:=specializename+'$'+pt2.resultdef.typesym.realname;
if first then
prettyname:=prettyname+pt2.resultdef.typesym.prettyname
else
prettyname:=prettyname+','+pt2.resultdef.typesym.prettyname;
end;
end
else
begin
Message(type_e_type_id_expected);
err:=true;
end;
pt2.free;
first:=false;
end;
block_type:=old_block_type;
if err then
begin
try_to_consume(_RSHARPBRACKET);
exit;
end;
{ search a generic with the given count of params }
countstr:='';
str(genericdeflist.Count,countstr);
{ use the name of the symbol as procvars return a user friendly version
of the name }
if symname='' then
genname:=ttypesym(genericdef.typesym).realname
else
genname:=symname;
{ in case of non-Delphi mode the type name could already be a generic
def (but maybe the wrong one) }
if assigned(genericdef) and (df_generic in genericdef.defoptions) then
begin
{ remove the type count suffix from the generic's name }
for i:=Length(genname) downto 1 do
if genname[i]='$' then
begin
genname:=copy(genname,1,i-1);
break;
end;
end;
genname:=genname+'$'+countstr;
ugenname:=upper(genname);
if assigned(genericdef) and (genericdef.owner.symtabletype in [objectsymtable,recordsymtable]) then
begin
if genericdef.owner.symtabletype = objectsymtable then
found:=searchsym_in_class(tobjectdef(genericdef.owner.defowner),tobjectdef(genericdef.owner.defowner),ugenname,srsym,st,false)
else
found:=searchsym_in_record(tabstractrecorddef(genericdef.owner.defowner),ugenname,srsym,st);
end
else
found:=searchsym(ugenname,srsym,st);
if not found or (srsym.typ<>typesym) then
begin
identifier_not_found(genname);
genericdeflist.Free;
generictypelist.Free;
exit;
end;
{ we've found the correct def }
genericdef:=tstoreddef(ttypesym(srsym).typedef);
{ build the new type's name }
specializename:=genname+specializename;
uspecializename:=upper(specializename);
prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
{ select the symtable containing the params }
case genericdef.typ of
procdef:
st:=genericdef.GetSymtable(gs_para);
objectdef,
recorddef:
st:=genericdef.GetSymtable(gs_record);
arraydef:
st:=tarraydef(genericdef).symtable;
procvardef:
st:=genericdef.GetSymtable(gs_para);
else
internalerror(200511182);
end;
{ build the list containing the types for the generic params }
gencount:=0;
for i:=0 to st.SymList.Count-1 do
begin
srsym:=tsym(st.SymList[i]);
if sp_generic_para in srsym.symoptions then
begin
if gencount=genericdeflist.Count then
internalerror(2011042702);
generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[gencount]));
generictypelist.add(generictype);
inc(gencount);
end;
end;
{ Special case if we are referencing the current defined object }
if assigned(current_structdef) and
(current_structdef.objname^=uspecializename) then
tt:=current_structdef;
{ decide in which symtable to put the specialization }
if current_module.is_unit and current_module.in_interface then
specializest:=current_module.globalsymtable
else
specializest:=current_module.localsymtable;
{ Can we reuse an already specialized type? }
if not assigned(tt) then
begin
srsym:=tsym(specializest.find(uspecializename));
if assigned(srsym) then
begin
if srsym.typ<>typesym then
internalerror(200710171);
tt:=ttypesym(srsym).typedef;
end;
end;
if not assigned(tt) then
begin
{ Setup symtablestack at definition time
to get types right, however this is not perfect, we should probably record
the resolved symbols }
oldsymtablestack:=symtablestack;
oldextendeddefs:=current_module.extendeddefs;
current_module.extendeddefs:=TFPHashObjectList.create(true);
symtablestack:=tdefawaresymtablestack.create;
if not assigned(genericdef) then
internalerror(200705151);
hmodule:=find_module_from_symtable(genericdef.owner);
if hmodule=nil then
internalerror(200705152);
pu:=tused_unit(hmodule.used_units.first);
while assigned(pu) do
begin
if not assigned(pu.u.globalsymtable) then
internalerror(200705153);
symtablestack.push(pu.u.globalsymtable);
pu:=tused_unit(pu.next);
end;
if assigned(hmodule.globalsymtable) then
symtablestack.push(hmodule.globalsymtable);
{ push the localsymtable if needed }
if (hmodule<>current_module) or not current_module.in_interface then
symtablestack.push(current_module.localsymtable);
{ push a temporary global symtable so that the specialization is
added to the correct symtable; this symtable does not contain
any other symbols, so that the type resolution can not be
influenced by symbols in the current unit }
tempst:=tspecializesymtable.create(current_module.modulename^,current_module.moduleid);
symtablestack.push(tempst);
{ Reparse the original type definition }
if not err then
begin
if parse_class_parent then
begin
old_current_structdef:=current_structdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
if genericdef.owner.symtabletype in [recordsymtable,objectsymtable] then
current_structdef:=tabstractrecorddef(genericdef.owner.defowner)
else
current_structdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
end;
{ First a new typesym so we can reuse this specialization and
references to this specialization can be handled }
srsym:=ttypesym.create(specializename,generrordef);
specializest.insert(srsym);
{ specializations are declarations as such it is the wisest to
declare set the blocktype to "type"; otherwise we'll
experience unexpected side effects like the addition of
classrefdefs if we have a generic that's derived from another
generic }
old_block_type:=block_type;
block_type:=bt_type;
if not assigned(genericdef.generictokenbuf) then
internalerror(200511171);
current_scanner.startreplaytokens(genericdef.generictokenbuf,
genericdef.change_endian);
read_named_type(tt,specializename,genericdef,generictypelist,false);
ttypesym(srsym).typedef:=tt;
tt.typesym:=srsym;
if _prettyname<>'' then
ttypesym(tt.typesym).fprettyname:=_prettyname
else
ttypesym(tt.typesym).fprettyname:=prettyname;
{ Note regarding hint directives:
There is no need to remove the flags for them from the
specialized generic symbol, because hint directives that
follow the specialization are handled by the code in
pdecl.types_dec and added to the type symbol.
E.g.: TFoo = TBar<Blubb> deprecated;
Here the symbol TBar$1$Blubb will contain the
"sp_hint_deprecated" flag while the TFoo symbol won't.}
case tt.typ of
{ Build VMT indexes for classes and read hint directives }
objectdef:
begin
try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
consume(_SEMICOLON);
vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
vmtbuilder.generate_vmt;
vmtbuilder.free;
end;
{ handle params, calling convention, etc }
procvardef:
begin
if not check_proc_directive(true) then
begin
try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
consume(_SEMICOLON);
end;
parse_var_proc_directives(ttypesym(srsym));
handle_calling_convention(tprocvardef(tt));
if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
consume(_SEMICOLON);
end;
else
{ parse hint directives for records and arrays }
begin
try_consume_hintdirective(srsym.symoptions,srsym.deprecatedmsg);
consume(_SEMICOLON);
end;
end;
{ Consume the semicolon if it is also recorded }
try_to_consume(_SEMICOLON);
block_type:=old_block_type;
if parse_class_parent then
begin
current_structdef:=old_current_structdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
end;
end;
{ extract all created symbols and defs from the temporary symtable
and add them to the specializest }
for i:=0 to tempst.SymList.Count-1 do begin
item:=tempst.SymList.Items[i];
specializest.SymList.Add(tempst.SymList.NameOfIndex(i),item);
tsym(item).Owner:=specializest;
tempst.SymList.Extract(item);
end;
for i:=0 to tempst.DefList.Count-1 do begin
item:=tempst.DefList.Items[i];
specializest.DefList.Add(item);
tdef(item).owner:=specializest;
tempst.DefList.Extract(item);
end;
tempst.free;
{ Restore symtablestack }
current_module.extendeddefs.free;
current_module.extendeddefs:=oldextendeddefs;
symtablestack.free;
symtablestack:=oldsymtablestack;
end;
if not (token in [_GT, _RSHARPBRACKET]) then
begin
consume(_RSHARPBRACKET);
exit;
end
else
consume(token);
genericdeflist.free;
generictypelist.free;
if assigned(genericdef) then
begin
{ check the hints of the found generic symbol }
srsym:=genericdef.typesym;
check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
end;
end;
function parse_generic_parameters:TFPObjectList;
var
generictype : ttypesym;
begin
result:=TFPObjectList.Create(false);
repeat
if token=_ID then
begin
generictype:=ttypesym.create(orgpattern,cundefinedtype);
include(generictype.symoptions,sp_generic_para);
result.add(generictype);
end;
consume(_ID);
until not try_to_consume(_COMMA) ;
end;
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
var
i: longint;
generictype: ttypesym;
st: tsymtable;
begin
def.genericdef:=genericdef;
if not assigned(genericlist) then
exit;
case def.typ of
recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
arraydef: st:=tarraydef(def).symtable;
procvardef,procdef: st:=tabstractprocdef(def).parast;
else
internalerror(201101020);
end;
for i:=0 to genericlist.count-1 do
begin
generictype:=ttypesym(genericlist[i]);
if generictype.typedef.typ=undefineddef then
include(def.defoptions,df_generic)
else
include(def.defoptions,df_specialization);
st.insert(generictype);
end;
end;
end.

View File

@ -43,7 +43,7 @@ type
{$endif Test_Double_checksum}
const
CurrentPPUVersion = 140;
CurrentPPUVersion = 141;
{ buffer sizes }
maxentrysize = 1024;

View File

@ -1352,17 +1352,22 @@ implementation
old_current_structdef: tabstractrecorddef;
old_current_genericdef,
old_current_specializedef: tstoreddef;
old_parse_generic: boolean;
begin
old_current_procinfo:=current_procinfo;
old_block_type:=block_type;
old_current_structdef:=current_structdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
old_parse_generic:=parse_generic;
current_procinfo:=self;
current_structdef:=procdef.struct;
if assigned(current_structdef) and (df_generic in current_structdef.defoptions) then
current_genericdef:=current_structdef;
begin
current_genericdef:=current_structdef;
parse_generic:=true;
end;
if assigned(current_structdef) and (df_specialization in current_structdef.defoptions) then
current_specializedef:=current_structdef;
@ -1474,6 +1479,7 @@ implementation
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
current_procinfo:=old_current_procinfo;
parse_generic:=old_parse_generic;
{ Restore old state }
block_type:=old_block_type;
@ -1949,14 +1955,50 @@ implementation
procedure specialize_objectdefs(p:TObject;arg:pointer);
var
i : longint;
hp : tdef;
oldcurrent_filepos : tfileposinfo;
oldsymtablestack : tsymtablestack;
oldextendeddefs : TFPHashObjectList;
pu : tused_unit;
hmodule : tmodule;
specobj : tabstractrecorddef;
procedure process_abstractrecorddef(def:tabstractrecorddef);
var
i : longint;
hp : tdef;
begin
for i:=0 to def.symtable.DefList.Count-1 do
begin
hp:=tdef(def.symtable.DefList[i]);
if hp.typ=procdef then
begin
{ only generate the code if we need a body }
if assigned(tprocdef(hp).struct) and not tprocdef(hp).forwarddef then
continue;
if assigned(tprocdef(hp).genericdef) and
(tprocdef(hp).genericdef.typ=procdef) and
assigned(tprocdef(tprocdef(hp).genericdef).generictokenbuf) then
begin
oldcurrent_filepos:=current_filepos;
current_filepos:=tprocdef(tprocdef(hp).genericdef).fileinfo;
{ use the index the module got from the current compilation process }
current_filepos.moduleindex:=hmodule.unit_index;
current_tokenpos:=current_filepos;
current_scanner.startreplaytokens(tprocdef(tprocdef(hp).genericdef).generictokenbuf,
tprocdef(tprocdef(hp).genericdef).change_endian);
read_proc_body(nil,tprocdef(hp));
current_filepos:=oldcurrent_filepos;
end
else
MessagePos1(tprocdef(hp).fileinfo,sym_e_forward_not_resolved,tprocdef(hp).fullprocname(false));
end
else
if hp.typ in [objectdef,recorddef] then
{ generate code for subtypes as well }
process_abstractrecorddef(tabstractrecorddef(hp));
end;
end;
begin
if not((tsym(p).typ=typesym) and
(ttypesym(p).typedef.typesym=tsym(p)) and
@ -1994,29 +2036,7 @@ implementation
symtablestack.push(hmodule.localsymtable);
{ procedure definitions for classes or objects }
for i:=0 to specobj.symtable.DefList.Count-1 do
begin
hp:=tdef(specobj.symtable.DefList[i]);
if hp.typ=procdef then
begin
if assigned(tprocdef(hp).genericdef) and
(tprocdef(hp).genericdef.typ=procdef) and
assigned(tprocdef(tprocdef(hp).genericdef).generictokenbuf) then
begin
oldcurrent_filepos:=current_filepos;
current_filepos:=tprocdef(tprocdef(hp).genericdef).fileinfo;
{ use the index the module got from the current compilation process }
current_filepos.moduleindex:=hmodule.unit_index;
current_tokenpos:=current_filepos;
current_scanner.startreplaytokens(tprocdef(tprocdef(hp).genericdef).generictokenbuf,
tprocdef(tprocdef(hp).genericdef).change_endian);
read_proc_body(nil,tprocdef(hp));
current_filepos:=oldcurrent_filepos;
end
else
MessagePos1(tprocdef(hp).fileinfo,sym_e_forward_not_resolved,tprocdef(hp).fullprocname(false));
end;
end;
process_abstractrecorddef(specobj);
{ Restore symtablestack }
current_module.extendeddefs.free;

View File

@ -52,8 +52,6 @@ interface
{ generate persistent type information like VMT, RTTI and inittables }
procedure write_persistent_type_info(st:tsymtable);
procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname : string);
implementation
uses
@ -74,7 +72,7 @@ implementation
nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,
{ parser }
scanner,
pbase,pexpr,pdecsub,pdecvar,pdecobj,pdecl;
pbase,pexpr,pdecsub,pdecvar,pdecobj,pdecl,pgenutil;
procedure resolve_forward_types;
@ -143,271 +141,8 @@ implementation
end;
procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname : string);
var
st : TSymtable;
srsym : tsym;
pt2 : tnode;
first,
err : boolean;
i : longint;
sym : tsym;
genericdef : tstoreddef;
generictype : ttypesym;
generictypelist : TFPObjectList;
oldsymtablestack : tsymtablestack;
oldextendeddefs : TFPHashObjectList;
hmodule : tmodule;
pu : tused_unit;
prettyname : ansistring;
uspecializename,
specializename : string;
vmtbuilder : TVMTBuilder;
onlyparsepara : boolean;
specializest : tsymtable;
item: psymtablestackitem;
begin
{ retrieve generic def that we are going to replace }
genericdef:=tstoreddef(tt);
tt:=nil;
onlyparsepara:=false;
procedure id_type(var def : tdef;isforwarddef,checkcurrentrecdef,allowgenericsyms:boolean;out srsym:tsym;out srsymtable:tsymtable); forward;
if not(df_generic in genericdef.defoptions) then
begin
Message(parser_e_special_onlygenerics);
tt:=generrordef;
onlyparsepara:=true;
end;
{ only need to record the tokens, then we don't know the type yet ... }
if parse_generic then
begin
{ ... but we have to insert a def into the symtable else the deflist
of generic and specialization might not be equally sized which
is later assumed }
tt:=tundefineddef.create;
if parse_class_parent then
tt:=genericdef;
onlyparsepara:=true;
end;
{ Only parse the parameters for recovery or
for recording in genericbuf }
if onlyparsepara then
begin
consume(_LSHARPBRACKET);
repeat
pt2:=factor(false,true);
pt2.free;
until not try_to_consume(_COMMA);
consume(_RSHARPBRACKET);
exit;
end;
if not try_to_consume(_LT) then
consume(_LSHARPBRACKET);
{ Parse generic parameters, for each undefineddef in the symtable of
the genericdef we need to have a new def }
err:=false;
first:=true;
generictypelist:=TFPObjectList.create(false);
case genericdef.typ of
procdef:
st:=genericdef.GetSymtable(gs_para);
objectdef,
recorddef:
st:=genericdef.GetSymtable(gs_record);
arraydef:
st:=tarraydef(genericdef).symtable;
procvardef:
st:=genericdef.GetSymtable(gs_para);
else
internalerror(200511182);
end;
{ Parse type parameters }
if not assigned(genericdef.typesym) then
internalerror(200710173);
specializename:=genericdef.typesym.realname;
prettyname:=genericdef.typesym.prettyname+'<';
for i:=0 to st.SymList.Count-1 do
begin
sym:=tsym(st.SymList[i]);
if (sp_generic_para in sym.symoptions) then
begin
if not first then
consume(_COMMA)
else
first:=false;
pt2:=factor(false,true);
if pt2.nodetype=typen then
begin
if df_generic in pt2.resultdef.defoptions then
Message(parser_e_no_generics_as_params);
generictype:=ttypesym.create(sym.realname,pt2.resultdef);
generictypelist.add(generictype);
if not assigned(pt2.resultdef.typesym) then
message(type_e_generics_cannot_reference_itself)
else
begin
specializename:=specializename+'$'+pt2.resultdef.typesym.realname;
if i=0 then
prettyname:=prettyname+pt2.resultdef.typesym.prettyname
else
prettyname:=prettyname+','+pt2.resultdef.typesym.prettyname;
end;
end
else
begin
Message(type_e_type_id_expected);
err:=true;
end;
pt2.free;
end;
end;
prettyname:=prettyname+'>';
uspecializename:=upper(specializename);
{ force correct error location if too much type parameters are passed }
if not (token in [_RSHARPBRACKET,_GT]) then
consume(_RSHARPBRACKET);
{ Special case if we are referencing the current defined object }
if assigned(current_structdef) and
(current_structdef.objname^=uspecializename) then
tt:=current_structdef;
{ for units specializations can already be needed in the interface, therefor we
will use the global symtable. Programs don't have a globalsymtable and there we
use the localsymtable }
if current_module.is_unit then
specializest:=current_module.globalsymtable
else
specializest:=current_module.localsymtable;
{ Can we reuse an already specialized type? }
if not assigned(tt) then
begin
srsym:=tsym(specializest.find(uspecializename));
if assigned(srsym) then
begin
if srsym.typ<>typesym then
internalerror(200710171);
tt:=ttypesym(srsym).typedef;
end;
end;
if not assigned(tt) then
begin
{ Setup symtablestack at definition time
to get types right, however this is not perfect, we should probably record
the resolved symbols }
oldsymtablestack:=symtablestack;
oldextendeddefs:=current_module.extendeddefs;
current_module.extendeddefs:=TFPHashObjectList.create(true);
symtablestack:=tdefawaresymtablestack.create;
if not assigned(genericdef) then
internalerror(200705151);
hmodule:=find_module_from_symtable(genericdef.owner);
if hmodule=nil then
internalerror(200705152);
pu:=tused_unit(hmodule.used_units.first);
while assigned(pu) do
begin
if not assigned(pu.u.globalsymtable) then
internalerror(200705153);
symtablestack.push(pu.u.globalsymtable);
pu:=tused_unit(pu.next);
end;
if assigned(hmodule.globalsymtable) then
symtablestack.push(hmodule.globalsymtable);
{ hacky, but necessary to insert the newly generated class properly }
item:=oldsymtablestack.stack;
while assigned(item) and (item^.symtable.symtablelevel>main_program_level) do
item:=item^.next;
if assigned(item) and (item^.symtable<>symtablestack.top) then
symtablestack.push(item^.symtable);
{ Reparse the original type definition }
if not err then
begin
{ First a new typesym so we can reuse this specialization and
references to this specialization can be handled }
srsym:=ttypesym.create(specializename,generrordef);
specializest.insert(srsym);
if not assigned(genericdef.generictokenbuf) then
internalerror(200511171);
current_scanner.startreplaytokens(genericdef.generictokenbuf,
genericdef.change_endian);
read_named_type(tt,specializename,genericdef,generictypelist,false);
ttypesym(srsym).typedef:=tt;
tt.typesym:=srsym;
if _prettyname<>'' then
ttypesym(tt.typesym).fprettyname:=_prettyname
else
ttypesym(tt.typesym).fprettyname:=prettyname;
case tt.typ of
{ Build VMT indexes for classes }
objectdef:
begin
vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
vmtbuilder.generate_vmt;
vmtbuilder.free;
end;
{ handle params, calling convention, etc }
procvardef:
begin
if not check_proc_directive(true) then
begin
try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
consume(_SEMICOLON);
end;
parse_var_proc_directives(ttypesym(srsym));
handle_calling_convention(tprocvardef(tt));
if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
consume(_SEMICOLON);
end;
end;
{ Consume the semicolon if it is also recorded }
try_to_consume(_SEMICOLON);
end;
{ Restore symtablestack }
current_module.extendeddefs.free;
current_module.extendeddefs:=oldextendeddefs;
symtablestack.free;
symtablestack:=oldsymtablestack;
end
else
begin
{ There is comment few lines before ie 200512115
saying "We are parsing the same objectdef, the def index numbers
are the same". This is wrong (index numbers are not same)
in case there is specialization (S2 in this case) inside
specialized generic (G2 in this case) which is equal to
some previous specialization (S1 in this case). In that case,
new symbol is not added to currently specialized type
(S in this case) for that specializations (S2 in this case),
and this results in that specialization and generic definition
don't have same number of elements in their object symbol tables.
This patch adds undefined def to ensure that those
two symbol tables will have same number of elements.
}
tundefineddef.create;
end;
generictypelist.free;
if not try_to_consume(_GT) then
consume(_RSHARPBRACKET);
end;
procedure id_type(var def : tdef;isforwarddef,checkcurrentrecdef:boolean); forward;
{ def is the outermost type in which other types have to be searched
@ -423,6 +158,8 @@ implementation
var
t2: tdef;
structstackindex: longint;
srsym: tsym;
srsymtable: tsymtable;
begin
if assigned(currentstructstack) then
structstackindex:=currentstructstack.count-1
@ -431,12 +168,7 @@ implementation
{ handle types inside classes, e.g. TNode.TLongint }
while (token=_POINT) do
begin
if parse_generic then
begin
consume(_POINT);
consume(_ID);
end
else if is_class_or_object(def) or is_record(def) then
if is_class_or_object(def) or is_record(def) then
begin
consume(_POINT);
if (structstackindex>=0) and
@ -451,7 +183,7 @@ implementation
structstackindex:=-1;
symtablestack.push(tabstractrecorddef(def).symtable);
t2:=generrordef;
id_type(t2,isforwarddef,false);
id_type(t2,isforwarddef,false,false,srsym,srsymtable);
symtablestack.pop(tabstractrecorddef(def).symtable);
def:=t2;
end;
@ -497,18 +229,18 @@ implementation
result:=false;
end;
procedure id_type(var def : tdef;isforwarddef,checkcurrentrecdef:boolean);
procedure id_type(var def : tdef;isforwarddef,checkcurrentrecdef,allowgenericsyms:boolean;out srsym:tsym;out srsymtable:tsymtable);
{ reads a type definition }
{ to a appropriating tdef, s gets the name of }
{ the type to allow name mangling }
var
is_unit_specific : boolean;
pos : tfileposinfo;
srsym : tsym;
srsymtable : TSymtable;
s,sorg : TIDString;
t : ttoken;
begin
srsym:=nil;
srsymtable:=nil;
s:=pattern;
sorg:=orgpattern;
pos:=current_tokenpos;
@ -528,7 +260,10 @@ implementation
table as forwarddef are not resolved directly }
if assigned(srsym) and
(srsym.typ=typesym) and
(ttypesym(srsym).typedef.typ=errordef) then
((ttypesym(srsym).typedef.typ=errordef) or
(not allowgenericsyms and
(ttypesym(srsym).typedef.typ=undefineddef) and
not (sp_generic_para in srsym.symoptions))) then
begin
Message1(type_e_type_is_not_completly_defined,ttypesym(srsym).realname);
def:=generrordef;
@ -571,6 +306,8 @@ implementation
t2 : tdef;
dospecialize,
again : boolean;
srsym : tsym;
srsymtable : tsymtable;
begin
dospecialize:=false;
repeat
@ -617,7 +354,7 @@ implementation
end
else
begin
id_type(def,stoIsForwardDef in options,true);
id_type(def,stoIsForwardDef in options,true,true,srsym,srsymtable);
parse_nested_types(def,stoIsForwardDef in options,nil);
end;
end;
@ -631,9 +368,9 @@ implementation
until not again;
if ([stoAllowSpecialization,stoAllowTypeDef] * options <> []) and
(m_delphi in current_settings.modeswitches) then
dospecialize:=token=_LSHARPBRACKET;
dospecialize:=token in [_LSHARPBRACKET,_LT];
if dospecialize then
generate_specialization(def,stoParseClassParent in options,'')
generate_specialization(def,stoParseClassParent in options,'',nil,'')
else
begin
if assigned(current_specializedef) and (def=current_specializedef.genericdef) then
@ -644,7 +381,14 @@ implementation
begin
def:=current_genericdef
end
else if (df_generic in def.defoptions) then
else if (df_generic in def.defoptions) and
not
(
parse_generic and
(current_genericdef.typ in [recorddef,objectdef]) and
sym_is_owned_by(srsym,tabstractrecorddef(current_genericdef).symtable)
)
then
begin
Message(parser_e_no_generics_as_types);
def:=generrordef;
@ -983,7 +727,16 @@ implementation
else if assigned(genericlist) then
current_genericdef:=current_structdef;
{ nested types of specializations are specializations as well }
if assigned(old_current_structdef) and
(df_specialization in old_current_structdef.defoptions) then
include(current_structdef.defoptions,df_specialization);
insert_generic_parameter_types(current_structdef,genericdef,genericlist);
{ when we are parsing a generic already then this is a generic as
well }
if old_parse_generic then
include(current_structdef.defoptions, df_generic);
parse_generic:=(df_generic in current_structdef.defoptions);
if m_advanced_records in current_settings.modeswitches then
parse_record_members
@ -1090,9 +843,40 @@ implementation
def:=ttypenode(pt1).resultdef;
{ Delphi mode specialization? }
if (m_delphi in current_settings.modeswitches) then
dospecialize:=token=_LSHARPBRACKET;
dospecialize:=token=_LSHARPBRACKET
else
{ in non-Delphi modes we might get a inline specialization
without "specialize" or "<T>" of the same type we're
currently parsing, so we need to handle that special }
if not dospecialize and
assigned(ttypenode(pt1).typesym) and
(ttypenode(pt1).typesym.typ=typesym) and
(sp_generic_dummy in ttypenode(pt1).typesym.symoptions) and
assigned(current_structdef) and
(
(
not (m_delphi in current_settings.modeswitches) and
(ttypesym(ttypenode(pt1).typesym).typedef.typ=undefineddef) and
(df_generic in current_structdef.defoptions) and
(ttypesym(ttypenode(pt1).typesym).typedef.owner=current_structdef.owner) and
(upper(ttypenode(pt1).typesym.realname)=copy(current_structdef.objname^,1,pos('$',current_structdef.objname^)-1))
) or (
(df_specialization in current_structdef.defoptions) and
(ttypesym(ttypenode(pt1).typesym).typedef=current_structdef.genericdef)
)
)
then
begin
def:=current_structdef;
{ handle nested types }
post_comp_expr_gendef(def);
end;
if dospecialize then
generate_specialization(def,false,name)
begin
generate_specialization(def,false,name,nil,'');
{ handle nested types }
post_comp_expr_gendef(def);
end
else
begin
if assigned(current_specializedef) and (def=current_specializedef.genericdef) then
@ -1103,7 +887,17 @@ implementation
begin
def:=current_genericdef
end
else if (df_generic in def.defoptions) then
else if (df_generic in def.defoptions) and
{ TODO : check once nested generics are allowed }
not
(
parse_generic and
(current_genericdef.typ in [recorddef,objectdef]) and
(def.typ in [recorddef,objectdef]) and
(ttypenode(pt1).typesym<>nil) and
sym_is_owned_by(ttypenode(pt1).typesym,tabstractrecorddef(current_genericdef).symtable)
)
then
begin
Message(parser_e_no_generics_as_types);
def:=generrordef;

View File

@ -167,7 +167,11 @@ type
sp_implicitrename,
sp_hint_experimental,
sp_generic_para,
sp_has_deprecated_msg
sp_has_deprecated_msg,
sp_generic_dummy { this is used for symbols that are generated when a
generic is encountered to ease inline
specializations, etc; those symbols can be
"overridden" with a completely different symbol }
);
tsymoptions=set of tsymoption;
@ -471,7 +475,7 @@ type
{ options for symtables }
tsymtableoption = (
sto_has_helper { contains at least one helper symbol }
sto_has_helper { contains at least one helper symbol }
);
tsymtableoptions = set of tsymtableoption;

View File

@ -120,6 +120,7 @@ interface
end;
ttypesym = class(Tstoredsym)
public
typedef : tdef;
typedefderef : tderef;
fprettyname : ansistring;
@ -1981,13 +1982,13 @@ implementation
procedure ttypesym.buildderef;
begin
typedefderef.build(typedef);
typedefderef.build(typedef);
end;
procedure ttypesym.deref;
begin
typedef:=tdef(typedefderef.resolve);
typedef:=tdef(typedefderef.resolve);
end;

View File

@ -166,6 +166,11 @@ interface
function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override;
end;
tspecializesymtable = class(tglobalsymtable)
public
function iscurrentunit:boolean;override;
end;
twithsymtable = class(TSymtable)
withrefnode : tobject; { tnode }
constructor create(aowner:tdef;ASymList:TFPHashObjectList;refnode:tobject{tnode});
@ -214,14 +219,20 @@ interface
procedure incompatibletypes(def1,def2:tdef);
procedure hidesym(sym:TSymEntry);
procedure duplicatesym(var hashedid:THashedIDString;dupsym,origsym:TSymEntry);
function handle_generic_dummysym(sym:TSymEntry;var symoptions:tsymoptions):boolean;
{*** Search ***}
procedure addsymref(sym:tsym);
function is_owned_by(childdef,ownerdef:tdef):boolean;
function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean;
function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean;
function is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean;
function is_visible_for_object(sym:tsym;contextobjdef:tabstractrecorddef):boolean;
function searchsym(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean;
function searchsym_maybe_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchoption:boolean;option:tsymoption):boolean;
{ searches for a symbol with the given name that has the given option in
symoptions set }
function searchsym_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;option:tsymoption):boolean;
function searchsym_type(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean;
function searchsym_in_module(pm:pointer;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean;
function searchsym_in_named_module(const unitname, symname: TIDString; out srsym: tsym; out srsymtable: tsymtable): boolean;
@ -342,7 +353,6 @@ implementation
var
dupnr : longint; { unique number for duplicate symbols }
{*****************************************************************************
TStoredSymtable
*****************************************************************************}
@ -1467,6 +1477,8 @@ implementation
hsym:=tsym(FindWithHash(hashedid));
if assigned(hsym) then
begin
if (sym is tstoredsym) and handle_generic_dummysym(hsym,tstoredsym(sym).symoptions) then
exit;
if hsym.typ=symconst.namespacesym then
begin
case sym.typ of
@ -1598,6 +1610,16 @@ implementation
end;
{*****************************************************************************
tspecializesymtable
*****************************************************************************}
function tspecializesymtable.iscurrentunit: boolean;
begin
Result := true;
end;
{****************************************************************************
TWITHSYMTABLE
****************************************************************************}
@ -1806,6 +1828,29 @@ implementation
include(tsym(dupsym).symoptions,sp_implicitrename);
end;
function handle_generic_dummysym(sym:TSymEntry;var symoptions:tsymoptions):boolean;
begin
result:=false;
if not assigned(sym) or not (sym is tstoredsym) then
Internalerror(2011081101);
{ For generics a dummy symbol without the parameter count is created
if such a symbol not yet exists so that different parts of the
parser can find that symbol. If that symbol is still a
undefineddef we replace the generic dummy symbol's
name with a "dup" name and use the new symbol as the generic dummy
symbol }
if (sp_generic_dummy in tstoredsym(sym).symoptions) and
(sym.typ=typesym) and (ttypesym(sym).typedef.typ=undefineddef) and
(m_delphi in current_settings.modeswitches) then
begin
inc(dupnr);
sym.Owner.SymList.Rename(upper(sym.realname),'dup_'+tostr(dupnr)+sym.realname);
include(tsym(sym).symoptions,sp_implicitrename);
{ we need to find the new symbol now if checking for a dummy }
include(symoptions,sp_generic_dummy);
result:=true;
end;
end;
{*****************************************************************************
Search
@ -1833,6 +1878,13 @@ implementation
result:=is_owned_by(tdef(childdef.owner.defowner),ownerdef);
end;
function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean;
begin
result:=childsym.owner=symtable;
if not result and (childsym.owner.symtabletype in [objectsymtable,recordsymtable]) then
result:=sym_is_owned_by(tabstractrecorddef(childsym.owner.defowner).typesym,symtable);
end;
function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean;
var
symownerdef : tabstractrecorddef;
@ -1962,6 +2014,11 @@ implementation
function searchsym(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean;
begin
result:=searchsym_maybe_with_symoption(s,srsym,srsymtable,false,sp_none);
end;
function searchsym_maybe_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchoption:boolean;option:tsymoption):boolean;
var
hashedid : THashedIDString;
contextstructdef : tabstractrecorddef;
@ -1975,6 +2032,12 @@ implementation
srsymtable:=stackitem^.symtable;
if (srsymtable.symtabletype=objectsymtable) then
begin
{ TODO : implement the search for an option in classes as well }
if searchoption then
begin
result:=false;
exit;
end;
if searchsym_in_class(tobjectdef(srsymtable.defowner),tobjectdef(srsymtable.defowner),s,srsym,srsymtable,true) then
begin
result:=true;
@ -1997,7 +2060,8 @@ implementation
else
contextstructdef:=current_structdef;
if not (srsym.owner.symtabletype in [objectsymtable,recordsymtable]) or
is_visible_for_object(srsym,contextstructdef) then
is_visible_for_object(srsym,contextstructdef) and
(not searchoption or (option in srsym.symoptions)) then
begin
{ we need to know if a procedure references symbols
in the static symtable, because then it can't be
@ -2017,6 +2081,11 @@ implementation
srsymtable:=nil;
end;
function searchsym_with_symoption(const s: TIDString;out srsym:tsym;out
srsymtable:TSymtable;option:tsymoption):boolean;
begin
result:=searchsym_maybe_with_symoption(s,srsym,srsymtable,true,option);
end;
function searchsym_type(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean;
var

View File

@ -1074,7 +1074,8 @@ const
(mask:sp_internal; str:'Internal'),
(mask:sp_implicitrename; str:'Implicit Rename'),
(mask:sp_generic_para; str:'Generic Parameter'),
(mask:sp_has_deprecated_msg; str:'Has Deprecated Message')
(mask:sp_has_deprecated_msg; str:'Has Deprecated Message'),
(mask:sp_generic_dummy; str:'Generic Dummy')
);
var
symoptions : tsymoptions;

View File

@ -1,3 +1,4 @@
{ %NORUN }
program tgeneric29;
{$mode delphi}

25
tests/test/tgeneric36.pp Normal file
View File

@ -0,0 +1,25 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: class only }
program tgeneric36;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = class
end;
TTest<T> = class
end;
TTest<T, S> = class
end;
begin
end.

25
tests/test/tgeneric37.pp Normal file
View File

@ -0,0 +1,25 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: record only }
program tgeneric37;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = record
end;
TTest<T> = record
end;
TTest<T, S> = record
end;
begin
end.

25
tests/test/tgeneric38.pp Normal file
View File

@ -0,0 +1,25 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: interface only }
program tgeneric38;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = interface
end;
TTest<T> = interface
end;
TTest<T, S> = interface
end;
begin
end.

19
tests/test/tgeneric39.pp Normal file
View File

@ -0,0 +1,19 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: procvars only }
program tgeneric39;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = procedure;
TTest<T> = procedure;
TTest<T, S> = procedure;
begin
end.

19
tests/test/tgeneric40.pp Normal file
View File

@ -0,0 +1,19 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: arrays only }
program tgeneric40;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = array of Integer;
TTest<T> = array of Integer;
TTest<T, S> = array of Integer;
begin
end.

19
tests/test/tgeneric41.pp Normal file
View File

@ -0,0 +1,19 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: method vars only }
program tgeneric41;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = procedure of object;
TTest<T> = procedure of object;
TTest<T, S> = procedure of object;
begin
end.

26
tests/test/tgeneric42.pp Normal file
View File

@ -0,0 +1,26 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: class only
Note: This tests a different code path than in the compiler than tgeneric36! }
program tgeneric42;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
end;
TTest = class
end;
TTest<T, S> = class
end;
begin
end.

26
tests/test/tgeneric43.pp Normal file
View File

@ -0,0 +1,26 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: record only
Note: This tests a different code path than in the compiler than tgeneric37! }
program tgeneric43;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = record
end;
TTest = record
end;
TTest<T, S> = record
end;
begin
end.

26
tests/test/tgeneric44.pp Normal file
View File

@ -0,0 +1,26 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: interface only
Note: This tests a different code path than in the compiler than tgeneric38! }
program tgeneric44;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = interface
end;
TTest = interface
end;
TTest<T, S> = interface
end;
begin
end.

20
tests/test/tgeneric45.pp Normal file
View File

@ -0,0 +1,20 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: procvars only
Note: This tests a different code path than in the compiler than tgeneric39! }
program tgeneric45;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = procedure;
TTest = procedure;
TTest<T, S> = procedure;
begin
end.

20
tests/test/tgeneric46.pp Normal file
View File

@ -0,0 +1,20 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: arrays only
Note: This tests a different code path than in the compiler than tgeneric40! }
program tgeneric40;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = array of Integer;
TTest = array of Integer;
TTest<T, S> = array of Integer;
begin
end.

20
tests/test/tgeneric47.pp Normal file
View File

@ -0,0 +1,20 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: method vars only
Note: This tests a different code path than in the compiler than tgeneric39! }
program tgeneric47;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = procedure of object;
TTest = procedure of object;
TTest<T, S> = procedure of object;
begin
end.

28
tests/test/tgeneric48.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ in mode Delphi generic types might be overloaded - here: a mix }
program tgeneric48;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest = class
end;
TTest<T> = record
end;
TTest<T, S> = interface
end;
TTest<T, S, R> = procedure;
TTest<T, S, R, Q> = array of Integer;
TTest<T, S, R, Q, P> = procedure of object;
begin
end.

38
tests/test/tgeneric49.pp Normal file
View File

@ -0,0 +1,38 @@
{ %NORUN }
{ This tests whether the correct deprecated messages are printed. As I don't
know of a way to check these inside a test this needs to be done by hand }
program tgeneric49;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
end deprecated 'Message A';
TTest = class
end deprecated 'Message B';
// this should print 'Message A'
TTestInteger = TTest<Integer>;
FooInt = Integer deprecated;
// this should print that TTest<T> and FooInt are deprecated
TTestFooInt = TTest<FooInt>;
var
// this should print 'Message B'
t: TTest;
// this should print nothing
t2: TTestInteger;
// this should print that TTest<T> and FooInt are deprecated
t3: TTest<FooInt>;
begin
// this should print that TTest<T> and FooInt are deprecated
t3 := TTest<FooInt>.Create;
end.

31
tests/test/tgeneric50.pp Normal file
View File

@ -0,0 +1,31 @@
{ %NORUN }
{ this tests that hint directives defined for a generic only apply when
specializung a generic and that specializations may introduce their own
directives }
program tgeneric50;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
end deprecated 'Message A' platform;
// these will both print that TTest<T> is deprecated and platform
TTestInteger = TTest<Integer> deprecated 'Message B' experimental;
TTestString = TTest<String>;
var
// this will print that TTestInteger is deprecated and experimental
t: TTestInteger;
// this will print nothing
t2: TTestString;
begin
// this will print that TTest<T> is deprecated and platform
t2 := TTest<String>.Create;
// this will print that TTestInteger is deprecated and experimental
t := TTestInteger.Create;
end.

39
tests/test/tgeneric51.pp Normal file
View File

@ -0,0 +1,39 @@
{ this tests that simple inline specializations work }
program tgeneric51;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$apptype console}
type
TTest<T> = class
function Test(a: T): T;
class function ClassTest(a: T): T;
end;
function TTest<T>.Test(a: T): T;
begin
Result := a;
end;
class function TTest<T>.ClassTest(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>;
res: Integer;
begin
t := TTest<Integer>.Create;
res := t.Test(42);
Writeln('t.Test: ', res);
if res <> 42 then
Halt(1);
res := TTest<Integer>.ClassTest(42);
Writeln('t.ClassTest: ', res);
if res <> 42 then
Halt(2);
Writeln('ok');
end.

38
tests/test/tgeneric52.pp Normal file
View File

@ -0,0 +1,38 @@
{ this tests that forced typecasts to inline specialized types work }
program tgeneric52;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$apptype console}
type
TTest = class
function Test: Integer;
end;
TTestGen<T> = class(TTest)
function Test: Integer;
end;
function TTest.Test: Integer;
begin
Result := 1;
end;
function TTestGen<T>.Test: Integer;
begin
Result := 2;
end;
var
t: TTest;
res: Integer;
begin
t := TTestGen<Integer>.Create;
res := TTestGen<Integer>(t).Test;
Writeln('t.Test: ', res);
if res <> 2 then
Halt(1);
Writeln('ok');
end.

39
tests/test/tgeneric53.pp Normal file
View File

@ -0,0 +1,39 @@
{ this tests that checked typecasts to inline specialized types work }
program tgeneric53;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$apptype console}
type
TTest = class
function Test: Integer;
end;
TTestGen<T> = class(TTest)
function Test: Integer;
end;
function TTest.Test: Integer;
begin
Result := 1;
end;
function TTestGen<T>.Test: Integer;
begin
Result := 2;
end;
var
t: TTest;
res: Integer;
begin
t := TTestGen<Integer>.Create;
res := (t as TTestGen<Integer>).Test;
Writeln('t.Test: ', res);
if res <> 2 then
Halt(1);
Writeln('ok');
end.

40
tests/test/tgeneric54.pp Normal file
View File

@ -0,0 +1,40 @@
{ this tests that type checks for inline specialized types work }
program tgeneric53;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$apptype console}
type
TTest = class
function Test: Integer;
end;
TTestGen<T> = class(TTest)
function Test: Integer;
end;
function TTest.Test: Integer;
begin
Result := 1;
end;
function TTestGen<T>.Test: Integer;
begin
Result := 2;
end;
var
t: TTest;
begin
t := TTestGen<Integer>.Create;
if t is TTestGen<Integer> then
Writeln('t is a TTestGen<Integer>')
else begin
Writeln('t is not a TTestGen<Integer>');
Halt(1);
end;
Writeln('ok');
end.

19
tests/test/tgeneric55.pp Normal file
View File

@ -0,0 +1,19 @@
{ %FAIL }
{ this tests that the dummy symbol that is introduced for generic "overloads"
can not be used when it shouldn't be - Test 1 }
program tgeneric55;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
end;
var
t: TTest;
begin
end.

20
tests/test/tgeneric56.pp Normal file
View File

@ -0,0 +1,20 @@
{ %FAIL }
{ this tests that the dummy symbol that is introduced for generic "overloads"
can not be used when it shouldn't be - Test 2 }
program tgeneric56;
{$ifdef fpc}
{$mode objfpc}
{$endif}
type
generic TTest<T> = class
end;
var
t: TTest;
begin
end.

21
tests/test/tgeneric57.pp Normal file
View File

@ -0,0 +1,21 @@
{ %FAIL }
{ this tests that the dummy symbol that is introduced for generic "overloads"
can not be used when it shouldn't be - Test 3 }
program tgeneric57;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
end;
var
t: TObject;
begin
t := TTest.Create;
end.

21
tests/test/tgeneric58.pp Normal file
View File

@ -0,0 +1,21 @@
{ %FAIL }
{ this tests that the dummy symbol that is introduced for generic "overloads"
can not be used when it shouldn't be - Test 4 }
program tgeneric58;
{$ifdef fpc}
{$mode objfpc}
{$endif}
type
generic TTest<T> = class
end;
var
t: TObject;
begin
t := TTest.Create;
end.

25
tests/test/tgeneric59.pp Normal file
View File

@ -0,0 +1,25 @@
{ TTest<T, S> is used from one unit, while TTest<T> is used from another }
program tgeneric59;
{$ifdef fpc}
{$mode delphi}
{$endif}
{$apptype console}
uses
ugeneric59a,
ugeneric59b;
type
TTestInteger = TTest<Integer>;
TTestIntegerString = TTest<Integer, String>;
var
res: Integer;
begin
res := TTestInteger.Test;
Writeln('TTestInteger.Test: ', res);
if res <> 2 then
Halt(1);
Writeln('ok');
end.

28
tests/test/tgeneric60.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: record in class }
program tgeneric60;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
type
TTestSub = record
class function Test(a: T): T; static;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric61.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: class in class }
program tgeneric61;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
type
TTestSub = class
class function Test(a: T): T;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric62.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: object in class }
program tgeneric62;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = class
type
TTestSub = object
function Test(a: T): T;
end;
end;
function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
t.Test(42);
end.

28
tests/test/tgeneric63.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: record in record }
program tgeneric63;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = record
type
TTestSub = record
class function Test(a: T): T; static;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric64.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: class in record }
program tgeneric64;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = record
type
TTestSub = class
class function Test(a: T): T;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric65.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: object in record }
program tgeneric65;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = record
type
TTestSub = object
function Test(a: T): T;
end;
end;
function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
t.Test(42);
end.

28
tests/test/tgeneric66.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: record in object }
program tgeneric66;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = object
type
TTestSub = record
class function Test(a: T): T; static;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric67.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: class in object }
program tgeneric66;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = object
type
TTestSub = class
class function Test(a: T): T;
end;
end;
class function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
TTest<Integer>.TTestSub.Test(42);
end.

28
tests/test/tgeneric68.pp Normal file
View File

@ -0,0 +1,28 @@
{ %NORUN }
{ this tests that nested non-generic structured types can be used inside
generics - here: object in object }
program tgeneric68;
{$ifdef fpc}
{$mode delphi}
{$endif}
type
TTest<T> = object
type
TTestSub = object
function Test(a: T): T;
end;
end;
function TTest<T>.TTestSub.Test(a: T): T;
begin
Result := a;
end;
var
t: TTest<Integer>.TTestSub;
begin
t.Test(42);
end.

24
tests/test/ugeneric59a.pp Normal file
View File

@ -0,0 +1,24 @@
unit ugeneric59a;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
type
TTest<T> = class
class function Test: Integer;
end;
TTest<T, S> = class
end;
implementation
class function TTest<T>.Test: Integer;
begin
Result := 1;
end;
end.

22
tests/test/ugeneric59b.pp Normal file
View File

@ -0,0 +1,22 @@
unit ugeneric59b;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
type
TTest<T> = class
class function Test: Integer;
end;
implementation
class function TTest<T>.Test: Integer;
begin
Result := 2;
end;
end.

View File

@ -5,13 +5,13 @@ program tw18567;
type
TSomeRecord <TData> = record
data: TData;
class operator Explicit(a: TData) : TSomeRecord;
class operator Explicit(a: TData) : TSomeRecord <TData>;
end;
class operator TSomeRecord <TData>.Explicit (a: TData): TSomeRecord;
class operator TSomeRecord <TData>.Explicit (a: TData): TSomeRecord <TData>;
begin
end;
begin
end.
end.