Skip to content

Commit 8054377

Browse files
committed
rustc_const_eval: support all unit enum variants.
1 parent 0ff828b commit 8054377

File tree

13 files changed

+180
-67
lines changed

13 files changed

+180
-67
lines changed

src/librustc/ich/impls_ty.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ for ::middle::const_val::ConstVal<'tcx> {
273273
ConstVal::Bool(value) => {
274274
value.hash_stable(hcx, hasher);
275275
}
276+
ConstVal::Char(value) => {
277+
value.hash_stable(hcx, hasher);
278+
}
279+
ConstVal::Variant(def_id) => {
280+
def_id.hash_stable(hcx, hasher);
281+
}
276282
ConstVal::Function(def_id, substs) => {
277283
def_id.hash_stable(hcx, hasher);
278284
substs.hash_stable(hcx, hasher);
@@ -296,9 +302,6 @@ for ::middle::const_val::ConstVal<'tcx> {
296302
value.hash_stable(hcx, hasher);
297303
times.hash_stable(hcx, hasher);
298304
}
299-
ConstVal::Char(value) => {
300-
value.hash_stable(hcx, hasher);
301-
}
302305
}
303306
}
304307
}

src/librustc/middle/const_val.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ pub enum ConstVal<'tcx> {
3838
Str(InternedString),
3939
ByteStr(Rc<Vec<u8>>),
4040
Bool(bool),
41+
Char(char),
42+
Variant(DefId),
4143
Function(DefId, &'tcx Substs<'tcx>),
4244
Struct(BTreeMap<ast::Name, ConstVal<'tcx>>),
4345
Tuple(Vec<ConstVal<'tcx>>),
4446
Array(Vec<ConstVal<'tcx>>),
4547
Repeat(Box<ConstVal<'tcx>>, u64),
46-
Char(char),
4748
}
4849

4950
impl<'tcx> ConstVal<'tcx> {
@@ -54,12 +55,13 @@ impl<'tcx> ConstVal<'tcx> {
5455
Str(_) => "string literal",
5556
ByteStr(_) => "byte string literal",
5657
Bool(_) => "boolean",
58+
Char(..) => "char",
59+
Variant(_) => "enum variant",
5760
Struct(_) => "struct",
5861
Tuple(_) => "tuple",
5962
Function(..) => "function definition",
6063
Array(..) => "array",
6164
Repeat(..) => "repeat",
62-
Char(..) => "char",
6365
}
6466
}
6567

src/librustc/mir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1307,10 +1307,11 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
13071307
write!(fmt, "b\"{}\"", escaped)
13081308
}
13091309
Bool(b) => write!(fmt, "{:?}", b),
1310+
Char(c) => write!(fmt, "{:?}", c),
1311+
Variant(def_id) |
13101312
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
13111313
Struct(_) | Tuple(_) | Array(_) | Repeat(..) =>
13121314
bug!("ConstVal `{:?}` should not be in MIR", const_val),
1313-
Char(c) => write!(fmt, "{:?}", c),
13141315
}
13151316
}
13161317

src/librustc/ty/mod.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
16931693
}
16941694
}
16951695

1696+
#[inline]
16961697
pub fn discriminants(&'a self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
16971698
-> impl Iterator<Item=ConstInt> + 'a {
16981699
let repr_type = self.repr.discr_type();
@@ -1706,7 +1707,13 @@ impl<'a, 'gcx, 'tcx> AdtDef {
17061707
Ok(ConstVal::Integral(v)) => {
17071708
discr = v;
17081709
}
1709-
_ => {}
1710+
err => {
1711+
if !expr_did.is_local() {
1712+
span_bug!(tcx.def_span(expr_did),
1713+
"variant discriminant evaluation succeeded \
1714+
in its crate but failed locally: {:?}", err);
1715+
}
1716+
}
17101717
}
17111718
}
17121719
prev_discr = Some(discr);
@@ -1740,7 +1747,15 @@ impl<'a, 'gcx, 'tcx> AdtDef {
17401747
explicit_value = v;
17411748
break;
17421749
}
1743-
_ => {
1750+
err => {
1751+
if !expr_did.is_local() {
1752+
span_bug!(tcx.def_span(expr_did),
1753+
"variant discriminant evaluation succeeded \
1754+
in its crate but failed locally: {:?}", err);
1755+
}
1756+
if explicit_index == 0 {
1757+
break;
1758+
}
17441759
explicit_index -= 1;
17451760
}
17461761
}

src/librustc_const_eval/eval.rs

+33-44
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc::middle::const_val::{ConstVal, ConstEvalErr, EvalResult, ErrKind};
1515
use rustc::hir::map as hir_map;
1616
use rustc::hir::map::blocks::FnLikeNode;
1717
use rustc::traits;
18-
use rustc::hir::def::Def;
18+
use rustc::hir::def::{Def, CtorKind};
1919
use rustc::hir::def_id::DefId;
2020
use rustc::ty::{self, Ty, TyCtxt};
2121
use rustc::ty::maps::Providers;
@@ -48,28 +48,6 @@ macro_rules! math {
4848
}
4949
}
5050

51-
fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
52-
variant_def: DefId)
53-
-> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> {
54-
if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) {
55-
let enum_node_id = tcx.hir.get_parent(variant_node_id);
56-
if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) {
57-
if let hir::ItemEnum(ref edef, _) = it.node {
58-
for variant in &edef.variants {
59-
if variant.node.data.id() == variant_node_id {
60-
return variant.node.disr_expr.map(|e| {
61-
let def_id = tcx.hir.body_owner_def_id(e);
62-
(&tcx.hir.body(e).value,
63-
tcx.item_tables(def_id))
64-
});
65-
}
66-
}
67-
}
68-
}
69-
}
70-
None
71-
}
72-
7351
/// * `def_id` is the id of the constant.
7452
/// * `substs` is the monomorphized substitutions for the expression.
7553
///
@@ -289,9 +267,22 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
289267
}
290268
}
291269
hir::ExprCast(ref base, _) => {
292-
match cast_const(tcx, cx.eval(base)?, ety) {
293-
Ok(val) => val,
294-
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
270+
let base_val = cx.eval(base)?;
271+
let base_ty = cx.tables.expr_ty(base);
272+
273+
// Avoid applying substitutions if they're empty, that'd ICE.
274+
let base_ty = if cx.substs.is_empty() {
275+
base_ty
276+
} else {
277+
base_ty.subst(tcx, cx.substs)
278+
};
279+
if ety == base_ty {
280+
base_val
281+
} else {
282+
match cast_const(tcx, base_val, ety) {
283+
Ok(val) => val,
284+
Err(kind) => signal!(e, kind),
285+
}
295286
}
296287
}
297288
hir::ExprPath(ref qpath) => {
@@ -317,27 +308,20 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
317308
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
318309
signal!(e, ErroneousReferencedConstant(box err))
319310
},
311+
}
320312
},
321-
Def::VariantCtor(variant_def, ..) => {
322-
if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) {
323-
let cx = ConstContext::with_tables(tcx, tables);
324-
match cx.eval(expr) {
325-
Ok(val) => val,
326-
Err(ConstEvalErr { kind: TypeckError, .. }) => {
327-
signal!(e, TypeckError);
328-
}
329-
Err(err) => {
330-
debug!("bad reference: {:?}, {:?}", err.description(), err.span);
331-
signal!(e, ErroneousReferencedConstant(box err))
332-
},
333-
}
334-
} else {
335-
signal!(e, UnimplementedConstVal("enum variants"));
336-
}
313+
Def::VariantCtor(variant_def, CtorKind::Const) => {
314+
Variant(variant_def)
315+
}
316+
Def::VariantCtor(_, CtorKind::Fn) => {
317+
signal!(e, UnimplementedConstVal("enum variants"));
337318
}
338-
Def::StructCtor(..) => {
319+
Def::StructCtor(_, CtorKind::Const) => {
339320
ConstVal::Struct(Default::default())
340321
}
322+
Def::StructCtor(_, CtorKind::Fn) => {
323+
signal!(e, UnimplementedConstVal("tuple struct constructors"))
324+
}
341325
Def::Local(def_id) => {
342326
debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args);
343327
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>,
578562
U8(u) => Ok(Char(u as char)),
579563
_ => bug!(),
580564
},
581-
_ => bug!(),
565+
_ => Err(CannotCast),
582566
}
583567
}
584568

@@ -622,6 +606,11 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
622606
Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
623607
Float(f) => cast_const_float(tcx, f, ty),
624608
Char(c) => cast_const_int(tcx, U32(c as u32), ty),
609+
Variant(v) => {
610+
let adt = tcx.lookup_adt_def(tcx.parent_def_id(v).unwrap());
611+
let idx = adt.variant_index_with_id(v);
612+
cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
613+
}
625614
Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
626615
ByteStr(b) => match ty.sty {
627616
ty::TyRawPtr(_) => {

src/librustc_const_eval/pattern.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
116116
ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
117117
ConstVal::Bool(b) => write!(f, "{:?}", b),
118118
ConstVal::Char(c) => write!(f, "{:?}", c),
119+
ConstVal::Variant(_) |
119120
ConstVal::Struct(_) |
120121
ConstVal::Tuple(_) |
121122
ConstVal::Function(..) |
@@ -620,7 +621,12 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
620621
let const_cx = eval::ConstContext::with_tables(self.tcx.global_tcx(), self.tables);
621622
match const_cx.eval(expr) {
622623
Ok(value) => {
623-
PatternKind::Constant { value: value }
624+
if let ConstVal::Variant(def_id) = value {
625+
let ty = self.tables.expr_ty(expr);
626+
self.lower_variant_or_leaf(Def::Variant(def_id), ty, vec![])
627+
} else {
628+
PatternKind::Constant { value: value }
629+
}
624630
}
625631
Err(e) => {
626632
self.errors.push(PatternError::ConstEval(e));

src/librustc_trans/mir/constant.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,13 @@ impl<'tcx> Const<'tcx> {
100100
ConstVal::Integral(ref i) => return Const::from_constint(ccx, i),
101101
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
102102
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
103+
ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
104+
ConstVal::Function(..) => C_null(type_of::type_of(ccx, ty)),
105+
ConstVal::Variant(_) |
103106
ConstVal::Struct(_) | ConstVal::Tuple(_) |
104107
ConstVal::Array(..) | ConstVal::Repeat(..) => {
105108
bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
106109
}
107-
ConstVal::Function(..) => {
108-
let llty = type_of::type_of(ccx, ty);
109-
return Const::new(C_null(llty), ty);
110-
}
111-
ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
112110
};
113111

114112
assert!(!ty.has_erasable_regions());

src/test/compile-fail/const-pattern-not-const-evaluable.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010

1111
#![feature(const_fn)]
1212

13+
#[derive(PartialEq, Eq)]
1314
enum Cake {
1415
BlackForest,
1516
Marmor,
1617
}
1718
use Cake::*;
1819

19-
const BOO: (Cake, Cake) = (Marmor, BlackForest);
20+
struct Pair<A, B>(A, B);
21+
22+
const BOO: Pair<Cake, Cake> = Pair(Marmor, BlackForest);
2023
//~^ ERROR: constant evaluation error [E0080]
21-
//~| unimplemented constant expression: enum variants
24+
//~| unimplemented constant expression: tuple struct constructors
2225
const FOO: Cake = BOO.1;
2326

2427
const fn foo() -> Cake {
2528
Marmor
26-
//~^ ERROR: constant evaluation error [E0080]
27-
//~| unimplemented constant expression: enum variants
2829
}
2930

3031
const WORKS: Cake = Marmor;
@@ -34,7 +35,7 @@ const GOO: Cake = foo();
3435
fn main() {
3536
match BlackForest {
3637
FOO => println!("hi"), //~ NOTE: for pattern here
37-
GOO => println!("meh"), //~ NOTE: for pattern here
38+
GOO => println!("meh"),
3839
WORKS => println!("möp"),
3940
_ => println!("bye"),
4041
}

src/test/compile-fail/issue-41394.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 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+
enum Foo {
12+
A = "" + 1
13+
//~^ ERROR binary operation `+` cannot be applied to type `&'static str`
14+
}
15+
16+
enum Bar {
17+
A = Foo::A as isize
18+
}
19+
20+
fn main() {}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2017 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+
#![crate_type = "lib"]
12+
13+
#[repr(u32)]
14+
pub enum Foo {
15+
Foo = Private::Variant as u32
16+
}
17+
18+
#[repr(u8)]
19+
enum Private {
20+
Variant = 42
21+
}
22+
23+
#[inline(always)]
24+
pub fn foo() -> Foo {
25+
Foo::Foo
26+
}
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2015 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+
#![feature(const_fn)]
12+
13+
#[derive(PartialEq, Eq)]
14+
enum Cake {
15+
BlackForest,
16+
Marmor,
17+
}
18+
use Cake::*;
19+
20+
const BOO: (Cake, Cake) = (Marmor, BlackForest);
21+
const FOO: Cake = BOO.1;
22+
23+
const fn foo() -> Cake {
24+
Marmor
25+
}
26+
27+
const WORKS: Cake = Marmor;
28+
29+
const GOO: Cake = foo();
30+
31+
fn main() {
32+
match BlackForest {
33+
FOO => println!("hi"),
34+
GOO => println!("meh"),
35+
WORKS => println!("möp"),
36+
_ => println!("bye"),
37+
}
38+
}

0 commit comments

Comments
 (0)