Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #100795 > unrolled thread
| Started by | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| First post | 2017-01-01 09:53 -0800 |
| Last post | 2017-01-03 17:43 -0800 |
| Articles | 16 — 5 participants |
Back to article view | Back to comp.lang.c
Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-01 09:53 -0800
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-01 10:16 -0800
Re: Automatic null checks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-01-01 19:26 +0000
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-02 05:56 -0800
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-02 08:26 -0800
Re: Automatic null checks jacobnavia <jacob@jacob.remcomp.fr> - 2017-01-01 22:39 +0100
Re: Automatic null checks supercat@casperkitty.com - 2017-01-01 14:24 -0800
Re: Automatic null checks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-01-01 23:00 +0000
Re: Automatic null checks supercat@casperkitty.com - 2017-01-01 16:42 -0800
Re: Automatic null checks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-01-02 02:08 +0000
Re: Automatic null checks supercat@casperkitty.com - 2017-01-02 09:38 -0800
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-02 10:25 -0800
Re: Automatic null checks Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-01-02 18:48 +0000
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-02 07:45 -0800
Re: Automatic null checks Les Cargill <lcargill99@comcast.com> - 2017-01-03 07:11 -0600
Re: Automatic null checks Thiago Adams <thiago.adams@gmail.com> - 2017-01-03 17:43 -0800
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-01 09:53 -0800 |
| Subject | Automatic null checks |
| Message-ID | <a12ea453-3855-4ae0-a469-5d4a7e13de33@googlegroups.com> |
I am implementing the concept of null checking in
my static analyser.
I would like to know your thoughts.
Why null checks?
* For safety
* For performance
* Because the concept is already in your code
For safety because the code will be reviewed by a machine
to detect possible inconsistencies.
For performance, because some programmers don´t trust their
own review and make redundant checks for null.
This concept is already in your code, and sometimes we
document it with asserts.
These checks can be done with no changes in the language
using macro annotations.
At the same time, it can be considered as an extension to
C type system.
I added a new type qualifier '_opt' (optional - can be null)
similar of 'const' in the grammar.
I defined _opt as empty macro for C compilers and for the
static analyser it is defined as token '?'.
(There are more options to define of course)
Samples:
int * _own p; //p can be null
or if you want to see without macro
int * ? p; //p can be null
int * _own * p; //p is pointer not null to a pointer that can be null
int * p; //p is pointer that cannot be null
The last 'Type* p' is ambiguous in existing code,
because in some cases p can be null or not.
In the type system p can be null of course, but programmers
creates functions or paths where pointers cannot be
null in their logic. The concept of 'can be null' is already
there in our code, but not in the type system.
I want to use 'Type * p' as non null in my code.
Rules for the type system:
int * p1;
p1 = NULL; //error you cannot assign null-pointer for p
p1 = 0;
p1 = (void*)0;
//warning: is like to compare if an unsigned number is < 0
if (p1 == NULL ) { }
int * _opt p2;
p2 = NULL; //ok
p2 = 0; //ok
Theses checks are very easy to do.
More interesting checks are when we have to check the paths:
if (p1 != NULL)
{
// void F(int *p);
// p1 is not null here, OK
F(p1);
}
// p1 can be null here!
F(p1);
I am calling this type of qualifier as 'path-dependent qualifier'
because the type conversions depends where you are in the code.
Something I am not sure is if these type qualifiers should be
propagated automatically.
For instance:
int * _opt F2();
int * p1 = F2(); //p1 is _opt
int * p2 = NULL; //p2 is _opt
These remove ambiguity for existing code without
any annotation.
Today, const doesn't have this automatic-propagation.
X *p = ReturnsConstXPointer();
//invalid conversion from 'const X*' to 'X*
So, there are two options.
1) We can require annotations to make types compatible
2) automatic propagation of 'path-dependent qualifiers'
Other differences from const is that const does not depends
on paths.
So, for this qualifier (_opt) we already have implicit
type conversions.
void F(int * _opt p)
{
if (p1 != NULL)
{
// void F2(int *p);
F(p1); //(int *) cast is implicit in this path
}
}
I have more qualifiers with similar properties,
but I think it's better to see each one individually
because the subject can grown.
These qualifiers that are 'path-sensible' or 'path-dependent '
opens the door in the type system for many other cases.
For instance, 'initialized', 'destroyed' or 'moved' is a property
that is path-dependent.
We can think is other simple cases:
void F2(unsigned int i);
void F(int i)
{
if (i >= 0)
{
F2(i);
}
F2(i);
}
I think this is an interesting topic and it's something
that can be done/used today without any changing in
the language and at the same time it can be something
consistent aiming to extend the C type system.
[toc] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-01 10:16 -0800 |
| Message-ID | <9f2bc2b7-ddb9-49d3-ae85-b23141cbefb4@googlegroups.com> |
| In reply to | #100795 |
On Sunday, January 1, 2017 at 3:53:30 PM UTC-2, Thiago Adams wrote:
[...]
> I have more qualifiers with similar properties,
> but I think it's better to see each one individually
> because the subject can grown.
>
> These qualifiers that are 'path-sensible' or 'path-dependent '
> opens the door in the type system for many other cas
One more 'path-dependent qualifier' is '_uninitialized'
void X_Init(X * _uninitialized x);
X x;
X_Init(&x); //ok
X_Init(&x); //error
I think the sample is easy to understand.
The _uninitialized qualifier tells us that the previous
state of the variable cannot be used. But we can
assign the variable.
int _uninitialized i;
if (i != 0) //error
{
}
i = 1;
if (i != 0) //ok now I is 'initialized'
{
}
int i = 1; // 'initialized' (auto-propagation)
int i; // _uninitialized (auto-propagation)
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-01-01 19:26 +0000 |
| Message-ID | <87r34moa3a.fsf@bsb.me.uk> |
| In reply to | #100797 |
Thiago Adams <thiago.adams@gmail.com> writes: > On Sunday, January 1, 2017 at 3:53:30 PM UTC-2, Thiago Adams wrote: > [...] >> I have more qualifiers with similar properties, >> but I think it's better to see each one individually >> because the subject can grown. >> >> These qualifiers that are 'path-sensible' or 'path-dependent ' >> opens the door in the type system for many other cas > > One more 'path-dependent qualifier' is '_uninitialized' > > void X_Init(X * _uninitialized x); > > X x; > > X_Init(&x); //ok > > X_Init(&x); //error This one seems to be in the wrong place. x is initialised in both cases. Don't you mean that x can point to an uninitialised X? (But then I don't understand the difference between the two calls.) <snip> -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-02 05:56 -0800 |
| Message-ID | <ea19bb39-af47-47cb-a30b-4dcccc78912e@googlegroups.com> |
| In reply to | #100804 |
On Sunday, January 1, 2017 at 5:26:39 PM UTC-2, Ben Bacarisse wrote: > Thiago Adams writes: > > > On Sunday, January 1, 2017 at 3:53:30 PM UTC-2, Thiago Adams wrote: > > [...] > >> I have more qualifiers with similar properties, > >> but I think it's better to see each one individually > >> because the subject can grown. > >> > >> These qualifiers that are 'path-sensible' or 'path-dependent ' > >> opens the door in the type system for many other cas > > > > One more 'path-dependent qualifier' is '_uninitialized' > > > > void X_Init(X * _uninitialized x); > > > > X x; > > > > X_Init(&x); //ok > > > > X_Init(&x); //error > > This one seems to be in the wrong place. x is initialised in both > cases. Don't you mean that x can point to an uninitialised X? (But > then I don't understand the difference between the two calls.) > Yes, thanks. I did two mistakes in this sample: First the modifier _uninitialized is for X (not for the pointer). void X_Init(_uninitialized X * x); void X_Other(X * x); X x1; X_Init(&x1); //ok X x2; X_Other(&x2); //error The second problem is that given the function Init the static analyser cannot understand that this function will initialize x. For the operator = the effect is known. The destructor 'X_Destroy' also have a similar problem. For now, I am using a type qualifier _dtor to tell the effect. I can create a type qualifier _init. So both _init and _dtor tells not only property of types but they teach the static analyser what happens with the variable. They are an especial class of qualifiers that tells what happens not what they are. void X_Init(_uninitialized X * _init p); void X_Destroy(X * _dtor p);
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-02 08:26 -0800 |
| Message-ID | <47c85e87-fd00-405b-92c2-db9088905a71@googlegroups.com> |
| In reply to | #100874 |
On Monday, January 2, 2017 at 11:57:10 AM UTC-2, Thiago Adams wrote: [...] > The destructor 'X_Destroy' also have a similar problem. > For now, I am using a type qualifier _dtor to tell the effect. > I can create a type qualifier _init. > So both _init and _dtor tells not only property of types but they > teach the static analyser what happens with the variable. > They are an especial class of qualifiers that tells what > happens not what they are. > > void X_Init(_uninitialized X * _init p); > void X_Destroy(X * _dtor p); The problem to understand the behavior of some function's interface is that sometimes the effect is conditioned with the function result. Result X_Init(X* _init p); if result == ok then the p was initialized. I guess this kind of annotation is more for "design-by-contract programming" than type-system topic.
[toc] | [prev] | [next] | [standalone]
| From | jacobnavia <jacob@jacob.remcomp.fr> |
|---|---|
| Date | 2017-01-01 22:39 +0100 |
| Message-ID | <o4bsrh$ns0$1@dont-email.me> |
| In reply to | #100797 |
Le 01/01/2017 à 19:16, Thiago Adams a écrit :
> On Sunday, January 1, 2017 at 3:53:30 PM UTC-2, Thiago Adams wrote:
> [...]
>> I have more qualifiers with similar properties,
>> but I think it's better to see each one individually
>> because the subject can grown.
>>
>> These qualifiers that are 'path-sensible' or 'path-dependent '
>> opens the door in the type system for many other cas
>
> One more 'path-dependent qualifier' is '_uninitialized'
>
> void X_Init(X * _uninitialized x);
>
> X x;
>
> X_Init(&x); //ok
>
> X_Init(&x); //error
>
>
> I think the sample is easy to understand.
> The _uninitialized qualifier tells us that the previous
> state of the variable cannot be used. But we can
> assign the variable.
>
> int _uninitialized i;
>
> if (i != 0) //error
> {
> }
>
> i = 1;
>
> if (i != 0) //ok now I is 'initialized'
> {
> }
>
> int i = 1; // 'initialized' (auto-propagation)
> int i; // _uninitialized (auto-propagation)
>
>
>
>
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-01-01 14:24 -0800 |
| Message-ID | <33078933-d344-4aa3-b6ea-e657c7e28cb5@googlegroups.com> |
| In reply to | #100795 |
On Sunday, January 1, 2017 at 11:53:30 AM UTC-6, Thiago Adams wrote: > I am implementing the concept of null checking in > my static analyser. A fundamental difficulty with non-nullable pointer/reference types, which renders absurd the notion that null pointers were a "billion-dollar mistake", is that it will often be necessary to read some items out of an array of pointers before all members of the array have been written, and *something* has to happen if code attempts to read from an array element which has not been written. Having something predictable happen is almost certainly better than having something unpredictable happen (the benefits may in come cases not be sufficient to justify the costs, but even in those cases the unpredictable behavior would merely be tolerable, not desirable). While it would be possible to do without null pointers if arrays of pointers were initialized with the address of some dummy object, but that wouldn't really help anything. Trying to use a pointer to the dummy object would not be so "obviously" wrong as trying to use a null pointer, but would very seldom be in any way "right". From a practical standpoint, if a language implementer wants to avoid weird and wacky consequences of null pointer access, the best thing an implementer can do is trap all attempts to dereference a null pointer add or subtract any non-zero integer to/from a null pointer, and maybe also trap attempts to subtract a null pointer from any non-null pointer or vice versa. If a pointer is non-null, adding or subtracting an integer from it will generally not yield a null pointer unless the integer is grossly out of range, so a compiler will in many cases be able to hoist null checks outside loops, limiting their performance impact. If a compiler performs the null checks as indicated, improper attempts to use null pointers may cause Abnormal Program Termination, but would not otherwise summon nasal demons or do anything else nasty.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-01-01 23:00 +0000 |
| Message-ID | <87y3yumln1.fsf@bsb.me.uk> |
| In reply to | #100824 |
supercat@casperkitty.com writes:
> On Sunday, January 1, 2017 at 11:53:30 AM UTC-6, Thiago Adams wrote:
>> I am implementing the concept of null checking in
>> my static analyser.
>
> A fundamental difficulty with non-nullable pointer/reference types, which
> renders absurd the notion that null pointers were a "billion-dollar mistake",
> is that it will often be necessary to read some items out of an array of
> pointers before all members of the array have been written, and *something*
> has to happen if code attempts to read from an array element which has not
> been written.
But something can (and should) always be written. Other languages deal
with this using a discriminated union or "maybe" type of some sort and
the array is always initially filled with values of the "nothing" type.
The only way to access an element is with a type-checked case statement
(or expression) of some form:
for (int i = 0; i < A.len; i++)
case A[i] of
type nothing: skip;
type some p: process_pointer(p);
Apologies if this syntax is not clear, I just make it up.
The key difference is that the compiler can check that a NULL pointer is
never used.
<skip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-01-01 16:42 -0800 |
| Message-ID | <08b6c636-00d1-4f36-805a-9b2b5ef53f7e@googlegroups.com> |
| In reply to | #100829 |
On Sunday, January 1, 2017 at 5:00:09 PM UTC-6, Ben Bacarisse wrote: > supercat writes: > > A fundamental difficulty with non-nullable pointer/reference types, which > > renders absurd the notion that null pointers were a "billion-dollar mistake", > > is that it will often be necessary to read some items out of an array of > > pointers before all members of the array have been written, and *something* > > has to happen if code attempts to read from an array element which has not > > been written. > > But something can (and should) always be written. Other languages deal > with this using a discriminated union or "maybe" type of some sort and > the array is always initially filled with values of the "nothing" type. The problem is that it's generally not possible to statically ensure that no element of the array will be written without having been read first, except by imposing severe restrictions on the order in which things are done (E.g. requiring that all elements be written in a loop which is statically verifiable as writing all of them, and no element is read before or within that loop except using forms that can be statically verified as only accessing subscripts which have previously been written). Even if code isn't supposed to read elements that haven't been written, a compiler which is trying to offer any kind of safety will have to be prepared for the possibility that it might (it would be silly for a compiler to try to offer static safety, but then have programs jump the rails, rather than terminating in controlled fashion, if e.g. an array which is supposed to hold a permutation of the numbers 0 to 49 instead has a duplicated value and a missing value and, as a consequence, the array element corresponding to the missing value never gets written).
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-01-02 02:08 +0000 |
| Message-ID | <878tqumcxg.fsf@bsb.me.uk> |
| In reply to | #100838 |
supercat@casperkitty.com writes: > On Sunday, January 1, 2017 at 5:00:09 PM UTC-6, Ben Bacarisse wrote: >> supercat writes: >> > A fundamental difficulty with non-nullable pointer/reference types, which >> > renders absurd the notion that null pointers were a "billion-dollar mistake", >> > is that it will often be necessary to read some items out of an array of >> > pointers before all members of the array have been written, and *something* >> > has to happen if code attempts to read from an array element which has not >> > been written. >> >> But something can (and should) always be written. Other languages deal >> with this using a discriminated union or "maybe" type of some sort and >> the array is always initially filled with values of the "nothing" type. > > The problem is that it's generally not possible to statically ensure that > no element of the array will be written without having been read first, > except by imposing severe restrictions on the order in which things are > done (E.g. requiring that all elements be written in a loop which is > statically verifiable as writing all of them, and no element is read > before or within that loop except using forms that can be statically > verified as only accessing subscripts which have previously been written). > Even if code isn't supposed to read elements that haven't been written, > a compiler which is trying to offer any kind of safety will have to be > prepared for the possibility that it might (it would be silly for a > compiler to try to offer static safety, but then have programs jump the > rails, rather than terminating in controlled fashion, if e.g. an array > which is supposed to hold a permutation of the numbers 0 to 49 instead > has a duplicated value and a missing value and, as a consequence, the > array element corresponding to the missing value never gets written). I can't unpick that. Are you changing the "fundamental difficulty" into "making it type-safe will be less efficient"? -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-01-02 09:38 -0800 |
| Message-ID | <ae340449-b836-408f-b390-0916eadf49a4@googlegroups.com> |
| In reply to | #100848 |
On Sunday, January 1, 2017 at 8:08:17 PM UTC-6, Ben Bacarisse wrote: > supercat writes: > > The problem is that it's generally not possible to statically ensure that > > no element of the array will be written without having been read first, > > except by imposing severe restrictions on the order in which things are > > done (E.g. requiring that all elements be written in a loop which is > > statically verifiable as writing all of them, and no element is read > > before or within that loop except using forms that can be statically > > verified as only accessing subscripts which have previously been written). > > Even if code isn't supposed to read elements that haven't been written, > > a compiler which is trying to offer any kind of safety will have to be > > prepared for the possibility that it might (it would be silly for a > > compiler to try to offer static safety, but then have programs jump the > > rails, rather than terminating in controlled fashion, if e.g. an array > > which is supposed to hold a permutation of the numbers 0 to 49 instead > > has a duplicated value and a missing value and, as a consequence, the > > array element corresponding to the missing value never gets written). > > I can't unpick that. Are you changing the "fundamental difficulty" into > "making it type-safe will be less efficient"? There are many situations where it will be necessary to read some items out of an array of pointers before any objects *exist* to which some other items could meaningfully point. A common pattern, for example, is to have an array which is large enough to hold all the pointers that may need to be stored, along with a count of how many have been stored so far. If there won't be any nulls in the portion of the array from 0 to count-1, and if code shouldn't be accessing any elements beyond that, I don't think any useful purpose would be served by requiring that code which reads array items explicitly acknowledge the possibility of nulls, nor that code which creates the array must populate every slot with a non-null pointer that code will never use unless something goes wrong. At minimum, when adding such constructs to a language, it's important to be very clear about whether they are expected to be robust or leaky; if a compiler would be entitled to assume that a pointer will never be null, that could enable some optimizations, but such optimizations could undermine user-written safety-check code which would otherwise trap if a pointer is unexpectedly null. I could see uses for "non-nullable" pointer types in an implementation which is designed to ensure that improper use of a null pointer will fire an implementation-defined trap whose default behavior would be Abnormal Program Termination. Such types could give an implementation flexibility with regard to where null checks should be performed. I don't see any way to allow for robust static validation, however, and I would be concerned that leaky static validation could be worse than none at all.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-02 10:25 -0800 |
| Message-ID | <18e35fb2-998b-461d-9603-1bf89217eb86@googlegroups.com> |
| In reply to | #100883 |
On Monday, January 2, 2017 at 3:38:39 PM UTC-2, supe...@casperkitty.com wrote:
[...]
>I don't see any way to allow for
> robust static validation, however, and I would be concerned that leaky static
> validation could be worse than none at all.
In case the static analyser cannot determine if the pointer is null it will emit a warning message and the programmer is responsible.
It's simple to find an example:
typedef struct {
Type * _opt pPointer;
} X;
#define X_INIT {NULL}
void X_Start(X* x)
{
//Type* Type_CreatePointer();
x->pPointer = Type_CreatePointer();
}
void X_Other(X* x)
{
//is x->pPointer null?
x->pPointer
}
The documentation says "You must call X_Start before any use of X, if you don't do it your program will crash".
To be consistent with this documentation:
void X_Other(X* x)
{
//call start before!
(Type*)(x->pPointer)
}
On the other hand, if the X object can handle the null state.
void X_Other(X* x)
{
if (x->pPointer != NULL)
{
//ok
x->pPointer
}
}
I try to break big problems in small ones, and preferentially,
independent from each other.
We build functions and data types.
But there are some clue between these parts that
cause some coupling. This clue is the contract between
function calls.
Having some automatic check for us, you can review functions individually.
Let's say you are reviewing this function that will
be used in a rocket.
void F(X * p)
{
p->i = 1;
}
Then you have to review all calls to this function, right?
This piece of code is depending on callers.
Programmers use to do some checks with macros:
void F(X * p)
{
REQUIRE_NON_NULL(p);
p->i = 1;
F2(p)
}
and then if F calls F2
void F2(X * p)
{
REQUIRE_NON_NULL(p);
...
}
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-01-02 18:48 +0000 |
| Message-ID | <871swll2ma.fsf@bsb.me.uk> |
| In reply to | #100883 |
supercat@casperkitty.com writes: > On Sunday, January 1, 2017 at 8:08:17 PM UTC-6, Ben Bacarisse wrote: >> supercat writes: >> > The problem is that it's generally not possible to statically ensure that >> > no element of the array will be written without having been read first, >> > except by imposing severe restrictions on the order in which things are >> > done (E.g. requiring that all elements be written in a loop which is >> > statically verifiable as writing all of them, and no element is read >> > before or within that loop except using forms that can be statically >> > verified as only accessing subscripts which have previously been written). >> > Even if code isn't supposed to read elements that haven't been written, >> > a compiler which is trying to offer any kind of safety will have to be >> > prepared for the possibility that it might (it would be silly for a >> > compiler to try to offer static safety, but then have programs jump the >> > rails, rather than terminating in controlled fashion, if e.g. an array >> > which is supposed to hold a permutation of the numbers 0 to 49 instead >> > has a duplicated value and a missing value and, as a consequence, the >> > array element corresponding to the missing value never gets written). >> >> I can't unpick that. Are you changing the "fundamental difficulty" into >> "making it type-safe will be less efficient"? > > There are many situations where it will be necessary to read some items out > of an array of pointers before any objects *exist* to which some other > items could meaningfully point. You are simply stating the problem with nullable pointers. I sketched out how other languages get round this situation. You can't use an array of pointers because, since they can't be null, there is nothing for them to point at. <snip> -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-02 07:45 -0800 |
| Message-ID | <f5885913-14cf-4399-b1a3-0e5f08b0815b@googlegroups.com> |
| In reply to | #100824 |
On Sunday, January 1, 2017 at 8:24:41 PM UTC-2, supe...@casperkitty.com wrote: > On Sunday, January 1, 2017 at 11:53:30 AM UTC-6, Thiago Adams wrote: > > I am implementing the concept of null checking in > > my static analyser. > > A fundamental difficulty with non-nullable pointer/reference types, which > renders absurd the notion that null pointers were a "billion-dollar mistake", > is that it will often be necessary to read some items out of an array of > pointers before all members of the array have been written, and *something* > has to happen if code attempts to read from an array element which has not > been written. Having something predictable happen is almost certainly > better than having something unpredictable happen (the benefits may in come > cases not be sufficient to justify the costs, but even in those cases the > unpredictable behavior would merely be tolerable, not desirable). > > While it would be possible to do without null pointers if arrays of > pointers were initialized with the address of some dummy object, but > that wouldn't really help anything. Trying to use a pointer to the > dummy object would not be so "obviously" wrong as trying to use a null > pointer, but would very seldom be in any way "right". > > From a practical standpoint, if a language implementer wants to avoid > weird and wacky consequences of null pointer access, the best thing an > implementer can do is trap all attempts to dereference a null pointer > add or subtract any non-zero integer to/from a null pointer, and maybe > also trap attempts to subtract a null pointer from any non-null pointer > or vice versa. If a pointer is non-null, adding or subtracting an integer > from it will generally not yield a null pointer unless the integer is > grossly out of range, so a compiler will in many cases be able to hoist > null checks outside loops, limiting their performance impact. If a > compiler performs the null checks as indicated, improper attempts to > use null pointers may cause Abnormal Program Termination, but would not > otherwise summon nasal demons or do anything else nasty. This seams to be an special case. The proposed type-qualifiers can be cast to something else any time. There is no runtime overhead. For 'Type * _opt p' sometimes the static analyser cannot see if the pointer is null or not. Where appropriate the programmer should use if (p != NULL) or cast (Type*) . The typescript language already has the 'Non-nullable types' I am using it as reference (but with changes of course for C). Typescript don't use qualifiers but 'union types'. https://github.com/Microsoft/TypeScript/pull/7140 I am also using this implementation to see how smart are the path checkers. Something I spot that is different, is that typescript don't generate warning if you compare a non-null with null. Going back again, I want to say one difference between non-null type and nullable-type (optional). The non-null type is something that doesn't depends on paths. It's like const. (easy to implement) The nullable type is harder because we need to follow the possible paths.
[toc] | [prev] | [next] | [standalone]
| From | Les Cargill <lcargill99@comcast.com> |
|---|---|
| Date | 2017-01-03 07:11 -0600 |
| Message-ID | <o4g7p6$h04$1@dont-email.me> |
| In reply to | #100795 |
Thiago Adams wrote: > > I am implementing the concept of null checking in > my static analyser. > > I would like to know your thoughts. > > Why null checks? > > * For safety > * For performance > * Because the concept is already in your code > <snip> > > I think this is an interesting topic and it's something > that can be done/used today without any changing in > the language and at the same time it can be something > consistent aiming to extend the C type system. > > Extending the C type system means it's really fundamentally no longer C. I'd put my efforts into the languages which aim to replace C. -- Les Cargill
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2017-01-03 17:43 -0800 |
| Message-ID | <bb0f8203-e96b-4a05-bfc5-ec067c2162b0@googlegroups.com> |
| In reply to | #100974 |
On Tuesday, January 3, 2017 at 11:10:34 AM UTC-2, Les Cargill wrote: > Thiago Adams wrote: > > > > I am implementing the concept of null checking in > > my static analyser. > > > > I would like to know your thoughts. > > > > Why null checks? > > > > * For safety > > * For performance > > * Because the concept is already in your code > > > <snip> > > > > I think this is an interesting topic and it's something > > that can be done/used today without any changing in > > the language and at the same time it can be something > > consistent aiming to extend the C type system. > > > > > > Extending the C type system means it's really fundamentally > no longer C. > > I'd put my efforts into the languages which aim to replace C. > Programmers trust C and C compilers for critical applications. They would trust this static analyser because it doesn't have any effect on the generated code. All qualifiers can be defined as empty for the normal C compiler. I cannot say the same for a new language or for C++ template classes for type annotation. ( not_null - https://github.com/Microsoft/GSL/blob/master/gsl/gsl ) const was added into C language at some point. Someone could say that its no more C because the type system has changed. The C XX language is what the standard says it is.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web