Implement generalized object and type parameter bounds (Fixes #16462)
This commit is contained in:
parent
3ee047ae1f
commit
1b487a8906
272 changed files with 6783 additions and 3154 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue