Interesting coding idea

T

Thad Smith

Dave said:
Static analysis can't catch all problems of this sort. Consider:
--------
/*Somewhere*/
void foo(int *a,int *b)
{
*a=(*b)++;
}

/*Somewhere else*/
void bar(int x)
{
/*Do some stuff, including:*/
foo(&x,&x);
}
--------

Good point, Dave. While that can be done by a diagnostic virtual
machine, it could also be done by a diagnostic compiler generating
native code, which would insert explicit tests in the generated code for
duplicate pointers which must be different for well-defined behavior.
The example code for the VM (snipped) required that the compiler
identify sequence points, a diagnostic-only feature. If the compiler
generates diagnostic code, it could generate tests in native code (or
function calls) as well.

In your example of pointers passed to a function, detection of the error
depends on the run-time behavior of the program. If pointers are chosen
in an apparently random manner, the code must actually execute the case
in which the pointers match in order to determine an error. I suppose
it is possible to make a static/dynamic diagnostic system that takes the
approach that if it can't prove that the pointers are different, and
there are multiple updates through the pointer pairs between sequence
points, it would issue a warning. It would then be up to the programmer
to prove to the compiler/interpreter than the pointers can't match or to
recode such that it doesn't matter.
--------
char buf[10];
char *str=some_string_pointer;
buf[0]='\0';
/*We want to warn about this (claimed buffer size larger than actual
destination buffer) even if str fits into buf
*/
strncat(buf,str,20);
--------

If a function gets a buffer size argument larger than the real buffer
size, that's a bug, even if what ends up being written into that buffer
does fit; we want to catch that bug as soon as possible even if the
behavior is actually well-defined until a user's cat starts sleeping
on the keyboard.
To protect against overflow, we really want
strncat(buf, str, sizeof(buf)-strlen(buf)-1);
That can be detected with static analysis, in some cases, as well. To
check dynamically for potential errors, we would verify that
len <= sizeof(buf)-strlen(buf)-1,
assuming that debug_strncat() has access to sizeof(buf).


If we're storing pointers as segment-offset-size, then a little bit of
implementation magic will give it the appropriate size.
Note that this isn't directly available to the code the programmer sees if
(as is likely) the buffer isn't a local or global array; buffers passed in
(as a pointer) from elsewhere or obtained from malloc are the ones most
likely to have mismatched sizes, and sizeof won't give the size of the
buffer in those cases.

I lean more towards a diagnostic compiler and library for catching these
types of errors. So pointers _could_ be implemented as an
(address,length) pair, which would include values returned by malloc.
This would allow a diagnostic implementation of strncat() to do the
checking mentioned above.
Once you're doing aggressive dynamic checking in the implementation's
runtime environment anyways, it's much simpler for all concerned to let
the library function check the sizes; it knows how buffer size and size
arguments are related, and has access to implementation magic to get at
the information it needs to check them.

Agreed.

Thad
 
M

Michael Mendelsohn

Thad said:
Good point, Dave. While that can be done by a diagnostic virtual
machine, it could also be done by a diagnostic compiler generating
native code, which would insert explicit tests in the generated code for
duplicate pointers which must be different for well-defined behavior.

In this case, shouldn't the programmer actually be the one to insert the
diagnostic?
A simple assert(a!=b) would do, and it would document to even the most
inexperienced reader that there's a precondition to using this function.
(With Douglas Adams, it would also serve to remind the _programmer_ that
there might be a better way to do this.)

With this kind of approach, the compiler ought to issue a warning unless
the assert statement is also present.

I somehow doubt that there are C compilers out there that actually use
the assert statements meaningfully.

Cheers
Michael
 
T

Thad Smith

Michael said:
In this case, shouldn't the programmer actually be the one to insert the
diagnostic?
A simple assert(a!=b) would do, and it would document to even the most
inexperienced reader that there's a precondition to using this function.

Such augmentation of code would help find the pesky conditions and MAY
help with overall clarity and reduction of errors.
With this kind of approach, the compiler ought to issue a warning unless
the assert statement is also present.

You run the risk of making the code overly cluttered with assertions
that might be obvious, for other reasons, to the programmer. For
example, if you wrote
int i;
...
i = diceroll();
i++;

your diagnostic compiler might complain if you didn't insert
assert (i < INT_MAX);
before incrementing i, but you know that diceroll() can't return
anything larger than 6.

The approach discussed earlier in the thread was on automating such
tests for existing code through a diagnostic virtual machine interpreter
and/or diagnostic compiler + library. Sometimes I think it makes sense
to have multiple levels of code: the code that specifies the work to be
done, optional code that can be enabled for different build versions,
the comments explaining all the higher-level considerations for the
human reader, and the tests / assertions that can be sprinkled in the
code to aid failure detection. Sometimes putting them all together
makes the code hard to read.

Thad
 
M

Michael Mendelsohn

Thad said:
You run the risk of making the code overly cluttered with assertions
that might be obvious, for other reasons, to the programmer. For
example, if you wrote
int i;
...
i = diceroll();
i++;

your diagnostic compiler might complain if you didn't insert
assert (i < INT_MAX);
before incrementing i, but you know that diceroll() can't return
anything larger than 6.

I suddenly understand that postconditions have a value that goes beyond
helping human readers and error detection in the postconditioned module
itself.
The approach discussed earlier in the thread was on automating such
tests for existing code through a diagnostic virtual machine interpreter
and/or diagnostic compiler + library.

I understand that.
I think "Design by Contract" ought to help here.
Sometimes I think it makes sense
to have multiple levels of code: the code that specifies the work to be
done, optional code that can be enabled for different build versions,
the comments explaining all the higher-level considerations for the
human reader, and the tests / assertions that can be sprinkled in the
code to aid failure detection. Sometimes putting them all together
makes the code hard to read.

I believe that's why the assertions get their own section in a function
definition in some languages.

Mind you, I'm no evangelist, in fact, I've never used a language that
had contracts designed into it from the outset (Eiffel comes to mind),
but they seem to be naturally suited if you want "more aggressive
testing" as the program is running.

Cheers
Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,150
Messages
2,570,853
Members
47,394
Latest member
Olekdev

Latest Threads

Top