Types as Contracts

I basically got this upside down. My "brutal" proposal was:

  1. every address in memory has an "is dereferencable pointer" bit.
  2. if a pointer is loaded from an address without that bit, you get a non-dereferencable pointer.
  3. pointer stores preserve the bit status of the original pointer.
  4. integer stores always leave the bit clear.
  5. integer to pointer casts create a dereferencable pointer, and are the only way of creating a new dereferencable pointer out of nothing.
  6. memcpy leaves the bit as it was before.

note 1: this disregards unaligned accesses, but they can probably be handled too.
note 2: transmute is equivalent to a type-punned load.

Derivative properties:

  1. having the "dereferencable" bit set always causes less UB than having it clear. This means that a load/store pair can always be removed without making code less defined.
  2. loads of dereferencable pointers always sync against either an int->ptr cast or a primitive pointer operation.
  3. All integers with the same numeric value should be equivalent.
  4. memcpy can't be implemented in Rust because you can't copy the dereferencable bits manually, but rather needs to be a compiler intrinsic.
  5. int->ptr cast becomes a "side-effectful" unsimplifiable operation. I'm not sure how bad it is from an optimization perspective, but I don't see a good way around it (this might mean we want to shift users who merely want to smuggle an integer inside a pointer, like slice iterators, to use a type-punned load).