Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Const raw pointer to usize cast allowed in release mode, disallowed in debug mode #80875

Closed
DutchGhost opened this issue Jan 10, 2021 · 2 comments · Fixed by #81779
Closed

Const raw pointer to usize cast allowed in release mode, disallowed in debug mode #80875

DutchGhost opened this issue Jan 10, 2021 · 2 comments · Fixed by #81779
Labels
A-const-eval Area: constant evaluation (mir interpretation) A-diagnostics Area: Messages for errors, warnings, and lints E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.

Comments

@DutchGhost
Copy link
Contributor

DutchGhost commented Jan 10, 2021

While making a PoC for #67456 it was discovered that the code beblow fails to compile in debug mode (#67456 (comment)), saying that "pointer-to-integer cast needs an rfc before being allowed in constants", while in release mode, the code compiles and runs.
Here's a playground link.

#![feature(const_ptr_is_null)]
#![feature(const_raw_ptr_to_usize_cast)]
#![feature(const_raw_ptr_deref)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_panic)]


mod slice {
    use core::mem;
    use core::ptr;
    
    pub(crate) const unsafe fn is_aligned_and_not_null<T>(ptr: *const T) -> bool {
        !ptr.is_null() && ptr as usize % mem::align_of::<T>() == 0
    }

    pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
        debug_assert!(
            is_aligned_and_not_null(data),
            "attempt to create unaligned or null slice"
        );
        debug_assert!(
            mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize,
            "attempt to create slice covering at least half the address space"
        );
        // SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
        unsafe { &*ptr::slice_from_raw_parts(data, len) }
    }
    
    pub const fn from_ref<T>(reference: &T) -> &[T] {
        unsafe {
            from_raw_parts(reference, 1)
        }
    }
}

const X: &[usize] = slice::from_ref(&1);

fn main() {
    dbg!(X);
}

Here's the error message in debug mode:

error: any use of this value will cause an error
  --> src/main.rs:13:27
   |
13 |         !ptr.is_null() && ptr as usize % mem::align_of::<T>() == 0
   |                           ^^^^^^^^^^^^
   |                           |
   |                           "pointer-to-integer cast" needs an rfc before being allowed inside constants
   |                           inside `is_aligned_and_not_null::<usize>` at src/main.rs:13:27
   |                           inside `slice::from_raw_parts::<usize>` at src/main.rs:18:13
   |                           inside `slice::from_ref::<usize>` at src/main.rs:31:13
   |                           inside `X` at src/main.rs:36:21
...
36 | const X: &[usize] = slice::from_ref(&1);
   | ----------------------------------------
   |
   = note: `#[deny(const_err)]` on by default

error: aborting due to previous error; 1 warning emitted

error: could not compile `playground`
@SkiFire13
Copy link
Contributor

If you change the first debug_assert to an assert it will fail to build in release mode too. Playground
I guess this is because in release mode the debug_assert is never executed and so the invalid function is never used in a const environment. Still I would expect it to fail anyway, so I think this is still a bug.

@camelid camelid added the A-const-eval Area: constant evaluation (mir interpretation) label Jan 11, 2021
@RalfJung
Copy link
Member

Cc @rust-lang/wg-const-eval

This is expected behavior. Casting a pointer to an integer sometimes works (when the pointer was created by casting an integer to a pointer), and sometimes doesn't. So this is a dynamic check performed when the code in question is being evaluated. That means there is no check in dead code.

So this is very similar to how debug_assert!(1/0 == 0) will only raise a "div-by-zero" error in debug builds, but not in release builds.

However, what could be improved is the error message. We should just say that "pointers cannot be cast to integers unless they were created from an integer"; or something like that.

@oli-obk oli-obk added A-diagnostics Area: Messages for errors, warnings, and lints E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. labels Jan 19, 2021
@bors bors closed this as completed in b263981 Feb 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: constant evaluation (mir interpretation) A-diagnostics Area: Messages for errors, warnings, and lints E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants