Beforehand, to provide a context, here are some excerpts from private 
correspondence:

On 11.12.2016 22:00:37 +0300, [email protected] wrote in 
[email protected]:
my progress thus far
9) Method reference directives are now allowed:
        type M = reference to procedure varargs cdecl;
DCC-style syntax is used
On 27.06.2017 11:44:40 +0200, Sven Barth wrote in 
CAFMUeB96Piu=zntclasvzm9_pyekqscfom_+irbxjeoneky...@mail.gmail.com:
the syntax for cblock references was selected on purpose to be compatible with the syntax for 
Delphi style "reference to" method pointers, only with "cdecl" as calling 
convention.
On 10.02.2018 14:34:42 +0100, Sven Barth wrote in 
[email protected]:
It seems that anonymous functions do indeed support calling conventions... dang 
it... I had hoped to use the calling convention as a marker for the difference 
which is why I told Jonas to implement cblocks this way. But on the other hand 
I'd say that it is really unusual to use a calling convention with anonymous 
functions
On 23.08.2019 06:54:21 +0200, Sven Barth wrote in 
[email protected]:
There isn't much use for "reference to XXX; cdecl;" in the field for normal 
anonymous functions. Yes, it works, but really, who uses it that way?
So the solution is simply to have "reference to XXX; cdecl;" be parsed as a 
cblock if modeswitch CBLOCKS is set and as a normal anonymous function reference 
otherwise. This way it can be controlled on a per-unit base.

Counterpoints:
1) In my professional opinion, the current "solution" is a horrible yucky hack: 
different (albeit similar) entities look exactly the same, and they cannot be used 
simultaneously.
2) As a compiler developer, my job is to provide orthogonal building blocks and ensure 
that every conceivable semantically valid combination of them actually works. When one 
applies reasoning akin to "who uses it that way", they inevitably reduce the 
power of a language to a subset defined by their often limited understanding of what can 
and should be possible.
3) My first example back in 2016 intentionally featured a valid use case: CDECL 
is crucial for VARARGS. (Somehow, not only that case, but the whole point that 
DCC accepts calling conventions went unnoticed.)

Seeing that introducing this syntax collision was clearly unintentional, I 
cannot understand the seeming reluctance to remedy it.
I have a couple of proposals:
1) Instead of the CDecl directive, use the C directive:
        type M = reference to procedure c;
Pros: Since DCC does not support this MacPas directive, and old MacPas code 
does not have the REFERENCE TO syntax, there would be no collision whatsoever.
Cons: Since C and CDECL are semantically the same, these syntaxes still look 
confusingly the same:
        type M = reference to procedure cdecl;  // closure
        type M = reference to procedure c;      // C-block
I wonder from where that desire to have the Delphi-like syntax for C-block 
references came. If it stemmed from the assumption that having the same syntax 
would simplify the compiler, then we have a solid basis for changing it, 
because that assumption was wrong. Jonas parses them via procvar_dec, and I 
parse closure reference types via parse_proc_dec under 
symtablestack.push(invokable_interface.symtable), and it makes perfect sense 
since such types /are/ interfaces (to the point that they are directly 
implementable on classes).
2) Same as (1), but without REFERENCE TO:
        type M = procedure c;
Pros: No collision with DCC. Easy to parse.
Cons: Collision with procedural types in MacPas. Since, presumably, MacPas is 
for compatibility with legacy code, this collision is less of a problem than 
the collision with DCC; but a collision nevertheless.
3) Attributes:
        type [CBlock] M = reference to procedure;
        type [CBlock] M = procedure;
Pros: In my book, the most elegant solution.
Cons: Attributes are applied to symbols, not type definitions; which is 
problematical for nameless type definitions, parsing, and semantics.
4) Distinct syntax, without adding new keywords:
        type M = procedure with var;    // a closure is literally a "procedure with 
[captured outer] variables"
        type M = procedure with record; // captured variables are kept in a 
kind of record
        type M = procedure with out name;       // clever!
        type M = procedure is c;        // meaning "is [a] c[-block]"
5) Distinct syntax with a new keyword:
        type M = procedure cblock;

If there is a consensus that this is a blocker, I am ready to do the work ASAP. 
provided we select the syntax. Personally, I am inclined towards these three:
        type M = procedure (const N: Integer) with var;
        type M = procedure (const N: Integer) cblock;
        type M = reference to procedure (const N: Integer) c;
Again, in the last case (like with the current collision), the non-existent 
unified C-block/closure reference parser would not be able to reuse as much 
existing infrastructure as two separate parsers currently do reuse (but do not 
coöperate).
(Also, it appears that there is a couple of C-block-related bugs, which I am 
ready to tackle as well.)

--
βþ
_______________________________________________
fpc-devel maillist  -  [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to