Hi Martin,

That's interesting, thanks. It is important to note that in C++, neither of the candidate syntaxes is backwards-compatible in the way you describe. The attribute-like P2935 syntax is:

[[ pre : expression ]]
[[ post optional-name-for-return-value : expression ]]
[[ assert : expression ]]

That does not follow the standard attribute grammar, which in C++ is:

[[ attribute-token ]]
[[ attribute-token ( parameter-clause ) ]]

because the colon is not allowed in standard attribute grammar. So in C++, even if we choose P2935 (the attribute-like syntax), a pre-Contracts compiler is still *required* to diagnose a syntax error if it sees a contract. That's why we call P2935 the attribute-*like* syntax, it's *like* an attribute because it has [[ ... ]] , but isn't actually an attribute because it doesn't follow attribute grammar.

So for C++, the option to have a backwards-compatible syntax isn't even on the table anymore (we discussed this option in SG21 – it would require us to put parentheses around the expression and would forbid having anything between the pre/post/assert and that parenthesised expression – and that's just too limiting for this feature) so it's simply not a consideration for us.

I understand that in C, the situation is different because you're allowed to token-ignore anything between [[ ... ]] even if it features a colon.

Cheers,
Timur

On 6 Oct 2023, at 17:30, Martin Uecker <ma.uecker@gmail.com> wrote:

Am Freitag, dem 06.10.2023 um 09:40 +0300 schrieb Ville Voutilainen:
On Fri, 6 Oct 2023 at 08:31, Martin Uecker <ma.uecker@gmail.com> wrote:
To be honest, I think it is a mistake also for C++.  I believe
that even for C++ people will end up trying to introduce this into
existing code and then are forced to make it ignorable by wrapping it
into  #ifdef  or macros.  The end result would neither be nice
nor ignorable.

Sure, but that's no different from conditionally adopting any other
new language facility that
the older implementations don't recognize. Which for C++ is almost
every new language facility.
We have standardized feature-detection macros, even, to help with
preprocessor-based conditional
migration.

The difference is that this seems to be a feature which could be
usefully be added to an existing codebase incrementally.  That
does not make too much sense for every new feature. 


It wouldn't cause me loss of sleep if C compilers were allowed to
completely ignore a contract annotation.
Fixing syntax errors when using a compiler that doesn't ignore them is
compatible with compilers that would
ignore the annotations.

Right. And especially in a context where headers are shared, the
scenario that for some transition time (probably decades) the code
needs be processed also by compilers not supporting contracts seems
very likely.

For C I think it would be important to have a syntax that is ignorable.

Just to make sure I understand.. in effect, to make the feature
optional? So as to not hoist
its burden on implementations that don't want it?

No, I would make it mandatory for new C versions. But a useful goal for
C could be to make the code acceptable to older compilers that do
not have full support for it during a transition time without needing 
preprocessor wrappers.



A syntax as proposed here (it seems, correct me if I am wrong) such
as:

void f(int x)
 pre <audit> (x > 0);

would be a pretty bad choice for C.

The <audit> part is at this point hypothetical. I'm curious why you
think that's a bad choice.

One problem is introducing a three letter keyword.  Yes, _Pre would
be an option, but if we want to transition to "pre" this would still
be a problem later. 

The next problem is that this syntax can not be hidden behind a macro.
This would work for:

_Pre (<audit>, (x > 0))

Also some attribute-like mechanism would work

[[ __pre__ (x > 0) ]]

where the parsing context could allow a compiler to treat the
keywords specially.

So there is no fundamental problem with this syntax. It is just that
it is not really designed for a feature that can be phased in gradually
into existing code.  But this is exactly how contracts would be most
useful in C: By enhancing existing code step by step.

If I understand things correctly, it's novel for C to have function
parameters be in scope
after leaving a function declarator and before a function definition
begins. In C++, that hasn't
been novel for ages, with trailing return types,
noexcept-specifications, and constraints all
using function parameters.

This is also true, but I think this is not a major issue.

Martin