Date: Wed, 29 Apr 2026 07:31:10 +0200
Thanks Jens!
Am Dienstag, dem 28.04.2026 um 23:54 +0200 schrieb Jens Maurer:
>
> On 4/28/26 19:25, Martin Uecker wrote:
> > Am Dienstag, dem 28.04.2026 um 18:43 +0200 schrieb Jens Maurer:
> > >
> > > On 4/28/26 15:56, Martin Uecker wrote:
> > > > > In Aaron's and my alternate world, we'd need a more nuanced brush for
> > > > > "dropping qualifiers" when forming the signature of a function in C.
> > > > >
> > > > > void f(int * const); === void f(int *);
> > > > >
> > > > > But maybe don't drop the _Optional here when forming the signature:
> > > > >
> > > > > void f(int * _Optional);
> > > >
> > > > But "not dropping the qualifier" would follow naturally when putting
> > > > it on the pointee. So this would seem to give you the right semantics
> > > > using existing language mechanisms when putting on the pointee. So now
> > > > I am even more confused about what the problem is.
> > >
> > > I guess part of the problem is how much of _Optional is part of the
> > > type system.
> >
> > Whether one wants a type-system solution or not can certainly
> > be discussed.
> >
> > But I actually didn't want to discuss the overall design, but
> > understand the C++-specific issue which the papers tries to point out.
> > I am still completely at loss regarding this point.
> >
> > To me it seems that _Optional, as designed, would also do exactly
> > the right thing for templates.
>
> Let's try again, and with a more basic template,
> in a hypothetical world where C's _Optional feature is
> transplanted into C++.
(btw. an entirely plausible design could also be to simply
ingore C's _Optional in the type system)
>
> template<class T>
> int f(T *ptr);
>
> _Optional int *p;
> int x = f(p);
>
> What is the type of "T" here? Assuming _Optional works like "const",
> T = _Optional int.
Yes.
>
> Now, suppose f is defined as follows:
>
> template<class T>
> int f(T *ptr)
> {
> T x = 0;
> int y;
> return 0;
> }
>
> Is the definition of the local variable "x" valid? Its type is
> "_Optional int", and I believe the _Optional paper says that's
> ill-formed. It feels novel that a qualifier on a type can
> radically change the answer to the question "can I have an
> object of that type". (I suppose C can get to a similar place
> with its typeof operator.)
This is why C has typeof_unqual (which I now think should have
been typeof), so in C you might write a macro like
#define foo(p) \
do { \
auto q = (p); \
typeof_unqual(*q) x = 0; \
... \
} while();
So I wonder how C++ deals with volatile or const? While differently,
both would radically change the nature of the templated function
as well? For example, consider:
template<class T>
int f(T *ptr)
{
T x = 0;
x++; // breaks for const, inefficient for volatile
return x;
}
So in C++ you would also need to remove qualifiers explicitely
already.
On the other hand, consider this example.
template<class T>
int f(T x)
{
*x = 5;
}
_Optional int *p = ...
f(p);
You would certainly *want* this to fail because of the missing
null pointer check in "f" and similar for "const". Any
such annotation that does not do this, would be useless.
So to me it is clear that qualifiers need to propagate into
templates and this is the correct design for both "const"
and "_Optional".
> So, the possibly pre-existing template "f" above, when now used
> with an _Optional int*, is ill-formed in its definition,
> which used to work fine for T=int or T="const int".
>
> Can we put an overload of f triggering on the presence of
> _Optional next to the above "f"? Not really.
>
> template<class T>
> int f(_Optional T *ptr);
>
> doesn't work; the "T" must be the only type-specifier.
>
> > > > and for "_Optional"
> > > > it says that an access without prior null pointer check
> > > > is forbidden.
> > >
> > > And that seems qualitatively different to me: We (want to)
> > > diagnose the formation of *x (the lvalue) without a surrounding
> > > "if" guard, not the use of that lvalue afterwards.
> > >
> > > So, it's a restriction on stuff you can do with the pointer x,
> > > not on stuff you can do with the lvalue formed by *x.
> >
> > We would want to diagnose the unprotected access and not
> > already the formation of *x. So in both cases we
> > restrict how the lvalue *x can be accessed while the pointer
> > value itself can be passed around without any restriction.
>
> At least in C++, writing *x (where x is a null pointer)
> is undefined behavior, because it (colloquially) creates
> a "null lvalue", which is undefined behavior. Without any
> access via *x itself. So, it's not about "how the lvalue
> can be used"; there might not be an lvalue to start with.
Interesting, I didn't know there is a difference. But what does
"writing *x" mean? Clearly, sizeof(*x) is allowed? What
about &*x?
In C it is only when *x is evaluated, i.e. the pointer
is dereferenced.
Martin
Am Dienstag, dem 28.04.2026 um 23:54 +0200 schrieb Jens Maurer:
>
> On 4/28/26 19:25, Martin Uecker wrote:
> > Am Dienstag, dem 28.04.2026 um 18:43 +0200 schrieb Jens Maurer:
> > >
> > > On 4/28/26 15:56, Martin Uecker wrote:
> > > > > In Aaron's and my alternate world, we'd need a more nuanced brush for
> > > > > "dropping qualifiers" when forming the signature of a function in C.
> > > > >
> > > > > void f(int * const); === void f(int *);
> > > > >
> > > > > But maybe don't drop the _Optional here when forming the signature:
> > > > >
> > > > > void f(int * _Optional);
> > > >
> > > > But "not dropping the qualifier" would follow naturally when putting
> > > > it on the pointee. So this would seem to give you the right semantics
> > > > using existing language mechanisms when putting on the pointee. So now
> > > > I am even more confused about what the problem is.
> > >
> > > I guess part of the problem is how much of _Optional is part of the
> > > type system.
> >
> > Whether one wants a type-system solution or not can certainly
> > be discussed.
> >
> > But I actually didn't want to discuss the overall design, but
> > understand the C++-specific issue which the papers tries to point out.
> > I am still completely at loss regarding this point.
> >
> > To me it seems that _Optional, as designed, would also do exactly
> > the right thing for templates.
>
> Let's try again, and with a more basic template,
> in a hypothetical world where C's _Optional feature is
> transplanted into C++.
(btw. an entirely plausible design could also be to simply
ingore C's _Optional in the type system)
>
> template<class T>
> int f(T *ptr);
>
> _Optional int *p;
> int x = f(p);
>
> What is the type of "T" here? Assuming _Optional works like "const",
> T = _Optional int.
Yes.
>
> Now, suppose f is defined as follows:
>
> template<class T>
> int f(T *ptr)
> {
> T x = 0;
> int y;
> return 0;
> }
>
> Is the definition of the local variable "x" valid? Its type is
> "_Optional int", and I believe the _Optional paper says that's
> ill-formed. It feels novel that a qualifier on a type can
> radically change the answer to the question "can I have an
> object of that type". (I suppose C can get to a similar place
> with its typeof operator.)
This is why C has typeof_unqual (which I now think should have
been typeof), so in C you might write a macro like
#define foo(p) \
do { \
auto q = (p); \
typeof_unqual(*q) x = 0; \
... \
} while();
So I wonder how C++ deals with volatile or const? While differently,
both would radically change the nature of the templated function
as well? For example, consider:
template<class T>
int f(T *ptr)
{
T x = 0;
x++; // breaks for const, inefficient for volatile
return x;
}
So in C++ you would also need to remove qualifiers explicitely
already.
On the other hand, consider this example.
template<class T>
int f(T x)
{
*x = 5;
}
_Optional int *p = ...
f(p);
You would certainly *want* this to fail because of the missing
null pointer check in "f" and similar for "const". Any
such annotation that does not do this, would be useless.
So to me it is clear that qualifiers need to propagate into
templates and this is the correct design for both "const"
and "_Optional".
> So, the possibly pre-existing template "f" above, when now used
> with an _Optional int*, is ill-formed in its definition,
> which used to work fine for T=int or T="const int".
>
> Can we put an overload of f triggering on the presence of
> _Optional next to the above "f"? Not really.
>
> template<class T>
> int f(_Optional T *ptr);
>
> doesn't work; the "T" must be the only type-specifier.
>
> > > > and for "_Optional"
> > > > it says that an access without prior null pointer check
> > > > is forbidden.
> > >
> > > And that seems qualitatively different to me: We (want to)
> > > diagnose the formation of *x (the lvalue) without a surrounding
> > > "if" guard, not the use of that lvalue afterwards.
> > >
> > > So, it's a restriction on stuff you can do with the pointer x,
> > > not on stuff you can do with the lvalue formed by *x.
> >
> > We would want to diagnose the unprotected access and not
> > already the formation of *x. So in both cases we
> > restrict how the lvalue *x can be accessed while the pointer
> > value itself can be passed around without any restriction.
>
> At least in C++, writing *x (where x is a null pointer)
> is undefined behavior, because it (colloquially) creates
> a "null lvalue", which is undefined behavior. Without any
> access via *x itself. So, it's not about "how the lvalue
> can be used"; there might not be an lvalue to start with.
Interesting, I didn't know there is a difference. But what does
"writing *x" mean? Clearly, sizeof(*x) is allowed? What
about &*x?
In C it is only when *x is evaluated, i.e. the pointer
is dereferenced.
Martin
Received on 2026-04-29 05:31:14
