Date: Tue, 10 Feb 2026 20:16:16 +0100
Hi Marcin,
On 2026-02-10T16:49:55+0100, Marcin Jaczewski wrote:
[...]
> > > > 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.
> >
>
> How `[[nodiscard]]` is part of type? it's more usage of value.
qualifiers are also about usage of the value (actually, lvalue; at least
in C; C++ has to many XXvalue for me to understand).
const is about how you can use an lvalue. And it is part of the type.
[[nodiscard]] is essentially the same: what can do you do with this
lvalue (the function designator).
> > > 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.
> >
>
> Do not in 99.99% cases you only call functions like this directly?
> How many times do you send `malloc` as a callback to other functions?
I can't imagine all the ways people use the language.
And I can't remember all the ways I've used functions that return
a resource.
Yes, 99.99% sounds like a reasonable guesstimate.
> And if you do this many times, why not make custom type that itself have this
> attribute? if you need to guarantee that `int` can't be discarded in some nested
> callback hell then maybe the problem lies elsewhere? I see more
> as local helper for corner cases like `empty()` not to build new types.
Casts are always bad. Sometimes, they're necessary, but only because
the language is not well designed in that corner case. We should try to
reduce those cases, not add more.
>
> Beside if you allow attributes aspart of type system then you do not answer me:
>
> ```
> std::is_same_t<int, [[foo]] int>
> ```
>
> is true?
No, it should be false.
> Can I overload over attributes? Can I have template
> specialization based on it?
> How name mangling will handle it?
I'm a C programmer, and am happy to not need to answer that question.
Overloading is the largest problem of C++, IMO.
[...]
> > > > 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.
> >
>
> This is the same code as the previous example. This means my answer
> is related to this too. If I recall C would say is ok, I would say is not.
Yup, ISO C says this is okay. And indeed, that's the key for the
ignorability of restrict. If this had to be diagnosed, restrict
wouldn't be ignorable (at least for some meaning of ignorable).
The strtol(3) function uses restrict in its parameters, and it is fine
(and even common) to call strtol(p,&p,0).
I also agree with you that this should not be fine, and thus restrict
should be removed from the specification of strtol(3).
void f(int *p [[restrict]], int **pp [[restrict]]);
void g(int *p , int **pp );
Should &f and &g be used interchangably as function pointers? Of course
not, or we'd have the same issues as with [[nodiscard]]. We want
diagnostics for misuses. [[restrict]] should not be discarded without
a cast. Then, the ignorability of [[restrict]] is not a thing anymore.
Have a lovely night!
Alex
> But this depends on how you choose to model it.
>
> I prefer to look at this as related memory groups, not that these
> pointers overlap.
>
>
> btw we bit do offtomic as this thread is about nrvo, I could continue this but
> others would not like too much spam on only tangentially related topics.
>
> >
> > Cheers,
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es>
On 2026-02-10T16:49:55+0100, Marcin Jaczewski wrote:
[...]
> > > > 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.
> >
>
> How `[[nodiscard]]` is part of type? it's more usage of value.
qualifiers are also about usage of the value (actually, lvalue; at least
in C; C++ has to many XXvalue for me to understand).
const is about how you can use an lvalue. And it is part of the type.
[[nodiscard]] is essentially the same: what can do you do with this
lvalue (the function designator).
> > > 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.
> >
>
> Do not in 99.99% cases you only call functions like this directly?
> How many times do you send `malloc` as a callback to other functions?
I can't imagine all the ways people use the language.
And I can't remember all the ways I've used functions that return
a resource.
Yes, 99.99% sounds like a reasonable guesstimate.
> And if you do this many times, why not make custom type that itself have this
> attribute? if you need to guarantee that `int` can't be discarded in some nested
> callback hell then maybe the problem lies elsewhere? I see more
> as local helper for corner cases like `empty()` not to build new types.
Casts are always bad. Sometimes, they're necessary, but only because
the language is not well designed in that corner case. We should try to
reduce those cases, not add more.
>
> Beside if you allow attributes aspart of type system then you do not answer me:
>
> ```
> std::is_same_t<int, [[foo]] int>
> ```
>
> is true?
No, it should be false.
> Can I overload over attributes? Can I have template
> specialization based on it?
> How name mangling will handle it?
I'm a C programmer, and am happy to not need to answer that question.
Overloading is the largest problem of C++, IMO.
[...]
> > > > 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.
> >
>
> This is the same code as the previous example. This means my answer
> is related to this too. If I recall C would say is ok, I would say is not.
Yup, ISO C says this is okay. And indeed, that's the key for the
ignorability of restrict. If this had to be diagnosed, restrict
wouldn't be ignorable (at least for some meaning of ignorable).
The strtol(3) function uses restrict in its parameters, and it is fine
(and even common) to call strtol(p,&p,0).
I also agree with you that this should not be fine, and thus restrict
should be removed from the specification of strtol(3).
void f(int *p [[restrict]], int **pp [[restrict]]);
void g(int *p , int **pp );
Should &f and &g be used interchangably as function pointers? Of course
not, or we'd have the same issues as with [[nodiscard]]. We want
diagnostics for misuses. [[restrict]] should not be discarded without
a cast. Then, the ignorability of [[restrict]] is not a thing anymore.
Have a lovely night!
Alex
> But this depends on how you choose to model it.
>
> I prefer to look at this as related memory groups, not that these
> pointers overlap.
>
>
> btw we bit do offtomic as this thread is about nrvo, I could continue this but
> others would not like too much spam on only tangentially related topics.
>
> >
> > Cheers,
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es>
-- <https://www.alejandro-colomar.es>
Received on 2026-02-10 19:16:31
