759 karma · joined February 5, 2020
int16_t a = 20000;
int16_t b = a + a;
But even ignoring size, it can bite you: unsigned int c = 1;
signed int d = -2;
if(c + d > 0)
puts("1 + -2 > 0");
That code prints because of balancing: The signed value gets cast to an unsized int, which can't hold the value.For not directly integer related semantics, creating an out-of-bounds pointer is UB, unless it points to one past the end of the object, in which case you can create the pointer but can't read from it. And if an object's lifetime has ended, pointers to it aren't valid anymore even if you don't even read from it:
void *foo_1 = malloc(1);
free(foo_1);
void *foo_2 = malloc(1);
printf("Checking for same allocatoin: %d", foo_1 == foo_2);
If you go by the standard, strictly speaking this is UB: #include <stdio.h>
int main(void) {
puts("Hello World);
}
But don't worry, real compilers will refuse to compile this. Most don't follow the standard anyways, because they don't treat writing to a memory location as side effect.A large source of gotchas in C are the standard libraries. For example here's a taste of the trouble its locales cause: https://github.com/mpv-player/mpv/commit/1e70e82baa9193f6f02...
The thing with a tighter bound on the lifetime is that VM carrying a lifetime for itself is fundamentally flawed. A solution would be to remove said lifetime, and have all methods on VM that return lifetimed objects take said lifetime from the reference to the VM.
As a result, when the VM gets dropped, the memory gets deallocated even if there are still objects that reference it.
Somewhat related, there also is the issue that when you create multiple VMs, you can use classes/objects/… created by one VM with another.
ctypes.c_int.from_address(id(8)+24).value = 9Besides, that's the safety of the code running inside the VM. Wasm is about the safety of the host.
main:
.cfi_startproc
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc bar.baz()\
.filter(some_filter)\
.map(some_op)\
.min()\
.foo()
Data/control flows from top to bottom. One operation per line. But with freestanding functions: min(map(some_op, filter(some_filter, bar.baz()))).foo()
To follow the flow of data/control, you start in the middle, go right, then skip left to filter, read rightwards to see which filter, skip left to map, read rightwards to see what map, go left to min, then skip all the way to the right. Just splitting it into multiple lines doesn't help, you need to introduce intermediate variables (and make sure they don't clobber any existing ones) and repeat yourself whether they clarify things or not. The same issue exists for list/dict/set comprehensions.