Skip to content
New issue

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

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

Already on GitHub? # to your account

Fix ICE on const eval of union field #47794

Merged
merged 2 commits into from
Jan 28, 2018
Merged

Fix ICE on const eval of union field #47794

merged 2 commits into from
Jan 28, 2018

Conversation

etaoins
Copy link
Contributor

@etaoins etaoins commented Jan 27, 2018

MIR's Const::get_field() attempts to retrieve the value for a given field in a constant. In the case of a union constant it was falling through to a generic const_get_elt based on the field index. As union fields don't have an index this caused an ICE in llvm_field_index.

Fix by simply returning the current value when accessing any field in a union. This works because all union fields start at byte offset 0.

The added test uses const_fn it ensure the field is extracted using MIR's const evaluation. The crash is reproducible without it, however.

Fixes #47788

r? @eddyb

MIR's `Const::get_field()` attempts to retrieve the value for a given
field in a constant. In the case of a union constant it was falling
through to a generic `const_get_elt` based on the field index. As union
fields don't have an index this caused an ICE in `llvm_field_index`.

Fix by simply returning the current value when accessing any field in a
union. This works because all union fields start at byte offset 0.

The added test uses `const_fn` it ensure the field is extracted using
MIR's const evaluation. The crash is reproducible without it, however.

Fixes #47788
@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @eddyb (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.


union DummyUnion {
field1: i32,
field2: i32,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if you make them different types, say, i64 for the second field`, and maybe do some arithmetic on it?

@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

Thank you for the PR, however #46882 should fix the ICE and implement the correct semantics in all cases, so I'm not sure we should merge this partial fix. cc @oli-obk

@etaoins
Copy link
Contributor Author

etaoins commented Jan 27, 2018

@eddyb

I can confirm that #46882 fixes this. My only concern is this a regression against stable as it's possible to crash nightly and beta (but not 1.23) with the following:

union DummyUnion {
    field1: i32,
    field2: i32,
}

const UNION: DummyUnion = DummyUnion { field1: 1 };

fn read_field1() -> i32 {
    const FIELD1: i32 = unsafe { UNION.field1 };
    FIELD1
}

fn main() {
    read_field1();
}

@kennytm kennytm added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 27, 2018
@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

Yes but what does stable do if the types aren't the same? It can't possibly work. Oh well.

cc @rust-lang/compiler

@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

Also I think your test is still subtly cheating, try an union between i64 and (i32, i32), where you initialize the former and read the latter. Should fail on both stable and this PR.

@eddyb eddyb added I-nominated T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 27, 2018
@etaoins
Copy link
Contributor Author

etaoins commented Jan 27, 2018

This passes on both stable and this PR but ICEs on nightly and beta:

type Field1 = u64;
type Field2 = (u32, u32);

union DummyUnion {
    field1: Field1,
    field2: Field2,
}

const UNION: DummyUnion = DummyUnion {
    field1: 0xdeadbeeff00fb00f,
};

fn read_field1() -> Field1 {
    const FIELD1: Field1 = unsafe { UNION.field1 };
    FIELD1
}

fn read_field2() -> Field2 {
    const FIELD2: Field2 = unsafe { UNION.field2 };
    FIELD2
}

fn main() {
    assert_eq!(read_field1(), 0xdeadbeeff00fb00f);

    let (first, second) = read_field2();
    assert_eq!(first, 0xf00fb00f);
    assert_eq!(second, 0xdeadbeef);
}

@petrochenkov
Copy link
Contributor

This is not supposed to work on stable.
There's a test ensuring this doesn't work - https://github.com/rust-lang/rust/blob/master/src/test/ui/union/union-const-eval.rs.
Const functions however change the intended error into a warning somehow and it's a bug.

@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

@etaoins Sorry, what I meant was this:

const FIELD2_1: u32 = unsafe { UNION.field2.1 };

I'm willing to backport this PR to beta just to get the half-broken behavior back, and the code changed here should get removed by #46882 anyway, so it should all work out.

@oli-obk
Copy link
Contributor

oli-obk commented Jan 27, 2018

This code will get removed by #46882 and the underlying issue will be fixed. I'll add this test to my PR, it should simply work

@etaoins
Copy link
Contributor Author

etaoins commented Jan 27, 2018

@eddyb

Yup, that finally kills it. Running this will segfault on stable and fail the assertion on my PR:

type Field1 = u64;
type Field2 = (u32, u32);

union DummyUnion {
    field1: Field1,
    field2: Field2,
}

const UNION: DummyUnion = DummyUnion {
    field1: 0xdeadbeeff00fb00f,
};

fn read_field2_1() -> u32 {
    const FIELD2_1: u32 = unsafe { UNION.field2.1 };
    FIELD2_1
}

fn main() {
    assert_eq!(read_field2_1(), 0xdeadbeef);
}

@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

@bors r+

@bors
Copy link
Contributor

bors commented Jan 27, 2018

📌 Commit ed7e4e1 has been approved by eddyb

@eddyb eddyb added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 27, 2018
@eddyb
Copy link
Member

eddyb commented Jan 27, 2018

I think we should merge this and backport it, it shouldn't allow anything that didn't work before.

@bors
Copy link
Contributor

bors commented Jan 28, 2018

⌛ Testing commit ed7e4e1 with merge 5e7fd65...

bors added a commit that referenced this pull request Jan 28, 2018
…r=eddyb

Fix ICE on const eval of union field

MIR's `Const::get_field()` attempts to retrieve the value for a given field in a constant. In the case of a union constant it was falling through to a generic `const_get_elt` based on the field index. As union fields don't have an index this caused an ICE in `llvm_field_index`.

Fix by simply returning the current value when accessing any field in a union. This works because all union fields start at byte offset 0.

The added test uses `const_fn` it ensure the field is extracted using MIR's const evaluation. The crash is reproducible without it, however.

Fixes #47788

r? @eddyb
@bors
Copy link
Contributor

bors commented Jan 28, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: eddyb
Pushing 5e7fd65 to master...

@bors bors merged commit ed7e4e1 into rust-lang:master Jan 28, 2018
@etaoins
Copy link
Contributor Author

etaoins commented Jan 29, 2018

@eddyb What's the process for backporting? Should I open a PR against the beta branch?

@eddyb
Copy link
Member

eddyb commented Jan 29, 2018

@etaoins We first have to accept it and then we typically batch them.

@nikomatsakis nikomatsakis added beta-accepted Accepted for backporting to the compiler in the beta channel. and removed I-nominated labels Feb 1, 2018
@nikomatsakis
Copy link
Contributor

Accepting for backport.

@MaloJaffre MaloJaffre mentioned this pull request Feb 1, 2018
bors added a commit that referenced this pull request Feb 1, 2018
bors added a commit that referenced this pull request Feb 2, 2018
@alexcrichton alexcrichton removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Feb 2, 2018
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
beta-accepted Accepted for backporting to the compiler in the beta channel. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants