]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/const_val.rs
Fix regression on `include!(line!())`.
[rust.git] / src / librustc / middle / const_val.rs
1 // Copyright 2012-2016 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 use self::ConstVal::*;
12 pub use rustc_const_math::ConstInt;
13
14 use hir;
15 use hir::def::Def;
16 use hir::def_id::DefId;
17 use ty::TyCtxt;
18 use ty::subst::Substs;
19 use util::common::ErrorReported;
20 use rustc_const_math::*;
21
22 use graphviz::IntoCow;
23 use errors::DiagnosticBuilder;
24 use syntax::symbol::InternedString;
25 use syntax::ast;
26 use syntax_pos::Span;
27
28 use std::borrow::Cow;
29 use std::collections::BTreeMap;
30 use std::rc::Rc;
31
32 pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>;
33
34 #[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
35 pub enum ConstVal<'tcx> {
36     Float(ConstFloat),
37     Integral(ConstInt),
38     Str(InternedString),
39     ByteStr(Rc<Vec<u8>>),
40     Bool(bool),
41     Char(char),
42     Variant(DefId),
43     Function(DefId, &'tcx Substs<'tcx>),
44     Struct(BTreeMap<ast::Name, ConstVal<'tcx>>),
45     Tuple(Vec<ConstVal<'tcx>>),
46     Array(Vec<ConstVal<'tcx>>),
47     Repeat(Box<ConstVal<'tcx>>, u64),
48 }
49
50 impl<'tcx> ConstVal<'tcx> {
51     pub fn description(&self) -> &'static str {
52         match *self {
53             Float(f) => f.description(),
54             Integral(i) => i.description(),
55             Str(_) => "string literal",
56             ByteStr(_) => "byte string literal",
57             Bool(_) => "boolean",
58             Char(..) => "char",
59             Variant(_) => "enum variant",
60             Struct(_) => "struct",
61             Tuple(_) => "tuple",
62             Function(..) => "function definition",
63             Array(..) => "array",
64             Repeat(..) => "repeat",
65         }
66     }
67
68     pub fn to_const_int(&self) -> Option<ConstInt> {
69         match *self {
70             ConstVal::Integral(i) => Some(i),
71             ConstVal::Bool(b) => Some(ConstInt::U8(b as u8)),
72             ConstVal::Char(ch) => Some(ConstInt::U32(ch as u32)),
73             _ => None
74         }
75     }
76 }
77
78 #[derive(Clone, Debug)]
79 pub struct ConstEvalErr<'tcx> {
80     pub span: Span,
81     pub kind: ErrKind<'tcx>,
82 }
83
84 #[derive(Clone, Debug)]
85 pub enum ErrKind<'tcx> {
86     CannotCast,
87     MissingStructField,
88     NegateOn(ConstVal<'tcx>),
89     NotOn(ConstVal<'tcx>),
90
91     NonConstPath,
92     UnimplementedConstVal(&'static str),
93     ExpectedConstTuple,
94     ExpectedConstStruct,
95     IndexedNonVec,
96     IndexNotUsize,
97     IndexOutOfBounds { len: u64, index: u64 },
98
99     MiscBinaryOp,
100     MiscCatchAll,
101
102     IndexOpFeatureGated,
103     Math(ConstMathErr),
104
105     ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
106
107     TypeckError
108 }
109
110 impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> {
111     fn from(err: ConstMathErr) -> ErrKind<'tcx> {
112         match err {
113             ConstMathErr::UnsignedNegation => ErrKind::TypeckError,
114             _ => ErrKind::Math(err)
115         }
116     }
117 }
118
119 #[derive(Clone, Debug)]
120 pub enum ConstEvalErrDescription<'a> {
121     Simple(Cow<'a, str>),
122 }
123
124 impl<'a> ConstEvalErrDescription<'a> {
125     /// Return a one-line description of the error, for lints and such
126     pub fn into_oneline(self) -> Cow<'a, str> {
127         match self {
128             ConstEvalErrDescription::Simple(simple) => simple,
129         }
130     }
131 }
132
133 impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
134     pub fn description(&self) -> ConstEvalErrDescription {
135         use self::ErrKind::*;
136         use self::ConstEvalErrDescription::*;
137
138         macro_rules! simple {
139             ($msg:expr) => ({ Simple($msg.into_cow()) });
140             ($fmt:expr, $($arg:tt)+) => ({
141                 Simple(format!($fmt, $($arg)+).into_cow())
142             })
143         }
144
145         match self.kind {
146             CannotCast => simple!("can't cast this type"),
147             NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
148             NotOn(ref const_val) => simple!("not on {}", const_val.description()),
149
150             MissingStructField  => simple!("nonexistent struct field"),
151             NonConstPath        => simple!("non-constant path in constant expression"),
152             UnimplementedConstVal(what) =>
153                 simple!("unimplemented constant expression: {}", what),
154             ExpectedConstTuple => simple!("expected constant tuple"),
155             ExpectedConstStruct => simple!("expected constant struct"),
156             IndexedNonVec => simple!("indexing is only supported for arrays"),
157             IndexNotUsize => simple!("indices must be of type `usize`"),
158             IndexOutOfBounds { len, index } => {
159                 simple!("index out of bounds: the len is {} but the index is {}",
160                         len, index)
161             }
162
163             MiscBinaryOp => simple!("bad operands for binary"),
164             MiscCatchAll => simple!("unsupported constant expr"),
165             IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
166             Math(ref err) => Simple(err.description().into_cow()),
167
168             ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
169
170             TypeckError => simple!("type-checking failed"),
171         }
172     }
173
174     pub fn struct_error(&self,
175         tcx: TyCtxt<'a, 'gcx, 'tcx>,
176         primary_span: Span,
177         primary_kind: &str)
178         -> DiagnosticBuilder<'gcx>
179     {
180         let mut err = self;
181         while let &ConstEvalErr {
182             kind: ErrKind::ErroneousReferencedConstant(box ref i_err), ..
183         } = err {
184             err = i_err;
185         }
186
187         let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
188         err.note(tcx, primary_span, primary_kind, &mut diag);
189         diag
190     }
191
192     pub fn note(&self,
193         _tcx: TyCtxt<'a, 'gcx, 'tcx>,
194         primary_span: Span,
195         primary_kind: &str,
196         diag: &mut DiagnosticBuilder)
197     {
198         match self.description() {
199             ConstEvalErrDescription::Simple(message) => {
200                 diag.span_label(self.span, &message);
201             }
202         }
203
204         if !primary_span.contains(self.span) {
205             diag.span_note(primary_span,
206                         &format!("for {} here", primary_kind));
207         }
208     }
209
210     pub fn report(&self,
211         tcx: TyCtxt<'a, 'gcx, 'tcx>,
212         primary_span: Span,
213         primary_kind: &str)
214     {
215         if let ErrKind::TypeckError = self.kind {
216             return;
217         }
218         self.struct_error(tcx, primary_span, primary_kind).emit();
219     }
220 }
221
222 /// Returns the value of the length-valued expression
223 pub fn eval_length(tcx: TyCtxt,
224                    count: hir::BodyId,
225                    reason: &str)
226                    -> Result<usize, ErrorReported>
227 {
228     let count_expr = &tcx.hir.body(count).value;
229     let count_def_id = tcx.hir.body_owner_def_id(count);
230     let substs = Substs::empty();
231     match tcx.at(count_expr.span).const_eval((count_def_id, substs)) {
232         Ok(Integral(Usize(count))) => {
233             let val = count.as_u64(tcx.sess.target.uint_type);
234             assert_eq!(val as usize as u64, val);
235             Ok(val as usize)
236         },
237         Ok(_) |
238         Err(ConstEvalErr { kind: ErrKind::TypeckError, .. }) => Err(ErrorReported),
239         Err(err) => {
240             let mut diag = err.struct_error(tcx, count_expr.span, reason);
241
242             if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node {
243                 if let Def::Local(..) = path.def {
244                     diag.note(&format!("`{}` is a variable",
245                                        tcx.hir.node_to_pretty_string(count_expr.id)));
246                 }
247             }
248
249             diag.emit();
250             Err(ErrorReported)
251         }
252     }
253 }