From 8054377f8f4dfaf766bcff40e7a720c90c5e33be Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 19 Apr 2017 15:16:19 +0300 Subject: [PATCH] rustc_const_eval: support all unit enum variants. --- src/librustc/ich/impls_ty.rs | 9 ++- src/librustc/middle/const_val.rs | 6 +- src/librustc/mir/mod.rs | 3 +- src/librustc/ty/mod.rs | 19 ++++- src/librustc_const_eval/eval.rs | 77 ++++++++----------- src/librustc_const_eval/pattern.rs | 8 +- src/librustc_trans/mir/constant.rs | 8 +- .../const-pattern-not-const-evaluable.rs | 11 +-- src/test/compile-fail/issue-41394.rs | 20 +++++ src/test/run-pass/auxiliary/issue-41394.rs | 26 +++++++ src/test/run-pass/const-pattern-variant.rs | 38 +++++++++ .../issue-23898.rs} | 5 +- src/test/run-pass/issue-41394.rs | 17 ++++ 13 files changed, 180 insertions(+), 67 deletions(-) create mode 100644 src/test/compile-fail/issue-41394.rs create mode 100644 src/test/run-pass/auxiliary/issue-41394.rs create mode 100644 src/test/run-pass/const-pattern-variant.rs rename src/test/{compile-fail/non-constant-enum-for-vec-repeat.rs => run-pass/issue-23898.rs} (73%) create mode 100644 src/test/run-pass/issue-41394.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index f55462fb5deb..16af98c20354 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -273,6 +273,12 @@ for ::middle::const_val::ConstVal<'tcx> { 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 @@ for ::middle::const_val::ConstVal<'tcx> { value.hash_stable(hcx, hasher); times.hash_stable(hcx, hasher); } - ConstVal::Char(value) => { - value.hash_stable(hcx, hasher); - } } } } diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index b65dbdbbcc25..ec7b3c4dd8df 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -38,12 +38,13 @@ pub enum ConstVal<'tcx> { Str(InternedString), ByteStr(Rc>), Bool(bool), + Char(char), + Variant(DefId), Function(DefId, &'tcx Substs<'tcx>), Struct(BTreeMap>), Tuple(Vec>), Array(Vec>), Repeat(Box>, u64), - Char(char), } impl<'tcx> ConstVal<'tcx> { @@ -54,12 +55,13 @@ impl<'tcx> ConstVal<'tcx> { 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", } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 9ff64b295b76..bfb72b5df7b2 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1307,10 +1307,11 @@ fn fmt_const_val(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), } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 1588773479c5..cc5eb768d9b3 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1693,6 +1693,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { } } + #[inline] pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> impl Iterator + 'a { let repr_type = self.repr.discr_type(); @@ -1706,7 +1707,13 @@ impl<'a, 'gcx, 'tcx> AdtDef { 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 @@ impl<'a, 'gcx, 'tcx> AdtDef { 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; } } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 5c421df92c7e..e9352f53c92d 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -15,7 +15,7 @@ use rustc::middle::const_val::{ConstVal, ConstEvalErr, EvalResult, ErrKind}; 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(_) => { diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index a470d549d05e..0dfafeb6fb83 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -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 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, '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)); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index e938913a3f11..040194e63d07 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -100,15 +100,13 @@ impl<'tcx> Const<'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()); diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index b40aa2a8e27d..71cac3edbc18 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -10,21 +10,22 @@ #![feature(const_fn)] +#[derive(PartialEq, Eq)] enum Cake { BlackForest, Marmor, } use Cake::*; -const BOO: (Cake, Cake) = (Marmor, BlackForest); +struct Pair(A, B); + +const BOO: Pair = 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 GOO: Cake = foo(); 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 index 000000000000..1fb3b7c4ee12 --- /dev/null +++ b/src/test/compile-fail/issue-41394.rs @@ -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 or the MIT license +// , 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/run-pass/auxiliary/issue-41394.rs b/src/test/run-pass/auxiliary/issue-41394.rs new file mode 100644 index 000000000000..f06b81279ac4 --- /dev/null +++ b/src/test/run-pass/auxiliary/issue-41394.rs @@ -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 or the MIT license +// , 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 index 000000000000..104ab6e19db6 --- /dev/null +++ b/src/test/run-pass/const-pattern-variant.rs @@ -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 or the MIT license +// , 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/compile-fail/non-constant-enum-for-vec-repeat.rs b/src/test/run-pass/issue-23898.rs similarity index 73% rename from src/test/compile-fail/non-constant-enum-for-vec-repeat.rs rename to src/test/run-pass/issue-23898.rs index cadfec5a38d3..3f5546ce83dd 100644 --- a/src/test/compile-fail/non-constant-enum-for-vec-repeat.rs +++ b/src/test/run-pass/issue-23898.rs @@ -8,13 +8,10 @@ // 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. +// 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)]; - //~^ ERROR constant evaluation error - //~| unimplemented constant expression: enum variants } diff --git a/src/test/run-pass/issue-41394.rs b/src/test/run-pass/issue-41394.rs new file mode 100644 index 000000000000..798905599a85 --- /dev/null +++ b/src/test/run-pass/issue-41394.rs @@ -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 or the MIT license +// , 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); +}