From 666575861405712d302fe32cbe563ced8d98b8ad Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Mon, 4 May 2015 01:55:16 -0600 Subject: [PATCH] Fix issue where trait-associated consts could cause ICEs in match patterns. This allows some lookup of trait-associated consts during type-checking, which may be helpful for future fixes as well. --- src/librustc/middle/const_eval.rs | 48 ++++++++++++++----- src/librustc_trans/trans/_match.rs | 3 +- src/librustc_typeck/check/_match.rs | 4 +- .../associated-const-range-match-patterns.rs | 35 ++++++++++++++ 4 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 src/test/run-pass/associated-const-range-match-patterns.rs diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 091092e3b607..4faf46921d8e 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -126,8 +126,10 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, Some(ref_id) => { let trait_id = ty::trait_of_item(tcx, def_id) .unwrap(); + let substs = ty::node_id_item_substs(tcx, ref_id) + .substs; resolve_trait_associated_const(tcx, ti, trait_id, - ref_id) + substs) } // Technically, without knowing anything about the // expression that generates the obligation, we could @@ -172,8 +174,10 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, // a trait-associated const if the caller gives us // the expression that refers to it. Some(ref_id) => { + let substs = ty::node_id_item_substs(tcx, ref_id) + .substs; resolve_trait_associated_const(tcx, ti, trait_id, - ref_id).map(|e| e.id) + substs).map(|e| e.id) } None => None } @@ -633,9 +637,23 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) { uint_shift_body overflowing_shr const_uint ShiftRightWithOverflow }} +// After type checking, `eval_const_expr_partial` should always suffice. The +// reason for providing `eval_const_expr_with_substs` is to allow +// trait-associated consts to be evaluated *during* type checking, when the +// substs for each expression have not been written into `tcx` yet. pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, e: &Expr, ty_hint: Option>) -> EvalResult { + eval_const_expr_with_substs(tcx, e, ty_hint, |id| { + ty::node_id_item_substs(tcx, id).substs + }) +} + +pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, + e: &Expr, + ty_hint: Option>, + get_substs: S) -> EvalResult + where S: Fn(ast::NodeId) -> subst::Substs<'tcx> { fn fromb(b: bool) -> const_val { const_int(b as i64) } let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e)); @@ -826,8 +844,11 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, def::FromTrait(trait_id) => match tcx.map.find(def_id.node) { Some(ast_map::NodeTraitItem(ti)) => match ti.node { ast::ConstTraitItem(ref ty, _) => { - (resolve_trait_associated_const(tcx, ti, - trait_id, e.id), + let substs = get_substs(e.id); + (resolve_trait_associated_const(tcx, + ti, + trait_id, + substs), Some(&**ty)) } _ => (None, None) @@ -926,10 +947,9 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, ti: &'tcx ast::TraitItem, trait_id: ast::DefId, - ref_id: ast::NodeId) + rcvr_substs: subst::Substs<'tcx>) -> Option<&'tcx Expr> { - let rcvr_substs = ty::node_id_item_substs(tcx, ref_id).substs; let subst::SeparateVecsPerParamSpace { types: rcvr_type, selfs: rcvr_self, @@ -1081,19 +1101,21 @@ pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option { }) } -pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, - a: &Expr, - b: &Expr, - ty_hint: Option>) - -> Option { - let a = match eval_const_expr_partial(tcx, a, ty_hint) { +pub fn compare_lit_exprs<'tcx, S>(tcx: &ty::ctxt<'tcx>, + a: &Expr, + b: &Expr, + ty_hint: Option>, + get_substs: S) -> Option + where S: Fn(ast::NodeId) -> subst::Substs<'tcx> { + let a = match eval_const_expr_with_substs(tcx, a, ty_hint, + |id| {get_substs(id)}) { Ok(a) => a, Err(e) => { tcx.sess.span_err(a.span, &e.description()); return None; } }; - let b = match eval_const_expr_partial(tcx, b, ty_hint) { + let b = match eval_const_expr_with_substs(tcx, b, ty_hint, get_substs) { Ok(b) => b, Err(e) => { tcx.sess.span_err(b.span, &e.description()); diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 84d464e8f078..dd1e9494780e 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -233,7 +233,8 @@ struct ConstantExpr<'a>(&'a ast::Expr); impl<'a> ConstantExpr<'a> { fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool { - match const_eval::compare_lit_exprs(tcx, self.0, other.0, None) { + match const_eval::compare_lit_exprs(tcx, self.0, other.0, None, + |id| {ty::node_id_item_substs(tcx, id).substs}) { Some(result) => result == Ordering::Equal, None => panic!("compare_list_exprs: type mismatch"), } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 1f4d6cc2fd47..63470604084f 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -98,7 +98,9 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, lhs_eq_rhs && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(lhs_ty)); if numeric_or_char { - match const_eval::compare_lit_exprs(tcx, &**begin, &**end, Some(lhs_ty)) { + match const_eval::compare_lit_exprs(tcx, &**begin, &**end, Some(lhs_ty), + |id| {fcx.item_substs()[&id].substs + .clone()}) { Some(Ordering::Less) | Some(Ordering::Equal) => {} Some(Ordering::Greater) => { diff --git a/src/test/run-pass/associated-const-range-match-patterns.rs b/src/test/run-pass/associated-const-range-match-patterns.rs new file mode 100644 index 000000000000..d38ccca68914 --- /dev/null +++ b/src/test/run-pass/associated-const-range-match-patterns.rs @@ -0,0 +1,35 @@ +// 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(associated_consts)] + +struct Foo; + +trait HasNum { + const NUM: isize; +} +impl HasNum for Foo { + const NUM: isize = 1; +} + +fn main() { + assert!(match 2 { + Foo::NUM ... 3 => true, + _ => false, + }); + assert!(match 0 { + -1 ... ::NUM => true, + _ => false, + }); + assert!(match 1 { + ::NUM ... ::NUM => true, + _ => false, + }); +}