C++ Logo

liaison

Advanced search

Re: [wg14/wg21 liaison] P2961R1 syntax for Contracts: viable for C?

From: Martin Uecker <ma.uecker_at_[hidden]>
Date: Sat, 07 Oct 2023 12:46:17 +0200
Am Samstag, dem 07.10.2023 um 12:24 +0300 schrieb Timur Doumler:
> To clarify:
> In C++, even if we go with the "function-like" P2961 syntax, we can't make contracts always look like function-like macros. Yes, the most basic contracts indeed look syntactically like
> function calls or function-like macros:
>
> pre (x != 0)
>
> Then we have postconditions that name the return value. This already doesn't look like a function call anymore because of the colon. Maybe this still qualifies as valid function-like macro
> syntax? I'm not sure:
>
> post (ret: ret != 0)
>
> And then we have a number of extensions which we already know we want to standardise for C++ after we get the initial, basic version through (which we call the "Contracts MVP"). All of
> these post-MVP extensions are *not* compatible with function-like macro syntax.
>
> For example, we need the ability to annotate a contract with multiple labels simultaneously. The exact syntax is to be decided, but if we go for P2961 rather than P2935 then it could be
> something like
>
> pre <audit, new> (x != 0)
>
> or perhaps
>
> pre (x != 0) [audit, new]
>
> In any case, you cannot absorb these labels into a single keyword and make the whole thing functional-macro-like, unless you want a combinatorial explosion of keywords.
>
> We will further have labels that take parameters, such as
>
> pre <audit_level(3)> (x != 0)
>
> There is no way to do this with a function-like macro syntax as far as I can see.

You could make these additional arguments of the "macro":

pre (x != 0, audit_level(3))

or maybe:

pre (<audit_level(3)>, x != 0)

>
> And then we want to introduce captures for contracts, which would work similar to lambda captures. They allow you to do things like "remembering" a value at the point when the function
> begins and then use that value in a postcondition that is evaluated when the function returns, for example
>
> void increment_size()
> post [old_size = size()] (size() == old_size + 1);

You could do

  post ( [old_size = size()] (size() == old_size + 1) );


I do not know whether this would "ruin" the syntax too much, but
then you could actually hide this behind a macro for older
compilers.


Of course, you can do this also for the original syntax,

#define CONTRAST_POST (x) post x

int foo(int)
  CONTRAST_POST ([] ())

but if people have to work with these macro wrappers anyway, then
we effectively end up with two ways to write contracts: The macro
wrappers and a second "nice" syntax people have to learn in
addition to it and which can then only be used with projects
that only need to work with very new compilers.

One can argue that the macro wrappers are a transitional
tool and in the future people can switch to the nice syntax
and this is certainly a correct and reasonable argument.
For C with more priority on existing code and old projects,
I would argue that it may be better to standardize a
macro-compatible syntax, making it as good as possible given
the constraints, and then only after gaining some actual
experience with the feature - and assuming everything works
well - one could then think about introducing a nicer
syntax (and at that time, sentiments about how it should
look like or what is important and what not may have
changed, which could then be taken into account)


> There are more such planned extensions; if you're curious, please check out P2755.
>
> Perhaps, for C you will never want to adopt any of these extensions, and that's fine. But I think it's important to mention that at least in C++, they will come. Having a syntax for
> contracts that is fully compatible with function-like macro syntax is therefore just as much a non-goal for us as having a syntax for contracts that is fully compatible with attribute
> syntax, or having a syntax for contracts that is compatible with any other existing feature. That's not what we're going for in C++. Contracts are a new language feature and like any new
> language feature they will have a new, not backwards-compatible syntax in C++, one way or another (whether we adopt P2935 or P2961).
>
> The only question here — why I started this thread — is which of these syntaxes would create fewer problems for C.
>
> Hope that makes sense?

Yes and no. I understand that this is the WG21 of doing things.

But essentially you give me the choice between two bad*
options, telling me that all other approaches have been ruled
out already.

(* with bad, I do not mean it is bad per se, but that one should
approach this more carefully, see below.)

I tried a little bit to explain above what - in my opinion - is more
the C way of introducing new features: more slowly and carefully, and
with a focus on existing code and projects, on transitional processes,
managing risks, and with a long-term perspective.

I think one has to acknowledge that C and C++ simply have fundamentally
different development philosophy and priorities here. And maybe
you are right and we are wrong, but this is a different discussion.

So at this time, one can not simply decide what will create fewer
problems in the future. This is why I would prefer not to commit
to using any fundamentally new syntax invented from scratch
without any actual experience in C, but rather evolve things more
carefully step by step. Both macros and attributes would make this
easier.

But assuming I have to pick one of the two proposals, I would
still prefer the attribute-like for the reasons discussed before.
It is less risky and takes less design space away. I personally
find the syntax still reasonably pleasant, and the arguments about
user confusion with attributes not really convincing (or the
arguments against using attributes in general).

In any case, you wanted to hear my thoughts... If you pick the
other syntax, then this is how it is and we will deal with this
somehow. I do not think this syntax is terrible, I just would
prefer the other one.

Martin

>
> Please let me know if you have any further thoughts on this. Thanks!
>
> Cheers,
> Timur
>
> > On 6 Oct 2023, at 21:40, Martin Uecker <ma.uecker_at_[hidden]> wrote:
> >
> > Am Freitag, dem 06.10.2023 um 14:14 -0400 schrieb Aaron Ballman:
> > > On Fri, Oct 6, 2023 at 2:00 PM Martin Uecker <ma.uecker_at_[hidden]> wrote:
> > > >
> > > > Am Freitag, dem 06.10.2023 um 13:57 -0400 schrieb Aaron Ballman via Liaison:
> > > > > On Fri, Oct 6, 2023 at 12:56 PM Timur Doumler <cpp_at_[hidden]> wrote:
> > > > > >
> > > > > > OK, thanks, I understand that argument now.
> > > > > >
> > > > > > I am not sure though if it makes much sense to design our syntax around that, given that we *already* know we can't (and don't want to) achieve this kind of seamless backwards-
> > > > > > compatibility in C++, and by extension in any code shared between C and C++, so only codebases in pure C would benefit from such a possibility. And you won't get backwards-
> > > > > > compatibility with older compilers, only with newer compilers in "old standard" mode, because every existing compiler I'm aware of rejects the colon between [[...]] as a syntax
> > > > > > error today.
> > > > > >
> > > > > > However, my understanding is that with either of the syntax proposals, we would be able to achieve backwards-compability with older compilers via wrapping the whole feature in
> > > > > > macros, in exactly the same way we do for other new features that add new syntax.
> > > > > >
> > > > > > I am really interested whether anyone here has a preference for one syntax and against the other for any other reason that does *not* have to do with ignorability?
> > > > >
> > > > > I strongly prefer the syntax that looks like a function-like macro
> > > > > over the syntax that looks like an attribute. I think it eases
> > > > > portability, especially when compiling newer source code with an older
> > > > > implementation. I don't think we should be making the user scan for
> > > > > whether there's one colon or two colons to know what language feature
> > > > > they're using. I think constructs like lambdas can already look quite
> > > > > noisy with their balanced delimiters and this introduces another
> > > > > instance of that. Also, it helps this confusion by making it more
> > > > > visually distinct:
> > > > >
> > > > > void func(int i) <Which comes first: type attributes or contracts?>;
> > > > >
> > > > > While users still need to know which order to put things in because we
> > > > > don't allow interleaving there, at least they're not trying to
> > > > > remember how to order two things both using [[]] syntax.
> > > >
> > > >
> > > > I think function-like macros were not proposed. But I would also
> > > > prefer this.
> > >
> > > Heh, sorry for being unclear, I didn't mean to imply it was actually a
> > > function-like macro under the hood. I meant `pre(operand)` where the
> > > syntax looks like a function call or function-like macro invocation.
> >
> > Yes, I was also not clear. I think it would be fine if it
> > is fixed to always be like a function-like macro and
> > not have the [] <> prefixed versions, and it it ideally
> > had a longer name that we can make a true keyword later.
> >
> > Martin
> >
> > >
> > > ~Aaron
> > >
> > > >
> > > > Martin
> > > >
> > > > >
> > > > > ~Aaron
> > > > >
> > > > > >
> > > > > > Cheers,
> > > > > > Timur
> > > > > >
> > > > > > > On 6 Oct 2023, at 19:35, Jens Maurer <jens.maurer_at_[hidden]> wrote:
> > > > > > >
> > > > > > > 
> > > > > > >
> > > > > > > > On 06/10/2023 18.32, Timur Doumler wrote:
> > > > > > > >
> > > > > > > >
> > > > > > > > > On 6 Oct 2023, at 19:28, Jens Maurer <jens.maurer_at_[hidden]> wrote:
> > > > > > > > > > If that is indeed the case, then the attribute-like syntax for Contracts would not be ignorable in C, either.
> > > > > > > > >
> > > > > > > > > Right, but the argument is that implementations can add the small extension
> > > > > > > > > to parse-ignore ":" in that spot right now, and then be future-proof for
> > > > > > > > > ignoring future attribute-like contracts.
> > > > > > > >
> > > > > > > > Right. Yes, I can follow that argument. But that begs the question: what is so special or different about Contracts that you want this feature in particular to be backwards-
> > > > > > > > compatibly-ignorable by older compilers, considering that we don't do that for any other new language feature where we add new syntax to the language?
> > > > > > >
> > > > > > > The argument, as far as I understand, is that contracts in particular are well
> > > > > > > suited to be retrofitted on existing code bases that need to be compatible
> > > > > > > with older compilers / language versions.
> > > > > > >
> > > > > > > For any other new language feature, you can just choose to ignore it for your
> > > > > > > meant-to-be-compatible code base. But contracts are so valuable to find
> > > > > > > bugs in existing software, so you want them everywhere ASAP.
> > > > > > >
> > > > > > > (I'm just repeating an argument I think I heard. This is not my opinion.)
> > > > > > >
> > > > > > > Jens
> > > > > > >
> > > > > >
> > > > > _______________________________________________
> > > > > Liaison mailing list
> > > > > Liaison_at_[hidden]
> > > > > Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/liaison
> > > > > Link to this post: http://lists.isocpp.org/liaison/2023/10/1274.php
> > > >
> >

Received on 2023-10-07 10:46:23