Skip to content

Storing a function pointer in a const usize works on nightly #51559

@gnzlbg

Description

@gnzlbg
Contributor

Some people were of the opinion that this shouldn't work, but it currently does. playground:

#![feature(atomic_integers, const_fn)]
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;

const unsafe fn transmute(x: *mut u8) -> usize {
    union T {
        a: *mut u8,
        b: usize
    }
    T { a: x }.b
}

const BAR: *mut u8 = ((|| 3) as fn() -> i32) as *mut u8;
const FOO: AtomicUsize = AtomicUsize::new(unsafe { transmute(BAR) });
// static FOO: AtomicUsize = AtomicUsize::new(unsafe { transmute(BAR) }); // ALSO OK
// const BAZ: AtomicUsize = AtomicUsize::new(BAR as usize); ERRORs

fn main() {
    let l = FOO.load(Ordering::Relaxed);
    let l: fn() -> i32 = unsafe { std::mem::transmute(l) };
    assert_eq!(l(), 3);
}

cc @rkruppe @eddyb @oli-obk

Activity

changed the title [-]Storing a function pointer in a static usize works on nightly[/-] [+]Storing a function pointer in a const usize works on nightly[/+] on Jun 14, 2018
gnzlbg

gnzlbg commented on Jun 14, 2018

@gnzlbg
ContributorAuthor
oli-obk

oli-obk commented on Jun 14, 2018

@oli-obk
Contributor

Some people were of the opinion that this shouldn't work

It should work, it's just super scary.

EDIT: it should not and does not work. If we allowed this, various surprising things can happen, especially around implicit promotion. If you want to store pointers and integers, you can use a raw pointer.

scottmcm

scottmcm commented on Jun 15, 2018

@scottmcm
Member

It should work

For my own education, can you elaborate on why function pointers are ok? I'd thought that the runtime addresses of things -- including functions -- shouldn't be accessible at const-time since we don't know where they'll live at runtime. Could I make a const array that usize long?

oli-obk

oli-obk commented on Jun 15, 2018

@oli-obk
Contributor

When compiling to LLVM, the real function handle or pointer address is obtained. If you try doing anything weird with pointers at compile time, you will quickly notice that you are not at runtime. For example, you cannot divide such a usize by anything. You can add or subtract integers, because that's just pointer offsetting, but you can't inspect the address in any way.

This also means that no, you cannot use this usize for array lengths or enum discriminants. This is due to the fact that miri pointers are not just addresses, but abstract pointers that are in a separate layer from the bytes of normal memory like the one of integers.

scottmcm

scottmcm commented on Jun 15, 2018

@scottmcm
Member

Thanks for the explanation.

I guess that means that such a usize couldn't be passed to a const generic either?

gnzlbg

gnzlbg commented on Jun 15, 2018

@gnzlbg
ContributorAuthor

I guess that means that such a usize couldn't be passed to a const generic either?

The thing is const generics don't know where the usize comes from, so they could try to do something with it that is not allowed if it is just a pointer address. Until monomorphization we can't know if there is an issue.

Note that converting a pointer to an usize and back just to be able to put it in const, statics, or use it with const generics, is a real pain. We would be better off in this case with an AtomicPtr<T> type, such that I can write AtomicPtr<fn()->i32> and avoid the conversion to usize.

For const generics, C++ accepts function pointers at the type level and that works just fine with clang, so I expect fn bar<const PTR: fn()->i32>() {} to work fine as well.

hanna-kruppe

hanna-kruppe commented on Jun 15, 2018

@hanna-kruppe
Contributor

How would AtomicPtr<T> work?

  • is AtomicPtr<i32> disallowed but AtomicPtr<*const i32> and AtomicPtr<fn(&str) -> &str> work? that seems very weird and special cased (especially for fn types that involve higher rank lifetimes, as in my example)
  • or is AtomicPtr<T> ~= *mut T? then AtomicPtr<fn()> is a double indirection and to use it you need to worry about managing the memory it points to (which contains the fn())
gnzlbg

gnzlbg commented on Jun 15, 2018

@gnzlbg
ContributorAuthor

How would AtomicPtr work?

I haven't thought this through beyond having used the atomic pointers in the C++ standard library: http://en.cppreference.com/w/cpp/atomic/atomic

One way to implement this could be AtomicPtr<T> where T: AtomicPtrConstraint where we provide blanket impls for &'a, &'a mut, *const, *mut, fn() -> T, fn(T) -> U, fn(T, U) -> V, ... or use some compiler magic to achieve a similar effect...

eddyb

eddyb commented on Jun 15, 2018

@eddyb
Member

This is a bit offtopic, but regarding AtomicPtr, @nikomatsakis mentioned at least once the possibility of introducing some new type, to make fn(A) -> B sugar for &ThatType<(A,), B>.
I guess it can't entirely be "sugar", because of existing impls, but at least both could work the same.

RalfJung

RalfJung commented on Jun 19, 2018

@RalfJung
Member

Hehe, this is cute. :) And I agree it is working as intended. (Nice that the translation to LLVM actually gets this right, should there be a testcase to make sure it stays that way? :D )

added
E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
on Jun 19, 2018
oli-obk

oli-obk commented on Jun 20, 2018

@oli-obk
Contributor

So... other than testing this, all we need is a PR that adds *const T -> usize casts in constants under a feature gate (and makes those casts unsafe in constants and const fn)

15 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @eddyb@RalfJung@oli-obk@gnzlbg@kazcw

      Issue actions

        Storing a function pointer in a const usize works on nightly · Issue #51559 · rust-lang/rust