Skip to content

Commit 3eca70a

Browse files
committed
Auto merge of #50072 - oli-obk:discriminants, r=eddyb
Allow variant discriminant initializers to refer to other initializer… …s of the same enum r? @eddyb fixes the 2.4 failure of #49765 cc @durka @retep998
2 parents 1515cde + 195c9f4 commit 3eca70a

File tree

6 files changed

+158
-37
lines changed

6 files changed

+158
-37
lines changed

src/librustc/ty/mod.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -1982,32 +1982,38 @@ impl<'a, 'gcx, 'tcx> AdtDef {
19821982
tcx: TyCtxt<'a, 'gcx, 'tcx>,
19831983
variant_index: usize)
19841984
-> Discr<'tcx> {
1985-
let repr_type = self.repr.discr_type();
1986-
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
1985+
let (val, offset) = self.discriminant_def_for_variant(variant_index);
1986+
let explicit_value = val
1987+
.and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did))
1988+
.unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx.global_tcx()));
1989+
explicit_value.checked_add(tcx, offset as u128).0
1990+
}
1991+
1992+
/// Yields a DefId for the discriminant and an offset to add to it
1993+
/// Alternatively, if there is no explicit discriminant, returns the
1994+
/// inferred discriminant directly
1995+
pub fn discriminant_def_for_variant(
1996+
&self,
1997+
variant_index: usize,
1998+
) -> (Option<DefId>, usize) {
19871999
let mut explicit_index = variant_index;
2000+
let expr_did;
19882001
loop {
19892002
match self.variants[explicit_index].discr {
1990-
ty::VariantDiscr::Relative(0) => break,
2003+
ty::VariantDiscr::Relative(0) => {
2004+
expr_did = None;
2005+
break;
2006+
},
19912007
ty::VariantDiscr::Relative(distance) => {
19922008
explicit_index -= distance;
19932009
}
1994-
ty::VariantDiscr::Explicit(expr_did) => {
1995-
match self.eval_explicit_discr(tcx, expr_did) {
1996-
Some(discr) => {
1997-
explicit_value = discr;
1998-
break;
1999-
},
2000-
None => {
2001-
if explicit_index == 0 {
2002-
break;
2003-
}
2004-
explicit_index -= 1;
2005-
}
2006-
}
2010+
ty::VariantDiscr::Explicit(did) => {
2011+
expr_did = Some(did);
2012+
break;
20072013
}
20082014
}
20092015
}
2010-
explicit_value.checked_add(tcx, (variant_index - explicit_index) as u128).0
2016+
(expr_did, variant_index - explicit_index)
20112017
}
20122018

20132019
pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {

src/librustc_mir/hair/cx/expr.rs

+75-3
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,84 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
589589
// Check to see if this cast is a "coercion cast", where the cast is actually done
590590
// using a coercion (or is a no-op).
591591
if let Some(&TyCastKind::CoercionCast) = cx.tables()
592-
.cast_kinds()
593-
.get(source.hir_id) {
592+
.cast_kinds()
593+
.get(source.hir_id) {
594594
// Convert the lexpr to a vexpr.
595595
ExprKind::Use { source: source.to_ref() }
596596
} else {
597-
ExprKind::Cast { source: source.to_ref() }
597+
// check whether this is casting an enum variant discriminant
598+
// to prevent cycles, we refer to the discriminant initializer
599+
// which is always an integer and thus doesn't need to know the
600+
// enum's layout (or its tag type) to compute it during const eval
601+
// Example:
602+
// enum Foo {
603+
// A,
604+
// B = A as isize + 4,
605+
// }
606+
// The correct solution would be to add symbolic computations to miri,
607+
// so we wouldn't have to compute and store the actual value
608+
let var = if let hir::ExprPath(ref qpath) = source.node {
609+
let def = cx.tables().qpath_def(qpath, source.hir_id);
610+
cx
611+
.tables()
612+
.node_id_to_type(source.hir_id)
613+
.ty_adt_def()
614+
.and_then(|adt_def| {
615+
match def {
616+
Def::VariantCtor(variant_id, CtorKind::Const) => {
617+
let idx = adt_def.variant_index_with_id(variant_id);
618+
let (d, o) = adt_def.discriminant_def_for_variant(idx);
619+
use rustc::ty::util::IntTypeExt;
620+
let ty = adt_def.repr.discr_type().to_ty(cx.tcx());
621+
Some((d, o, ty))
622+
}
623+
_ => None,
624+
}
625+
})
626+
} else {
627+
None
628+
};
629+
let source = if let Some((did, offset, ty)) = var {
630+
let mk_const = |val| Expr {
631+
temp_lifetime,
632+
ty,
633+
span: expr.span,
634+
kind: ExprKind::Literal {
635+
literal: Literal::Value {
636+
value: cx.tcx().mk_const(ty::Const {
637+
val,
638+
ty,
639+
}),
640+
},
641+
},
642+
}.to_ref();
643+
let offset = mk_const(
644+
ConstVal::Value(Value::ByVal(PrimVal::Bytes(offset as u128))),
645+
);
646+
match did {
647+
Some(did) => {
648+
// in case we are offsetting from a computed discriminant
649+
// and not the beginning of discriminants (which is always `0`)
650+
let substs = Substs::identity_for_item(cx.tcx(), did);
651+
let lhs = mk_const(ConstVal::Unevaluated(did, substs));
652+
let bin = ExprKind::Binary {
653+
op: BinOp::Add,
654+
lhs,
655+
rhs: offset,
656+
};
657+
Expr {
658+
temp_lifetime,
659+
ty,
660+
span: expr.span,
661+
kind: bin,
662+
}.to_ref()
663+
},
664+
None => offset,
665+
}
666+
} else {
667+
source.to_ref()
668+
};
669+
ExprKind::Cast { source }
598670
}
599671
}
600672
hir::ExprType(ref source, _) => return source.make_mirror(cx),

src/test/ui/const-eval/enum_discr.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-pass
12+
// run-pass
13+
14+
enum Foo {
15+
X = 42,
16+
Y = Foo::X as isize - 3,
17+
}
18+
19+
enum Bar {
20+
X,
21+
Y = Bar::X as isize + 2,
22+
}
23+
24+
enum Boo {
25+
X = Boo::Y as isize * 2,
26+
Y = 9,
27+
}
28+
29+
fn main() {
30+
assert_eq!(Foo::X as isize, 42);
31+
assert_eq!(Foo::Y as isize, 39);
32+
assert_eq!(Bar::X as isize, 0);
33+
assert_eq!(Bar::Y as isize, 2);
34+
assert_eq!(Boo::X as isize, 18);
35+
assert_eq!(Boo::Y as isize, 9);
36+
}

src/test/ui/issue-23302-1.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
error[E0391]: cycle detected when const-evaluating `X::A::{{initializer}}`
1+
error[E0391]: cycle detected when processing `X::A::{{initializer}}`
22
--> $DIR/issue-23302-1.rs:14:9
33
|
44
LL | A = X::A as isize, //~ ERROR E0391
5-
| ^^^^
5+
| ^^^^^^^^^^^^^
66
|
7-
note: ...which requires computing layout of `X`...
8-
= note: ...which again requires const-evaluating `X::A::{{initializer}}`, completing the cycle
7+
= note: ...which again requires processing `X::A::{{initializer}}`, completing the cycle
8+
note: cycle used when const-evaluating `X::A::{{initializer}}`
9+
--> $DIR/issue-23302-1.rs:14:9
10+
|
11+
LL | A = X::A as isize, //~ ERROR E0391
12+
| ^^^^^^^^^^^^^
913

1014
error: aborting due to previous error
1115

src/test/ui/issue-23302-2.stderr

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
error[E0391]: cycle detected when const-evaluating `Y::A::{{initializer}}`
1+
error[E0391]: cycle detected when processing `Y::A::{{initializer}}`
22
--> $DIR/issue-23302-2.rs:14:9
33
|
44
LL | A = Y::B as isize, //~ ERROR E0391
5-
| ^^^^
5+
| ^^^^^^^^^^^^^
66
|
7-
note: ...which requires computing layout of `Y`...
8-
= note: ...which again requires const-evaluating `Y::A::{{initializer}}`, completing the cycle
7+
= note: ...which again requires processing `Y::A::{{initializer}}`, completing the cycle
8+
note: cycle used when const-evaluating `Y::A::{{initializer}}`
9+
--> $DIR/issue-23302-2.rs:14:9
10+
|
11+
LL | A = Y::B as isize, //~ ERROR E0391
12+
| ^^^^^^^^^^^^^
913

1014
error: aborting due to previous error
1115

src/test/ui/issue-36163.stderr

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
error[E0391]: cycle detected when const-evaluating `Foo::B::{{initializer}}`
1+
error[E0391]: cycle detected when processing `Foo::B::{{initializer}}`
22
--> $DIR/issue-36163.rs:14:9
33
|
44
LL | B = A, //~ ERROR E0391
55
| ^
66
|
7-
note: ...which requires processing `Foo::B::{{initializer}}`...
7+
note: ...which requires processing `A`...
8+
--> $DIR/issue-36163.rs:11:18
9+
|
10+
LL | const A: isize = Foo::B as isize;
11+
| ^^^^^^^^^^^^^^^
12+
= note: ...which again requires processing `Foo::B::{{initializer}}`, completing the cycle
13+
note: cycle used when const-evaluating `Foo::B::{{initializer}}`
814
--> $DIR/issue-36163.rs:14:9
915
|
1016
LL | B = A, //~ ERROR E0391
1117
| ^
12-
note: ...which requires const-evaluating `A`...
13-
--> $DIR/issue-36163.rs:11:18
14-
|
15-
LL | const A: isize = Foo::B as isize;
16-
| ^^^^^^
17-
note: ...which requires computing layout of `Foo`...
18-
= note: ...which again requires const-evaluating `Foo::B::{{initializer}}`, completing the cycle
1918

2019
error: aborting due to previous error
2120

0 commit comments

Comments
 (0)