mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
Allow variant discriminant initializers to refer to other initializers of the same enum
This commit is contained in:
parent
883bf4ba2e
commit
195c9f47e9
@ -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> {
|
||||
|
@ -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),
|
||||
|
36
src/test/ui/const-eval/enum_discr.rs
Normal file
36
src/test/ui/const-eval/enum_discr.rs
Normal file
@ -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);
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}}`...
|
||||
--> $DIR/issue-36163.rs:14:9
|
||||
|
|
||||
LL | B = A, //~ ERROR E0391
|
||||
| ^
|
||||
note: ...which requires const-evaluating `A`...
|
||||
note: ...which requires processing `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
|
||||
| ^^^^^^^^^^^^^^^
|
||||
= 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
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user