1. 14
  1. 4

    .nonAliasValue() is gonna cost someone a weekend in 3 years.

    1. 3

      This is the main reason that I prefer C++ to C for systems programming (Rust would also be good, for the same reason). When doing low-level things, you are very likely to be part of the TCB and having a language that lets you create templates that wrap pointers and convey intent is essential. For example, in snmalloc we have different pointer types for pointers that have come from the user, pointers that should refer to an allocation and pointers that refer to a slab and disallow any implicit casts between them. Some can only be done via hooks at the integration layer that we can use to guarantee things like setting bounds on a CHERI system or recolouring memory with MTE. We’ve caught several bugs at compile time that would have needed debugging in production in a C version.

      1. 1

        I guess it would be cumbersome, but, couldn’t you use the following in C?

        struct user_ptr { void *p; };
        struct slab_ptr { void *p; };

        Or perhaps this is why you need templates, so you can do stuff regardless of pointer type?

        template <typename T>
        void do_stuff_with(T ptr) {
            // Do stuff with ptr.p
            // Works with user_ptr.
            // Works with slab_ptr too.
        1. 4

          I guess it would be cumbersome, but, couldn’t you use the following in C?

          You probably could, as long as you have only one kind of thing. The problem that we hit is that we need to convey two dimensions in the type system. In C, the second aspect of that gets erased to void unless you make a load of these different structures. Our CapPtr abstraction models four dimensions including the pointee type. If we expanded them all by hand then we’d have dozens of C structures and it would be very complex to encode the valid transitions in that state space. In contrast, with templates we can model transitions on each dimension independently. For example, any pointer coming from the user is wild and can be transitioned to tame by calling the hook that validates it, without caring what its other dimensions are. With C, we’d have to explicitly write conversion functions for a load of different structures to others. We could do this with macros, but then there’s no type checking and it would be error prone.