Skip to content

Commit

Permalink
rewrite the method-receiver matching code
Browse files Browse the repository at this point in the history
the old code was *so terrible*.
  • Loading branch information
Ariel Ben-Yehuda committed Dec 27, 2015
1 parent 2ced149 commit efc4575
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 359 deletions.
1 change: 0 additions & 1 deletion src/librustc/middle/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,6 @@ pub struct RegionParameterDef {
impl RegionParameterDef {
pub fn to_early_bound_region(&self) -> ty::Region {
ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.def_id,
space: self.space,
index: self.index,
name: self.name,
Expand Down
1 change: 0 additions & 1 deletion src/librustc/middle/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,6 @@ pub enum Region {

#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
pub struct EarlyBoundRegion {
pub def_id: DefId,
pub space: subst::ParamSpace,
pub index: u32,
pub name: Name,
Expand Down
3 changes: 1 addition & 2 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,7 @@ impl fmt::Debug for ty::Region {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ty::ReEarlyBound(ref data) => {
write!(f, "ReEarlyBound({:?}, {:?}, {}, {})",
data.def_id,
write!(f, "ReEarlyBound({:?}, {}, {})",
data.space,
data.index,
data.name)
Expand Down
1 change: 0 additions & 1 deletion src/librustc_driver/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
-> ty::Region {
let name = token::intern(name);
ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.infcx.tcx.map.local_def_id(ast::DUMMY_NODE_ID),
space: space,
index: index,
name: name,
Expand Down
2 changes: 0 additions & 2 deletions src/librustc_metadata/tydecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,12 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
}
'B' => {
assert_eq!(self.next(), '[');
let def_id = self.parse_def();
let space = self.parse_param_space();
assert_eq!(self.next(), '|');
let index = self.parse_u32();
assert_eq!(self.next(), '|');
let name = token::intern(&self.parse_str(']'));
ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: def_id,
space: space,
index: index,
name: name
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,7 @@ pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) {
mywrite!(w, "]");
}
ty::ReEarlyBound(ref data) => {
mywrite!(w, "B[{}|{}|{}|{}]",
(cx.ds)(data.def_id),
mywrite!(w, "B[{}|{}|{}]",
data.space.to_uint(),
data.index,
data.name);
Expand Down
103 changes: 34 additions & 69 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use middle::resolve_lifetime as rl;
use middle::privacy::{AllPublic, LastMod};
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
use middle::traits;
use middle::ty::{self, RegionEscape, Ty, ToPredicate, HasTypeFlags};
use middle::ty::{self, Ty, ToPredicate, HasTypeFlags};
use middle::ty::wf::object_region_bounds;
use require_c_abi_if_variadic;
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope,
Expand Down Expand Up @@ -169,10 +169,8 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime)
ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id), lifetime.name))
}

Some(&rl::DefEarlyBoundRegion(space, index, id)) => {
let def_id = tcx.map.local_def_id(id);
Some(&rl::DefEarlyBoundRegion(space, index, _)) => {
ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: def_id,
space: space,
index: index,
name: lifetime.name
Expand Down Expand Up @@ -1797,75 +1795,31 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>,
// lifetime elision, we can determine it in two ways. First (determined
// here), if self is by-reference, then the implied output region is the
// region of the self parameter.
let mut explicit_self_category_result = None;
let (self_ty, implied_output_region) = match opt_self_info {
let (self_ty, explicit_self_category) = match opt_self_info {
None => (None, None),
Some(self_info) => {
// This type comes from an impl or trait; no late-bound
// regions should be present.
assert!(!self_info.untransformed_self_ty.has_escaping_regions());

// Figure out and record the explicit self category.
let explicit_self_category =
determine_explicit_self_category(this, &rb, &self_info);
explicit_self_category_result = Some(explicit_self_category);
match explicit_self_category {
ty::StaticExplicitSelfCategory => {
(None, None)
}
ty::ByValueExplicitSelfCategory => {
(Some(self_info.untransformed_self_ty), None)
}
ty::ByReferenceExplicitSelfCategory(region, mutability) => {
(Some(this.tcx().mk_ref(
this.tcx().mk_region(region),
ty::TypeAndMut {
ty: self_info.untransformed_self_ty,
mutbl: mutability
})),
Some(region))
}
ty::ByBoxExplicitSelfCategory => {
(Some(this.tcx().mk_box(self_info.untransformed_self_ty)), None)
}
}
}
Some(self_info) => determine_self_type(this, &rb, self_info)
};

// HACK(eddyb) replace the fake self type in the AST with the actual type.
let input_params = if self_ty.is_some() {
let arg_params = if self_ty.is_some() {
&decl.inputs[1..]
} else {
&decl.inputs[..]
};
let input_tys = input_params.iter().map(|a| ty_of_arg(this, &rb, a, None));
let input_pats: Vec<String> = input_params.iter()
.map(|a| pprust::pat_to_string(&*a.pat))
.collect();
let self_and_input_tys: Vec<Ty> =
self_ty.into_iter().chain(input_tys).collect();

let arg_tys: Vec<Ty> =
arg_params.iter().map(|a| ty_of_arg(this, &rb, a, None)).collect();
let arg_pats: Vec<String> =
arg_params.iter().map(|a| pprust::pat_to_string(&*a.pat)).collect();

// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
let implied_output_region = match implied_output_region {
Some(r) => Ok(r),
None => {
let input_tys = if self_ty.is_some() {
// Skip the first argument if `self` is present.
&self_and_input_tys[1..]
} else {
&self_and_input_tys[..]
};

find_implied_output_region(this.tcx(), input_tys, input_pats)
}
let implied_output_region = match explicit_self_category {
Some(ty::ByReferenceExplicitSelfCategory(region, _)) => Ok(region),
_ => find_implied_output_region(this.tcx(), &arg_tys, arg_pats)
};

let output_ty = match decl.output {
hir::Return(ref output) if output.node == hir::TyInfer =>
ty::FnConverging(this.ty_infer(None, None, None, output.span)),
hir::Return(ref output) =>
ty::FnConverging(convert_ty_with_lifetime_elision(this,
implied_output_region,
Expand All @@ -1878,28 +1832,37 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>,
unsafety: unsafety,
abi: abi,
sig: ty::Binder(ty::FnSig {
inputs: self_and_input_tys,
inputs: self_ty.into_iter().chain(arg_tys).collect(),
output: output_ty,
variadic: decl.variadic
}),
}, explicit_self_category_result)
}, explicit_self_category)
}

fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
rscope: &RegionScope,
self_info: &SelfInfo<'a, 'tcx>)
-> ty::ExplicitSelfCategory
fn determine_self_type<'a, 'tcx>(this: &AstConv<'tcx>,
rscope: &RegionScope,
self_info: SelfInfo<'a, 'tcx>)
-> (Option<Ty<'tcx>>, Option<ty::ExplicitSelfCategory>)
{
let self_ty = self_info.untransformed_self_ty;
return match self_info.explicit_self.node {
hir::SelfStatic => ty::StaticExplicitSelfCategory,
hir::SelfValue(_) => ty::ByValueExplicitSelfCategory,
hir::SelfStatic => (None, Some(ty::StaticExplicitSelfCategory)),
hir::SelfValue(_) => {
(Some(self_ty), Some(ty::ByValueExplicitSelfCategory))
}
hir::SelfRegion(ref lifetime, mutability, _) => {
let region =
opt_ast_region_to_region(this,
rscope,
self_info.explicit_self.span,
lifetime);
ty::ByReferenceExplicitSelfCategory(region, mutability)
(Some(this.tcx().mk_ref(
this.tcx().mk_region(region),
ty::TypeAndMut {
ty: self_ty,
mutbl: mutability
})),
Some(ty::ByReferenceExplicitSelfCategory(region, mutability)))
}
hir::SelfExplicit(ref ast_type, _) => {
let explicit_type = ast_ty_to_ty(this, rscope, &**ast_type);
Expand Down Expand Up @@ -1944,15 +1907,17 @@ fn determine_explicit_self_category<'a, 'tcx>(this: &AstConv<'tcx>,
impl_modifiers,
method_modifiers);

if impl_modifiers >= method_modifiers {
let category = if impl_modifiers >= method_modifiers {
ty::ByValueExplicitSelfCategory
} else {
match explicit_type.sty {
ty::TyRef(r, mt) => ty::ByReferenceExplicitSelfCategory(*r, mt.mutbl),
ty::TyBox(_) => ty::ByBoxExplicitSelfCategory,
_ => ty::ByValueExplicitSelfCategory,
}
}
};

(Some(explicit_type), Some(category))
}
};

Expand Down
50 changes: 47 additions & 3 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {

let item = fcx.tcx().impl_or_trait_item(fcx.tcx().map.local_def_id(item_id));

let mut implied_bounds = match item.container() {
ty::TraitContainer(_) => vec![],
ty::ImplContainer(def_id) => impl_implied_bounds(fcx, def_id, span)
let (mut implied_bounds, self_ty) = match item.container() {
ty::TraitContainer(_) => (vec![], fcx.tcx().mk_self_type()),
ty::ImplContainer(def_id) => (impl_implied_bounds(fcx, def_id, span),
fcx.tcx().lookup_item_type(def_id).ty)
};

match item {
Expand All @@ -152,6 +153,8 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
let predicates = fcx.instantiate_bounds(span, free_substs, &method.predicates);
this.check_fn_or_method(fcx, span, &method_ty, &predicates,
free_id_outlive, &mut implied_bounds);
this.check_method_receiver(fcx, span, &method,
free_id_outlive, self_ty);
}
ty::TypeTraitItem(assoc_type) => {
if let Some(ref ty) = assoc_type.ty {
Expand Down Expand Up @@ -377,6 +380,47 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
self.check_where_clauses(fcx, span, predicates);
}

fn check_method_receiver<'fcx>(&mut self,
fcx: &FnCtxt<'fcx,'tcx>,
span: Span,
method: &ty::Method<'tcx>,
free_id_outlive: CodeExtent,
self_ty: ty::Ty<'tcx>)
{
// check that the type of the method's receiver matches the
// method's first parameter.

let free_substs = &fcx.inh.infcx.parameter_environment.free_substs;
let fty = fcx.instantiate_type_scheme(span, free_substs, &method.fty);
let sig = fcx.tcx().liberate_late_bound_regions(free_id_outlive, &fty.sig);

debug!("check_method_receiver({:?},cat={:?},self_ty={:?},sig={:?})",
method.name, method.explicit_self, self_ty, sig);

let rcvr_ty = match method.explicit_self {
ty::StaticExplicitSelfCategory => return,
ty::ByValueExplicitSelfCategory => self_ty,
ty::ByReferenceExplicitSelfCategory(region, mutability) => {
fcx.tcx().mk_ref(fcx.tcx().mk_region(region), ty::TypeAndMut {
ty: self_ty,
mutbl: mutability
})
}
ty::ByBoxExplicitSelfCategory => fcx.tcx().mk_box(self_ty)
};
let rcvr_ty = fcx.instantiate_type_scheme(span, free_substs, &rcvr_ty);
let rcvr_ty = fcx.tcx().liberate_late_bound_regions(free_id_outlive,
&ty::Binder(rcvr_ty));

debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty);

let _ = ::require_same_types(
fcx.tcx(), Some(fcx.infcx()), false, span,
sig.inputs[0], rcvr_ty,
|| "mismatched method receiver".to_owned()
);
}

fn check_variances_for_type_defn(&self,
item: &hir::Item,
ast_generics: &hir::Generics)
Expand Down
Loading

0 comments on commit efc4575

Please sign in to comment.