Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #100795 > unrolled thread

Automatic null checks

Started byThiago Adams <thiago.adams@gmail.com>
First post2017-01-01 09:53 -0800
Last post2017-01-03 17:43 -0800
Articles 16 — 5 participants

Back to article view | Back to comp.lang.c


Contents

  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

#100795 — Automatic null checks

FromThiago Adams <thiago.adams@gmail.com>
Date2017-01-01 09:53 -0800
SubjectAutomatic 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]


#100797

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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]


#100804

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-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]


#100874

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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]


#100880

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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]


#100817

Fromjacobnavia <jacob@jacob.remcomp.fr>
Date2017-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]


#100824

Fromsupercat@casperkitty.com
Date2017-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]


#100829

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-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]


#100838

Fromsupercat@casperkitty.com
Date2017-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]


#100848

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-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]


#100883

Fromsupercat@casperkitty.com
Date2017-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]


#100888

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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]


#100891

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-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]


#100878

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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]


#100974

FromLes Cargill <lcargill99@comcast.com>
Date2017-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]


#101071

FromThiago Adams <thiago.adams@gmail.com>
Date2017-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