Stream: t-lang

Topic: function pointer cast: safe arg type changes


oli (Apr 29 2020 at 07:57, on Zulip):

I was just looking at a situation where I would've liked to be able to do

fn bar(_: &i32) {}
let x: fn(&mut i32) = bar;

I believe if that coercion would be legal, it would not be a breaking change if a public function in an API went from a mutable reference to an immutable one. I think there are other function pointer casts that could be legal. A function taking a raw pointer could be used as a function pointer with a reference argument. Basically whenever you could transmute the function pointer argument to the real function's argument without any change in ABI, such casts could be ok. Of course we don't want to lock ourselves into platform defined behaviour, but at least for references and raw pointers there should be no difference ever from an ABI perspective, right?

Josh Triplett (Apr 29 2020 at 08:09, on Zulip):

Almost. Raw pointers can have mutable aliases, references cannot without UB, so there are circumstances where that isn't safe.

Josh Triplett (Apr 29 2020 at 08:09, on Zulip):

But many type conversions would be fine. I think they'd need an "into" or "as" or something though.

oli (Apr 29 2020 at 08:22, on Zulip):

ah interesting. converting function pointers between each other via into would indeed be neat

pnkfelix (Apr 29 2020 at 15:43, on Zulip):

I feel like I'm missing something; can @Josh Triplett or @oli spell out the scenario that Josh is describing? (Where are you anticipating the alias of a raw pointer arising? Is it from an invocation of the function pointer x on a *const i32 input?)

pnkfelix (Apr 29 2020 at 15:45, on Zulip):

Or wait, maybe @Josh Triplett was talking about the generalizations that @oli listed, such as "a function taking a raw pointer could be used as a function pointer with a reference argument ..."

pnkfelix (Apr 29 2020 at 15:46, on Zulip):

@oli in any case, even looking at your original example... I'm not so sure you can make even the original coercion that you described. At least, not based on the type signatures alone

oli (Apr 29 2020 at 15:47, on Zulip):

hmm.. why not? Is there an ABI problem?

pnkfelix (Apr 29 2020 at 15:47, on Zulip):

Consider e.g. fn baz<'a>(p: &'a i32, q: &mut &'a i32) { *q = p; }

pnkfelix (Apr 29 2020 at 15:47, on Zulip):

You cannot just turn the &'a i32 into a &'a mut i32 blindly

pnkfelix (Apr 29 2020 at 15:48, on Zulip):

the soundness of the body of bazrelies on it being sharable

oli (Apr 29 2020 at 15:48, on Zulip):

the function still needs to compile

oli (Apr 29 2020 at 15:49, on Zulip):

oh

oli (Apr 29 2020 at 15:49, on Zulip):

hmm

pnkfelix (Apr 29 2020 at 15:49, on Zulip):

maybe I misunderstand the coercion you desire

pnkfelix (Apr 29 2020 at 15:49, on Zulip):

but if you want let x: fn(&mut i32) = baz; to compile without knowing the body of baz, then you cannot blindly replace &T with &mut T

oli (Apr 29 2020 at 15:49, on Zulip):

basically I want to convert baz to a function pointer for<'a> baz(p: &'a mut i32, q: &mut &'a i32) { *q = p; }

pnkfelix (Apr 29 2020 at 15:50, on Zulip):

right, but you can see where that goes wrong, right?

oli (Apr 29 2020 at 15:50, on Zulip):

but I see now that there are implications I didn't think about

pnkfelix (Apr 29 2020 at 15:50, on Zulip):

clearly we need a richer hierarchy here. :)

oli (Apr 29 2020 at 15:52, on Zulip):

I would have thought a wrapper function fn foo<'a>(p: &'a mut i32, q: &mut &'a i32) { baz(p, q) } can exist without any problems and it bascially has no code

oli (Apr 29 2020 at 15:53, on Zulip):

hmm yes that compiles, so I'm again unsure why this specific example is a counterexample to my idea, but I see that there could be something in this space and will play a bit now to see if I can come up with problematic examples

pnkfelix (Apr 29 2020 at 15:53, on Zulip):

let me double-check whether I am misunderstanding my own exmaple

pnkfelix (Apr 29 2020 at 15:54, on Zulip):

Oh, right

pnkfelix (Apr 29 2020 at 15:54, on Zulip):

its okay that it gets aliased, since its being aliased as a &T

pnkfelix (Apr 29 2020 at 15:54, on Zulip):

not as a &mut T

pnkfelix (Apr 29 2020 at 15:54, on Zulip):

so never mind me

oli (Apr 29 2020 at 15:56, on Zulip):

but it would definitely be bad to convert &mut &'a i32 to &mut &'a mut i32

pnkfelix (Apr 29 2020 at 15:56, on Zulip):

right. Its probably something where you would need to apply our variance rules to determine legality

RalfJ (Apr 30 2020 at 06:53, on Zulip):

Note that if you change the function ptr type, that's effectively a transmute of the argument from caller type to callee type

RalfJ (Apr 30 2020 at 06:53, on Zulip):

transmuting a mutable ref to a shared ref is not the same as the normal coercion, in terms of Stacked Borrows

RalfJ (Apr 30 2020 at 06:53, on Zulip):

but I think Stacked Borrows 2 is fine with such transmutes, and the reborrow-on-fn-entry will anyway ensure that the callee has the kind of tag/permission it expects

RalfJ (Apr 30 2020 at 06:54, on Zulip):

this is something to keep in mind though in general -- we'd be introducing transmutes here, not casts. (ptr-int-casts are another example of casts that are not equivalent to transmutes.)

Last update: Jun 05 2020 at 22:35UTC