]> git.lizzy.rs Git - rust.git/commitdiff
rustc_const_eval: support all unit enum variants.
authorEduard-Mihai Burtescu <edy.burt@gmail.com>
Wed, 19 Apr 2017 12:16:19 +0000 (15:16 +0300)
committerEduard-Mihai Burtescu <edy.burt@gmail.com>
Sun, 23 Apr 2017 08:11:57 +0000 (11:11 +0300)
14 files changed:
src/librustc/ich/impls_ty.rs
src/librustc/middle/const_val.rs
src/librustc/mir/mod.rs
src/librustc/ty/mod.rs
src/librustc_const_eval/eval.rs
src/librustc_const_eval/pattern.rs
src/librustc_trans/mir/constant.rs
src/test/compile-fail/const-pattern-not-const-evaluable.rs
src/test/compile-fail/issue-41394.rs [new file with mode: 0644]
src/test/compile-fail/non-constant-enum-for-vec-repeat.rs [deleted file]
src/test/run-pass/auxiliary/issue-41394.rs [new file with mode: 0644]
src/test/run-pass/const-pattern-variant.rs [new file with mode: 0644]
src/test/run-pass/issue-23898.rs [new file with mode: 0644]
src/test/run-pass/issue-41394.rs [new file with mode: 0644]

index f55462fb5deb636925504c6105ac71869b434d89..16af98c2035480ef382ab55f7dbed9a5b521fb9a 100644 (file)
@@ -273,6 +273,12 @@ fn hash_stable<W: StableHasherResult>(&self,
             ConstVal::Bool(value) => {
                 value.hash_stable(hcx, hasher);
             }
+            ConstVal::Char(value) => {
+                value.hash_stable(hcx, hasher);
+            }
+            ConstVal::Variant(def_id) => {
+                def_id.hash_stable(hcx, hasher);
+            }
             ConstVal::Function(def_id, substs) => {
                 def_id.hash_stable(hcx, hasher);
                 substs.hash_stable(hcx, hasher);
@@ -296,9 +302,6 @@ fn hash_stable<W: StableHasherResult>(&self,
                 value.hash_stable(hcx, hasher);
                 times.hash_stable(hcx, hasher);
             }
-            ConstVal::Char(value) => {
-                value.hash_stable(hcx, hasher);
-            }
         }
     }
 }
index b65dbdbbcc250105a04ab779be3ed890e5c2f6df..ec7b3c4dd8dffd4514205789364611c8a89a4a26 100644 (file)
@@ -38,12 +38,13 @@ pub enum ConstVal<'tcx> {
     Str(InternedString),
     ByteStr(Rc<Vec<u8>>),
     Bool(bool),
+    Char(char),
+    Variant(DefId),
     Function(DefId, &'tcx Substs<'tcx>),
     Struct(BTreeMap<ast::Name, ConstVal<'tcx>>),
     Tuple(Vec<ConstVal<'tcx>>),
     Array(Vec<ConstVal<'tcx>>),
     Repeat(Box<ConstVal<'tcx>>, u64),
-    Char(char),
 }
 
 impl<'tcx> ConstVal<'tcx> {
@@ -54,12 +55,13 @@ pub fn description(&self) -> &'static str {
             Str(_) => "string literal",
             ByteStr(_) => "byte string literal",
             Bool(_) => "boolean",
+            Char(..) => "char",
+            Variant(_) => "enum variant",
             Struct(_) => "struct",
             Tuple(_) => "tuple",
             Function(..) => "function definition",
             Array(..) => "array",
             Repeat(..) => "repeat",
-            Char(..) => "char",
         }
     }
 
index 9ff64b295b765ee8a5f67026f15312c2938cadc0..bfb72b5df7b2e7143d9dc058490c12f2444f375b 100644 (file)
@@ -1307,10 +1307,11 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
             write!(fmt, "b\"{}\"", escaped)
         }
         Bool(b) => write!(fmt, "{:?}", b),
+        Char(c) => write!(fmt, "{:?}", c),
+        Variant(def_id) |
         Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
         Struct(_) | Tuple(_) | Array(_) | Repeat(..) =>
             bug!("ConstVal `{:?}` should not be in MIR", const_val),
-        Char(c) => write!(fmt, "{:?}", c),
     }
 }
 
index 1588773479c5e8b1fc47acc7dbd4d53f14e36e81..cc5eb768d9b3254e923dbe0c52a1586394d84359 100644 (file)
@@ -1693,6 +1693,7 @@ pub fn variant_of_def(&self, def: Def) -> &VariantDef {
         }
     }
 
+    #[inline]
     pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
                          -> impl Iterator<Item=ConstInt> + 'a {
         let repr_type = self.repr.discr_type();
@@ -1706,7 +1707,13 @@ pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
                     Ok(ConstVal::Integral(v)) => {
                         discr = v;
                     }
-                    _ => {}
+                    err => {
+                        if !expr_did.is_local() {
+                            span_bug!(tcx.def_span(expr_did),
+                                "variant discriminant evaluation succeeded \
+                                 in its crate but failed locally: {:?}", err);
+                        }
+                    }
                 }
             }
             prev_discr = Some(discr);
@@ -1740,7 +1747,15 @@ pub fn discriminant_for_variant(&self,
                             explicit_value = v;
                             break;
                         }
-                        _ => {
+                        err => {
+                            if !expr_did.is_local() {
+                                span_bug!(tcx.def_span(expr_did),
+                                    "variant discriminant evaluation succeeded \
+                                     in its crate but failed locally: {:?}", err);
+                            }
+                            if explicit_index == 0 {
+                                break;
+                            }
                             explicit_index -= 1;
                         }
                     }
index 5c421df92c7e27236f75afdb149694fb8ec8c2d3..e9352f53c92d68c724d5934465cc9ebe202a2abc 100644 (file)
@@ -15,7 +15,7 @@
 use rustc::hir::map as hir_map;
 use rustc::hir::map::blocks::FnLikeNode;
 use rustc::traits;
-use rustc::hir::def::Def;
+use rustc::hir::def::{Def, CtorKind};
 use rustc::hir::def_id::DefId;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
@@ -48,28 +48,6 @@ macro_rules! math {
     }
 }
 
-fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                  variant_def: DefId)
-                                  -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> {
-    if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) {
-        let enum_node_id = tcx.hir.get_parent(variant_node_id);
-        if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) {
-            if let hir::ItemEnum(ref edef, _) = it.node {
-                for variant in &edef.variants {
-                    if variant.node.data.id() == variant_node_id {
-                        return variant.node.disr_expr.map(|e| {
-                            let def_id = tcx.hir.body_owner_def_id(e);
-                            (&tcx.hir.body(e).value,
-                             tcx.item_tables(def_id))
-                        });
-                    }
-                }
-            }
-        }
-    }
-    None
-}
-
 /// * `def_id` is the id of the constant.
 /// * `substs` is the monomorphized substitutions for the expression.
 ///
@@ -289,9 +267,22 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
         }
       }
       hir::ExprCast(ref base, _) => {
-        match cast_const(tcx, cx.eval(base)?, ety) {
-            Ok(val) => val,
-            Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
+        let base_val = cx.eval(base)?;
+        let base_ty = cx.tables.expr_ty(base);
+
+        // Avoid applying substitutions if they're empty, that'd ICE.
+        let base_ty = if cx.substs.is_empty() {
+            base_ty
+        } else {
+            base_ty.subst(tcx, cx.substs)
+        };
+        if ety == base_ty {
+            base_val
+        } else {
+            match cast_const(tcx, base_val, ety) {
+                Ok(val) => val,
+                Err(kind) => signal!(e, kind),
+            }
         }
       }
       hir::ExprPath(ref qpath) => {
@@ -317,27 +308,20 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
                             debug!("bad reference: {:?}, {:?}", err.description(), err.span);
                             signal!(e, ErroneousReferencedConstant(box err))
                         },
+                    }
               },
-              Def::VariantCtor(variant_def, ..) => {
-                  if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) {
-                      let cx = ConstContext::with_tables(tcx, tables);
-                      match cx.eval(expr) {
-                          Ok(val) => val,
-                          Err(ConstEvalErr { kind: TypeckError, .. }) => {
-                              signal!(e, TypeckError);
-                          }
-                          Err(err) => {
-                              debug!("bad reference: {:?}, {:?}", err.description(), err.span);
-                              signal!(e, ErroneousReferencedConstant(box err))
-                          },
-                      }
-                  } else {
-                      signal!(e, UnimplementedConstVal("enum variants"));
-                  }
+              Def::VariantCtor(variant_def, CtorKind::Const) => {
+                Variant(variant_def)
+              }
+              Def::VariantCtor(_, CtorKind::Fn) => {
+                  signal!(e, UnimplementedConstVal("enum variants"));
               }
-              Def::StructCtor(..) => {
+              Def::StructCtor(_, CtorKind::Const) => {
                   ConstVal::Struct(Default::default())
               }
+              Def::StructCtor(_, CtorKind::Fn) => {
+                  signal!(e, UnimplementedConstVal("tuple struct constructors"))
+              }
               Def::Local(def_id) => {
                   debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args);
                   if let Some(val) = cx.fn_args.as_ref().and_then(|args| args.get(&def_id)) {
@@ -578,7 +562,7 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             U8(u) => Ok(Char(u as char)),
             _ => bug!(),
         },
-        _ => bug!(),
+        _ => Err(CannotCast),
     }
 }
 
@@ -622,6 +606,11 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
         Float(f) => cast_const_float(tcx, f, ty),
         Char(c) => cast_const_int(tcx, U32(c as u32), ty),
+        Variant(v) => {
+            let adt = tcx.lookup_adt_def(tcx.parent_def_id(v).unwrap());
+            let idx = adt.variant_index_with_id(v);
+            cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
+        }
         Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
         ByteStr(b) => match ty.sty {
             ty::TyRawPtr(_) => {
index a470d549d05efc2414506f40d72d29c0dcc68e65..0dfafeb6fb8362b95c7f8af1fc0953307e12e18e 100644 (file)
@@ -116,6 +116,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
         ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
         ConstVal::Bool(b) => write!(f, "{:?}", b),
         ConstVal::Char(c) => write!(f, "{:?}", c),
+        ConstVal::Variant(_) |
         ConstVal::Struct(_) |
         ConstVal::Tuple(_) |
         ConstVal::Function(..) |
@@ -620,7 +621,12 @@ fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> {
         let const_cx = eval::ConstContext::with_tables(self.tcx.global_tcx(), self.tables);
         match const_cx.eval(expr) {
             Ok(value) => {
-                PatternKind::Constant { value: value }
+                if let ConstVal::Variant(def_id) = value {
+                    let ty = self.tables.expr_ty(expr);
+                    self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![])
+                } else {
+                    PatternKind::Constant { value: value }
+                }
             }
             Err(e) => {
                 self.errors.push(PatternError::ConstEval(e));
index e938913a3f11774ed74d664595c2ba4d6a811363..040194e63d07e0ec6f549244ec495d3f3951abae 100644 (file)
@@ -100,15 +100,13 @@ pub fn from_constval<'a>(ccx: &CrateContext<'a, 'tcx>,
             ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
             ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
             ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
+            ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
+            ConstVal::Function(..) => C_null(type_of::type_of(ccx, ty)),
+            ConstVal::Variant(_) |
             ConstVal::Struct(_) | ConstVal::Tuple(_) |
             ConstVal::Array(..) | ConstVal::Repeat(..) => {
                 bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
             }
-            ConstVal::Function(..) => {
-                let llty = type_of::type_of(ccx, ty);
-                return Const::new(C_null(llty), ty);
-            }
-            ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
         };
 
         assert!(!ty.has_erasable_regions());
index b40aa2a8e27dcdf67c250265c17081355c165dcc..71cac3edbc188beb9542952db26707d3d81c05d0 100644 (file)
 
 #![feature(const_fn)]
 
+#[derive(PartialEq, Eq)]
 enum Cake {
     BlackForest,
     Marmor,
 }
 use Cake::*;
 
-const BOO: (Cake, Cake) = (Marmor, BlackForest);
+struct Pair<A, B>(A, B);
+
+const BOO: Pair<Cake, Cake> = Pair(Marmor, BlackForest);
 //~^ ERROR: constant evaluation error [E0080]
-//~| unimplemented constant expression: enum variants
+//~| unimplemented constant expression: tuple struct constructors
 const FOO: Cake = BOO.1;
 
 const fn foo() -> Cake {
     Marmor
-        //~^ ERROR: constant evaluation error [E0080]
-        //~| unimplemented constant expression: enum variants
 }
 
 const WORKS: Cake = Marmor;
@@ -34,7 +35,7 @@ const fn foo() -> Cake {
 fn main() {
     match BlackForest {
         FOO => println!("hi"), //~ NOTE: for pattern here
-        GOO => println!("meh"), //~ NOTE: for pattern here
+        GOO => println!("meh"),
         WORKS => println!("möp"),
         _ => println!("bye"),
     }
diff --git a/src/test/compile-fail/issue-41394.rs b/src/test/compile-fail/issue-41394.rs
new file mode 100644 (file)
index 0000000..1fb3b7c
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2017 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.
+
+enum Foo {
+    A = "" + 1
+    //~^ ERROR binary operation `+` cannot be applied to type `&'static str`
+}
+
+enum Bar {
+    A = Foo::A as isize
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs b/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs
deleted file mode 100644 (file)
index cadfec5..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 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.
-
-// Note: This test is checking that we forbid a coding pattern that
-// Issue #5873 explicitly wants to allow.
-
-enum State { ST_NULL, ST_WHITESPACE }
-
-fn main() {
-    [State::ST_NULL; (State::ST_WHITESPACE as usize)];
-    //~^ ERROR constant evaluation error
-    //~| unimplemented constant expression: enum variants
-}
diff --git a/src/test/run-pass/auxiliary/issue-41394.rs b/src/test/run-pass/auxiliary/issue-41394.rs
new file mode 100644 (file)
index 0000000..f06b812
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2017 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.
+
+#![crate_type = "lib"]
+
+#[repr(u32)]
+pub enum Foo {
+    Foo = Private::Variant as u32
+}
+
+#[repr(u8)]
+enum Private {
+    Variant = 42
+}
+
+#[inline(always)]
+pub fn foo() -> Foo {
+    Foo::Foo
+}
diff --git a/src/test/run-pass/const-pattern-variant.rs b/src/test/run-pass/const-pattern-variant.rs
new file mode 100644 (file)
index 0000000..104ab6e
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright 2015 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.
+
+#![feature(const_fn)]
+
+#[derive(PartialEq, Eq)]
+enum Cake {
+    BlackForest,
+    Marmor,
+}
+use Cake::*;
+
+const BOO: (Cake, Cake) = (Marmor, BlackForest);
+const FOO: Cake = BOO.1;
+
+const fn foo() -> Cake {
+    Marmor
+}
+
+const WORKS: Cake = Marmor;
+
+const GOO: Cake = foo();
+
+fn main() {
+    match BlackForest {
+        FOO => println!("hi"),
+        GOO => println!("meh"),
+        WORKS => println!("möp"),
+        _ => println!("bye"),
+    }
+}
diff --git a/src/test/run-pass/issue-23898.rs b/src/test/run-pass/issue-23898.rs
new file mode 100644 (file)
index 0000000..3f5546c
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+// Note: This test was used to demonstrate #5873 (now #23898).
+
+enum State { ST_NULL, ST_WHITESPACE }
+
+fn main() {
+    [State::ST_NULL; (State::ST_WHITESPACE as usize)];
+}
diff --git a/src/test/run-pass/issue-41394.rs b/src/test/run-pass/issue-41394.rs
new file mode 100644 (file)
index 0000000..7989055
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2017 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.
+
+// aux-build:issue-41394.rs
+
+extern crate issue_41394 as lib;
+
+fn main() {
+    assert_eq!(lib::foo() as u32, 42);
+}