Skip to content
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

Allow variant discriminant initializers to refer to other initializer… #50072

Merged
merged 1 commit into from
Apr 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1972,32 +1972,38 @@ impl<'a, 'gcx, 'tcx> AdtDef {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: usize)
-> Discr<'tcx> {
let repr_type = self.repr.discr_type();
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
let (val, offset) = self.discriminant_def_for_variant(variant_index);
let explicit_value = val
.and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did))
.unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx.global_tcx()));
explicit_value.checked_add(tcx, offset as u128).0
}

/// Yields a DefId for the discriminant and an offset to add to it
/// Alternatively, if there is no explicit discriminant, returns the
/// inferred discriminant directly
pub fn discriminant_def_for_variant(
&self,
variant_index: usize,
) -> (Option<DefId>, usize) {
let mut explicit_index = variant_index;
let expr_did;
loop {
match self.variants[explicit_index].discr {
ty::VariantDiscr::Relative(0) => break,
ty::VariantDiscr::Relative(0) => {
expr_did = None;
break;
},
ty::VariantDiscr::Relative(distance) => {
explicit_index -= distance;
}
ty::VariantDiscr::Explicit(expr_did) => {
match self.eval_explicit_discr(tcx, expr_did) {
Some(discr) => {
explicit_value = discr;
break;
},
None => {
if explicit_index == 0 {
break;
}
explicit_index -= 1;
}
}
ty::VariantDiscr::Explicit(did) => {
expr_did = Some(did);
break;
}
}
}
explicit_value.checked_add(tcx, (variant_index - explicit_index) as u128).0
(expr_did, variant_index - explicit_index)
}

pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {
Expand Down
78 changes: 75 additions & 3 deletions src/librustc_mir/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,12 +589,84 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
// Check to see if this cast is a "coercion cast", where the cast is actually done
// using a coercion (or is a no-op).
if let Some(&TyCastKind::CoercionCast) = cx.tables()
.cast_kinds()
.get(source.hir_id) {
.cast_kinds()
.get(source.hir_id) {
// Convert the lexpr to a vexpr.
ExprKind::Use { source: source.to_ref() }
} else {
ExprKind::Cast { source: source.to_ref() }
// check whether this is casting an enum variant discriminant
// to prevent cycles, we refer to the discriminant initializer
// which is always an integer and thus doesn't need to know the
// enum's layout (or its tag type) to compute it during const eval
// Example:
// enum Foo {
// A,
// B = A as isize + 4,
// }
// The correct solution would be to add symbolic computations to miri,
// so we wouldn't have to compute and store the actual value
let var = if let hir::ExprPath(ref qpath) = source.node {
let def = cx.tables().qpath_def(qpath, source.hir_id);
cx
.tables()
.node_id_to_type(source.hir_id)
.ty_adt_def()
.and_then(|adt_def| {
match def {
Def::VariantCtor(variant_id, CtorKind::Const) => {
let idx = adt_def.variant_index_with_id(variant_id);
let (d, o) = adt_def.discriminant_def_for_variant(idx);
use rustc::ty::util::IntTypeExt;
let ty = adt_def.repr.discr_type().to_ty(cx.tcx());
Some((d, o, ty))
}
_ => None,
}
})
} else {
None
};
let source = if let Some((did, offset, ty)) = var {
let mk_const = |val| Expr {
temp_lifetime,
ty,
span: expr.span,
kind: ExprKind::Literal {
literal: Literal::Value {
value: cx.tcx().mk_const(ty::Const {
val,
ty,
}),
},
},
}.to_ref();
let offset = mk_const(
ConstVal::Value(Value::ByVal(PrimVal::Bytes(offset as u128))),
);
match did {
Some(did) => {
// in case we are offsetting from a computed discriminant
// and not the beginning of discriminants (which is always `0`)
let substs = Substs::identity_for_item(cx.tcx(), did);
let lhs = mk_const(ConstVal::Unevaluated(did, substs));
let bin = ExprKind::Binary {
op: BinOp::Add,
lhs,
rhs: offset,
};
Expr {
temp_lifetime,
ty,
span: expr.span,
kind: bin,
}.to_ref()
},
None => offset,
}
} else {
source.to_ref()
};
ExprKind::Cast { source }
}
}
hir::ExprType(ref source, _) => return source.make_mirror(cx),
Expand Down
36 changes: 36 additions & 0 deletions src/test/ui/const-eval/enum_discr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-pass
// run-pass

enum Foo {
X = 42,
Y = Foo::X as isize - 3,
}

enum Bar {
X,
Y = Bar::X as isize + 2,
}

enum Boo {
X = Boo::Y as isize * 2,
Y = 9,
}

fn main() {
assert_eq!(Foo::X as isize, 42);
assert_eq!(Foo::Y as isize, 39);
assert_eq!(Bar::X as isize, 0);
assert_eq!(Bar::Y as isize, 2);
assert_eq!(Boo::X as isize, 18);
assert_eq!(Boo::Y as isize, 9);
}
12 changes: 8 additions & 4 deletions src/test/ui/issue-23302-1.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
error[E0391]: cycle detected when const-evaluating `X::A::{{initializer}}`
error[E0391]: cycle detected when processing `X::A::{{initializer}}`
--> $DIR/issue-23302-1.rs:14:9
|
LL | A = X::A as isize, //~ ERROR E0391
| ^^^^
| ^^^^^^^^^^^^^
|
note: ...which requires computing layout of `X`...
= note: ...which again requires const-evaluating `X::A::{{initializer}}`, completing the cycle
= note: ...which again requires processing `X::A::{{initializer}}`, completing the cycle
note: cycle used when const-evaluating `X::A::{{initializer}}`
--> $DIR/issue-23302-1.rs:14:9
|
LL | A = X::A as isize, //~ ERROR E0391
| ^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
12 changes: 8 additions & 4 deletions src/test/ui/issue-23302-2.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
error[E0391]: cycle detected when const-evaluating `Y::A::{{initializer}}`
error[E0391]: cycle detected when processing `Y::A::{{initializer}}`
--> $DIR/issue-23302-2.rs:14:9
|
LL | A = Y::B as isize, //~ ERROR E0391
| ^^^^
| ^^^^^^^^^^^^^
|
note: ...which requires computing layout of `Y`...
= note: ...which again requires const-evaluating `Y::A::{{initializer}}`, completing the cycle
= note: ...which again requires processing `Y::A::{{initializer}}`, completing the cycle
note: cycle used when const-evaluating `Y::A::{{initializer}}`
--> $DIR/issue-23302-2.rs:14:9
|
LL | A = Y::B as isize, //~ ERROR E0391
| ^^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
17 changes: 8 additions & 9 deletions src/test/ui/issue-36163.stderr
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
error[E0391]: cycle detected when const-evaluating `Foo::B::{{initializer}}`
error[E0391]: cycle detected when processing `Foo::B::{{initializer}}`
--> $DIR/issue-36163.rs:14:9
|
LL | B = A, //~ ERROR E0391
| ^
|
note: ...which requires processing `Foo::B::{{initializer}}`...
note: ...which requires processing `A`...
--> $DIR/issue-36163.rs:11:18
|
LL | const A: isize = Foo::B as isize;
| ^^^^^^^^^^^^^^^
= note: ...which again requires processing `Foo::B::{{initializer}}`, completing the cycle
note: cycle used when const-evaluating `Foo::B::{{initializer}}`
--> $DIR/issue-36163.rs:14:9
|
LL | B = A, //~ ERROR E0391
| ^
note: ...which requires const-evaluating `A`...
--> $DIR/issue-36163.rs:11:18
|
LL | const A: isize = Foo::B as isize;
| ^^^^^^
note: ...which requires computing layout of `Foo`...
= note: ...which again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle

error: aborting due to previous error

Expand Down