Skip to content

Should unions use type based const qualification? #90268

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

Closed
tmiasko opened this issue Oct 25, 2021 · 9 comments · Fixed by #90373
Closed

Should unions use type based const qualification? #90268

tmiasko opened this issue Oct 25, 2021 · 9 comments · Fixed by #90373
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) C-bug Category: This is a bug. F-untagged_unions `#![feature(untagged_unions)]`

Comments

@tmiasko
Copy link
Contributor

tmiasko commented Oct 25, 2021

For example, after laundering interior mutability through a union, a resulting constant undergoes promotion:

#![feature(untagged_unions)]
use std::cell::Cell;

pub const CELL: Option<Cell<u32>> = {
    union U { i: u32, c: Cell<u32> }
    Some(unsafe { U { i: 0 }.c })
};

fn main() {
    let _ = &CELL;
}
error[E0080]: it is undefined behavior to use this value
 --> u.rs:9:1
  |
9 | fn main() {
  | ^^^^^^^^^ type validation failed at .<deref>.<enum-variant(Some)>.0.value: encountered `UnsafeCell` in a `const`
...

@rustbot label +A-const-eval +F-untagged_unions

@rustbot rustbot added A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) F-untagged_unions `#![feature(untagged_unions)]` labels Oct 25, 2021
@tmiasko tmiasko changed the title Const qualification should consider all union variants? Should unions use type based const qualification? Oct 25, 2021
@oli-obk oli-obk added the C-bug Category: This is a bug. label Oct 26, 2021
@oli-obk
Copy link
Contributor

oli-obk commented Oct 26, 2021

Yea, unions should not be "more powerful" than a plain transmute imo.

cc @rust-lang/wg-const-eval

@RalfJung
Copy link
Member

RalfJung commented Oct 26, 2021

I assume with transmute one can also produce a similar example as the OP? So in which sense are unions more powerful than transmute, @oli-obk?

Why does this example behave any different than the following?

use std::cell::Cell;

pub const CELL: Option<Cell<u32>> = Some(Cell::new(0));

fn main() {
    let _ = &CELL;
}

@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 26, 2021

The union local initially starts without interior mutability qualif. This persist after the first assignment, since u32 does not have interior mutability The move from the cell variant is qualified based on the qualification of the local, so the overall constant is consider not to have interior mutability.

The use of the constant in the main function is promoted and fails dynamic checks.

@RalfJung
Copy link
Member

I totally forgot promotion of &CONST looks into the definition of CONST... that'll continue to haunt us for a while I think. :/

Yeah, the analysis clearly is wrong for reading from union fields.

But even for values like U { i: 0 }, I have no idea what the wider consequences are (given that the type U is considered interior mutable).

@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 27, 2021

The example can be also modified by moving union access to the runtime code, in
which case it will compile successfully, with promoted being placed in .rodata
section:

#![feature(untagged_unions)]
use std::cell::Cell;

pub union U { i: u32, c: Cell<u32> }

pub const CELL: U = {
    U { i: 0 }
};

fn main() {
    let cell: &'static U = &CELL;
    unsafe { (cell.c).set(1) };
}
$ rustc a.rs
$ ./a
Segmentation fault

In the context of static analysis, I think it would make sense to use strictly
type based analysis for unions, in which case the first assignment to a union
local would already qualify the local as having interior mutability.

@RalfJung
Copy link
Member

But also, there's supposed to be a 2nd line of defense in our interning code that is somehow failing.

@RalfJung
Copy link
Member

Turns out interning relies in validity checking for this:

// Validation will ensure that there is no `UnsafeCell` on an immutable allocation.

The example in the OP was caught by validity, so that is good. But the 2nd example was not caught, because we never actually hit an UnsafeCell -- we stop at the union boundary.

So maybe the union part of the visitor should also have the kind of check UnsafeCell does?

@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 28, 2021

@RalfJung do we have an established approach to testing that this second line of defense works? Carefully crafted constant with undefined behaviour might do its job in this case (not sure yet), but maybe we have better alternatives?

@RalfJung
Copy link
Member

We have -Zunleash-the-miri-inside-of-you but that only helps for const checks, not for promoted restrictions... I don't think we have anything for promoteds (and it seems non-trivial, we would need a way to force something to be promoted despite it violating the usual conditions).

@bors bors closed this as completed in 9ed5b94 Oct 29, 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, covers all const contexts (static, const fn, ...) C-bug Category: This is a bug. F-untagged_unions `#![feature(untagged_unions)]`
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants