]> git.lizzy.rs Git - rust.git/commitdiff
Avoid ever constructing cyclic types in the first place, rather than detecting them...
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 9 Sep 2014 21:45:51 +0000 (17:45 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 9 Sep 2014 22:33:07 +0000 (18:33 -0400)
src/librustc/middle/ty.rs
src/librustc/middle/typeck/infer/combine.rs
src/librustc/middle/typeck/infer/mod.rs
src/librustc/middle/typeck/infer/resolve.rs
src/test/compile-fail/occurs-check-2.rs [new file with mode: 0644]
src/test/compile-fail/occurs-check.rs

index feed76233d68aaf1b2e384e85d1a2d62119c37c1..69b73436477c1ac6b5ee0c980398a33f070feb20 100644 (file)
@@ -1002,7 +1002,8 @@ pub enum type_err {
     terr_float_mismatch(expected_found<ast::FloatTy>),
     terr_traits(expected_found<ast::DefId>),
     terr_builtin_bounds(expected_found<BuiltinBounds>),
-    terr_variadic_mismatch(expected_found<bool>)
+    terr_variadic_mismatch(expected_found<bool>),
+    terr_cyclic_ty,
 }
 
 /// Bounds suitable for a named type parameter like `A` in `fn foo<A>`
@@ -3790,6 +3791,7 @@ fn tstore_to_closure(s: &TraitStore) -> String {
     }
 
     match *err {
+        terr_cyclic_ty => "cyclic type of infinite size".to_string(),
         terr_mismatch => "types differ".to_string(),
         terr_fn_style_mismatch(values) => {
             format!("expected {} fn, found {} fn",
index 66caf10cb408ca59794fcefdf16f144f7a2434cc..17e7aa8ddc34cf3085f870be1aa8d127d635bc6c 100644 (file)
@@ -39,6 +39,7 @@
 use middle::ty::{IntType, UintType};
 use middle::ty::{BuiltinBounds};
 use middle::ty;
+use middle::ty_fold;
 use middle::typeck::infer::equate::Equate;
 use middle::typeck::infer::glb::Glb;
 use middle::typeck::infer::lub::Lub;
@@ -48,7 +49,7 @@
 use middle::typeck::infer::{MiscVariable, TypeTrace};
 use middle::typeck::infer::type_variable::{RelationDir, EqTo,
                                            SubtypeOf, SupertypeOf};
-use middle::ty_fold::{RegionFolder, TypeFoldable};
+use middle::ty_fold::{TypeFoldable};
 use util::ppaux::Repr;
 
 use std::result;
@@ -56,6 +57,7 @@
 use syntax::ast::{Onceness, FnStyle};
 use syntax::ast;
 use syntax::abi;
+use syntax::codemap::Span;
 
 pub trait Combine<'tcx> {
     fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx>;
@@ -637,10 +639,14 @@ pub fn instantiate(&self,
                 Some(t) => t, // ...already instantiated.
                 None => {     // ...not yet instantiated:
                     // Generalize type if necessary.
-                    let generalized_ty = match dir {
-                        EqTo => a_ty,
-                        SupertypeOf | SubtypeOf => self.generalize(a_ty)
-                    };
+                    let generalized_ty = try!(match dir {
+                        EqTo => {
+                            self.generalize(a_ty, b_vid, false)
+                        }
+                        SupertypeOf | SubtypeOf => {
+                            self.generalize(a_ty, b_vid, true)
+                        }
+                    });
                     debug!("instantiate(a_ty={}, dir={}, \
                                         b_vid={}, generalized_ty={})",
                            a_ty.repr(tcx), dir, b_vid.repr(tcx),
@@ -678,15 +684,85 @@ pub fn instantiate(&self,
         Ok(())
     }
 
-    fn generalize(&self, t: ty::t) -> ty::t {
-        // FIXME(#16847): This is non-ideal because we don't give a
-        // very descriptive origin for this region variable.
+    fn generalize(&self,
+                  ty: ty::t,
+                  for_vid: ty::TyVid,
+                  make_region_vars: bool)
+                  -> cres<ty::t>
+    {
+        /*!
+         * Attempts to generalize `ty` for the type variable
+         * `for_vid`.  This checks for cycle -- that is, whether the
+         * type `ty` references `for_vid`. If `make_region_vars` is
+         * true, it will also replace all regions with fresh
+         * variables. Returns `ty_err` in the case of a cycle, `Ok`
+         * otherwise.
+         */
+
+        let mut generalize = Generalizer { infcx: self.infcx,
+                                           span: self.trace.origin.span(),
+                                           for_vid: for_vid,
+                                           make_region_vars: make_region_vars,
+                                           cycle_detected: false };
+        let u = ty.fold_with(&mut generalize);
+        if generalize.cycle_detected {
+            Err(ty::terr_cyclic_ty)
+        } else {
+            Ok(u)
+        }
+    }
+}
 
-        let infcx = self.infcx;
-        let span = self.trace.origin.span();
-        t.fold_with(
-            &mut RegionFolder::regions(
-                self.infcx.tcx,
-                |_| infcx.next_region_var(MiscVariable(span))))
+struct Generalizer<'cx, 'tcx:'cx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+    span: Span,
+    for_vid: ty::TyVid,
+    make_region_vars: bool,
+    cycle_detected: bool,
+}
+
+impl<'cx, 'tcx> ty_fold::TypeFolder<'tcx> for Generalizer<'cx, 'tcx> {
+    fn tcx(&self) -> &ty::ctxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_ty(&mut self, t: ty::t) -> ty::t {
+        // Check to see whether the type we are genealizing references
+        // `vid`. At the same time, also update any type variables to
+        // the values that they are bound to. This is needed to truly
+        // check for cycles, but also just makes things readable.
+        //
+        // (In particular, you could have something like `$0 = Box<$1>`
+        //  where `$1` has already been instantiated with `Box<$0>`)
+        match ty::get(t).sty {
+            ty::ty_infer(ty::TyVar(vid)) => {
+                if vid == self.for_vid {
+                    self.cycle_detected = true;
+                    ty::mk_err()
+                } else {
+                    match self.infcx.type_variables.borrow().probe(vid) {
+                        Some(u) => self.fold_ty(u),
+                        None => t,
+                    }
+                }
+            }
+            _ => {
+                ty_fold::super_fold_ty(self, t)
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region) -> ty::Region {
+        match r {
+            ty::ReLateBound(..) | ty::ReEarlyBound(..) => r,
+            _ if self.make_region_vars => {
+                // FIXME: This is non-ideal because we don't give a
+                // very descriptive origin for this region variable.
+                self.infcx.next_region_var(MiscVariable(self.span))
+            }
+            _ => r,
+        }
     }
 }
+
+
index 44ee7ba2de6e4beefdfe74e1b4169fe596ab7d05..f917ced08ce9435c94dc59f984417f997c137a9d 100644 (file)
@@ -266,7 +266,6 @@ pub enum fixup_err {
     unresolved_int_ty(IntVid),
     unresolved_float_ty(FloatVid),
     unresolved_ty(TyVid),
-    cyclic_ty(TyVid),
     unresolved_region(RegionVid),
     region_var_bound_by_region_var(RegionVid, RegionVid)
 }
@@ -282,7 +281,6 @@ pub fn fixup_err_to_string(f: fixup_err) -> String {
            the type explicitly".to_string()
       }
       unresolved_ty(_) => "unconstrained type".to_string(),
-      cyclic_ty(_) => "cyclic type of infinite size".to_string(),
       unresolved_region(_) => "unconstrained region".to_string(),
       region_var_bound_by_region_var(r1, r2) => {
         format!("region var {:?} bound by another region var {:?}; \
index dcdae7ed29c8d33a306af5c94a6bb4f3cda26ebf..569206f6754f9917679e64c87b043ee7d060a7e7 100644 (file)
@@ -51,7 +51,7 @@
 use middle::ty::{IntType, UintType};
 use middle::ty;
 use middle::ty_fold;
-use middle::typeck::infer::{cyclic_ty, fixup_err, fres, InferCtxt};
+use middle::typeck::infer::{fixup_err, fres, InferCtxt};
 use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty};
 use syntax::codemap::Span;
 use util::common::indent;
@@ -78,7 +78,6 @@ pub struct ResolveState<'a, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'tcx>,
     modes: uint,
     err: Option<fixup_err>,
-    v_seen: Vec<TyVid> ,
     type_depth: uint,
 }
 
@@ -90,7 +89,6 @@ pub fn resolver<'a, 'tcx>(infcx: &'a InferCtxt<'a, 'tcx>,
         infcx: infcx,
         modes: modes,
         err: None,
-        v_seen: Vec::new(),
         type_depth: 0,
     }
 }
@@ -126,9 +124,7 @@ pub fn resolve_type_chk(&mut self,
         // n.b. This is a hokey mess because the current fold doesn't
         // allow us to pass back errors in any useful way.
 
-        assert!(self.v_seen.is_empty());
-        let rty = indent(|| self.resolve_type(typ) );
-        assert!(self.v_seen.is_empty());
+        let rty = self.resolve_type(typ);
         match self.err {
           None => {
             debug!("Resolved {} to {} (modes={:x})",
@@ -205,33 +201,19 @@ pub fn resolve_region_var(&mut self, rid: RegionVid) -> ty::Region {
     }
 
     pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t {
-        if self.v_seen.contains(&vid) {
-            self.err = Some(cyclic_ty(vid));
-            return ty::mk_var(self.infcx.tcx, vid);
-        } else {
-            self.v_seen.push(vid);
-            let tcx = self.infcx.tcx;
-
-            // Nonobvious: prefer the most specific type
-            // (i.e., the lower bound) to the more general
-            // one.  More general types in Rust (e.g., fn())
-            // tend to carry more restrictions or higher
-            // perf. penalties, so it pays to know more.
-
-            let t1 = match self.infcx.type_variables.borrow().probe(vid) {
-                Some(t) => {
-                    self.resolve_type(t)
-                }
-                None => {
-                    if self.should(force_tvar) {
-                        self.err = Some(unresolved_ty(vid));
-                    }
-                    ty::mk_var(tcx, vid)
+        let tcx = self.infcx.tcx;
+        let t1 = match self.infcx.type_variables.borrow().probe(vid) {
+            Some(t) => {
+                self.resolve_type(t)
+            }
+            None => {
+                if self.should(force_tvar) {
+                    self.err = Some(unresolved_ty(vid));
                 }
-            };
-            self.v_seen.pop().unwrap();
-            return t1;
-        }
+                ty::mk_var(tcx, vid)
+            }
+        };
+        return t1;
     }
 
     pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t {
diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs
new file mode 100644 (file)
index 0000000..69c012e
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2012 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.
+
+use std::gc::GC;
+
+fn main() {
+    let f;
+    let g;
+    g = f;
+    f = box(GC) g; //~ ERROR cyclic type of infinite size
+}
index a2148c07187e8a9d27d5669912af3daad2946784..a00528616c3e5e3d0f4cdb5ac0edbf83a23bbcb9 100644 (file)
@@ -12,6 +12,6 @@
 use std::gc::GC;
 
 fn main() {
-    let f; //~ ERROR cyclic type of infinite size
-    f = box(GC) f;
+    let f;
+    f = box(GC) f; //~ ERROR cyclic type of infinite size
 }