Miri ICE/possible miscompilation with diverging function returning repr(transparent)
wrapper with uninhabited ZST, transmuted to function pointer returning wrapped field.
#135802
Labels
A-ABI
Area: Concerning the application binary interface (ABI)
A-miri
Area: The miri tool
C-bug
Category: This is a bug.
I-ICE
Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
needs-triage
This issue may need triage. Remove it if it has been sufficiently triaged.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
This code ICEs Miri, the normal compiler compiles this without complaint, but might be miscompiling it1, though a
const
version using-Zunleash-the-miri-inside-of-you
does ICE the compiler.Code
modified version using `-Zunleash-the-miri-inside-of-you` that ICEs rustc
RUSTFLAGS="-Zunleash-the-miri-inside-of-you" cargo +nightly run
Backtrace:
Meta
rustc --version --verbose
:Error output
Error output
ICE file
@rustbot label +A-miri +A-ABI
This shouldn't ICE regardless, but whether there is a miscompilation depends on if the original code is sound:
CC rust-lang/unsafe-code-guidelines#485
Possible Miscompilation
Does
#[repr(transparent)]
s ABI-compatility guarantee apply in the prescence of uninhabited 1-ZST fields?A
#[repr(transparent)]
wrapper that contains an uninhabited 1-ZST field getsUninhabited
ABI IIUC, which may contradict the guarantees about#[repr(transparent)]
wrappers being ABI-compatible.Consider
Foo
from above:If
#[repr(transparent)]
's ABI-compatility guarantees do apply, then the above code should be sound:Foo
, it must diverge (e.g. by panicking).Foo
is ABI-compatible withField
, then it should be sound to transmute a function pointer to a divergingfn() -> Foo
into afn() -> Field
that also diverges.rustc_layout(debug)
Using
#![feature(rustc_attrs)]
and#[rustc_layout(debug)]
we see that the ABI it has iswhereas
Field
hasPossible Miscompilation
Assuming this is supposed to be sound, then there is an observable miscompilation here too. Where
Field
is returned by invisible reference,fn(u32) -> Foo
does not returnFoo
by invisible reference, so transmutingfn(u32) -> Foo
intofn(u32) -> Field
will pass a different number of arguments.Specifically on
x86_64-unknown-linux-gnu
: IfField
is returned on the stack, thenfn(..) -> Foo
does not reserve stack space or pass a hidden reference parameter, butfn(..) -> Field
does. This can be made into an observable miscompilation if the function takes parameters: (playground link, IIUC: the invisible reference is being passed, but the callee isn't expecting it, so it thinks it's the first "real" parameter instead (caller: ret by-ref inrdi
,x
inrsi
; callee: ret uninhabited,x
inrdi
)).Footnotes
see "Possible Miscompilation" section ↩
The text was updated successfully, but these errors were encountered: