C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Named Return Value Optimisation [[nrvo]]

From: Alejandro Colomar <une+cxx_std-proposals_at_[hidden]>
Date: Tue, 10 Feb 2026 16:28:31 +0100
Hi Marcin,

On 2026-02-10T16:05:00+0100, Marcin Jaczewski wrote:
[...]
> > > I mean something diffrent, I word this wrongly.
> > > I means that variable itself should have attribute like:
> > >
> > > ```
> > > typeof(int (void)) *fp2 [[nodiscard]];
> > > ```
> > >
> > > And this would fit the definition of `f` as its attribute is linked to
> > > name not to its type.
> > > And this will be fine in the current paradigm.
> >
> > That would be the wrong choice. The attribute should be part of the
> > type, to make sure type conversions are okay.
> >
> > Let's make it more complex:
> >
> > [[nodiscard]] int f(void);
> > int g(void);
> >
> > void h(int (*)(void) );
> > void i(int (*)(void)[[nodiscard]]);
> >
> > int
> > main(void)
> > {
> > h(&f); // This should be diagnosed.
> > i(&f);
> > h(&g);
> > i(&g);
> > }
> >
> > Essentially, [[nodiscard]] should behave like a function qualifier, so
> > that pointers to functions should be able to gain it but not discard it.
> >
> > Otherwise, we have a hole.
>
> But this exactly same case I showed, if you change attribute to argument:
> ```
> void i(int (*arg [[nodiscard]] )(void));
> ```
> This is the property of the argument of the `i` function.

You had to name the argument, when this is really a property of the
type, and should be expressible in an abstract declarator.

>
>
> Only problem start when you try pass `i` itself to another function:
>
> ```
> void foo(void (*)(int (*)(void)));
> ```
>
> Here its become probably very painful to handle it but how many times
> you need arbitrary nesting?

And indeed this shows that the right thing to attribute is the type.
It's not a matter of pain; it's a matter of correctness.

> Besdied, we can avoid all this by baning both cases like:
>
> ```
> h(&f); // error
> i(&f); //error too
> h([[trust_me_bro]]&f); // ok, explicit "cast"
> i([[trust_me_bro]]&f); // ok, explicit "cast"
> ```

This is bad; really bad. I don't want the compiler to trust myself.

It's like if we said we had to use

  foo(const bar);

to avoid diagnostics about const-corectness misuses. This is the wrong
approach. Const correctness is verified by the compiler, and attributes
should be verified by the compiler too.

> > > > > and this could be still ignorable.
> > > >
> > > > In part, this is a problem because either
> > > >
> > > > a) ignorability of attributes is the problem
> > > > or
> > > > b) [[nodiscard]] should not be ignorable, and should be replaced by a
> > > > non-ignorable attribute.
> > > >
> > > > And if b) is chosen, then few very ignorable attributes would remain;
> > > > so that ignorability of attributes would render attributes DoA. *The*
> > > > truly ignorable attribute is [[maybe_unused]].
> > > >
> > > I can imagine many others that could work fine and be enforced by the compiler
> > > but safely ignored if the compiler does not bother implementing them.
> > >
> > > ```
> > > int* foo(int* b [[restrict(A,B)]], int* c [[restrict(C)]])
> > > [[restrict(A,B,C)]]
> > > {
> > > *b += 2;
> > > *c += 2;
> > > return *b < *c ? b : c;
> > > }
> > > ```
> > >
> > > Now if the compiler regonzie this attribute then he could only do 2
> > > loads as pointers should not overlap.
> > > If not, then it only generates less optimal code. if the programmer
> > > lied then we get UB, same as `[[noreturn]]`.
> >
> > restrict is terrible, and your suggested [[restrict]] is similarly
> > terrible.
> >
> > A [[restrict]] replacement of restrict should prioritize diagnostics
> > over optimizations. Thus, it would end up being similar to
> > [[nodiscard]].
> >
> > Tricky question: should the following code result in a diagnostic?
> >
> > alx_at_devuan:~/tmp$ cat restrict.c
> > void f(int *p [[restrict]], int **pp [[restrict]]);
> >
> > int
> > main(void)
> > {
> > int i = 42;
> > int *p = &i;
> >
> > f(p, &p);
> > }
>
> At least my "restric" have explicit arbitrary groups, and would reject
> this code as values are from the same "group".

Yeah, I wasn't going into that. I kept it simple, for the question.

> Beside I would here ask programet to be explicit:
>
> ```
> f(p, [[trust_me_restric]]&p); //otherwise diagnostic
> ```

This is wrong. Programmers should not be trusted for correctness.

> overall I would use my restic too to handle life time ranges in program like:
> ```
> [[restrict(stack)]]
> [[restrict(temporal)]]
> [[restrict(global)]]
> ```
>
> Of course it will be painful when you use multiple nested `*` but we
> probably should not mix diffrent group too much.
>
> >
> > Similarly tricky question: should the following code result in a
> > diagnostic?
> >
> > alx_at_devuan:~/tmp$ cat restrict.c
> > void f(int *restrict p, int **restrict pp);
> >
> > int
> > main(void)
> > {
> > int i = 42;
> > int *p = &i;
> >
> > f(p, &p);
> > }

You didn't reply to the tricky questions. Please reply, as they are
quite important regarding the behavior and design of restrict and any
replacements for it.


Cheers,
Alex

-- 
<https://www.alejandro-colomar.es>

Received on 2026-02-10 15:28:57