C's function prototypes, syntactic sugar added circa 1990, were transformative for C programming.
https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
It is fundamentally like the 80286 segments, but with all sorts of usability troubles solved. The 80286 segments were impractical because there were a small number available and because the OS couldn't safely hand over direct control. Every little segment adjustment required calling the OS.
> Google is committed to supporting MTE throughout the Android software stack. We are working with select Arm System On Chip (SoC) partners to test MTE support and look forward to wider deployment of MTE in the Android software and hardware ecosystem. Based on the current data points, MTE provides tremendous benefits at acceptable performance costs. We are considering MTE as a possible foundational requirement for certain tiers of Android devices.
https://security.googleblog.com/2019/08/adopting-arm-memory-...
> Starting in Android 11, for 64-bit processes, all heap allocations have an implementation defined tag set in the top byte of the pointer on devices with kernel support for ARM Top-byte Ignore (TBI). Any application that modifies this tag is terminated when the tag is checked during deallocation. This is necessary for future hardware with ARM Memory Tagging Extension (MTE) support.
>....
> This will disable the Pointer Tagging feature for your application. Please note that this does not address the underlying code health problem. This escape hatch will disappear in future versions of Android, because issues of this nature will be incompatible with MTE
https://source.android.com/devices/tech/debug/tagged-pointer...
So unless you have other official feedback from Google management, I will keep repeating myself.
I agree 100%. This also reminds me of this article:
https://nibblestew.blogspot.com/2020/03/its-not-what-program...
HN discussion: https://news.ycombinator.com/item?id=22696229
Promises/async functions in JS and C# do absolutely nothing that you couldn't do without them. But they've had a structural effect on the average developer's ability to write scalable code.
In some ways it would have worked much better. Header files can easily be wrong. The object files being linked are what really matter.
So, suppose we decided to implement this today, on a GNU toolchain. At the call site, we'd determine the parameter types based on the conventional promotions. This info gets put into the assembly output with an assembler directive. The assembler sees that, then encodes it in an ELF section. It might get a new section called ".calltype" or it is an extension to the symbol table or it involves DWARF. Similar information is produced for the function body. The linker comes along, compares the two, and accepts or rejects as appropriate.
It also isn't a requirement that C++ use mangled names. Other ways of carrying the type information are possible. I like the idea of a reference to DWARF debug info, which C++ is already using to support stack unwinding for exceptions.
Overloading requires the type information to be part of the symbol "name" (ie, whatever is used for symbol lookup and linking) wether that is a mangled string or more complex data structure.
More specifically, at the moment, it evaluates to the size of the pointer itself, which is useless. On the other hand;
static void
foo(int a[..])
{
for (size_t i = 0; i < (sizeof a / sizeof int); i++)
{
// ...
}
}
... would be very useful, as it's the same syntax you can already use inside the function where the array is declared, which makes refactoring code into separate functions easier, as you don't have to replace instances of sizeof with your new size_t parameter name.The only thing I'd like to see is compatibility with the static keyword; so that you can declare it as a sized-array but still indicate a compile-time minimum number of array elements. At the moment, in C99, this does not compile without serious diagnostics which would immediately highlight the problem:
#include <stdio.h>
static void
foo(int a[static 4])
{
for (size_t i = 0; i < 4; i++)
printf("%d\n", a[i]);
}
int
main(void)
{
int a[] = { 1, 2, 3 };
foo(a); // Passing an array with 3 elements to a function that requires at least 4 elements
foo(NULL); // Passing no array to a function that requires an array with at least 4 elements
return 0;
}
demo.c:14:3: warning: array argument is too small; contains 3 elements, callee requires at least 4 [-Warray-bounds]
demo.c:15:3: warning: null passed to a callee that requires a non-null argument [-Wnonnull]Yes, it's not a lot of effort to manually add a size_t argument. But it is far too tedious and error-prone to expect a programmer to add all the bounds checks. Being able to effortlessly tell the compiler "please check for me" is the huge win.
The second huge win is that the array is type-checked. So if you pass it to another function, the compiler enforces that it must again be passed with the size included. You don't get that by manually adding a size argument.
If you're worried about performance test it and turn it off.
Just terminating the program won't be much better than OOB memory access in many cases.
Continuing but discarding OOB writes/use a dummy for OOB reads could lead to much worse behavior.
An exception (or setting errno since this is C) would need that exception to be handled somewhere in a sensible way in which case you could just as easily add manual bounds checking.
So those programmers might just use the sugared approach and avoid the problem of writing past the end of an array, without ever knowing how tedious and/or difficult debugging such problems can be. They might sort of never even realize that they dodged a bullet simply due to some sugar.
How do you see it?
>extern void foo(size_t dim, char *a);
And the like assumes that I have the space to waste a native type on every array. So if I’m using a 10 length array, I need to provision a native 32 or 64 bit value for “10”.
In embedded system this wouldn’t happen. At least not mine, I’m running up on limits all over the place even being careful with bitfields and appropriately sized types.
He’s right of course that foo(array[]) is converted to a pointer but that’s why I think you should always use array as a pointer so YOU know not to rely on its automatic protections.
I get the point; but I just don’t see C making this change.
So, I go back to the idea that it seems unlikely this would ever be an official C change.
Currently, f(a[]) with declaration void f(int a[]) passes a pointer to the first element, with no additional overhead.
Under the proposal, f(a[]) with declaration void f(int a[]) passes a pointer to the first element, with no additional overhead.
Help me understand why "other people would care"? What is the negative impact on someone who would not use the a[..] functionality?