提交 0d9e473b 编写于 作者: N Niko Matsakis

Comprehence cycle detection in `collect`. In some cases, the cycles we

report are not *necessary* cycles, but we'll work on refactoring them
over time. This overlaps with the cycle detection that astconv already
does: I left that code in because it gives a more targeted error
message, though perhaps less helpful in that it doesn't give the full
details of the cycle.
上级 15ef2c2e
......@@ -2546,6 +2546,13 @@ pub fn closure_type(&self,
{
self.closure_tys.borrow()[def_id].subst(self, substs)
}
pub fn type_parameter_def(&self,
node_id: ast::NodeId)
-> TypeParameterDef<'tcx>
{
self.ty_param_defs.borrow()[node_id].clone()
}
}
// Interns a type/name combination, stores the resulting box in cx.interner,
......
......@@ -53,7 +53,7 @@
use middle::def;
use middle::resolve_lifetime as rl;
use middle::privacy::{AllPublic, LastMod};
use middle::subst::{FnSpace, ParamSpace, TypeSpace, SelfSpace, Subst, Substs};
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
use middle::traits;
use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty};
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
......@@ -73,11 +73,14 @@
pub trait AstConv<'tcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
fn get_item_type_scheme(&self, id: ast::DefId) -> ty::TypeScheme<'tcx>;
fn get_item_type_scheme(&self, span: Span, id: ast::DefId)
-> Result<ty::TypeScheme<'tcx>, ErrorReported>;
fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>>;
fn get_trait_def(&self, span: Span, id: ast::DefId)
-> Result<Rc<ty::TraitDef<'tcx>>, ErrorReported>;
fn get_type_parameter_bounds(&self, space: ParamSpace, index: u32) -> Vec<ty::PolyTraitRef<'tcx>>;
fn get_type_parameter_bounds(&self, span: Span, def_id: ast::NodeId)
-> Result<Vec<ty::PolyTraitRef<'tcx>>, ErrorReported>;
/// Return an (optional) substitution to convert bound type parameters that
/// are in scope into free ones. This function should only return Some
......@@ -685,7 +688,14 @@ fn ast_path_to_trait_ref<'a,'tcx>(
-> Rc<ty::TraitRef<'tcx>>
{
debug!("ast_path_to_trait_ref {:?}", trait_segment);
let trait_def = this.get_trait_def(trait_def_id);
let trait_def = match this.get_trait_def(path.span, trait_def_id) {
Ok(trait_def) => trait_def,
Err(ErrorReported) => {
// No convenient way to recover from a cycle here. Just bail. Sorry!
this.tcx().sess.abort_if_errors();
this.tcx().sess.bug("ErrorReported returned, but no errors reports?")
}
};
let (regions, types, assoc_bindings) = match trait_segment.parameters {
ast::AngleBracketedParameters(ref data) => {
......@@ -862,14 +872,17 @@ fn ast_path_to_ty<'tcx>(
item_segment: &ast::PathSegment)
-> Ty<'tcx>
{
let ty::TypeScheme {
generics,
ty: decl_ty
} = this.get_item_type_scheme(did);
let substs = ast_path_substs_for_ty(this, rscope,
span, param_mode,
&generics, item_segment);
let tcx = this.tcx();
let substs = match this.get_item_type_scheme(path.span, did) {
Ok(ty::TypeScheme { generics, ty: decl_ty }) => {
ast_path_substs_for_ty(this, rscope,
span, param_mode,
&generics, item_segment)
}
Err(ErrorReported) => {
return TypeAndSubsts { substs: Substs::empty(), ty: tcx.types.err };
}
};
// FIXME(#12938): This is a hack until we have full support for DST.
if Some(did) == this.tcx().lang_items.owned_box() {
......@@ -1003,22 +1016,17 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>,
return (tcx.types.err, ty_path_def);
};
let mut suitable_bounds: Vec<_>;
let ty_param_name: ast::Name;
{ // contain scope of refcell:
let ty_param_defs = tcx.ty_param_defs.borrow();
let ty_param_def = &ty_param_defs[ty_param_node_id];
ty_param_name = ty_param_def.name;
// FIXME(#20300) -- search where clauses, not bounds
suitable_bounds =
traits::transitive_bounds(tcx,
&this.get_type_parameter_bounds(ty_param_def.space,
ty_param_def.index))
.filter(|b| trait_defines_associated_type_named(this, b.def_id(), assoc_name))
.collect();
}
let ty_param_name = tcx.ty_param_defs.borrow()[ty_param_node_id].name;
// FIXME(#20300) -- search where clauses, not bounds
let bounds =
this.get_type_parameter_bounds(ast_ty.span, ty_param_ndoe_id)
.unwrap_or(Vec::new());
let mut suitable_bounds: Vec<_> =
traits::transitive_bounds(tcx, &bounds)
.filter(|b| trait_defines_associated_type_named(this, b.def_id(), assoc_name))
.collect();
if suitable_bounds.len() == 0 {
span_err!(tcx.sess, span, E0220,
......
......@@ -106,7 +106,7 @@
use {CrateCtxt, lookup_full_def, require_same_types};
use TypeAndSubsts;
use lint;
use util::common::{block_query, indenter, loop_query};
use util::common::{block_query, ErrorReported, indenter, loop_query};
use util::ppaux::{self, Repr};
use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
use util::lev_distance::lev_distance;
......@@ -1206,12 +1206,16 @@ fn check_cast<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx }
fn get_item_type_scheme(&self, id: ast::DefId) -> ty::TypeScheme<'tcx> {
ty::lookup_item_type(self.tcx(), id)
fn get_item_type_scheme(&self, _: Span, id: ast::DefId)
-> Result<ty::TypeScheme<'tcx>, ErrorReported>
{
Ok(ty::lookup_item_type(self.tcx(), id))
}
fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>> {
ty::lookup_trait_def(self.tcx(), id)
fn get_trait_def(&self, _: Span, id: ast::DefId)
-> Result<Rc<ty::TraitDef<'tcx>>, ErrorReported>
{
Ok(ty::lookup_trait_def(self.tcx(), id))
}
fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
......@@ -1219,27 +1223,29 @@ fn get_free_substs(&self) -> Option<&Substs<'tcx>> {
}
fn get_type_parameter_bounds(&self,
space: ParamSpace,
index: u32)
-> Vec<ty::PolyTraitRef<'tcx>>
_: Span,
node_id: ast::NodeId)
-> Result<Vec<ty::PolyTraitRef<'tcx>>, ErrorReported>
{
self.inh.param_env.caller_bounds
.iter()
.filter_map(|predicate| {
match *predicate {
ty::Predicate::Trait(ref data) => {
if data.0.self_ty().is_param(space, index) {
Some(data.to_poly_trait_ref())
} else {
None
let def = self.tcx().type_parameter_def(node_id);
let r = self.inh.param_env.caller_bounds
.iter()
.filter_map(|predicate| {
match *predicate {
ty::Predicate::Trait(ref data) => {
if data.0.self_ty().is_param(def.space, def.index) {
Some(data.to_poly_trait_ref())
} else {
None
}
}
_ => {
None
}
}
}
_ => {
None
}
}
})
.collect()
})
.collect();
Ok(r)
}
fn ty_infer(&self, _span: Span) -> Ty<'tcx> {
......
......@@ -98,12 +98,13 @@ trait hierarchy is only necessary for shorthands like `T::X` or
use middle::ty_fold::{self, TypeFolder, TypeFoldable};
use middle::infer;
use rscope::*;
use util::common::memoized;
use util::common::{ErrorReported, memoized};
use util::nodemap::{FnvHashMap, FnvHashSet};
use util::ppaux;
use util::ppaux::{Repr,UserString};
use write_ty_to_tcx;
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
......@@ -121,7 +122,7 @@ trait hierarchy is only necessary for shorthands like `T::X` or
// Main entry point
pub fn collect_item_types(tcx: &ty::ctxt) {
let ccx = &CrateCtxt { tcx: tcx };
let ccx = &CrateCtxt { tcx: tcx, stack: RefCell::new(Vec::new()) };
match ccx.tcx.lang_items.ty_desc() {
Some(id) => { collect_intrinsic_type(ccx, id); }
......@@ -143,6 +144,10 @@ pub fn collect_item_types(tcx: &ty::ctxt) {
struct CrateCtxt<'a,'tcx:'a> {
tcx: &'a ty::ctxt<'tcx>,
// This stack is used to identify cycles in the user's source.
// Note that these cycles can cross multiple items.
stack: RefCell<Vec<AstConvRequest>>,
}
struct ItemCtxt<'a,'tcx:'a> {
......@@ -150,6 +155,13 @@ struct ItemCtxt<'a,'tcx:'a> {
generics: &'a ty::Generics<'tcx>,
}
#[derive(Copy, PartialEq, Eq)]
enum AstConvRequest {
GetItemTypeScheme(ast::DefId),
GetTraitDef(ast::DefId),
GetTypeParameterBounds(ast::NodeId),
}
///////////////////////////////////////////////////////////////////////////
// Zeroth phase: collect types of intrinsics
......@@ -217,6 +229,94 @@ fn method_ty(&self, method_id: ast::NodeId) -> Rc<ty::Method<'tcx>> {
}
}
}
fn cycle_check<F,R>(&self,
span: Span,
request: AstConvRequest,
code: F)
-> Result<R,ErrorReported>
where F: FnOnce() -> R
{
{
let mut stack = self.stack.borrow_mut();
match stack.iter().enumerate().rev().find(|&(_, r)| *r == request) {
None => { }
Some((i, _)) => {
let cycle = &stack[i..];
self.report_cycle(span, cycle);
return Err(ErrorReported);
}
}
stack.push(request);
}
let result = code();
self.stack.borrow_mut().pop();
Ok(result)
}
fn report_cycle(&self,
span: Span,
cycle: &[AstConvRequest])
{
assert!(!cycle.is_empty());
let tcx = self.tcx;
tcx.sess.span_err(
span,
&format!("unsupported cyclic reference between types/traits detected"));
match cycle[0] {
AstConvRequest::GetItemTypeScheme(def_id) |
AstConvRequest::GetTraitDef(def_id) => {
tcx.sess.note(
&format!("the cycle begins when processing `{}`...",
ty::item_path_str(tcx, def_id)));
}
AstConvRequest::GetTypeParameterBounds(id) => {
let def = tcx.type_parameter_def(id);
tcx.sess.note(
&format!("the cycle begins when computing the bounds \
for type parameter `{}`...",
def.name.user_string(tcx)));
}
}
for request in cycle[1..].iter() {
match *request {
AstConvRequest::GetItemTypeScheme(def_id) |
AstConvRequest::GetTraitDef(def_id) => {
tcx.sess.note(
&format!("...which then requires processing `{}`...",
ty::item_path_str(tcx, def_id)));
}
AstConvRequest::GetTypeParameterBounds(id) => {
let def = tcx.type_parameter_def(id);
tcx.sess.note(
&format!("...which then requires computing the bounds \
for type parameter `{}`...",
def.name.user_string(tcx)));
}
}
}
match cycle[0] {
AstConvRequest::GetItemTypeScheme(def_id) |
AstConvRequest::GetTraitDef(def_id) => {
tcx.sess.note(
&format!("...which then again requires processing `{}`, completing the cycle.",
ty::item_path_str(tcx, def_id)));
}
AstConvRequest::GetTypeParameterBounds(id) => {
let def = tcx.type_parameter_def(id);
tcx.sess.note(
&format!("...which then again requires computing the bounds \
for type parameter `{}`, completing the cycle.",
def.name.user_string(tcx)));
}
}
}
}
pub trait ToTy<'tcx> {
......@@ -232,25 +332,37 @@ fn to_ty<RS:RegionScope>(&self, rs: &RS, ast_ty: &ast::Ty) -> Ty<'tcx> {
impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> {
fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx }
fn get_item_type_scheme(&self, id: ast::DefId) -> ty::TypeScheme<'tcx> {
type_scheme_of_def_id(self.ccx, id)
fn get_item_type_scheme(&self, span: Span, id: ast::DefId)
-> Result<ty::TypeScheme<'tcx>, ErrorReported>
{
self.ccx.cycle_check(span, AstConvRequest::GetItemTypeScheme(id), || {
type_scheme_of_def_id(self.ccx, id)
})
}
fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>> {
get_trait_def(self.ccx, id)
fn get_trait_def(&self, span: Span, id: ast::DefId)
-> Result<Rc<ty::TraitDef<'tcx>>, ErrorReported>
{
self.ccx.cycle_check(span, AstConvRequest::GetTraitDef(id), || {
get_trait_def(self.ccx, id)
})
}
fn get_type_parameter_bounds(&self,
param: subst::ParamSpace,
index: u32)
-> Vec<ty::PolyTraitRef<'tcx>>
span: Span,
node_id: ast::NodeId)
-> Result<Vec<ty::PolyTraitRef<'tcx>>, ErrorReported>
{
// TODO out of range indices can occur when you have something
// like fn foo<T:U::X,U>() { }
match self.generics.types.opt_get(param, index as usize) {
Some(def) => def.bounds.trait_bounds.clone(),
None => Vec::new(),
}
self.ccx.cycle_check(span, AstConvRequest::GetTypeParameterBounds(node_id), || {
let def = self.tcx().type_parameter_def(node_id);
// TODO out of range indices can occur when you have something
// like fn foo<T:U::X,U>() { }
match self.generics.types.opt_get(def.space, def.index as usize) {
Some(def) => def.bounds.trait_bounds.clone(),
None => Vec::new(),
}
})
}
fn ty_infer(&self, span: Span) -> Ty<'tcx> {
......
// 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.
// Regression test for #15477. This test should pass, vs reporting an
// error as it does now, but at least this test shows it doesn't
// segfault.
trait Chromosome<X: Chromosome> {
//~^ ERROR cyclic reference detected
}
fn main() { }
// 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.
// Test a supertrait cycle where a trait extends itself.
trait Chromosome: Chromosome {
//~^ ERROR cyclic reference detected
}
fn main() { }
// 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.
// Test a supertrait cycle where the first trait we find (`A`) is not
// a direct participant in the cycle.
trait A: B {
}
trait B: C { }
//~^ ERROR cyclic reference detected
trait C: B { }
fn main() { }
// 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.
// Test a supertrait cycle where a trait extends itself.
trait Chromosome: Get<Struct> {
//~^ ERROR cyclic reference detected
}
trait Get<A> {
fn get(&self) -> A;
}
struct Struct<C:Chromosome> { c: C }
fn main() { }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册