Skip to content

Should null().add(0) work? #299

Closed
Closed
@mcy

Description

@mcy

This question comes up a lot in C/C++ land. Here is the status quo:

  • C rejects the program NULL + 0 as ill-formed, because arithmetic must land inside of an "allocation".
  • C++ accepts NULL + 0, by special case, an defines it to be NULL.
  • LLVM defines getelementptr inbounds T, T* 0, i64 0 to be T* 0, again by special case.
    • Incidentally, Clang lowers p + 0 to getelementptr inbounds in both language modes.
  • Rust rejects the program ptr::null::<T>().add(0), for the same reasons as C.
    • Miri correctly rejects it as UB (Miri seems to special-case null here? Am I reading this right?)

IME this has caused quite a bit of pain in C, where you might want to write:

void Foo(char* p, size_t len) {
  char* end = p + len;
  for(; p != end; p++) { ... }
}

This is UB for any sensible choice of an empty slice, including p = NULL. While Rust gives us the tools to avoid writing such code, it is... somewhat questionable to follow C rather than C++ here. Like, you could use wrapping_add(), but honestly that seems a bit silly given that LLVM goes out of its way to make this work, and to my knowledge Clang's frontend doesn't care except for -fsanitize=null.

I get the impression this wasn't a conscious decision and more of an emergent property of "you can only do add() on valid pointers", but it'd be nice to document it if it was. Although you could infer that p != NULL if you write p + n in a vaccum, I've never seen a situation where being able to deduce this is useful optimization fuel. That said, I would not mind being proven wrong for when I have equivalent conversations in C/C++ land. =)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions