Some refactoring and WIP on issue #2263.

This commit is contained in:
Lindsey Kuper 2012-05-23 14:12:40 -07:00
parent ef33c5c9bc
commit 7e70412711
5 changed files with 120 additions and 58 deletions

View File

@ -81,7 +81,7 @@ export ty_opaque_box, mk_opaque_box;
export ty_constr_arg;
export ty_float, mk_float, mk_mach_float, type_is_fp;
export ty_fn, fn_ty, mk_fn;
export ty_fn_proto, ty_fn_ret, ty_fn_ret_style;
export ty_fn_proto, ty_fn_ret, ty_fn_ret_style, tys_in_fn_ty;
export ty_int, mk_int, mk_mach_int, mk_char;
export ty_str, mk_str, type_is_str;
export ty_vec, mk_vec, type_is_vec;
@ -2090,6 +2090,11 @@ fn is_fn_ty(fty: t) -> bool {
}
}
// Returns a vec of all the input and output types of fty.
fn tys_in_fn_ty(fty: fn_ty) -> [t] {
fty.inputs.map({|a| a.ty}) + [fty.output]
}
// Just checks whether it's a fn that returns bool,
// not its purity.
fn is_pred_ty(fty: t) -> bool {

View File

@ -69,7 +69,8 @@ type parameter).
import astconv::{ast_conv, ast_ty_to_ty};
import collect::{methods}; // ccx.to_ty()
import method::{methods}; // methods for method::lookup
import regionmanip::{universally_quantify_regions_before_call,
import middle::ty::tys_in_fn_ty;
import regionmanip::{universally_quantify_from_sty,
region_of, replace_bound_regions,
collect_bound_regions_in_tys};
import rscope::*;
@ -718,7 +719,31 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let mut bot = false;
let fty = universally_quantify_regions_before_call(fcx, sp, fty);
// Replace all region parameters in the arguments and return
// type with fresh region variables.
#debug["check_call_or_bind: before universal quant., fty=%s",
fcx.ty_to_str(fty)];
// This is subtle: we expect `fty` to be a function type, which
// normally introduce a level of binding. In this case, we want to
// process the types bound by the function but not by any nested
// functions. Therefore, we match one level of structure.
let fty =
alt structure_of(fcx, sp, fty) {
sty @ ty::ty_fn(inner_fty) {
let all_tys = tys_in_fn_ty(inner_fty);
universally_quantify_from_sty(fcx, sp, all_tys, sty)
}
sty {
#debug["not a fn ty: %?", sty];
// if not a function type, we're gonna' report an error at
// some point, since the user is trying to call this thing
fty
}
};
#debug["check_call_or_bind: after universal quant., fty=%s",
fcx.ty_to_str(fty)];

View File

@ -1,7 +1,7 @@
/* Code to handle method lookups (which can be quite complex) */
import syntax::ast_map;
import regionmanip::universally_quantify_regions;
import regionmanip::universally_quantify_from_sty;
import middle::typeck::infer::{ty_and_region_var_methods};
enum lookup = {
@ -188,13 +188,12 @@ impl methods for lookup {
// Here "self" refers to the callee side...
let self_ty =
universally_quantify_regions(
self.fcx, self.expr.span, self_ty);
universally_quantify_from_sty(
self.fcx, self.expr.span, [self_ty],
ty::get(self_ty).struct);
// ... and "ty" refers to the caller side.
let ty =
universally_quantify_regions(
self.fcx, self.expr.span, self.self_ty);
let ty = self.self_ty;
// if we can assign the caller to the callee, that's a
// potential match. Collect those in the vector.

View File

@ -1,11 +1,11 @@
import middle::typeck::infer::{ty_and_region_var_methods};
import syntax::print::pprust::{expr_to_str};
// Helper functions related to manipulating region types.
// Helper for the other universally_quantify_*() routines. Extracts the bound
// regions from bound_tys and then replaces those same regions with fresh
// variables in `sty`, returning the resulting type.
// Extracts the bound regions from bound_tys and then replaces those same
// regions in `sty` with fresh region variables, returning the resulting type.
// Does not descend into fn types. This is used when deciding whether an impl
// applies at a given call site. See also universally_quantify_before_call().
fn universally_quantify_from_sty(fcx: @fn_ctxt,
span: span,
bound_tys: [ty::t],
@ -30,44 +30,6 @@ fn universally_quantify_from_sty(fcx: @fn_ctxt,
}
}
// Replaces all region parameters in the given type with region variables.
// Does not descend into fn types. This is used when deciding whether an impl
// applies at a given call site. See also universally_quantify_before_call().
fn universally_quantify_regions(fcx: @fn_ctxt,
span: span,
ty: ty::t) -> ty::t {
universally_quantify_from_sty(fcx, span, [ty], ty::get(ty).struct)
}
// Expects a function type. Replaces all region parameters in the arguments
// and return type with fresh region variables. This is used when typechecking
// function calls, bind expressions, and method calls.
fn universally_quantify_regions_before_call(fcx: @fn_ctxt,
span: span,
ty: ty::t) -> ty::t {
#debug["universally_quantify_before_call(ty=%s)",
fcx.ty_to_str(ty)];
// This is subtle: we expect `ty` to be a function type, which normally
// introduce a level of binding. In this case, we want to process the
// types bound by the function but not by any nested functions.
// Therefore, we match one level of structure.
alt structure_of(fcx, span, ty) {
sty @ ty::ty_fn(fty) {
let all_tys = fty.inputs.map({|a| a.ty}) + [fty.output];
universally_quantify_from_sty(fcx, span, all_tys, sty)
}
sty {
#debug["not a fn ty: %?", sty];
// if not a function type, we're gonna' report an error
// at some point, since the user is trying to call this thing
ty
}
}
}
fn replace_bound_regions(
tcx: ty::ctxt,
span: span,

View File

@ -147,12 +147,14 @@ import std::smallintmap::smallintmap;
import std::smallintmap::map;
import std::map::hashmap;
import middle::ty;
import middle::ty::{ty_vid, region_vid, vid};
import syntax::ast;
import middle::ty::{ty_vid, tys_in_fn_ty, region_vid, vid};
import syntax::{ast, ast_util};
import syntax::ast::{ret_style};
import util::ppaux::{ty_to_str, mt_to_str};
import result::{result, extensions, ok, err, map, map2, iter2};
import ty::type_is_bot;
import ty::{mk_fn, type_is_bot};
import check::regionmanip::{collect_bound_regions_in_tys,
replace_bound_regions};
import driver::session::session;
import util::common::{indent, indenter};
@ -453,6 +455,18 @@ impl ty_and_region_var_methods for infer_ctxt {
fn next_region_var() -> ty::region {
ret ty::re_var(self.next_region_var_id());
}
fn ty_to_str(t: ty::t) -> str {
ty_to_str(self.tcx,
self.resolve_type_vars_if_possible(t))
}
fn resolve_type_vars_if_possible(typ: ty::t) -> ty::t {
alt infer::resolve_deep(self, typ, false) {
result::ok(new_type) { ret new_type; }
result::err(_) { ret typ; }
}
}
}
impl unify_methods for infer_ctxt {
@ -1579,6 +1593,67 @@ impl of combine for sub {
}
}
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty> {
// Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions
// (issue #2263).
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
let a_isr =
collect_bound_regions_in_tys(self.tcx,
@nil,
tys_in_fn_ty(a)) {
|br|
let rvar = self.infcx().next_region_var();
#debug["Bound region %s maps to %s",
bound_region_to_str(self.tcx, br),
region_to_str(self.tcx, rvar)];
rvar
};
let a_ty = replace_bound_regions(self.tcx,
ast_util::dummy_sp(),
a_isr,
mk_fn(self.tcx, a));
#debug["a_ty: %s", self.infcx().ty_to_str(a_ty)];
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let b_isr =
collect_bound_regions_in_tys(self.tcx,
@nil,
tys_in_fn_ty(b)) {
|br| ty::re_bound(br) };
// FIXME: or maybe re_skolemized? What would that look like?
// (issue #2263)
let b_ty = replace_bound_regions(self.tcx,
ast_util::dummy_sp(),
b_isr,
mk_fn(self.tcx, b));
#debug["b_ty: %s", self.infcx().ty_to_str(b_ty)];
// Turn back into ty::fn_ty.
alt (ty::get(a_ty).struct, ty::get(b_ty).struct) {
(ty::ty_fn(a_fn_ty), ty::ty_fn(b_fn_ty)) {
// Try to compare the supertype and subtype now that they've been
// instantiated.
super_fns(self, a_fn_ty, b_fn_ty)
}
_ {
// Shouldn't happen.
self.infcx().tcx.sess.bug(
#fmt["%s: at least one of %s and %s isn't a fn_ty",
self.tag(),
self.infcx().ty_to_str(a_ty),
self.infcx().ty_to_str(b_ty)]);
}
}
}
// Traits please:
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
@ -1598,10 +1673,6 @@ impl of combine for sub {
super_args(self, a, b)
}
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty> {
super_fns(self, a, b)
}
fn substs(as: ty::substs, bs: ty::substs) -> cres<ty::substs> {
super_substs(self, as, bs)
}