Rust-GCC / gccrs

GCC Front-End for Rust
https://rust-gcc.github.io/
GNU General Public License v2.0
2.37k stars 151 forks source link

Pointer semantics are the exact same as C #2844

Open CohenArthur opened 7 months ago

CohenArthur commented 7 months ago

Per a conversation I've had on Zulip: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Rust.20Has.20Provenance.20RFC/near/420422460

Translating "bad but compliant with the C standard" C code into Rust shows that we have the same pointer semantics as the C language, while rustc differs greatly and has less undefined behavior in the example below. This is the proper Rust version, re-add commented out lines for the gccrs compatible one.

// #![feature(intrinsics)]

// #[lang = "sized"]
// pub trait Sized {}

static Y: i32 = 2;
static X: i32 = 1;

extern "C" {
    fn printf(fmt: *const i8, ...);
}

// extern "rust-intrinsic" {
//     fn transmute<T, U>(_: T) -> U;
//     fn size_of<T>() -> usize;
// }

use std::mem::transmute;

fn ptr_to_usize(x: *const i32) -> usize {
    unsafe { transmute::<*const i32, usize>(x) }
}

fn usize_to_ptr(x: usize) -> *const i32 {
    unsafe { transmute::<usize, *const i32>(x) }
}

fn bugzilla_report() {
    let p: *const i32;
    p = usize_to_ptr(ptr_to_usize(&X as *const i32) + 4);

    unsafe {
        printf(
            "&X=%p  &Y=%p  p=%p\n\0" as *const str as *const i8,
            &X as *const i32,
            &Y as *const i32,
            p,
        )
    }

    let b1 = p == &Y as *const i32;

    unsafe {
        printf(
            "(p==&Y) = %d\n\0" as *const str as *const i8,
            if b1 { 1 } else { 0 },
        )
    }
}

fn godbolt_example() {
    let p2 = [42 as u8];
    let p1 = [42 as u8];

    unsafe {
        printf(
            "p1: %p, p2: %p\n\0" as *const str as *const i8,
            transmute::<_, *const char>(&p1),
            transmute::<_, *const char>(&p2),
        )
    }

    let p1_ptr = usize_to_ptr(ptr_to_usize(unsafe { transmute::<_, *const i32>(&p1) }) + 1);
    let p2_ptr = unsafe { transmute::<_, *const i32>(&p2) };

    unsafe {
        if p2_ptr == p1_ptr {
            printf("1st attempt: pointers are equal\n\0" as *const str as *const i8)
        } else {
            printf("1st attempt: pointers are not equal\n\0" as *const str as *const i8)
        }

        if ptr_to_usize(p1_ptr) == ptr_to_usize(p2_ptr) {
            printf("2nd attempt: pointers are equal\n\0" as *const str as *const i8)
        } else {
            printf("2nd attempt: pointers are not equal\n\0" as *const str as *const i8)
        }
    }
}

fn main() {
    bugzilla_report();
    godbolt_example();
}

results:

arthur@platypus ~/G/r/gccrs (master)> rustc -C opt-level=2 ub-rust.rs
arthur@platypus ~/G/r/gccrs (master)> ./ub-rust
&X=0x5555aab59004  &Y=0x5555aab59000  p=0x5555aab59008
(p==&Y) = 0
p1: 0x7fff3d4594fe, p2: 0x7fff3d4594ff
1st attempt: pointers are equal
2nd attempt: pointers are equal
arthur@platypus ~/G/r/gccrs (master)> gccrs ub-rust.rs -O2 -o ub-or-not
arthur@platypus ~/G/r/gccrs (master)> ./ub-or-not
&X=0x404020  &Y=0x404024  p=0x404024
(p==&Y) = 1
p1: 0x7ffc0434ca3e, p2: 0x7ffc0434ca3f
1st attempt: pointers are not equal
2nd attempt: pointers are equal
CohenArthur commented 7 months ago

this ties in with the semantics checking project we have for this year's GSoC