Skip to content

Commit c8b0d66

Browse files
committed
auto merge of rust-lang#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 rust-lang#5527
2 parents 29f817f + c4d56b7 commit c8b0d66

File tree

6 files changed

+127
-51
lines changed

6 files changed

+127
-51
lines changed

src/librustc/middle/ty.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,8 @@ pub enum type_err {
10021002
terr_float_mismatch(expected_found<ast::FloatTy>),
10031003
terr_traits(expected_found<ast::DefId>),
10041004
terr_builtin_bounds(expected_found<BuiltinBounds>),
1005-
terr_variadic_mismatch(expected_found<bool>)
1005+
terr_variadic_mismatch(expected_found<bool>),
1006+
terr_cyclic_ty,
10061007
}
10071008

10081009
/// Bounds suitable for a named type parameter like `A` in `fn foo<A>`
@@ -3791,6 +3792,7 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String {
37913792
}
37923793

37933794
match *err {
3795+
terr_cyclic_ty => "cyclic type of infinite size".to_string(),
37943796
terr_mismatch => "types differ".to_string(),
37953797
terr_fn_style_mismatch(values) => {
37963798
format!("expected {} fn, found {} fn",

src/librustc/middle/typeck/infer/combine.rs

+90-14
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
3939
use middle::ty::{IntType, UintType};
4040
use middle::ty::{BuiltinBounds};
4141
use middle::ty;
42+
use middle::ty_fold;
4243
use middle::typeck::infer::equate::Equate;
4344
use middle::typeck::infer::glb::Glb;
4445
use middle::typeck::infer::lub::Lub;
@@ -48,14 +49,15 @@ use middle::typeck::infer::{InferCtxt, cres};
4849
use middle::typeck::infer::{MiscVariable, TypeTrace};
4950
use middle::typeck::infer::type_variable::{RelationDir, EqTo,
5051
SubtypeOf, SupertypeOf};
51-
use middle::ty_fold::{RegionFolder, TypeFoldable};
52+
use middle::ty_fold::{TypeFoldable};
5253
use util::ppaux::Repr;
5354

5455
use std::result;
5556

5657
use syntax::ast::{Onceness, FnStyle};
5758
use syntax::ast;
5859
use syntax::abi;
60+
use syntax::codemap::Span;
5961

6062
pub trait Combine<'tcx> {
6163
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx>;
@@ -637,10 +639,14 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
637639
Some(t) => t, // ...already instantiated.
638640
None => { // ...not yet instantiated:
639641
// Generalize type if necessary.
640-
let generalized_ty = match dir {
641-
EqTo => a_ty,
642-
SupertypeOf | SubtypeOf => self.generalize(a_ty)
643-
};
642+
let generalized_ty = try!(match dir {
643+
EqTo => {
644+
self.generalize(a_ty, b_vid, false)
645+
}
646+
SupertypeOf | SubtypeOf => {
647+
self.generalize(a_ty, b_vid, true)
648+
}
649+
});
644650
debug!("instantiate(a_ty={}, dir={}, \
645651
b_vid={}, generalized_ty={})",
646652
a_ty.repr(tcx), dir, b_vid.repr(tcx),
@@ -678,15 +684,85 @@ impl<'f, 'tcx> CombineFields<'f, 'tcx> {
678684
Ok(())
679685
}
680686

681-
fn generalize(&self, t: ty::t) -> ty::t {
682-
// FIXME(#16847): This is non-ideal because we don't give a
683-
// very descriptive origin for this region variable.
687+
fn generalize(&self,
688+
ty: ty::t,
689+
for_vid: ty::TyVid,
690+
make_region_vars: bool)
691+
-> cres<ty::t>
692+
{
693+
/*!
694+
* Attempts to generalize `ty` for the type variable
695+
* `for_vid`. This checks for cycle -- that is, whether the
696+
* type `ty` references `for_vid`. If `make_region_vars` is
697+
* true, it will also replace all regions with fresh
698+
* variables. Returns `ty_err` in the case of a cycle, `Ok`
699+
* otherwise.
700+
*/
701+
702+
let mut generalize = Generalizer { infcx: self.infcx,
703+
span: self.trace.origin.span(),
704+
for_vid: for_vid,
705+
make_region_vars: make_region_vars,
706+
cycle_detected: false };
707+
let u = ty.fold_with(&mut generalize);
708+
if generalize.cycle_detected {
709+
Err(ty::terr_cyclic_ty)
710+
} else {
711+
Ok(u)
712+
}
713+
}
714+
}
684715

685-
let infcx = self.infcx;
686-
let span = self.trace.origin.span();
687-
t.fold_with(
688-
&mut RegionFolder::regions(
689-
self.infcx.tcx,
690-
|_| infcx.next_region_var(MiscVariable(span))))
716+
struct Generalizer<'cx, 'tcx:'cx> {
717+
infcx: &'cx InferCtxt<'cx, 'tcx>,
718+
span: Span,
719+
for_vid: ty::TyVid,
720+
make_region_vars: bool,
721+
cycle_detected: bool,
722+
}
723+
724+
impl<'cx, 'tcx> ty_fold::TypeFolder<'tcx> for Generalizer<'cx, 'tcx> {
725+
fn tcx(&self) -> &ty::ctxt<'tcx> {
726+
self.infcx.tcx
727+
}
728+
729+
fn fold_ty(&mut self, t: ty::t) -> ty::t {
730+
// Check to see whether the type we are genealizing references
731+
// `vid`. At the same time, also update any type variables to
732+
// the values that they are bound to. This is needed to truly
733+
// check for cycles, but also just makes things readable.
734+
//
735+
// (In particular, you could have something like `$0 = Box<$1>`
736+
// where `$1` has already been instantiated with `Box<$0>`)
737+
match ty::get(t).sty {
738+
ty::ty_infer(ty::TyVar(vid)) => {
739+
if vid == self.for_vid {
740+
self.cycle_detected = true;
741+
ty::mk_err()
742+
} else {
743+
match self.infcx.type_variables.borrow().probe(vid) {
744+
Some(u) => self.fold_ty(u),
745+
None => t,
746+
}
747+
}
748+
}
749+
_ => {
750+
ty_fold::super_fold_ty(self, t)
751+
}
752+
}
753+
}
754+
755+
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
756+
match r {
757+
ty::ReLateBound(..) | ty::ReEarlyBound(..) => r,
758+
_ if self.make_region_vars => {
759+
// FIXME: This is non-ideal because we don't give a
760+
// very descriptive origin for this region variable.
761+
self.infcx.next_region_var(MiscVariable(self.span))
762+
}
763+
_ => r,
764+
}
691765
}
692766
}
767+
768+

src/librustc/middle/typeck/infer/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,6 @@ pub enum fixup_err {
266266
unresolved_int_ty(IntVid),
267267
unresolved_float_ty(FloatVid),
268268
unresolved_ty(TyVid),
269-
cyclic_ty(TyVid),
270269
unresolved_region(RegionVid),
271270
region_var_bound_by_region_var(RegionVid, RegionVid)
272271
}
@@ -282,7 +281,6 @@ pub fn fixup_err_to_string(f: fixup_err) -> String {
282281
the type explicitly".to_string()
283282
}
284283
unresolved_ty(_) => "unconstrained type".to_string(),
285-
cyclic_ty(_) => "cyclic type of infinite size".to_string(),
286284
unresolved_region(_) => "unconstrained region".to_string(),
287285
region_var_bound_by_region_var(r1, r2) => {
288286
format!("region var {:?} bound by another region var {:?}; \

src/librustc/middle/typeck/infer/resolve.rs

+14-32
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid};
5151
use middle::ty::{IntType, UintType};
5252
use middle::ty;
5353
use middle::ty_fold;
54-
use middle::typeck::infer::{cyclic_ty, fixup_err, fres, InferCtxt};
54+
use middle::typeck::infer::{fixup_err, fres, InferCtxt};
5555
use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty};
5656
use syntax::codemap::Span;
5757
use util::common::indent;
@@ -78,7 +78,6 @@ pub struct ResolveState<'a, 'tcx: 'a> {
7878
infcx: &'a InferCtxt<'a, 'tcx>,
7979
modes: uint,
8080
err: Option<fixup_err>,
81-
v_seen: Vec<TyVid> ,
8281
type_depth: uint,
8382
}
8483

@@ -90,7 +89,6 @@ pub fn resolver<'a, 'tcx>(infcx: &'a InferCtxt<'a, 'tcx>,
9089
infcx: infcx,
9190
modes: modes,
9291
err: None,
93-
v_seen: Vec::new(),
9492
type_depth: 0,
9593
}
9694
}
@@ -126,9 +124,7 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
126124
// n.b. This is a hokey mess because the current fold doesn't
127125
// allow us to pass back errors in any useful way.
128126

129-
assert!(self.v_seen.is_empty());
130-
let rty = indent(|| self.resolve_type(typ) );
131-
assert!(self.v_seen.is_empty());
127+
let rty = self.resolve_type(typ);
132128
match self.err {
133129
None => {
134130
debug!("Resolved {} to {} (modes={:x})",
@@ -205,33 +201,19 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
205201
}
206202

207203
pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t {
208-
if self.v_seen.contains(&vid) {
209-
self.err = Some(cyclic_ty(vid));
210-
return ty::mk_var(self.infcx.tcx, vid);
211-
} else {
212-
self.v_seen.push(vid);
213-
let tcx = self.infcx.tcx;
214-
215-
// Nonobvious: prefer the most specific type
216-
// (i.e., the lower bound) to the more general
217-
// one. More general types in Rust (e.g., fn())
218-
// tend to carry more restrictions or higher
219-
// perf. penalties, so it pays to know more.
220-
221-
let t1 = match self.infcx.type_variables.borrow().probe(vid) {
222-
Some(t) => {
223-
self.resolve_type(t)
224-
}
225-
None => {
226-
if self.should(force_tvar) {
227-
self.err = Some(unresolved_ty(vid));
228-
}
229-
ty::mk_var(tcx, vid)
204+
let tcx = self.infcx.tcx;
205+
let t1 = match self.infcx.type_variables.borrow().probe(vid) {
206+
Some(t) => {
207+
self.resolve_type(t)
208+
}
209+
None => {
210+
if self.should(force_tvar) {
211+
self.err = Some(unresolved_ty(vid));
230212
}
231-
};
232-
self.v_seen.pop().unwrap();
233-
return t1;
234-
}
213+
ty::mk_var(tcx, vid)
214+
}
215+
};
216+
return t1;
235217
}
236218

237219
pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t {
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2012 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+
use std::gc::GC;
12+
13+
fn main() {
14+
let f;
15+
let g;
16+
g = f;
17+
f = box(GC) g; //~ ERROR cyclic type of infinite size
18+
}

src/test/compile-fail/occurs-check.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
use std::gc::GC;
1313

1414
fn main() {
15-
let f; //~ ERROR cyclic type of infinite size
16-
f = box(GC) f;
15+
let f;
16+
f = box(GC) f; //~ ERROR cyclic type of infinite size
1717
}

0 commit comments

Comments
 (0)