about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-05-26 16:58:07 +0000
committerbors <bors@rust-lang.org>2015-05-26 16:58:07 +0000
commit0ea80faae8466ba7fcebf8e330a9038ff3917db4 (patch)
tree98a7e299fc0d8a999952b94f3a9db81aef4ac58e
parenta83201ff9fbf217e8dc963a65a992e6ead3aafba (diff)
parent8db699d18d263bf779f260ba55221a8c0ff2f5d8 (diff)
downloadrust-0ea80faae8466ba7fcebf8e330a9038ff3917db4.tar.gz
rust-0ea80faae8466ba7fcebf8e330a9038ff3917db4.zip
Auto merge of #25091 - quantheory:trait_associated_const_fixes, r=nikomatsakis
Closes #25046 (by rejecting the code that causes the ICE) and #24946. I haven't been able to deal with the array size or recursion issues yet for associated consts, though my hope was that the change I made for range match patterns might help with array sizes, too.

This PR is pretty much orthogonal to #25065.
-rw-r--r--src/librustc/middle/const_eval.rs48
-rw-r--r--src/librustc_trans/trans/_match.rs3
-rw-r--r--src/librustc_typeck/check/_match.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs31
-rw-r--r--src/librustc_typeck/diagnostics.rs1
-rw-r--r--src/test/compile-fail/associated-const-type-parameters.rs26
-rw-r--r--src/test/run-pass/associated-const-range-match-patterns.rs35
7 files changed, 133 insertions, 15 deletions
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index e5100c32cc2..188bf4bf77b 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -128,8 +128,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
@@ -174,8 +176,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
                     }
@@ -702,9 +706,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<Ty<'tcx>>) -> 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<Ty<'tcx>>,
+                                            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));
@@ -895,8 +913,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)
@@ -995,10 +1016,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,
@@ -1150,19 +1170,21 @@ pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<Ordering> {
     })
 }
 
-pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
-                               a: &Expr,
-                               b: &Expr,
-                               ty_hint: Option<Ty<'tcx>>)
-                               -> Option<Ordering> {
-    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<Ty<'tcx>>,
+                                  get_substs: S) -> Option<Ordering>
+        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 53a991170c7..b09f5a57e46 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 1f4d6cc2fd4..63470604084 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/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index eb6e90414e3..884933adbc4 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3759,8 +3759,36 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
                                                         &'a [ast::PathSegment],
                                                         def::Def)>
 {
+
+    // Associated constants can't depend on generic types.
+    fn have_disallowed_generic_consts<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                                def: def::Def,
+                                                ty: Ty<'tcx>,
+                                                span: Span,
+                                                node_id: ast::NodeId) -> bool {
+        match def {
+            def::DefAssociatedConst(..) => {
+                if ty::type_has_params(ty) || ty::type_has_self(ty) {
+                    span_err!(fcx.sess(), span, E0329,
+                              "Associated consts cannot depend \
+                               on type parameters or Self.");
+                    fcx.write_error(node_id);
+                    return true;
+                }
+            }
+            _ => {}
+        }
+        false
+    }
+
     // If fully resolved already, we don't have to do anything.
     if path_res.depth == 0 {
+        if let Some(ty) = opt_self_ty {
+            if have_disallowed_generic_consts(fcx, path_res.full_def(), ty,
+                                              span, node_id) {
+                return None;
+            }
+        }
         Some((opt_self_ty, &path.segments, path_res.base_def))
     } else {
         let mut def = path_res.base_def;
@@ -3776,6 +3804,9 @@ pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
         let item_name = item_segment.identifier.name;
         match method::resolve_ufcs(fcx, span, item_name, ty, node_id) {
             Ok((def, lp)) => {
+                if have_disallowed_generic_consts(fcx, def, ty, span, node_id) {
+                    return None;
+                }
                 // Write back the new resolution.
                 fcx.ccx.tcx.def_map.borrow_mut()
                        .insert(node_id, def::PathResolution {
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 5796a5599ce..8fd302a5f1f 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -1103,6 +1103,7 @@ register_diagnostics! {
     E0326, // associated const implemented with different type from trait
     E0327, // referred to method instead of constant in match pattern
     E0328, // cannot implement Unsize explicitly
+    E0329, // associated const depends on type parameter or Self.
     E0366, // dropck forbid specialization to concrete type or region
     E0367, // dropck forbid specialization to predicate not in struct/enum
     E0369, // binary operation `<op>` cannot be applied to types
diff --git a/src/test/compile-fail/associated-const-type-parameters.rs b/src/test/compile-fail/associated-const-type-parameters.rs
new file mode 100644
index 00000000000..e48ff59d1dc
--- /dev/null
+++ b/src/test/compile-fail/associated-const-type-parameters.rs
@@ -0,0 +1,26 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(associated_consts)]
+
+pub trait Foo {
+    const MIN: i32;
+
+    fn get_min() -> i32 {
+        Self::MIN //~ ERROR E0329
+    }
+}
+
+fn get_min<T: Foo>() -> i32 {
+    T::MIN; //~ ERROR E0329
+    <T as Foo>::MIN //~ ERROR E0329
+}
+
+fn main() {}
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 00000000000..d38ccca6891
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 ... <Foo as HasNum>::NUM => true,
+        _ => false,
+    });
+    assert!(match 1 {
+        <Foo as HasNum>::NUM ... <Foo>::NUM => true,
+        _ => false,
+    });
+}