Skip to content

Commit

Permalink
Some refactoring and WIP on issue rust-lang#2263.
Browse files Browse the repository at this point in the history
  • Loading branch information
lkuper committed May 24, 2012
1 parent ef33c5c commit 7e70412
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 58 deletions.
7 changes: 6 additions & 1 deletion src/rustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
29 changes: 27 additions & 2 deletions src/rustc/middle/typeck/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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)];

Expand Down
11 changes: 5 additions & 6 deletions src/rustc/middle/typeck/check/method.rs
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -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.
Expand Down
46 changes: 4 additions & 42 deletions src/rustc/middle/typeck/check/regionmanip.rs
Original file line number Diff line number Diff line change
@@ -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],
Expand All @@ -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,
Expand Down
85 changes: 78 additions & 7 deletions src/rustc/middle/typeck/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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> {
Expand All @@ -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)
}
Expand Down

0 comments on commit 7e70412

Please sign in to comment.