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