]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
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 | //#![allow(non_camel_case_types)] | |
12 | ||
13 | use rustc::middle::const_val::ConstVal::*; | |
14 | use rustc::middle::const_val::ConstVal; | |
15 | use self::ErrKind::*; | |
16 | use self::EvalHint::*; | |
17 | ||
18 | use rustc::hir::map as ast_map; | |
19 | use rustc::hir::map::blocks::FnLikeNode; | |
5bcae85e | 20 | use rustc::middle::cstore::InlinedItem; |
a7813a04 | 21 | use rustc::traits; |
3157f602 | 22 | use rustc::hir::def::{Def, PathResolution}; |
54a0048b SL |
23 | use rustc::hir::def_id::DefId; |
24 | use rustc::hir::pat_util::def_to_path; | |
25 | use rustc::ty::{self, Ty, TyCtxt, subst}; | |
26 | use rustc::ty::util::IntTypeExt; | |
5bcae85e SL |
27 | use rustc::traits::Reveal; |
28 | use rustc::util::common::ErrorReported; | |
54a0048b SL |
29 | use rustc::util::nodemap::NodeMap; |
30 | use rustc::lint; | |
31 | ||
32 | use graphviz::IntoCow; | |
33 | use syntax::ast; | |
34 | use rustc::hir::{Expr, PatKind}; | |
35 | use rustc::hir; | |
36 | use rustc::hir::intravisit::FnKind; | |
54a0048b SL |
37 | use syntax::ptr::P; |
38 | use syntax::codemap; | |
39 | use syntax::attr::IntType; | |
3157f602 | 40 | use syntax_pos::{self, Span}; |
54a0048b SL |
41 | |
42 | use std::borrow::Cow; | |
43 | use std::cmp::Ordering; | |
44 | use std::collections::hash_map::Entry::Vacant; | |
45 | ||
46 | use rustc_const_math::*; | |
5bcae85e | 47 | use rustc_errors::DiagnosticBuilder; |
54a0048b SL |
48 | |
49 | macro_rules! math { | |
50 | ($e:expr, $op:expr) => { | |
51 | match $op { | |
52 | Ok(val) => val, | |
53 | Err(e) => signal!($e, Math(e)), | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
a7813a04 XL |
58 | fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
59 | enum_def: DefId, | |
60 | variant_def: DefId) | |
61 | -> Option<&'tcx Expr> { | |
54a0048b SL |
62 | fn variant_expr<'a>(variants: &'a [hir::Variant], id: ast::NodeId) |
63 | -> Option<&'a Expr> { | |
64 | for variant in variants { | |
65 | if variant.node.data.id() == id { | |
66 | return variant.node.disr_expr.as_ref().map(|e| &**e); | |
67 | } | |
68 | } | |
69 | None | |
70 | } | |
71 | ||
72 | if let Some(enum_node_id) = tcx.map.as_local_node_id(enum_def) { | |
73 | let variant_node_id = tcx.map.as_local_node_id(variant_def).unwrap(); | |
74 | match tcx.map.find(enum_node_id) { | |
75 | None => None, | |
76 | Some(ast_map::NodeItem(it)) => match it.node { | |
77 | hir::ItemEnum(hir::EnumDef { ref variants }, _) => { | |
78 | variant_expr(variants, variant_node_id) | |
79 | } | |
80 | _ => None | |
81 | }, | |
82 | Some(_) => None | |
83 | } | |
84 | } else { | |
85 | None | |
86 | } | |
87 | } | |
88 | ||
89 | /// * `def_id` is the id of the constant. | |
90 | /// * `substs` is the monomorphized substitutions for the expression. | |
91 | /// | |
92 | /// `substs` is optional and is used for associated constants. | |
93 | /// This generally happens in late/trans const evaluation. | |
a7813a04 | 94 | pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
54a0048b | 95 | def_id: DefId, |
a7813a04 | 96 | substs: Option<&'tcx subst::Substs<'tcx>>) |
54a0048b SL |
97 | -> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)> { |
98 | if let Some(node_id) = tcx.map.as_local_node_id(def_id) { | |
99 | match tcx.map.find(node_id) { | |
100 | None => None, | |
101 | Some(ast_map::NodeItem(it)) => match it.node { | |
102 | hir::ItemConst(ref ty, ref const_expr) => { | |
a7813a04 | 103 | Some((&const_expr, tcx.ast_ty_to_prim_ty(ty))) |
54a0048b SL |
104 | } |
105 | _ => None | |
106 | }, | |
107 | Some(ast_map::NodeTraitItem(ti)) => match ti.node { | |
108 | hir::ConstTraitItem(_, _) => { | |
109 | if let Some(substs) = substs { | |
110 | // If we have a trait item and the substitutions for it, | |
111 | // `resolve_trait_associated_const` will select an impl | |
112 | // or the default. | |
113 | let trait_id = tcx.trait_of_item(def_id).unwrap(); | |
114 | resolve_trait_associated_const(tcx, ti, trait_id, substs) | |
115 | } else { | |
116 | // Technically, without knowing anything about the | |
117 | // expression that generates the obligation, we could | |
118 | // still return the default if there is one. However, | |
119 | // it's safer to return `None` than to return some value | |
120 | // that may differ from what you would get from | |
121 | // correctly selecting an impl. | |
122 | None | |
123 | } | |
124 | } | |
125 | _ => None | |
126 | }, | |
127 | Some(ast_map::NodeImplItem(ii)) => match ii.node { | |
128 | hir::ImplItemKind::Const(ref ty, ref expr) => { | |
a7813a04 | 129 | Some((&expr, tcx.ast_ty_to_prim_ty(ty))) |
54a0048b SL |
130 | } |
131 | _ => None | |
132 | }, | |
133 | Some(_) => None | |
134 | } | |
135 | } else { | |
136 | match tcx.extern_const_statics.borrow().get(&def_id) { | |
137 | Some(&None) => return None, | |
138 | Some(&Some((expr_id, ty))) => { | |
139 | return Some((tcx.map.expect_expr(expr_id), ty)); | |
140 | } | |
141 | None => {} | |
142 | } | |
143 | let mut used_substs = false; | |
144 | let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { | |
5bcae85e | 145 | Some((&InlinedItem::Item(_, ref item), _)) => match item.node { |
54a0048b | 146 | hir::ItemConst(ref ty, ref const_expr) => { |
a7813a04 | 147 | Some((&**const_expr, tcx.ast_ty_to_prim_ty(ty))) |
54a0048b SL |
148 | }, |
149 | _ => None | |
150 | }, | |
5bcae85e | 151 | Some((&InlinedItem::TraitItem(trait_id, ref ti), _)) => match ti.node { |
54a0048b SL |
152 | hir::ConstTraitItem(_, _) => { |
153 | used_substs = true; | |
154 | if let Some(substs) = substs { | |
155 | // As mentioned in the comments above for in-crate | |
156 | // constants, we only try to find the expression for | |
157 | // a trait-associated const if the caller gives us | |
158 | // the substitutions for the reference to it. | |
159 | resolve_trait_associated_const(tcx, ti, trait_id, substs) | |
160 | } else { | |
161 | None | |
162 | } | |
163 | } | |
164 | _ => None | |
165 | }, | |
5bcae85e | 166 | Some((&InlinedItem::ImplItem(_, ref ii), _)) => match ii.node { |
54a0048b | 167 | hir::ImplItemKind::Const(ref ty, ref expr) => { |
a7813a04 | 168 | Some((&**expr, tcx.ast_ty_to_prim_ty(ty))) |
54a0048b SL |
169 | }, |
170 | _ => None | |
171 | }, | |
172 | _ => None | |
173 | }; | |
174 | // If we used the substitutions, particularly to choose an impl | |
175 | // of a trait-associated const, don't cache that, because the next | |
176 | // lookup with the same def_id may yield a different result. | |
177 | if !used_substs { | |
178 | tcx.extern_const_statics | |
179 | .borrow_mut() | |
180 | .insert(def_id, expr_ty.map(|(e, t)| (e.id, t))); | |
181 | } | |
182 | expr_ty | |
183 | } | |
184 | } | |
185 | ||
a7813a04 XL |
186 | fn inline_const_fn_from_external_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
187 | def_id: DefId) | |
188 | -> Option<ast::NodeId> { | |
54a0048b SL |
189 | match tcx.extern_const_fns.borrow().get(&def_id) { |
190 | Some(&ast::DUMMY_NODE_ID) => return None, | |
191 | Some(&fn_id) => return Some(fn_id), | |
192 | None => {} | |
193 | } | |
194 | ||
195 | if !tcx.sess.cstore.is_const_fn(def_id) { | |
196 | tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); | |
197 | return None; | |
198 | } | |
199 | ||
200 | let fn_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { | |
5bcae85e SL |
201 | Some((&InlinedItem::Item(_, ref item), _)) => Some(item.id), |
202 | Some((&InlinedItem::ImplItem(_, ref item), _)) => Some(item.id), | |
54a0048b SL |
203 | _ => None |
204 | }; | |
205 | tcx.extern_const_fns.borrow_mut().insert(def_id, | |
206 | fn_id.unwrap_or(ast::DUMMY_NODE_ID)); | |
207 | fn_id | |
208 | } | |
209 | ||
a7813a04 XL |
210 | pub fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) |
211 | -> Option<FnLikeNode<'tcx>> | |
54a0048b SL |
212 | { |
213 | let fn_id = if let Some(node_id) = tcx.map.as_local_node_id(def_id) { | |
214 | node_id | |
215 | } else { | |
216 | if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { | |
217 | fn_id | |
218 | } else { | |
219 | return None; | |
220 | } | |
221 | }; | |
222 | ||
223 | let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { | |
224 | Some(fn_like) => fn_like, | |
225 | None => return None | |
226 | }; | |
227 | ||
228 | match fn_like.kind() { | |
229 | FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => { | |
230 | Some(fn_like) | |
231 | } | |
232 | FnKind::Method(_, m, _, _) => { | |
233 | if m.constness == hir::Constness::Const { | |
234 | Some(fn_like) | |
235 | } else { | |
236 | None | |
237 | } | |
238 | } | |
239 | _ => None | |
240 | } | |
241 | } | |
242 | ||
a7813a04 XL |
243 | pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
244 | expr: &Expr, | |
245 | pat_id: ast::NodeId, | |
246 | span: Span) | |
247 | -> Result<P<hir::Pat>, DefId> { | |
54a0048b SL |
248 | let pat_ty = tcx.expr_ty(expr); |
249 | debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id); | |
250 | match pat_ty.sty { | |
251 | ty::TyFloat(_) => { | |
252 | tcx.sess.add_lint( | |
253 | lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN, | |
254 | pat_id, | |
255 | span, | |
256 | format!("floating point constants cannot be used in patterns")); | |
257 | } | |
258 | ty::TyEnum(adt_def, _) | | |
259 | ty::TyStruct(adt_def, _) => { | |
260 | if !tcx.has_attr(adt_def.did, "structural_match") { | |
261 | tcx.sess.add_lint( | |
262 | lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, | |
263 | pat_id, | |
264 | span, | |
265 | format!("to use a constant of type `{}` \ | |
266 | in a pattern, \ | |
267 | `{}` must be annotated with `#[derive(PartialEq, Eq)]`", | |
268 | tcx.item_path_str(adt_def.did), | |
269 | tcx.item_path_str(adt_def.did))); | |
270 | } | |
271 | } | |
272 | _ => { } | |
273 | } | |
274 | let pat = match expr.node { | |
275 | hir::ExprTup(ref exprs) => | |
3157f602 XL |
276 | PatKind::Tuple(try!(exprs.iter() |
277 | .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span)) | |
278 | .collect()), None), | |
54a0048b SL |
279 | |
280 | hir::ExprCall(ref callee, ref args) => { | |
3157f602 | 281 | let def = tcx.expect_def(callee.id); |
54a0048b | 282 | if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) { |
3157f602 | 283 | entry.insert(PathResolution::new(def)); |
54a0048b | 284 | } |
3157f602 | 285 | let path = match def { |
54a0048b SL |
286 | Def::Struct(def_id) => def_to_path(tcx, def_id), |
287 | Def::Variant(_, variant_did) => def_to_path(tcx, variant_did), | |
a7813a04 | 288 | Def::Fn(..) | Def::Method(..) => return Ok(P(hir::Pat { |
54a0048b SL |
289 | id: expr.id, |
290 | node: PatKind::Lit(P(expr.clone())), | |
291 | span: span, | |
292 | })), | |
293 | _ => bug!() | |
294 | }; | |
295 | let pats = try!(args.iter() | |
296 | .map(|expr| const_expr_to_pat(tcx, &**expr, | |
297 | pat_id, span)) | |
298 | .collect()); | |
3157f602 | 299 | PatKind::TupleStruct(path, pats, None) |
54a0048b SL |
300 | } |
301 | ||
302 | hir::ExprStruct(ref path, ref fields, None) => { | |
303 | let field_pats = | |
304 | try!(fields.iter() | |
305 | .map(|field| Ok(codemap::Spanned { | |
3157f602 | 306 | span: syntax_pos::DUMMY_SP, |
54a0048b SL |
307 | node: hir::FieldPat { |
308 | name: field.name.node, | |
309 | pat: try!(const_expr_to_pat(tcx, &field.expr, | |
310 | pat_id, span)), | |
311 | is_shorthand: false, | |
312 | }, | |
313 | })) | |
314 | .collect()); | |
315 | PatKind::Struct(path.clone(), field_pats, false) | |
316 | } | |
317 | ||
318 | hir::ExprVec(ref exprs) => { | |
319 | let pats = try!(exprs.iter() | |
320 | .map(|expr| const_expr_to_pat(tcx, &expr, | |
321 | pat_id, span)) | |
322 | .collect()); | |
323 | PatKind::Vec(pats, None, hir::HirVec::new()) | |
324 | } | |
325 | ||
326 | hir::ExprPath(_, ref path) => { | |
3157f602 | 327 | match tcx.expect_def(expr.id) { |
5bcae85e | 328 | Def::Struct(..) | Def::Variant(..) => PatKind::Path(None, path.clone()), |
3157f602 | 329 | Def::Const(def_id) | Def::AssociatedConst(def_id) => { |
54a0048b SL |
330 | let substs = Some(tcx.node_id_item_substs(expr.id).substs); |
331 | let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap(); | |
332 | return const_expr_to_pat(tcx, expr, pat_id, span); | |
333 | }, | |
334 | _ => bug!(), | |
335 | } | |
336 | } | |
337 | ||
338 | _ => PatKind::Lit(P(expr.clone())) | |
339 | }; | |
340 | Ok(P(hir::Pat { id: expr.id, node: pat, span: span })) | |
341 | } | |
342 | ||
5bcae85e SL |
343 | pub fn report_const_eval_err<'a, 'tcx>( |
344 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
345 | err: &ConstEvalErr, | |
346 | primary_span: Span, | |
347 | primary_kind: &str) | |
348 | -> DiagnosticBuilder<'tcx> | |
349 | { | |
350 | let mut err = err; | |
351 | while let &ConstEvalErr { kind: ErroneousReferencedConstant(box ref i_err), .. } = err { | |
352 | err = i_err; | |
353 | } | |
354 | ||
355 | let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error"); | |
356 | note_const_eval_err(tcx, err, primary_span, primary_kind, &mut diag); | |
357 | diag | |
358 | } | |
359 | ||
360 | pub fn fatal_const_eval_err<'a, 'tcx>( | |
361 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
362 | err: &ConstEvalErr, | |
363 | primary_span: Span, | |
364 | primary_kind: &str) | |
365 | -> ! | |
366 | { | |
367 | report_const_eval_err(tcx, err, primary_span, primary_kind).emit(); | |
368 | tcx.sess.abort_if_errors(); | |
369 | unreachable!() | |
370 | } | |
371 | ||
372 | pub fn note_const_eval_err<'a, 'tcx>( | |
373 | _tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
374 | err: &ConstEvalErr, | |
375 | primary_span: Span, | |
376 | primary_kind: &str, | |
377 | diag: &mut DiagnosticBuilder) | |
378 | { | |
379 | match err.description() { | |
380 | ConstEvalErrDescription::Simple(message) => { | |
381 | diag.span_label(err.span, &message); | |
382 | } | |
383 | } | |
384 | ||
385 | if !primary_span.contains(err.span) { | |
386 | diag.span_note(primary_span, | |
387 | &format!("for {} here", primary_kind)); | |
388 | } | |
389 | } | |
390 | ||
a7813a04 XL |
391 | pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
392 | e: &Expr) -> ConstVal { | |
54a0048b SL |
393 | match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) { |
394 | Ok(r) => r, | |
395 | // non-const path still needs to be a fatal error, because enums are funky | |
396 | Err(s) => { | |
5bcae85e | 397 | report_const_eval_err(tcx, &s, e.span, "expression").emit(); |
54a0048b SL |
398 | match s.kind { |
399 | NonConstPath | | |
5bcae85e SL |
400 | UnimplementedConstVal(_) => tcx.sess.abort_if_errors(), |
401 | _ => {} | |
54a0048b | 402 | } |
5bcae85e | 403 | Dummy |
54a0048b SL |
404 | }, |
405 | } | |
406 | } | |
407 | ||
408 | pub type FnArgMap<'a> = Option<&'a NodeMap<ConstVal>>; | |
409 | ||
410 | #[derive(Clone)] | |
411 | pub struct ConstEvalErr { | |
412 | pub span: Span, | |
413 | pub kind: ErrKind, | |
414 | } | |
415 | ||
a7813a04 | 416 | #[derive(Clone)] |
54a0048b SL |
417 | pub enum ErrKind { |
418 | CannotCast, | |
419 | CannotCastTo(&'static str), | |
420 | InvalidOpForInts(hir::BinOp_), | |
421 | InvalidOpForBools(hir::BinOp_), | |
422 | InvalidOpForFloats(hir::BinOp_), | |
423 | InvalidOpForIntUint(hir::BinOp_), | |
424 | InvalidOpForUintInt(hir::BinOp_), | |
425 | NegateOn(ConstVal), | |
426 | NotOn(ConstVal), | |
427 | CallOn(ConstVal), | |
428 | ||
54a0048b SL |
429 | MissingStructField, |
430 | NonConstPath, | |
431 | UnimplementedConstVal(&'static str), | |
432 | UnresolvedPath, | |
433 | ExpectedConstTuple, | |
434 | ExpectedConstStruct, | |
435 | TupleIndexOutOfBounds, | |
436 | IndexedNonVec, | |
437 | IndexNegative, | |
438 | IndexNotInt, | |
3157f602 | 439 | IndexOutOfBounds { len: u64, index: u64 }, |
54a0048b SL |
440 | RepeatCountNotNatural, |
441 | RepeatCountNotInt, | |
442 | ||
443 | MiscBinaryOp, | |
444 | MiscCatchAll, | |
445 | ||
446 | IndexOpFeatureGated, | |
447 | Math(ConstMathErr), | |
448 | ||
449 | IntermediateUnsignedNegative, | |
450 | /// Expected, Got | |
451 | TypeMismatch(String, ConstInt), | |
5bcae85e | 452 | |
54a0048b | 453 | BadType(ConstVal), |
a7813a04 XL |
454 | ErroneousReferencedConstant(Box<ConstEvalErr>), |
455 | CharCast(ConstInt), | |
54a0048b SL |
456 | } |
457 | ||
458 | impl From<ConstMathErr> for ErrKind { | |
459 | fn from(err: ConstMathErr) -> ErrKind { | |
460 | Math(err) | |
461 | } | |
462 | } | |
463 | ||
5bcae85e SL |
464 | #[derive(Clone, Debug)] |
465 | pub enum ConstEvalErrDescription<'a> { | |
466 | Simple(Cow<'a, str>), | |
467 | } | |
468 | ||
469 | impl<'a> ConstEvalErrDescription<'a> { | |
470 | /// Return a one-line description of the error, for lints and such | |
471 | pub fn into_oneline(self) -> Cow<'a, str> { | |
472 | match self { | |
473 | ConstEvalErrDescription::Simple(simple) => simple, | |
474 | } | |
475 | } | |
476 | } | |
477 | ||
54a0048b | 478 | impl ConstEvalErr { |
5bcae85e | 479 | pub fn description(&self) -> ConstEvalErrDescription { |
54a0048b | 480 | use self::ErrKind::*; |
5bcae85e SL |
481 | use self::ConstEvalErrDescription::*; |
482 | ||
483 | macro_rules! simple { | |
484 | ($msg:expr) => ({ Simple($msg.into_cow()) }); | |
485 | ($fmt:expr, $($arg:tt)+) => ({ | |
486 | Simple(format!($fmt, $($arg)+).into_cow()) | |
487 | }) | |
488 | } | |
54a0048b SL |
489 | |
490 | match self.kind { | |
5bcae85e SL |
491 | CannotCast => simple!("can't cast this type"), |
492 | CannotCastTo(s) => simple!("can't cast this type to {}", s), | |
493 | InvalidOpForInts(_) => simple!("can't do this op on integrals"), | |
494 | InvalidOpForBools(_) => simple!("can't do this op on bools"), | |
495 | InvalidOpForFloats(_) => simple!("can't do this op on floats"), | |
496 | InvalidOpForIntUint(..) => simple!("can't do this op on an isize and usize"), | |
497 | InvalidOpForUintInt(..) => simple!("can't do this op on a usize and isize"), | |
498 | NegateOn(ref const_val) => simple!("negate on {}", const_val.description()), | |
499 | NotOn(ref const_val) => simple!("not on {}", const_val.description()), | |
500 | CallOn(ref const_val) => simple!("call on {}", const_val.description()), | |
501 | ||
502 | MissingStructField => simple!("nonexistent struct field"), | |
503 | NonConstPath => simple!("non-constant path in constant expression"), | |
54a0048b | 504 | UnimplementedConstVal(what) => |
5bcae85e SL |
505 | simple!("unimplemented constant expression: {}", what), |
506 | UnresolvedPath => simple!("unresolved path in constant expression"), | |
507 | ExpectedConstTuple => simple!("expected constant tuple"), | |
508 | ExpectedConstStruct => simple!("expected constant struct"), | |
509 | TupleIndexOutOfBounds => simple!("tuple index out of bounds"), | |
510 | IndexedNonVec => simple!("indexing is only supported for arrays"), | |
511 | IndexNegative => simple!("indices must be non-negative integers"), | |
512 | IndexNotInt => simple!("indices must be integers"), | |
3157f602 | 513 | IndexOutOfBounds { len, index } => { |
5bcae85e SL |
514 | simple!("index out of bounds: the len is {} but the index is {}", |
515 | len, index) | |
3157f602 | 516 | } |
5bcae85e SL |
517 | RepeatCountNotNatural => simple!("repeat count must be a natural number"), |
518 | RepeatCountNotInt => simple!("repeat count must be integers"), | |
54a0048b | 519 | |
5bcae85e SL |
520 | MiscBinaryOp => simple!("bad operands for binary"), |
521 | MiscCatchAll => simple!("unsupported constant expr"), | |
522 | IndexOpFeatureGated => simple!("the index operation on const values is unstable"), | |
523 | Math(ref err) => Simple(err.description().into_cow()), | |
54a0048b | 524 | |
5bcae85e SL |
525 | IntermediateUnsignedNegative => simple!( |
526 | "during the computation of an unsigned a negative \ | |
527 | number was encountered. This is most likely a bug in\ | |
528 | the constant evaluator"), | |
54a0048b SL |
529 | |
530 | TypeMismatch(ref expected, ref got) => { | |
5bcae85e | 531 | simple!("expected {}, found {}", expected, got.description()) |
54a0048b | 532 | }, |
5bcae85e SL |
533 | BadType(ref i) => simple!("value of wrong type: {:?}", i), |
534 | ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), | |
a7813a04 | 535 | CharCast(ref got) => { |
5bcae85e | 536 | simple!("only `u8` can be cast as `char`, not `{}`", got.description()) |
a7813a04 | 537 | }, |
54a0048b SL |
538 | } |
539 | } | |
540 | } | |
541 | ||
542 | pub type EvalResult = Result<ConstVal, ConstEvalErr>; | |
543 | pub type CastResult = Result<ConstVal, ErrKind>; | |
544 | ||
545 | // FIXME: Long-term, this enum should go away: trying to evaluate | |
546 | // an expression which hasn't been type-checked is a recipe for | |
547 | // disaster. That said, it's not clear how to fix ast_ty_to_ty | |
548 | // to avoid the ordering issue. | |
549 | ||
550 | /// Hint to determine how to evaluate constant expressions which | |
551 | /// might not be type-checked. | |
552 | #[derive(Copy, Clone, Debug)] | |
553 | pub enum EvalHint<'tcx> { | |
554 | /// We have a type-checked expression. | |
555 | ExprTypeChecked, | |
556 | /// We have an expression which hasn't been type-checked, but we have | |
557 | /// an idea of what the type will be because of the context. For example, | |
558 | /// the length of an array is always `usize`. (This is referred to as | |
559 | /// a hint because it isn't guaranteed to be consistent with what | |
560 | /// type-checking would compute.) | |
561 | UncheckedExprHint(Ty<'tcx>), | |
562 | /// We have an expression which has not yet been type-checked, and | |
563 | /// and we have no clue what the type will be. | |
564 | UncheckedExprNoHint, | |
565 | } | |
566 | ||
567 | impl<'tcx> EvalHint<'tcx> { | |
568 | fn erase_hint(&self) -> EvalHint<'tcx> { | |
569 | match *self { | |
570 | ExprTypeChecked => ExprTypeChecked, | |
571 | UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint, | |
572 | } | |
573 | } | |
574 | fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> { | |
575 | match *self { | |
576 | ExprTypeChecked => ExprTypeChecked, | |
577 | _ => UncheckedExprHint(ty), | |
578 | } | |
579 | } | |
580 | } | |
581 | ||
582 | macro_rules! signal { | |
583 | ($e:expr, $exn:expr) => { | |
584 | return Err(ConstEvalErr { span: $e.span, kind: $exn }) | |
585 | } | |
586 | } | |
587 | ||
588 | /// Evaluate a constant expression in a context where the expression isn't | |
589 | /// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, | |
590 | /// but a few places need to evaluate constants during type-checking, like | |
591 | /// computing the length of an array. (See also the FIXME above EvalHint.) | |
a7813a04 XL |
592 | pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
593 | e: &Expr, | |
594 | ty_hint: EvalHint<'tcx>, | |
595 | fn_args: FnArgMap) -> EvalResult { | |
54a0048b SL |
596 | // Try to compute the type of the expression based on the EvalHint. |
597 | // (See also the definition of EvalHint, and the FIXME above EvalHint.) | |
598 | let ety = match ty_hint { | |
599 | ExprTypeChecked => { | |
600 | // After type-checking, expr_ty is guaranteed to succeed. | |
601 | Some(tcx.expr_ty(e)) | |
602 | } | |
603 | UncheckedExprHint(ty) => { | |
604 | // Use the type hint; it's not guaranteed to be right, but it's | |
605 | // usually good enough. | |
606 | Some(ty) | |
607 | } | |
608 | UncheckedExprNoHint => { | |
609 | // This expression might not be type-checked, and we have no hint. | |
610 | // Try to query the context for a type anyway; we might get lucky | |
611 | // (for example, if the expression was imported from another crate). | |
612 | tcx.expr_ty_opt(e) | |
613 | } | |
614 | }; | |
615 | let result = match e.node { | |
616 | hir::ExprUnary(hir::UnNeg, ref inner) => { | |
617 | // unary neg literals already got their sign during creation | |
618 | if let hir::ExprLit(ref lit) = inner.node { | |
619 | use syntax::ast::*; | |
620 | use syntax::ast::LitIntType::*; | |
621 | const I8_OVERFLOW: u64 = ::std::i8::MAX as u64 + 1; | |
622 | const I16_OVERFLOW: u64 = ::std::i16::MAX as u64 + 1; | |
623 | const I32_OVERFLOW: u64 = ::std::i32::MAX as u64 + 1; | |
624 | const I64_OVERFLOW: u64 = ::std::i64::MAX as u64 + 1; | |
625 | match (&lit.node, ety.map(|t| &t.sty)) { | |
626 | (&LitKind::Int(I8_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I8))) | | |
627 | (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { | |
628 | return Ok(Integral(I8(::std::i8::MIN))) | |
629 | }, | |
630 | (&LitKind::Int(I16_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I16))) | | |
631 | (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { | |
632 | return Ok(Integral(I16(::std::i16::MIN))) | |
633 | }, | |
634 | (&LitKind::Int(I32_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I32))) | | |
635 | (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { | |
636 | return Ok(Integral(I32(::std::i32::MIN))) | |
637 | }, | |
638 | (&LitKind::Int(I64_OVERFLOW, Unsuffixed), Some(&ty::TyInt(IntTy::I64))) | | |
639 | (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { | |
640 | return Ok(Integral(I64(::std::i64::MIN))) | |
641 | }, | |
642 | (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) | | |
643 | (&LitKind::Int(n, Signed(IntTy::Is)), _) => { | |
644 | match tcx.sess.target.int_type { | |
3157f602 XL |
645 | IntTy::I16 => if n == I16_OVERFLOW { |
646 | return Ok(Integral(Isize(Is16(::std::i16::MIN)))); | |
647 | }, | |
54a0048b SL |
648 | IntTy::I32 => if n == I32_OVERFLOW { |
649 | return Ok(Integral(Isize(Is32(::std::i32::MIN)))); | |
650 | }, | |
651 | IntTy::I64 => if n == I64_OVERFLOW { | |
652 | return Ok(Integral(Isize(Is64(::std::i64::MIN)))); | |
653 | }, | |
654 | _ => bug!(), | |
655 | } | |
656 | }, | |
657 | _ => {}, | |
658 | } | |
659 | } | |
660 | match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { | |
661 | Float(f) => Float(-f), | |
662 | Integral(i) => Integral(math!(e, -i)), | |
663 | const_val => signal!(e, NegateOn(const_val)), | |
664 | } | |
665 | } | |
666 | hir::ExprUnary(hir::UnNot, ref inner) => { | |
667 | match eval_const_expr_partial(tcx, &inner, ty_hint, fn_args)? { | |
668 | Integral(i) => Integral(math!(e, !i)), | |
669 | Bool(b) => Bool(!b), | |
670 | const_val => signal!(e, NotOn(const_val)), | |
671 | } | |
672 | } | |
673 | hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")), | |
674 | hir::ExprBinary(op, ref a, ref b) => { | |
675 | let b_ty = match op.node { | |
676 | hir::BiShl | hir::BiShr => ty_hint.erase_hint(), | |
677 | _ => ty_hint | |
678 | }; | |
679 | // technically, if we don't have type hints, but integral eval | |
680 | // gives us a type through a type-suffix, cast or const def type | |
681 | // we need to re-eval the other value of the BinOp if it was | |
682 | // not inferred | |
683 | match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?, | |
684 | eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) { | |
685 | (Float(a), Float(b)) => { | |
3157f602 | 686 | use std::cmp::Ordering::*; |
54a0048b | 687 | match op.node { |
3157f602 XL |
688 | hir::BiAdd => Float(math!(e, a + b)), |
689 | hir::BiSub => Float(math!(e, a - b)), | |
690 | hir::BiMul => Float(math!(e, a * b)), | |
691 | hir::BiDiv => Float(math!(e, a / b)), | |
692 | hir::BiRem => Float(math!(e, a % b)), | |
693 | hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), | |
694 | hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), | |
695 | hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), | |
696 | hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), | |
697 | hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), | |
698 | hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), | |
54a0048b SL |
699 | _ => signal!(e, InvalidOpForFloats(op.node)), |
700 | } | |
701 | } | |
702 | (Integral(a), Integral(b)) => { | |
703 | use std::cmp::Ordering::*; | |
704 | match op.node { | |
705 | hir::BiAdd => Integral(math!(e, a + b)), | |
706 | hir::BiSub => Integral(math!(e, a - b)), | |
707 | hir::BiMul => Integral(math!(e, a * b)), | |
708 | hir::BiDiv => Integral(math!(e, a / b)), | |
709 | hir::BiRem => Integral(math!(e, a % b)), | |
710 | hir::BiBitAnd => Integral(math!(e, a & b)), | |
711 | hir::BiBitOr => Integral(math!(e, a | b)), | |
712 | hir::BiBitXor => Integral(math!(e, a ^ b)), | |
713 | hir::BiShl => Integral(math!(e, a << b)), | |
714 | hir::BiShr => Integral(math!(e, a >> b)), | |
715 | hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal), | |
716 | hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less), | |
717 | hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater), | |
718 | hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), | |
719 | hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), | |
720 | hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), | |
721 | _ => signal!(e, InvalidOpForInts(op.node)), | |
722 | } | |
723 | } | |
724 | (Bool(a), Bool(b)) => { | |
725 | Bool(match op.node { | |
726 | hir::BiAnd => a && b, | |
727 | hir::BiOr => a || b, | |
728 | hir::BiBitXor => a ^ b, | |
729 | hir::BiBitAnd => a & b, | |
730 | hir::BiBitOr => a | b, | |
731 | hir::BiEq => a == b, | |
732 | hir::BiNe => a != b, | |
733 | _ => signal!(e, InvalidOpForBools(op.node)), | |
734 | }) | |
735 | } | |
736 | ||
737 | _ => signal!(e, MiscBinaryOp), | |
738 | } | |
739 | } | |
740 | hir::ExprCast(ref base, ref target_ty) => { | |
a7813a04 | 741 | let ety = tcx.ast_ty_to_prim_ty(&target_ty).or(ety) |
54a0048b SL |
742 | .unwrap_or_else(|| { |
743 | tcx.sess.span_fatal(target_ty.span, | |
744 | "target type not found for const cast") | |
745 | }); | |
746 | ||
747 | let base_hint = if let ExprTypeChecked = ty_hint { | |
748 | ExprTypeChecked | |
749 | } else { | |
750 | match tcx.expr_ty_opt(&base) { | |
751 | Some(t) => UncheckedExprHint(t), | |
752 | None => ty_hint | |
753 | } | |
754 | }; | |
755 | ||
756 | let val = match eval_const_expr_partial(tcx, &base, base_hint, fn_args) { | |
757 | Ok(val) => val, | |
a7813a04 XL |
758 | Err(ConstEvalErr { kind: ErroneousReferencedConstant( |
759 | box ConstEvalErr { kind: TypeMismatch(_, val), .. }), .. }) | | |
54a0048b SL |
760 | Err(ConstEvalErr { kind: TypeMismatch(_, val), .. }) => { |
761 | // Something like `5i8 as usize` doesn't need a type hint for the base | |
762 | // instead take the type hint from the inner value | |
763 | let hint = match val.int_type() { | |
764 | Some(IntType::UnsignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_uint(ty)), | |
765 | Some(IntType::SignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_int(ty)), | |
766 | // we had a type hint, so we can't have an unknown type | |
767 | None => bug!(), | |
768 | }; | |
769 | eval_const_expr_partial(tcx, &base, hint, fn_args)? | |
770 | }, | |
771 | Err(e) => return Err(e), | |
772 | }; | |
773 | match cast_const(tcx, val, ety) { | |
774 | Ok(val) => val, | |
775 | Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), | |
776 | } | |
777 | } | |
778 | hir::ExprPath(..) => { | |
3157f602 XL |
779 | // This function can be used before type checking when not all paths are fully resolved. |
780 | // FIXME: There's probably a better way to make sure we don't panic here. | |
781 | let resolution = tcx.expect_resolution(e.id); | |
782 | if resolution.depth != 0 { | |
783 | signal!(e, UnresolvedPath); | |
784 | } | |
785 | match resolution.base_def { | |
54a0048b SL |
786 | Def::Const(def_id) | |
787 | Def::AssociatedConst(def_id) => { | |
788 | let substs = if let ExprTypeChecked = ty_hint { | |
789 | Some(tcx.node_id_item_substs(e.id).substs) | |
790 | } else { | |
791 | None | |
792 | }; | |
a7813a04 | 793 | if let Some((expr, ty)) = lookup_const_by_id(tcx, def_id, substs) { |
54a0048b SL |
794 | let item_hint = match ty { |
795 | Some(ty) => ty_hint.checked_or(ty), | |
796 | None => ty_hint, | |
797 | }; | |
a7813a04 XL |
798 | match eval_const_expr_partial(tcx, expr, item_hint, None) { |
799 | Ok(val) => val, | |
800 | Err(err) => { | |
801 | debug!("bad reference: {:?}, {:?}", err.description(), err.span); | |
802 | signal!(e, ErroneousReferencedConstant(box err)) | |
803 | }, | |
804 | } | |
54a0048b SL |
805 | } else { |
806 | signal!(e, NonConstPath); | |
807 | } | |
808 | }, | |
809 | Def::Variant(enum_def, variant_def) => { | |
810 | if let Some(const_expr) = lookup_variant_by_id(tcx, enum_def, variant_def) { | |
a7813a04 XL |
811 | match eval_const_expr_partial(tcx, const_expr, ty_hint, None) { |
812 | Ok(val) => val, | |
813 | Err(err) => { | |
814 | debug!("bad reference: {:?}, {:?}", err.description(), err.span); | |
815 | signal!(e, ErroneousReferencedConstant(box err)) | |
816 | }, | |
817 | } | |
54a0048b SL |
818 | } else { |
819 | signal!(e, UnimplementedConstVal("enum variants")); | |
820 | } | |
821 | } | |
822 | Def::Struct(..) => { | |
823 | ConstVal::Struct(e.id) | |
824 | } | |
825 | Def::Local(_, id) => { | |
826 | debug!("Def::Local({:?}): {:?}", id, fn_args); | |
827 | if let Some(val) = fn_args.and_then(|args| args.get(&id)) { | |
828 | val.clone() | |
829 | } else { | |
830 | signal!(e, NonConstPath); | |
831 | } | |
832 | }, | |
833 | Def::Method(id) | Def::Fn(id) => Function(id), | |
834 | _ => signal!(e, NonConstPath), | |
835 | } | |
836 | } | |
837 | hir::ExprCall(ref callee, ref args) => { | |
838 | let sub_ty_hint = ty_hint.erase_hint(); | |
839 | let callee_val = eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)?; | |
840 | let did = match callee_val { | |
841 | Function(did) => did, | |
842 | Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")), | |
843 | callee => signal!(e, CallOn(callee)), | |
844 | }; | |
845 | let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) { | |
846 | (fn_like.decl(), &fn_like.body().expr) | |
847 | } else { | |
848 | signal!(e, NonConstPath) | |
849 | }; | |
850 | let result = result.as_ref().expect("const fn has no result expression"); | |
851 | assert_eq!(decl.inputs.len(), args.len()); | |
852 | ||
853 | let mut call_args = NodeMap(); | |
854 | for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { | |
855 | let arg_hint = ty_hint.erase_hint(); | |
856 | let arg_val = eval_const_expr_partial( | |
857 | tcx, | |
858 | arg_expr, | |
859 | arg_hint, | |
860 | fn_args | |
861 | )?; | |
862 | debug!("const call arg: {:?}", arg); | |
863 | let old = call_args.insert(arg.pat.id, arg_val); | |
864 | assert!(old.is_none()); | |
865 | } | |
866 | debug!("const call({:?})", call_args); | |
867 | eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))? | |
868 | }, | |
a7813a04 XL |
869 | hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety, lit.span) { |
870 | Ok(val) => val, | |
871 | Err(err) => signal!(e, err), | |
872 | }, | |
54a0048b SL |
873 | hir::ExprBlock(ref block) => { |
874 | match block.expr { | |
875 | Some(ref expr) => eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)?, | |
876 | None => signal!(e, UnimplementedConstVal("empty block")), | |
877 | } | |
878 | } | |
879 | hir::ExprType(ref e, _) => eval_const_expr_partial(tcx, &e, ty_hint, fn_args)?, | |
880 | hir::ExprTup(_) => Tuple(e.id), | |
881 | hir::ExprStruct(..) => Struct(e.id), | |
882 | hir::ExprIndex(ref arr, ref idx) => { | |
883 | if !tcx.sess.features.borrow().const_indexing { | |
884 | signal!(e, IndexOpFeatureGated); | |
885 | } | |
886 | let arr_hint = ty_hint.erase_hint(); | |
887 | let arr = eval_const_expr_partial(tcx, arr, arr_hint, fn_args)?; | |
888 | let idx_hint = ty_hint.checked_or(tcx.types.usize); | |
889 | let idx = match eval_const_expr_partial(tcx, idx, idx_hint, fn_args)? { | |
890 | Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), | |
891 | Integral(_) => bug!(), | |
892 | _ => signal!(idx, IndexNotInt), | |
893 | }; | |
894 | assert_eq!(idx as usize as u64, idx); | |
895 | match arr { | |
3157f602 XL |
896 | Array(_, n) if idx >= n => { |
897 | signal!(e, IndexOutOfBounds { len: n, index: idx }) | |
898 | } | |
54a0048b SL |
899 | Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { |
900 | assert_eq!(n as usize as u64, n); | |
901 | eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)? | |
902 | } else { | |
903 | bug!() | |
904 | }, | |
905 | ||
3157f602 XL |
906 | Repeat(_, n) if idx >= n => { |
907 | signal!(e, IndexOutOfBounds { len: n, index: idx }) | |
908 | } | |
54a0048b SL |
909 | Repeat(elem, _) => eval_const_expr_partial( |
910 | tcx, | |
911 | &tcx.map.expect_expr(elem), | |
912 | ty_hint, | |
913 | fn_args, | |
914 | )?, | |
915 | ||
3157f602 XL |
916 | ByteStr(ref data) if idx >= data.len() as u64 => { |
917 | signal!(e, IndexOutOfBounds { len: data.len() as u64, index: idx }) | |
918 | } | |
54a0048b SL |
919 | ByteStr(data) => { |
920 | Integral(U8(data[idx as usize])) | |
921 | }, | |
922 | ||
54a0048b SL |
923 | _ => signal!(e, IndexedNonVec), |
924 | } | |
925 | } | |
926 | hir::ExprVec(ref v) => Array(e.id, v.len() as u64), | |
927 | hir::ExprRepeat(_, ref n) => { | |
928 | let len_hint = ty_hint.checked_or(tcx.types.usize); | |
929 | Repeat( | |
930 | e.id, | |
931 | match eval_const_expr_partial(tcx, &n, len_hint, fn_args)? { | |
932 | Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), | |
933 | Integral(_) => signal!(e, RepeatCountNotNatural), | |
934 | _ => signal!(e, RepeatCountNotInt), | |
935 | }, | |
936 | ) | |
937 | }, | |
938 | hir::ExprTupField(ref base, index) => { | |
939 | let base_hint = ty_hint.erase_hint(); | |
940 | let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?; | |
941 | if let Tuple(tup_id) = c { | |
942 | if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { | |
943 | if index.node < fields.len() { | |
944 | eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args)? | |
945 | } else { | |
946 | signal!(e, TupleIndexOutOfBounds); | |
947 | } | |
948 | } else { | |
949 | bug!() | |
950 | } | |
951 | } else { | |
952 | signal!(base, ExpectedConstTuple); | |
953 | } | |
954 | } | |
955 | hir::ExprField(ref base, field_name) => { | |
956 | let base_hint = ty_hint.erase_hint(); | |
957 | // Get the base expression if it is a struct and it is constant | |
958 | let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?; | |
959 | if let Struct(struct_id) = c { | |
960 | if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { | |
961 | // Check that the given field exists and evaluate it | |
962 | // if the idents are compared run-pass/issue-19244 fails | |
963 | if let Some(f) = fields.iter().find(|f| f.name.node | |
964 | == field_name.node) { | |
965 | eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args)? | |
966 | } else { | |
967 | signal!(e, MissingStructField); | |
968 | } | |
969 | } else { | |
970 | bug!() | |
971 | } | |
972 | } else { | |
973 | signal!(base, ExpectedConstStruct); | |
974 | } | |
975 | } | |
976 | hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")), | |
977 | _ => signal!(e, MiscCatchAll) | |
978 | }; | |
979 | ||
980 | match (ety.map(|t| &t.sty), result) { | |
a7813a04 XL |
981 | (Some(ref ty_hint), Integral(i)) => match infer(i, tcx, ty_hint) { |
982 | Ok(inferred) => Ok(Integral(inferred)), | |
983 | Err(err) => signal!(e, err), | |
984 | }, | |
54a0048b SL |
985 | (_, result) => Ok(result), |
986 | } | |
987 | } | |
988 | ||
a7813a04 XL |
989 | fn infer<'a, 'tcx>(i: ConstInt, |
990 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
991 | ty_hint: &ty::TypeVariants<'tcx>) | |
992 | -> Result<ConstInt, ErrKind> { | |
54a0048b SL |
993 | use syntax::ast::*; |
994 | ||
54a0048b SL |
995 | match (ty_hint, i) { |
996 | (&ty::TyInt(IntTy::I8), result @ I8(_)) => Ok(result), | |
997 | (&ty::TyInt(IntTy::I16), result @ I16(_)) => Ok(result), | |
998 | (&ty::TyInt(IntTy::I32), result @ I32(_)) => Ok(result), | |
999 | (&ty::TyInt(IntTy::I64), result @ I64(_)) => Ok(result), | |
1000 | (&ty::TyInt(IntTy::Is), result @ Isize(_)) => Ok(result), | |
1001 | ||
1002 | (&ty::TyUint(UintTy::U8), result @ U8(_)) => Ok(result), | |
1003 | (&ty::TyUint(UintTy::U16), result @ U16(_)) => Ok(result), | |
1004 | (&ty::TyUint(UintTy::U32), result @ U32(_)) => Ok(result), | |
1005 | (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result), | |
1006 | (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result), | |
1007 | ||
1008 | (&ty::TyInt(IntTy::I8), Infer(i)) => Ok(I8(i as i64 as i8)), | |
1009 | (&ty::TyInt(IntTy::I16), Infer(i)) => Ok(I16(i as i64 as i16)), | |
1010 | (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i64 as i32)), | |
1011 | (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i64)), | |
1012 | (&ty::TyInt(IntTy::Is), Infer(i)) => { | |
3157f602 | 1013 | Ok(Isize(ConstIsize::new_truncating(i as i64, tcx.sess.target.int_type))) |
54a0048b SL |
1014 | }, |
1015 | ||
1016 | (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)), | |
1017 | (&ty::TyInt(IntTy::I16), InferSigned(i)) => Ok(I16(i as i16)), | |
1018 | (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)), | |
1019 | (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i)), | |
1020 | (&ty::TyInt(IntTy::Is), InferSigned(i)) => { | |
3157f602 | 1021 | Ok(Isize(ConstIsize::new_truncating(i, tcx.sess.target.int_type))) |
54a0048b SL |
1022 | }, |
1023 | ||
1024 | (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)), | |
1025 | (&ty::TyUint(UintTy::U16), Infer(i)) => Ok(U16(i as u16)), | |
1026 | (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)), | |
1027 | (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i)), | |
1028 | (&ty::TyUint(UintTy::Us), Infer(i)) => { | |
3157f602 | 1029 | Ok(Usize(ConstUsize::new_truncating(i, tcx.sess.target.uint_type))) |
54a0048b | 1030 | }, |
a7813a04 | 1031 | (&ty::TyUint(_), InferSigned(_)) => Err(IntermediateUnsignedNegative), |
54a0048b | 1032 | |
a7813a04 XL |
1033 | (&ty::TyInt(ity), i) => Err(TypeMismatch(ity.to_string(), i)), |
1034 | (&ty::TyUint(ity), i) => Err(TypeMismatch(ity.to_string(), i)), | |
54a0048b SL |
1035 | |
1036 | (&ty::TyEnum(ref adt, _), i) => { | |
1037 | let hints = tcx.lookup_repr_hints(adt.did); | |
1038 | let int_ty = tcx.enum_repr_type(hints.iter().next()); | |
a7813a04 | 1039 | infer(i, tcx, &int_ty.to_ty(tcx).sty) |
54a0048b | 1040 | }, |
a7813a04 | 1041 | (_, i) => Err(BadType(ConstVal::Integral(i))), |
54a0048b SL |
1042 | } |
1043 | } | |
1044 | ||
a7813a04 | 1045 | fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
54a0048b SL |
1046 | ti: &'tcx hir::TraitItem, |
1047 | trait_id: DefId, | |
a7813a04 | 1048 | rcvr_substs: &'tcx subst::Substs<'tcx>) |
54a0048b SL |
1049 | -> Option<(&'tcx Expr, Option<ty::Ty<'tcx>>)> |
1050 | { | |
1051 | let trait_ref = ty::Binder( | |
a7813a04 | 1052 | rcvr_substs.clone().erase_regions().to_trait_ref(tcx, trait_id) |
54a0048b SL |
1053 | ); |
1054 | debug!("resolve_trait_associated_const: trait_ref={:?}", | |
1055 | trait_ref); | |
1056 | ||
1057 | tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); | |
5bcae85e | 1058 | tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| { |
a7813a04 XL |
1059 | let mut selcx = traits::SelectionContext::new(&infcx); |
1060 | let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), | |
1061 | trait_ref.to_poly_trait_predicate()); | |
1062 | let selection = match selcx.select(&obligation) { | |
1063 | Ok(Some(vtable)) => vtable, | |
1064 | // Still ambiguous, so give up and let the caller decide whether this | |
1065 | // expression is really needed yet. Some associated constant values | |
1066 | // can't be evaluated until monomorphization is done in trans. | |
1067 | Ok(None) => { | |
1068 | return None | |
1069 | } | |
1070 | Err(_) => { | |
1071 | return None | |
1072 | } | |
1073 | }; | |
54a0048b | 1074 | |
a7813a04 | 1075 | // NOTE: this code does not currently account for specialization, but when |
5bcae85e | 1076 | // it does so, it should hook into the Reveal to determine when the |
a7813a04 | 1077 | // constant should resolve; this will also require plumbing through to this |
5bcae85e | 1078 | // function whether we are in "trans mode" to pick the right Reveal |
a7813a04 XL |
1079 | // when constructing the inference context above. |
1080 | match selection { | |
1081 | traits::VtableImpl(ref impl_data) => { | |
1082 | match tcx.associated_consts(impl_data.impl_def_id) | |
1083 | .iter().find(|ic| ic.name == ti.name) { | |
1084 | Some(ic) => lookup_const_by_id(tcx, ic.def_id, None), | |
1085 | None => match ti.node { | |
1086 | hir::ConstTraitItem(ref ty, Some(ref expr)) => { | |
1087 | Some((&*expr, tcx.ast_ty_to_prim_ty(ty))) | |
1088 | }, | |
1089 | _ => None, | |
54a0048b | 1090 | }, |
a7813a04 XL |
1091 | } |
1092 | } | |
1093 | _ => { | |
1094 | span_bug!(ti.span, | |
1095 | "resolve_trait_associated_const: unexpected vtable type") | |
54a0048b SL |
1096 | } |
1097 | } | |
a7813a04 | 1098 | }) |
54a0048b SL |
1099 | } |
1100 | ||
a7813a04 | 1101 | fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::Ty) -> CastResult { |
54a0048b SL |
1102 | let v = val.to_u64_unchecked(); |
1103 | match ty.sty { | |
1104 | ty::TyBool if v == 0 => Ok(Bool(false)), | |
1105 | ty::TyBool if v == 1 => Ok(Bool(true)), | |
1106 | ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i64 as i8))), | |
1107 | ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i64 as i16))), | |
1108 | ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i64 as i32))), | |
1109 | ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i64))), | |
1110 | ty::TyInt(ast::IntTy::Is) => { | |
3157f602 | 1111 | Ok(Integral(Isize(ConstIsize::new_truncating(v as i64, tcx.sess.target.int_type)))) |
54a0048b SL |
1112 | }, |
1113 | ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))), | |
1114 | ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))), | |
1115 | ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))), | |
1116 | ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v))), | |
1117 | ty::TyUint(ast::UintTy::Us) => { | |
3157f602 | 1118 | Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type)))) |
54a0048b | 1119 | }, |
a7813a04 | 1120 | ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() { |
3157f602 XL |
1121 | Infer(u) => Ok(Float(F64(u as f64))), |
1122 | InferSigned(i) => Ok(Float(F64(i as f64))), | |
a7813a04 | 1123 | _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), |
54a0048b | 1124 | }, |
a7813a04 | 1125 | ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() { |
3157f602 XL |
1126 | Infer(u) => Ok(Float(F32(u as f32))), |
1127 | InferSigned(i) => Ok(Float(F32(i as f32))), | |
a7813a04 | 1128 | _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), |
54a0048b | 1129 | }, |
54a0048b | 1130 | ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), |
a7813a04 XL |
1131 | ty::TyChar => match infer(val, tcx, &ty::TyUint(ast::UintTy::U8)) { |
1132 | Ok(U8(u)) => Ok(Char(u as char)), | |
1133 | // can only occur before typeck, typeck blocks `T as char` for `T` != `u8` | |
1134 | _ => Err(CharCast(val)), | |
1135 | }, | |
54a0048b SL |
1136 | _ => Err(CannotCast), |
1137 | } | |
1138 | } | |
1139 | ||
3157f602 XL |
1140 | fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1141 | val: ConstFloat, | |
1142 | ty: ty::Ty) -> CastResult { | |
54a0048b | 1143 | match ty.sty { |
3157f602 XL |
1144 | ty::TyInt(_) | ty::TyUint(_) => { |
1145 | let i = match val { | |
1146 | F32(f) if f >= 0.0 => Infer(f as u64), | |
1147 | FInfer { f64: f, .. } | | |
1148 | F64(f) if f >= 0.0 => Infer(f as u64), | |
1149 | ||
1150 | F32(f) => InferSigned(f as i64), | |
1151 | FInfer { f64: f, .. } | | |
1152 | F64(f) => InferSigned(f as i64) | |
1153 | }; | |
1154 | ||
1155 | if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) { | |
1156 | return Err(CannotCast); | |
1157 | } | |
1158 | ||
1159 | cast_const_int(tcx, i, ty) | |
1160 | } | |
1161 | ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val { | |
1162 | F32(f) => f as f64, | |
1163 | FInfer { f64: f, .. } | F64(f) => f | |
1164 | }))), | |
1165 | ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val { | |
1166 | F64(f) => f as f32, | |
1167 | FInfer { f32: f, .. } | F32(f) => f | |
1168 | }))), | |
54a0048b SL |
1169 | _ => Err(CannotCast), |
1170 | } | |
1171 | } | |
1172 | ||
a7813a04 | 1173 | fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult { |
54a0048b SL |
1174 | match val { |
1175 | Integral(i) => cast_const_int(tcx, i, ty), | |
1176 | Bool(b) => cast_const_int(tcx, Infer(b as u64), ty), | |
1177 | Float(f) => cast_const_float(tcx, f, ty), | |
1178 | Char(c) => cast_const_int(tcx, Infer(c as u64), ty), | |
1179 | Function(_) => Err(UnimplementedConstVal("casting fn pointers")), | |
5bcae85e | 1180 | ByteStr(b) => match ty.sty { |
a7813a04 XL |
1181 | ty::TyRawPtr(_) => { |
1182 | Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr")) | |
1183 | }, | |
5bcae85e SL |
1184 | ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { |
1185 | ty::TyArray(ty, n) if ty == tcx.types.u8 && n == b.len() => Ok(ByteStr(b)), | |
1186 | ty::TySlice(_) => { | |
1187 | Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")) | |
1188 | }, | |
1189 | _ => Err(CannotCast), | |
1190 | }, | |
1191 | _ => Err(CannotCast), | |
1192 | }, | |
1193 | Str(s) => match ty.sty { | |
1194 | ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")), | |
1195 | ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty { | |
1196 | ty::TyStr => Ok(Str(s)), | |
1197 | _ => Err(CannotCast), | |
1198 | }, | |
a7813a04 XL |
1199 | _ => Err(CannotCast), |
1200 | }, | |
54a0048b SL |
1201 | _ => Err(CannotCast), |
1202 | } | |
1203 | } | |
1204 | ||
a7813a04 XL |
1205 | fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind, |
1206 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1207 | ty_hint: Option<Ty<'tcx>>, | |
1208 | span: Span) | |
1209 | -> Result<ConstVal, ErrKind> { | |
54a0048b SL |
1210 | use syntax::ast::*; |
1211 | use syntax::ast::LitIntType::*; | |
1212 | match *lit { | |
1213 | LitKind::Str(ref s, _) => Ok(Str((*s).clone())), | |
1214 | LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())), | |
1215 | LitKind::Byte(n) => Ok(Integral(U8(n))), | |
1216 | LitKind::Int(n, Signed(ity)) => { | |
a7813a04 | 1217 | infer(InferSigned(n as i64), tcx, &ty::TyInt(ity)).map(Integral) |
54a0048b SL |
1218 | }, |
1219 | ||
1220 | LitKind::Int(n, Unsuffixed) => { | |
1221 | match ty_hint.map(|t| &t.sty) { | |
1222 | Some(&ty::TyInt(ity)) => { | |
a7813a04 | 1223 | infer(InferSigned(n as i64), tcx, &ty::TyInt(ity)).map(Integral) |
54a0048b SL |
1224 | }, |
1225 | Some(&ty::TyUint(uty)) => { | |
a7813a04 | 1226 | infer(Infer(n), tcx, &ty::TyUint(uty)).map(Integral) |
54a0048b SL |
1227 | }, |
1228 | None => Ok(Integral(Infer(n))), | |
1229 | Some(&ty::TyEnum(ref adt, _)) => { | |
1230 | let hints = tcx.lookup_repr_hints(adt.did); | |
1231 | let int_ty = tcx.enum_repr_type(hints.iter().next()); | |
a7813a04 | 1232 | infer(Infer(n), tcx, &int_ty.to_ty(tcx).sty).map(Integral) |
54a0048b SL |
1233 | }, |
1234 | Some(ty_hint) => bug!("bad ty_hint: {:?}, {:?}", ty_hint, lit), | |
1235 | } | |
1236 | }, | |
1237 | LitKind::Int(n, Unsigned(ity)) => { | |
a7813a04 | 1238 | infer(Infer(n), tcx, &ty::TyUint(ity)).map(Integral) |
54a0048b SL |
1239 | }, |
1240 | ||
3157f602 XL |
1241 | LitKind::Float(ref n, fty) => { |
1242 | Ok(Float(parse_float(n, Some(fty), span))) | |
1243 | } | |
54a0048b | 1244 | LitKind::FloatUnsuffixed(ref n) => { |
3157f602 XL |
1245 | let fty_hint = match ty_hint.map(|t| &t.sty) { |
1246 | Some(&ty::TyFloat(fty)) => Some(fty), | |
1247 | _ => None | |
1248 | }; | |
1249 | Ok(Float(parse_float(n, fty_hint, span))) | |
54a0048b SL |
1250 | } |
1251 | LitKind::Bool(b) => Ok(Bool(b)), | |
1252 | LitKind::Char(c) => Ok(Char(c)), | |
1253 | } | |
1254 | } | |
1255 | ||
3157f602 XL |
1256 | fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFloat { |
1257 | let val = match fty_hint { | |
1258 | Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32), | |
1259 | Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64), | |
1260 | None => { | |
1261 | num.parse::<f32>().and_then(|f32| { | |
1262 | num.parse::<f64>().map(|f64| { | |
1263 | FInfer { f32: f32, f64: f64 } | |
1264 | }) | |
1265 | }) | |
1266 | } | |
1267 | }; | |
1268 | val.unwrap_or_else(|_| { | |
1269 | // FIXME(#31407) this is only necessary because float parsing is buggy | |
1270 | span_bug!(span, "could not evaluate float literal (see issue #31407)"); | |
1271 | }) | |
1272 | } | |
1273 | ||
5bcae85e SL |
1274 | pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal) |
1275 | -> Result<Ordering, ErrorReported> | |
1276 | { | |
1277 | let result = match (a, b) { | |
54a0048b | 1278 | (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(), |
3157f602 | 1279 | (&Float(a), &Float(b)) => a.try_cmp(b).ok(), |
54a0048b SL |
1280 | (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)), |
1281 | (&Bool(a), &Bool(b)) => Some(a.cmp(&b)), | |
1282 | (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)), | |
1283 | (&Char(a), &Char(ref b)) => Some(a.cmp(b)), | |
1284 | _ => None, | |
5bcae85e SL |
1285 | }; |
1286 | ||
1287 | match result { | |
1288 | Some(result) => Ok(result), | |
1289 | None => { | |
1290 | // FIXME: can this ever be reached? | |
1291 | span_err!(tcx.sess, span, E0298, | |
1292 | "type mismatch comparing {} and {}", | |
1293 | a.description(), | |
1294 | b.description()); | |
1295 | Err(ErrorReported) | |
1296 | } | |
54a0048b SL |
1297 | } |
1298 | } | |
1299 | ||
a7813a04 | 1300 | pub fn compare_lit_exprs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
5bcae85e | 1301 | span: Span, |
a7813a04 | 1302 | a: &Expr, |
5bcae85e | 1303 | b: &Expr) -> Result<Ordering, ErrorReported> { |
54a0048b SL |
1304 | let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) { |
1305 | Ok(a) => a, | |
1306 | Err(e) => { | |
5bcae85e SL |
1307 | report_const_eval_err(tcx, &e, a.span, "expression").emit(); |
1308 | return Err(ErrorReported); | |
54a0048b SL |
1309 | } |
1310 | }; | |
1311 | let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) { | |
1312 | Ok(b) => b, | |
1313 | Err(e) => { | |
5bcae85e SL |
1314 | report_const_eval_err(tcx, &e, b.span, "expression").emit(); |
1315 | return Err(ErrorReported); | |
54a0048b SL |
1316 | } |
1317 | }; | |
5bcae85e | 1318 | compare_const_vals(tcx, span, &a, &b) |
54a0048b SL |
1319 | } |
1320 | ||
1321 | ||
5bcae85e SL |
1322 | /// Returns the value of the length-valued expression |
1323 | pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1324 | count_expr: &hir::Expr, | |
1325 | reason: &str) | |
1326 | -> Result<usize, ErrorReported> | |
1327 | { | |
54a0048b SL |
1328 | let hint = UncheckedExprHint(tcx.types.usize); |
1329 | match eval_const_expr_partial(tcx, count_expr, hint, None) { | |
1330 | Ok(Integral(Usize(count))) => { | |
1331 | let val = count.as_u64(tcx.sess.target.uint_type); | |
1332 | assert_eq!(val as usize as u64, val); | |
5bcae85e | 1333 | Ok(val as usize) |
54a0048b SL |
1334 | }, |
1335 | Ok(const_val) => { | |
5bcae85e SL |
1336 | struct_span_err!(tcx.sess, count_expr.span, E0306, |
1337 | "expected `usize` for {}, found {}", | |
1338 | reason, | |
1339 | const_val.description()) | |
1340 | .span_label(count_expr.span, &format!("expected `usize`")) | |
1341 | .emit(); | |
1342 | ||
1343 | Err(ErrorReported) | |
54a0048b SL |
1344 | } |
1345 | Err(err) => { | |
5bcae85e SL |
1346 | let mut diag = report_const_eval_err( |
1347 | tcx, &err, count_expr.span, reason); | |
1348 | ||
1349 | match count_expr.node { | |
54a0048b SL |
1350 | hir::ExprPath(None, hir::Path { |
1351 | global: false, | |
1352 | ref segments, | |
1353 | .. | |
5bcae85e SL |
1354 | }) if segments.len() == 1 => { |
1355 | if let Some(Def::Local(..)) = tcx.expect_def_or_none(count_expr.id) { | |
1356 | diag.note(&format!("`{}` is a variable", segments[0].name)); | |
1357 | } | |
54a0048b | 1358 | } |
5bcae85e SL |
1359 | _ => {} |
1360 | } | |
1361 | ||
1362 | diag.emit(); | |
1363 | Err(ErrorReported) | |
54a0048b SL |
1364 | } |
1365 | } | |
1366 | } |