about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ty/mod.rs40
-rw-r--r--src/librustc_mir/hair/cx/expr.rs78
-rw-r--r--src/test/ui/const-eval/enum_discr.rs36
-rw-r--r--src/test/ui/issue-23302-1.stderr12
-rw-r--r--src/test/ui/issue-23302-2.stderr12
-rw-r--r--src/test/ui/issue-36163.stderr17
6 files changed, 158 insertions, 37 deletions
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index d8aba024502..de043668410 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1982,32 +1982,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> {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index c0d28280946..d0c352319c8 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -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),
diff --git a/src/test/ui/const-eval/enum_discr.rs b/src/test/ui/const-eval/enum_discr.rs
new file mode 100644
index 00000000000..ba38a42092e
--- /dev/null
+++ b/src/test/ui/const-eval/enum_discr.rs
@@ -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);
+}
diff --git a/src/test/ui/issue-23302-1.stderr b/src/test/ui/issue-23302-1.stderr
index 0fbe2f7a411..6ff46a7e21f 100644
--- a/src/test/ui/issue-23302-1.stderr
+++ b/src/test/ui/issue-23302-1.stderr
@@ -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
 
diff --git a/src/test/ui/issue-23302-2.stderr b/src/test/ui/issue-23302-2.stderr
index 313cfa0c162..6b72a5b544d 100644
--- a/src/test/ui/issue-23302-2.stderr
+++ b/src/test/ui/issue-23302-2.stderr
@@ -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
 
diff --git a/src/test/ui/issue-36163.stderr b/src/test/ui/issue-36163.stderr
index 541f54ca768..7199fffd386 100644
--- a/src/test/ui/issue-36163.stderr
+++ b/src/test/ui/issue-36163.stderr
@@ -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