auto merge of #17157 : nikomatsakis/rust/occurs-check, r=pcwalton
Avoid ever constructing cyclic types in the first place, rather than detecting them in resolve. This simplifies logic elsewhere in the compiler, in particular on the trait reform branch. r? @pnkfelix or @pcwalton cc #5527
This commit is contained in:
commit
c8b0d667c3
@ -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>`
|
||||
@ -3791,6 +3792,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",
|
||||
|
@ -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 infcx = self.infcx;
|
||||
let span = self.trace.origin.span();
|
||||
t.fold_with(
|
||||
&mut RegionFolder::regions(
|
||||
self.infcx.tcx,
|
||||
|_| infcx.next_region_var(MiscVariable(span))))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 {:?}; \
|
||||
|
@ -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)
|
||||
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));
|
||||
}
|
||||
None => {
|
||||
if self.should(force_tvar) {
|
||||
self.err = Some(unresolved_ty(vid));
|
||||
}
|
||||
ty::mk_var(tcx, 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 {
|
||||
|
18
src/test/compile-fail/occurs-check-2.rs
Normal file
18
src/test/compile-fail/occurs-check-2.rs
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user