Implement generalized object and type parameter bounds (Fixes #16462)

This commit is contained in:
Niko Matsakis 2014-08-27 21:46:52 -04:00
parent 3ee047ae1f
commit 1b487a8906
272 changed files with 6783 additions and 3154 deletions

View file

@ -10,6 +10,8 @@
#![allow(non_camel_case_types)]
use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use syntax::ast;
use syntax::visit;
use syntax::visit::Visitor;
@ -105,3 +107,51 @@ pub fn block_query(b: ast::P<ast::Block>, p: |&ast::Expr| -> bool) -> bool {
visit::walk_block(&mut v, &*b, ());
return v.flag;
}
// K: Eq + Hash<S>, V, S, H: Hasher<S>
pub fn can_reach<S,H:Hasher<S>,T:Eq+Clone+Hash<S>>(
edges_map: &HashMap<T,Vec<T>,H>,
source: T,
destination: T)
-> bool
{
/*!
* Determines whether there exists a path from `source` to
* `destination`. The graph is defined by the `edges_map`, which
* maps from a node `S` to a list of its adjacent nodes `T`.
*
* Efficiency note: This is implemented in an inefficient way
* because it is typically invoked on very small graphs. If the graphs
* become larger, a more efficient graph representation and algorithm
* would probably be advised.
*/
if source == destination {
return true;
}
// Do a little breadth-first-search here. The `queue` list
// doubles as a way to detect if we've seen a particular FR
// before. Note that we expect this graph to be an *extremely
// shallow* tree.
let mut queue = vec!(source);
let mut i = 0;
while i < queue.len() {
match edges_map.find(queue.get(i)) {
Some(edges) => {
for target in edges.iter() {
if *target == destination {
return true;
}
if !queue.iter().any(|x| x == target) {
queue.push((*target).clone());
}
}
}
None => {}
}
i += 1;
}
return false;
}

View file

@ -23,11 +23,9 @@ use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup, ty_open};
use middle::ty::{ty_unboxed_closure};
use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer};
use middle::ty;
use middle::typeck::infer::region_inference;
use middle::typeck::infer::unify::VarValue as VV;
use middle::typeck::infer::unify;
use middle::typeck::infer;
use middle::typeck;
use middle::typeck::check::regionmanip;
use middle::typeck::infer;
use std::gc::Gc;
use std::rc::Rc;
@ -66,6 +64,22 @@ pub fn note_and_explain_region(cx: &ctxt,
}
}
fn item_scope_tag(item: &ast::Item) -> &'static str {
/*!
* When a free region is associated with `item`, how should we describe
* the item in the error message.
*/
match item.node {
ast::ItemImpl(..) => "impl",
ast::ItemStruct(..) => "struct",
ast::ItemEnum(..) => "enum",
ast::ItemTrait(..) => "trait",
ast::ItemFn(..) => "function body",
_ => "item"
}
}
pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
-> (String, Option<Span>) {
return match region {
@ -87,9 +101,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
Some(ast_map::NodeStmt(stmt)) => {
explain_span(cx, "statement", stmt.span)
}
Some(ast_map::NodeItem(it)) if (match it.node {
ast::ItemFn(..) => true, _ => false}) => {
explain_span(cx, "function body", it.span)
Some(ast_map::NodeItem(it)) => {
let tag = item_scope_tag(&*it);
explain_span(cx, tag, it.span)
}
Some(_) | None => {
// this really should not happen
@ -112,17 +126,17 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
match cx.map.find(fr.scope_id) {
Some(ast_map::NodeBlock(ref blk)) => {
let (msg, opt_span) = explain_span(cx, "block", blk.span);
(format!("{} {}", prefix, msg), opt_span)
let (msg, opt_span) = explain_span(cx, "block", blk.span);
(format!("{} {}", prefix, msg), opt_span)
}
Some(ast_map::NodeItem(it)) if match it.node {
ast::ItemImpl(..) => true, _ => false} => {
let (msg, opt_span) = explain_span(cx, "impl", it.span);
(format!("{} {}", prefix, msg), opt_span)
Some(ast_map::NodeItem(it)) => {
let tag = item_scope_tag(&*it);
let (msg, opt_span) = explain_span(cx, tag, it.span);
(format!("{} {}", prefix, msg), opt_span)
}
Some(_) | None => {
// this really should not happen
(format!("{} node {}", prefix, fr.scope_id), None)
// this really should not happen
(format!("{} node {}", prefix, fr.scope_id), None)
}
}
}
@ -143,7 +157,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
};
fn explain_span(cx: &ctxt, heading: &str, span: Span)
-> (String, Option<Span>) {
-> (String, Option<Span>) {
let lo = cx.sess.codemap().lookup_char_pos_adj(span.lo);
(format!("the {} at {}:{}", heading, lo.line, lo.col.to_uint()),
Some(span))
@ -273,7 +287,7 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
_ => { }
}
push_sig_to_string(cx, &mut s, '(', ')', sig);
push_sig_to_string(cx, &mut s, '(', ')', sig, "");
s
}
@ -296,34 +310,34 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
}
};
let bounds_str = cty.bounds.user_string(cx);
match cty.store {
ty::UniqTraitStore => {
assert_eq!(cty.onceness, ast::Once);
s.push_str("proc");
push_sig_to_string(cx, &mut s, '(', ')', &cty.sig);
push_sig_to_string(cx, &mut s, '(', ')', &cty.sig,
bounds_str.as_slice());
}
ty::RegionTraitStore(..) => {
match cty.onceness {
ast::Many => {}
ast::Once => s.push_str("once ")
}
push_sig_to_string(cx, &mut s, '|', '|', &cty.sig);
push_sig_to_string(cx, &mut s, '|', '|', &cty.sig,
bounds_str.as_slice());
}
}
if !cty.bounds.is_empty() {
s.push_str(":");
s.push_str(cty.bounds.repr(cx).as_slice());
}
s
s.into_owned()
}
fn push_sig_to_string(cx: &ctxt,
s: &mut String,
bra: char,
ket: char,
sig: &ty::FnSig) {
sig: &ty::FnSig,
bounds: &str) {
s.push_char(bra);
let strs: Vec<String> = sig.inputs.iter().map(|a| fn_input_to_string(cx, *a)).collect();
s.push_str(strs.connect(", ").as_slice());
@ -332,6 +346,11 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
}
s.push_char(ket);
if !bounds.is_empty() {
s.push_str(":");
s.push_str(bounds);
}
if ty::get(sig.output).sty != ty_nil {
s.push_str(" -> ");
if ty::type_is_bot(sig.output) {
@ -383,18 +402,8 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
}
ty_infer(infer_ty) => infer_ty.to_string(),
ty_err => "[type error]".to_string(),
ty_param(ParamTy {idx: id, def_id: did, ..}) => {
let ident = match cx.ty_param_defs.borrow().find(&did.node) {
Some(def) => token::get_ident(def.ident).get().to_string(),
// This can only happen when a type mismatch error happens and
// the actual type has more type parameters than the expected one.
None => format!("<generic #{}>", id),
};
if !cx.sess.verbose() {
ident
} else {
format!("{}:{:?}", ident, did)
}
ty_param(ref param_ty) => {
param_ty.repr(cx)
}
ty_enum(did, ref substs) | ty_struct(did, ref substs) => {
let base = ty::item_path_str(cx, did);
@ -408,8 +417,8 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
let trait_def = ty::lookup_trait_def(cx, did);
let ty = parameterized(cx, base.as_slice(),
substs, &trait_def.generics);
let bound_sep = if bounds.is_empty() { "" } else { "+" };
let bound_str = bounds.repr(cx);
let bound_str = bounds.user_string(cx);
let bound_sep = if bound_str.is_empty() { "" } else { "+" };
format!("{}{}{}",
ty,
bound_sep,
@ -573,6 +582,14 @@ impl<T:Repr> Repr for Vec<T> {
}
}
impl<T:UserString> UserString for Vec<T> {
fn user_string(&self, tcx: &ctxt) -> String {
let strs: Vec<String> =
self.iter().map(|t| t.user_string(tcx)).collect();
strs.connect(", ")
}
}
impl Repr for def::Def {
fn repr(&self, _tcx: &ctxt) -> String {
format!("{:?}", *self)
@ -581,16 +598,18 @@ impl Repr for def::Def {
impl Repr for ty::TypeParameterDef {
fn repr(&self, tcx: &ctxt) -> String {
format!("TypeParameterDef({:?}, {})", self.def_id,
format!("TypeParameterDef({}, {})",
self.def_id.repr(tcx),
self.bounds.repr(tcx))
}
}
impl Repr for ty::RegionParameterDef {
fn repr(&self, _tcx: &ctxt) -> String {
format!("RegionParameterDef({}, {:?})",
fn repr(&self, tcx: &ctxt) -> String {
format!("RegionParameterDef(name={}, def_id={}, bounds={})",
token::get_name(self.name),
self.def_id)
self.def_id.repr(tcx),
self.bounds.repr(tcx))
}
}
@ -638,18 +657,31 @@ impl Repr for subst::RegionSubsts {
}
}
impl Repr for ty::BuiltinBounds {
fn repr(&self, _tcx: &ctxt) -> String {
let mut res = Vec::new();
for b in self.iter() {
res.push(match b {
ty::BoundSend => "Send".to_owned(),
ty::BoundSized => "Sized".to_owned(),
ty::BoundCopy => "Copy".to_owned(),
ty::BoundSync => "Sync".to_owned(),
});
}
res.connect("+")
}
}
impl Repr for ty::ExistentialBounds {
fn repr(&self, tcx: &ctxt) -> String {
self.user_string(tcx)
}
}
impl Repr for ty::ParamBounds {
fn repr(&self, tcx: &ctxt) -> String {
let mut res = Vec::new();
for b in self.builtin_bounds.iter() {
res.push(match b {
ty::BoundStatic => "'static".to_string(),
ty::BoundSend => "Send".to_string(),
ty::BoundSized => "Sized".to_string(),
ty::BoundCopy => "Copy".to_string(),
ty::BoundSync => "Sync".to_string(),
});
}
res.push(self.builtin_bounds.repr(tcx));
for t in self.trait_bounds.iter() {
res.push(t.repr(tcx));
}
@ -663,6 +695,15 @@ impl Repr for ty::TraitRef {
}
}
impl Repr for ty::TraitDef {
fn repr(&self, tcx: &ctxt) -> String {
format!("TraitDef(generics={}, bounds={}, trait_ref={})",
self.generics.repr(tcx),
self.bounds.repr(tcx),
self.trait_ref.repr(tcx))
}
}
impl Repr for ast::Expr {
fn repr(&self, _tcx: &ctxt) -> String {
format!("expr({}: {})", self.id, pprust::expr_to_string(self))
@ -675,12 +716,24 @@ impl Repr for ast::Path {
}
}
impl UserString for ast::Path {
fn user_string(&self, _tcx: &ctxt) -> String {
pprust::path_to_string(self)
}
}
impl Repr for ast::Item {
fn repr(&self, tcx: &ctxt) -> String {
format!("item({})", tcx.map.node_to_string(self.id))
}
}
impl Repr for ast::Lifetime {
fn repr(&self, _tcx: &ctxt) -> String {
format!("lifetime({}: {})", self.id, pprust::lifetime_to_string(self))
}
}
impl Repr for ast::Stmt {
fn repr(&self, _tcx: &ctxt) -> String {
format!("stmt({}: {})",
@ -724,11 +777,7 @@ impl Repr for ty::Region {
bound_region.repr(tcx))
}
ty::ReFree(ref fr) => {
format!("ReFree({}, {})",
fr.scope_id,
fr.bound_region.repr(tcx))
}
ty::ReFree(ref fr) => fr.repr(tcx),
ty::ReScope(id) => {
format!("ReScope({})", id)
@ -753,6 +802,20 @@ impl Repr for ty::Region {
}
}
impl UserString for ty::Region {
fn user_string(&self, tcx: &ctxt) -> String {
region_to_string(tcx, "", false, *self)
}
}
impl Repr for ty::FreeRegion {
fn repr(&self, tcx: &ctxt) -> String {
format!("ReFree({}, {})",
self.scope_id,
self.bound_region.repr(tcx))
}
}
impl Repr for ast::DefId {
fn repr(&self, tcx: &ctxt) -> String {
// Unfortunately, there seems to be no way to attempt to print
@ -833,6 +896,12 @@ impl Repr for ast::Name {
}
}
impl UserString for ast::Name {
fn user_string(&self, _tcx: &ctxt) -> String {
token::get_name(*self).get().to_string()
}
}
impl Repr for ast::Ident {
fn repr(&self, _tcx: &ctxt) -> String {
token::get_ident(*self).get().to_string()
@ -928,21 +997,14 @@ impl Repr for ty::BuiltinBound {
impl UserString for ty::BuiltinBound {
fn user_string(&self, _tcx: &ctxt) -> String {
match *self {
ty::BoundStatic => "'static".to_string(),
ty::BoundSend => "Send".to_string(),
ty::BoundSized => "Sized".to_string(),
ty::BoundCopy => "Copy".to_string(),
ty::BoundSync => "Sync".to_string(),
ty::BoundSend => "Send".to_owned(),
ty::BoundSized => "Sized".to_owned(),
ty::BoundCopy => "Copy".to_owned(),
ty::BoundSync => "Sync".to_owned(),
}
}
}
impl Repr for ty::BuiltinBounds {
fn repr(&self, tcx: &ctxt) -> String {
self.user_string(tcx)
}
}
impl Repr for Span {
fn repr(&self, tcx: &ctxt) -> String {
tcx.sess.codemap().span_to_string(*self).to_string()
@ -956,6 +1018,43 @@ impl<A:UserString> UserString for Rc<A> {
}
}
impl UserString for ty::ParamBounds {
fn user_string(&self, tcx: &ctxt) -> String {
let mut result = Vec::new();
let s = self.builtin_bounds.user_string(tcx);
if !s.is_empty() {
result.push(s);
}
for n in self.trait_bounds.iter() {
result.push(n.user_string(tcx));
}
result.connect("+")
}
}
impl UserString for ty::ExistentialBounds {
fn user_string(&self, tcx: &ctxt) -> String {
if self.builtin_bounds.contains_elem(ty::BoundSend) &&
self.region_bound == ty::ReStatic
{ // Region bound is implied by builtin bounds:
return self.builtin_bounds.repr(tcx);
}
let mut res = Vec::new();
let region_str = self.region_bound.user_string(tcx);
if !region_str.is_empty() {
res.push(region_str);
}
for bound in self.builtin_bounds.iter() {
res.push(bound.user_string(tcx));
}
res.connect("+")
}
}
impl UserString for ty::BuiltinBounds {
fn user_string(&self, tcx: &ctxt) -> String {
self.iter()
@ -1083,33 +1182,55 @@ impl<T:Repr> Repr for infer::Bounds<T> {
}
}
impl<K:Repr,V:Repr> Repr for VV<K,V> {
fn repr(&self, tcx: &ctxt) -> String {
match *self {
unify::Redirect(ref k) =>
format!("Redirect({})", k.repr(tcx)),
unify::Root(ref v, r) =>
format!("Root({}, {})", v.repr(tcx), r)
}
}
}
impl Repr for region_inference::VarValue {
fn repr(&self, tcx: &ctxt) -> String {
match *self {
infer::region_inference::NoValue =>
format!("NoValue"),
infer::region_inference::Value(r) =>
format!("Value({})", r.repr(tcx)),
infer::region_inference::ErrorValue =>
format!("ErrorValue"),
}
}
}
impl Repr for ty::ExplicitSelfCategory {
fn repr(&self, _: &ctxt) -> String {
explicit_self_category_to_str(self).to_string()
}
}
impl Repr for regionmanip::WfConstraint {
fn repr(&self, tcx: &ctxt) -> String {
match *self {
regionmanip::RegionSubRegionConstraint(_, r_a, r_b) => {
format!("RegionSubRegionConstraint({}, {})",
r_a.repr(tcx),
r_b.repr(tcx))
}
regionmanip::RegionSubParamConstraint(_, r, p) => {
format!("RegionSubParamConstraint({}, {})",
r.repr(tcx),
p.repr(tcx))
}
}
}
}
impl UserString for ParamTy {
fn user_string(&self, tcx: &ctxt) -> String {
let id = self.idx;
let did = self.def_id;
let ident = match tcx.ty_param_defs.borrow().find(&did.node) {
Some(def) => token::get_ident(def.ident).get().to_string(),
// This can only happen when a type mismatch error happens and
// the actual type has more type parameters than the expected one.
None => format!("<generic #{}>", id),
};
ident
}
}
impl Repr for ParamTy {
fn repr(&self, tcx: &ctxt) -> String {
self.user_string(tcx)
}
}
impl<A:Repr,B:Repr> Repr for (A,B) {
fn repr(&self, tcx: &ctxt) -> String {
let &(ref a, ref b) = self;
format!("({},{})", a.repr(tcx), b.repr(tcx))
}
}