提交 ab34864a 编写于 作者: D Daniel Micay

Merge pull request #7187 from nikomatsakis/issue-3238-defer-reasoning-about-regions

Defer reasoning about regions until after regionck
......@@ -15,6 +15,9 @@
the region hierarchy. The second pass infers which types must be
region parameterized.
Most of the documentation on regions can be found in
`middle/typeck/infer/region_inference.rs`
*/
......
......@@ -15,6 +15,7 @@
use middle::typeck::check::{check_block, check_expr_has_type, FnCtxt};
use middle::typeck::check::{instantiate_path, lookup_def};
use middle::typeck::check::{structure_of, valid_range_bounds};
use middle::typeck::infer;
use middle::typeck::require_same_types;
use std::hashmap::{HashMap, HashSet};
......@@ -29,8 +30,8 @@ pub fn check_match(fcx: @mut FnCtxt,
arms: &[ast::arm]) {
let tcx = fcx.ccx.tcx;
let pattern_ty = fcx.infcx().next_ty_var();
check_expr_has_type(fcx, discrim, pattern_ty);
let discrim_ty = fcx.infcx().next_ty_var();
check_expr_has_type(fcx, discrim, discrim_ty);
// Typecheck the patterns first, so that we get types for all the
// bindings.
......@@ -38,17 +39,22 @@ pub fn check_match(fcx: @mut FnCtxt,
let pcx = pat_ctxt {
fcx: fcx,
map: pat_id_map(tcx.def_map, arm.pats[0]),
match_region: ty::re_scope(expr.id),
block_region: ty::re_scope(arm.body.node.id)
};
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, pattern_ty);}
for arm.pats.iter().advance |p| { check_pat(&pcx, *p, discrim_ty);}
}
// The result of the match is the common supertype of all the
// arms. Start out the value as bottom, since it's the, well,
// bottom the type lattice, and we'll be moving up the lattice as
// we process each arm. (Note that any match with 0 arms is matching
// on any empty type and is therefore unreachable; should the flow
// of execution reach it, we will fail, so bottom is an appropriate
// type in that case)
let mut result_ty = ty::mk_bot();
// Now typecheck the blocks.
let mut result_ty = fcx.infcx().next_ty_var();
let mut arm_non_bot = false;
let mut saw_err = false;
let mut saw_err = ty::type_is_error(discrim_ty);
for arms.iter().advance |arm| {
let mut guard_err = false;
let mut guard_bot = false;
......@@ -75,26 +81,28 @@ pub fn check_match(fcx: @mut FnCtxt,
else if guard_bot {
fcx.write_bot(arm.body.node.id);
}
else if !ty::type_is_bot(bty) {
arm_non_bot = true; // If the match *may* evaluate to a non-_|_
// expr, the whole thing is non-_|_
}
demand::suptype(fcx, arm.body.span, result_ty, bty);
result_ty =
infer::common_supertype(
fcx.infcx(),
infer::MatchExpression(expr.span),
true, // result_ty is "expected" here
result_ty,
bty);
}
if saw_err {
result_ty = ty::mk_err();
}
else if !arm_non_bot {
} else if ty::type_is_bot(discrim_ty) {
result_ty = ty::mk_bot();
}
fcx.write_ty(expr.id, result_ty);
}
pub struct pat_ctxt {
fcx: @mut FnCtxt,
map: PatIdMap,
match_region: ty::Region, // Region for the match as a whole
block_region: ty::Region, // Region for the block of the arm
}
pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
......@@ -442,8 +450,8 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
// then the type of x is &M T where M is the mutability
// and T is the expected type
let region_var =
fcx.infcx().next_region_var_with_lb(
pat.span, pcx.block_region);
fcx.infcx().next_region_var(
infer::PatternRegion(pat.span));
let mt = ty::mt {ty: expected, mutbl: mutbl};
let region_ty = ty::mk_rptr(tcx, region_var, mt);
demand::eqtype(fcx, pat.span, region_ty, typ);
......@@ -544,9 +552,8 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
ast::pat_vec(ref before, slice, ref after) => {
let default_region_var =
fcx.infcx().next_region_var_with_lb(
pat.span, pcx.block_region
);
fcx.infcx().next_region_var(
infer::PatternRegion(pat.span));
let (elt_type, region_var) = match structure_of(
fcx, pat.span, expected
......@@ -651,3 +658,4 @@ pub fn check_pointer_pat(pcx: &pat_ctxt,
#[deriving(Eq)]
enum PointerKind { Managed, Send, Borrowed }
......@@ -35,7 +35,7 @@ pub fn suptype_with_fn(fcx: @mut FnCtxt,
ty_a: ty::t, ty_b: ty::t,
handle_err: &fn(span, ty::t, ty::t, &ty::type_err)) {
// n.b.: order of actual, expected is reversed
match infer::mk_subty(fcx.infcx(), b_is_expected, sp,
match infer::mk_subty(fcx.infcx(), b_is_expected, infer::Misc(sp),
ty_b, ty_a) {
result::Ok(()) => { /* ok */ }
result::Err(ref err) => {
......@@ -45,7 +45,7 @@ pub fn suptype_with_fn(fcx: @mut FnCtxt,
}
pub fn eqtype(fcx: @mut FnCtxt, sp: span, expected: ty::t, actual: ty::t) {
match infer::mk_eqty(fcx.infcx(), false, sp, actual, expected) {
match infer::mk_eqty(fcx.infcx(), false, infer::Misc(sp), actual, expected) {
Ok(()) => { /* ok */ }
Err(ref err) => {
fcx.report_mismatched_types(sp, expected, actual, err);
......
......@@ -619,14 +619,18 @@ pub fn consider_reborrow(&self,
autoref: None}))
}
ty::ty_rptr(_, self_mt) => {
let region = self.infcx().next_region_var_nb(self.expr.span);
let region =
self.infcx().next_region_var(
infer::Autoref(self.expr.span));
(ty::mk_rptr(tcx, region, self_mt),
ty::AutoDerefRef(ty::AutoDerefRef {
autoderefs: autoderefs+1,
autoref: Some(ty::AutoPtr(region, self_mt.mutbl))}))
}
ty::ty_evec(self_mt, vstore_slice(_)) => {
let region = self.infcx().next_region_var_nb(self.expr.span);
let region =
self.infcx().next_region_var(
infer::Autoref(self.expr.span));
(ty::mk_evec(tcx, self_mt, vstore_slice(region)),
ty::AutoDerefRef(ty::AutoDerefRef {
autoderefs: autoderefs,
......@@ -758,7 +762,9 @@ pub fn search_for_some_kind_of_autorefd_method(
-> Option<method_map_entry> {
// This is hokey. We should have mutability inference as a
// variable. But for now, try &const, then &, then &mut:
let region = self.infcx().next_region_var_nb(self.expr.span);
let region =
self.infcx().next_region_var(
infer::Autoref(self.expr.span));
for mutbls.iter().advance |mutbl| {
let autoref_ty = mk_autoref_ty(*mutbl, region);
match self.search_for_method(autoref_ty) {
......@@ -970,7 +976,8 @@ pub fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate)
let (_, opt_transformed_self_ty, fn_sig) =
replace_bound_regions_in_fn_sig(
tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig,
|_br| self.fcx.infcx().next_region_var_nb(self.expr.span));
|br| self.fcx.infcx().next_region_var(
infer::BoundRegionInFnCall(self.expr.span, br)));
let transformed_self_ty = opt_transformed_self_ty.get();
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty});
debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty));
......@@ -982,7 +989,7 @@ pub fn confirm_candidate(&self, rcvr_ty: ty::t, candidate: &Candidate)
// variables to unify etc). Since we checked beforehand, and
// nothing has changed in the meantime, this unification
// should never fail.
match self.fcx.mk_subty(false, self.self_expr.span,
match self.fcx.mk_subty(false, infer::Misc(self.self_expr.span),
rcvr_ty, transformed_self_ty) {
result::Ok(_) => (),
result::Err(_) => {
......
......@@ -467,8 +467,6 @@ fn gather_locals(fcx: @mut FnCtxt,
let pcx = pat_ctxt {
fcx: fcx,
map: pat_id_map(tcx.def_map, input.pat),
match_region: region,
block_region: region,
};
_match::check_pat(&pcx, input.pat, *arg_ty);
}
......@@ -686,9 +684,14 @@ pub fn search_in_scope_regions(&self,
result::Ok(self.block_region())
} else {
result::Err(RegionError {
msg: fmt!("named region `%s` not in scope here",
bound_region_ptr_to_str(self.tcx(), br)),
replacement: self.infcx().next_region_var_nb(span)
msg: {
fmt!("named region `%s` not in scope here",
bound_region_ptr_to_str(self.tcx(), br))
},
replacement: {
self.infcx().next_region_var(
infer::BoundRegionError(span))
}
})
}
}
......@@ -698,7 +701,7 @@ pub fn search_in_scope_regions(&self,
impl region_scope for FnCtxt {
fn anon_region(&self, span: span) -> Result<ty::Region, RegionError> {
result::Ok(self.infcx().next_region_var_nb(span))
result::Ok(self.infcx().next_region_var(infer::MiscVariable(span)))
}
fn self_region(&self, span: span) -> Result<ty::Region, RegionError> {
self.search_in_scope_regions(span, ty::br_self)
......@@ -845,11 +848,11 @@ pub fn opt_node_ty_substs(&self,
pub fn mk_subty(&self,
a_is_expected: bool,
span: span,
origin: infer::TypeOrigin,
sub: ty::t,
sup: ty::t)
-> Result<(), ty::type_err> {
infer::mk_subty(self.infcx(), a_is_expected, span, sub, sup)
infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup)
}
pub fn can_mk_subty(&self, sub: ty::t, sup: ty::t)
......@@ -857,9 +860,16 @@ pub fn can_mk_subty(&self, sub: ty::t, sup: ty::t)
infer::can_mk_subty(self.infcx(), sub, sup)
}
pub fn mk_assignty(&self, expr: @ast::expr, sub: ty::t, sup: ty::t)
pub fn mk_assignty(&self,
expr: @ast::expr,
sub: ty::t,
sup: ty::t)
-> Result<(), ty::type_err> {
match infer::mk_coercety(self.infcx(), false, expr.span, sub, sup) {
match infer::mk_coercety(self.infcx(),
false,
infer::ExprAssignable(expr),
sub,
sup) {
Ok(None) => result::Ok(()),
Err(ref e) => result::Err((*e)),
Ok(Some(adjustment)) => {
......@@ -876,20 +886,19 @@ pub fn can_mk_assignty(&self, sub: ty::t, sup: ty::t)
pub fn mk_eqty(&self,
a_is_expected: bool,
span: span,
origin: infer::TypeOrigin,
sub: ty::t,
sup: ty::t)
-> Result<(), ty::type_err> {
infer::mk_eqty(self.infcx(), a_is_expected, span, sub, sup)
infer::mk_eqty(self.infcx(), a_is_expected, origin, sub, sup)
}
pub fn mk_subr(&self,
a_is_expected: bool,
span: span,
origin: infer::SubregionOrigin,
sub: ty::Region,
sup: ty::Region)
-> Result<(), ty::type_err> {
infer::mk_subr(self.infcx(), a_is_expected, span, sub, sup)
sup: ty::Region) {
infer::mk_subr(self.infcx(), a_is_expected, origin, sub, sup)
}
pub fn with_region_lb<R>(@mut self, lb: ast::node_id, f: &fn() -> R)
......@@ -905,7 +914,9 @@ pub fn region_var_if_parameterized(&self,
rp: Option<ty::region_variance>,
span: span)
-> Option<ty::Region> {
rp.map(|_rp| self.infcx().next_region_var_nb(span))
rp.map(
|_| self.infcx().next_region_var(
infer::BoundRegionInTypeOrImpl(span)))
}
pub fn type_error_message(&self,
......@@ -1089,7 +1100,8 @@ pub fn impl_self_ty(vcx: &VtableContext,
};
let self_r = if region_param.is_some() {
Some(vcx.infcx.next_region_var_nb(location_info.span))
Some(vcx.infcx.next_region_var(
infer::BoundRegionInTypeOrImpl(location_info.span)))
} else {
None
};
......@@ -1352,7 +1364,8 @@ fn check_call(fcx: @mut FnCtxt,
let (_, _, fn_sig) =
replace_bound_regions_in_fn_sig(
fcx.tcx(), @Nil, None, &fn_sig,
|_br| fcx.infcx().next_region_var_nb(call_expr.span));
|br| fcx.infcx().next_region_var(
infer::BoundRegionInFnCall(call_expr.span, br)));
// Call the generic checker.
check_argument_types(fcx, call_expr.span, fn_sig.inputs, f,
......@@ -1423,27 +1436,42 @@ fn check_method_call(fcx: @mut FnCtxt,
// A generic function for checking the then and else in an if
// or if-check
fn check_then_else(fcx: @mut FnCtxt,
thn: &ast::blk,
elsopt: Option<@ast::expr>,
cond_expr: @ast::expr,
then_blk: &ast::blk,
opt_else_expr: Option<@ast::expr>,
id: ast::node_id,
_sp: span) {
let if_t =
match elsopt {
Some(els) => {
let if_t = fcx.infcx().next_ty_var();
check_block(fcx, thn);
let thn_t = fcx.node_ty(thn.node.id);
demand::suptype(fcx, thn.span, if_t, thn_t);
check_expr_has_type(fcx, els, if_t);
if_t
}
None => {
check_block_no_value(fcx, thn);
ty::mk_nil()
}
};
sp: span,
expected: Option<ty::t>) {
check_expr_has_type(fcx, cond_expr, ty::mk_bool());
let branches_ty = match opt_else_expr {
Some(else_expr) => {
check_block_with_expected(fcx, then_blk, expected);
let then_ty = fcx.node_ty(then_blk.node.id);
check_expr_with_opt_hint(fcx, else_expr, expected);
let else_ty = fcx.expr_ty(else_expr);
infer::common_supertype(fcx.infcx(),
infer::IfExpression(sp),
true,
then_ty,
else_ty)
}
None => {
check_block_no_value(fcx, then_blk);
ty::mk_nil()
}
};
let cond_ty = fcx.expr_ty(cond_expr);
let if_ty = if ty::type_is_error(cond_ty) {
ty::mk_err()
} else if ty::type_is_bot(cond_ty) {
ty::mk_bot()
} else {
branches_ty
};
fcx.write_ty(id, if_t);
fcx.write_ty(id, if_ty);
}
fn lookup_op_method(fcx: @mut FnCtxt,
......@@ -2085,7 +2113,7 @@ fn check_loop_body(fcx: @mut FnCtxt,
let expected_sty = unpack_expected(fcx, expected, |x| Some(copy *x));
let inner_ty = match expected_sty {
Some(ty::ty_closure(ref fty)) => {
match fcx.mk_subty(false, expr.span,
match fcx.mk_subty(false, infer::Misc(expr.span),
fty.sig.output, ty::mk_bool()) {
result::Ok(_) => {
ty::mk_closure(tcx, ty::ClosureTy {
......@@ -2395,7 +2423,8 @@ fn check_loop_body(fcx: @mut FnCtxt,
// Finally, borrowck is charged with guaranteeing that the
// value whose address was taken can actually be made to live
// as long as it needs to live.
let region = fcx.infcx().next_region_var_nb(expr.span);
let region = fcx.infcx().next_region_var(
infer::AddrOfRegion(expr.span));
let tm = ty::mt { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
let oprnd_t = if ty::type_is_error(tm.ty) {
......@@ -2437,7 +2466,7 @@ fn check_loop_body(fcx: @mut FnCtxt,
Some(t) => t, None => fcx.ret_ty
};
match expr_opt {
None => match fcx.mk_eqty(false, expr.span,
None => match fcx.mk_eqty(false, infer::Misc(expr.span),
ret_ty, ty::mk_nil()) {
result::Ok(_) => { /* fall through */ }
result::Err(_) => {
......@@ -2487,25 +2516,9 @@ fn check_loop_body(fcx: @mut FnCtxt,
fcx.write_nil(id);
}
}
ast::expr_if(cond, ref thn, elsopt) => {
check_expr_has_type(fcx, cond, ty::mk_bool());
check_then_else(fcx, thn, elsopt, id, expr.span);
let cond_ty = fcx.expr_ty(cond);
let then_ty = fcx.node_ty(thn.node.id);
let else_is_bot = elsopt.map_default(false, |els| {
ty::type_is_bot(fcx.expr_ty(*els))});
if ty::type_is_error(cond_ty) || ty::type_is_error(then_ty) {
fcx.write_error(id);
}
else if elsopt.map_default(false, |els| {
ty::type_is_error(fcx.expr_ty(*els)) }) {
fcx.write_error(id);
}
else if ty::type_is_bot(cond_ty) ||
(ty::type_is_bot(then_ty) && else_is_bot) {
fcx.write_bot(id);
}
// Other cases were handled by check_then_else
ast::expr_if(cond, ref then_blk, opt_else_expr) => {
check_then_else(fcx, cond, then_blk, opt_else_expr,
id, expr.span, expected);
}
ast::expr_while(cond, ref body) => {
check_expr_has_type(fcx, cond, ty::mk_bool());
......@@ -2533,30 +2546,6 @@ fn check_loop_body(fcx: @mut FnCtxt,
}
ast::expr_match(discrim, ref arms) => {
_match::check_match(fcx, expr, discrim, *arms);
let discrim_ty = fcx.expr_ty(discrim);
let arm_tys = arms.map(|a| fcx.node_ty(a.body.node.id));
if ty::type_is_error(discrim_ty) ||
arm_tys.iter().any_(|t| ty::type_is_error(*t)) {
fcx.write_error(id);
}
// keep in mind that `all` returns true in the empty vec case,
// which is what we want
else if ty::type_is_bot(discrim_ty) ||
arm_tys.iter().all(|t| ty::type_is_bot(*t)) {
fcx.write_bot(id);
}
else {
// Find the first non-_|_ arm.
// We know there's at least one because we already checked
// for n=0 as well as all arms being _|_ in the previous
// `if`.
for arm_tys.iter().advance |arm_ty| {
if !ty::type_is_bot(*arm_ty) {
fcx.write_ty(id, *arm_ty);
break;
}
}
}
}
ast::expr_fn_block(ref decl, ref body) => {
check_expr_fn(fcx, expr, None,
......@@ -2686,7 +2675,7 @@ fn types_compatible(fcx: @mut FnCtxt, sp: span,
let el = ty::sequence_element_type(fcx.tcx(),
t1);
infer::mk_eqty(fcx.infcx(), false,
sp, el, t2).is_ok()
infer::Misc(sp), el, t2).is_ok()
}
}
......@@ -2907,8 +2896,6 @@ pub fn check_decl_local(fcx: @mut FnCtxt, local: @ast::local) {
let pcx = pat_ctxt {
fcx: fcx,
map: pat_id_map(tcx.def_map, local.node.pat),
match_region: region,
block_region: region,
};
_match::check_pat(&pcx, local.node.pat, t);
let pat_ty = fcx.node_ty(local.node.pat.id);
......@@ -3412,7 +3399,7 @@ pub fn ast_expr_vstore_to_vstore(fcx: @mut FnCtxt,
ast::expr_vstore_uniq => ty::vstore_uniq,
ast::expr_vstore_box | ast::expr_vstore_mut_box => ty::vstore_box,
ast::expr_vstore_slice | ast::expr_vstore_mut_slice => {
let r = fcx.infcx().next_region_var_nb(e.span);
let r = fcx.infcx().next_region_var(infer::AddrOfSlice(e.span));
ty::vstore_slice(r)
}
}
......
......@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*
/*!
The region check is a final pass that runs over the AST after we have
inferred the type constraints but before we have actually finalized
......@@ -35,7 +35,9 @@
use middle::typeck::check::regionmanip::relate_nested_regions;
use middle::typeck::infer::resolve_and_force_all_but_regions;
use middle::typeck::infer::resolve_type;
use util::ppaux::{note_and_explain_region, ty_to_str, region_to_str};
use middle::typeck::infer;
use util::ppaux::{note_and_explain_region, ty_to_str,
region_to_str};
use middle::pat_util;
use std::result;
......@@ -224,7 +226,9 @@ fn constrain_bindings_in_pat(pat: @ast::pat, rcx: @mut Rcx) {
// variable's type enclose at least the variable's scope.
let encl_region = tcx.region_maps.encl_region(id);
constrain_regions_in_type_of_node(rcx, id, encl_region, span);
constrain_regions_in_type_of_node(
rcx, id, encl_region,
infer::BindingTypeIsNotValidAtDecl(span));
}
}
......@@ -298,7 +302,8 @@ fn visit_expr(expr: @ast::expr, (rcx, v): (@mut Rcx, rvt)) {
//
// FIXME(#6268) remove to support nested method calls
constrain_regions_in_type_of_node(
rcx, expr.id, ty::re_scope(expr.id), expr.span);
rcx, expr.id, ty::re_scope(expr.id),
infer::AutoBorrow(expr.span));
}
}
_ => {}
......@@ -361,8 +366,11 @@ fn visit_expr(expr: @ast::expr, (rcx, v): (@mut Rcx, rvt)) {
match ty::get(target_ty).sty {
ty::ty_trait(_, _, ty::RegionTraitStore(trait_region), _, _) => {
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, trait_region,
expr.span, source_ty);
constrain_regions_in_type(
rcx,
trait_region,
infer::RelateObjectBound(expr.span),
source_ty);
}
_ => ()
}
......@@ -379,7 +387,8 @@ fn visit_expr(expr: @ast::expr, (rcx, v): (@mut Rcx, rvt)) {
//
// FIXME(#6268) nested method calls requires that this rule change
let ty0 = rcx.resolve_node_type(expr.id);
constrain_regions_in_type(rcx, ty::re_scope(expr.id), expr.span, ty0);
constrain_regions_in_type(rcx, ty::re_scope(expr.id),
infer::AddrOf(expr.span), ty0);
}
ast::expr_match(discr, ref arms) => {
......@@ -418,20 +427,8 @@ fn constrain_callee(rcx: @mut Rcx,
match ty::get(callee_ty).sty {
ty::ty_bare_fn(*) => { }
ty::ty_closure(ref closure_ty) => {
match rcx.fcx.mk_subr(true, callee_expr.span,
call_region, closure_ty.region) {
result::Err(_) => {
tcx.sess.span_err(
callee_expr.span,
fmt!("cannot invoke closure outside of its lifetime"));
note_and_explain_region(
tcx,
"the closure is only valid for ",
closure_ty.region,
"");
}
result::Ok(_) => {}
}
rcx.fcx.mk_subr(true, infer::InvokeClosure(callee_expr.span),
call_region, closure_ty.region);
}
_ => {
// this should not happen, but it does if the program is
......@@ -479,7 +476,8 @@ fn constrain_call(rcx: @mut Rcx,
// ensure that any regions appearing in the argument type are
// valid for at least the lifetime of the function:
constrain_regions_in_type_of_node(
rcx, arg_expr.id, callee_region, arg_expr.span);
rcx, arg_expr.id, callee_region,
infer::CallArg(arg_expr.span));
// unfortunately, there are two means of taking implicit
// references, and we need to propagate constraints as a
......@@ -493,7 +491,7 @@ fn constrain_call(rcx: @mut Rcx,
// as loop above, but for receiver
for receiver.iter().advance |&r| {
constrain_regions_in_type_of_node(
rcx, r.id, callee_region, r.span);
rcx, r.id, callee_region, infer::CallRcvr(r.span));
if implicitly_ref_args {
guarantor::for_by_ref(rcx, r, callee_scope);
}
......@@ -502,7 +500,8 @@ fn constrain_call(rcx: @mut Rcx,
// constrain regions that may appear in the return type to be
// valid for the function call:
constrain_regions_in_type(
rcx, callee_region, call_expr.span, fn_sig.output);
rcx, callee_region, infer::CallReturn(call_expr.span),
fn_sig.output);
}
fn constrain_derefs(rcx: @mut Rcx,
......@@ -545,20 +544,8 @@ pub fn mk_subregion_due_to_derefence(rcx: @mut Rcx,
deref_span: span,
minimum_lifetime: ty::Region,
maximum_lifetime: ty::Region) {
match rcx.fcx.mk_subr(true, deref_span,
minimum_lifetime, maximum_lifetime) {
result::Ok(*) => {}
result::Err(*) => {
rcx.tcx().sess.span_err(
deref_span,
fmt!("dereference of reference outside its lifetime"));
note_and_explain_region(
rcx.tcx(),
"the reference is only valid for ",
maximum_lifetime,
"");
}
}
rcx.fcx.mk_subr(true, infer::DerefPointer(deref_span),
minimum_lifetime, maximum_lifetime)
}
......@@ -581,19 +568,8 @@ fn constrain_index(rcx: @mut Rcx,
match ty::get(indexed_ty).sty {
ty::ty_estr(ty::vstore_slice(r_ptr)) |
ty::ty_evec(_, ty::vstore_slice(r_ptr)) => {
match rcx.fcx.mk_subr(true, index_expr.span, r_index_expr, r_ptr) {
result::Ok(*) => {}
result::Err(*) => {
tcx.sess.span_err(
index_expr.span,
fmt!("index of slice outside its lifetime"));
note_and_explain_region(
tcx,
"the slice is only valid for ",
r_ptr,
"");
}
}
rcx.fcx.mk_subr(true, infer::IndexSlice(index_expr.span),
r_index_expr, r_ptr);
}
_ => {}
......@@ -616,25 +592,8 @@ fn constrain_free_variables(rcx: @mut Rcx,
let def = freevar.def;
let en_region = encl_region_of_def(rcx.fcx, def);
debug!("en_region = %s", en_region.repr(tcx));
match rcx.fcx.mk_subr(true, freevar.span,
region, en_region) {
result::Ok(()) => {}
result::Err(_) => {
tcx.sess.span_err(
freevar.span,
"captured variable does not outlive the enclosing closure");
note_and_explain_region(
tcx,
"captured variable is valid for ",
en_region,
"");
note_and_explain_region(
tcx,
"closure is valid for ",
region,
"");
}
}
rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span),
region, en_region);
}
}
......@@ -642,7 +601,7 @@ fn constrain_regions_in_type_of_node(
rcx: @mut Rcx,
id: ast::node_id,
minimum_lifetime: ty::Region,
span: span) -> bool
origin: infer::SubregionOrigin) -> bool
{
//! Guarantees that any lifetimes which appear in the type of
//! the node `id` (after applying adjustments) are valid for at
......@@ -655,18 +614,18 @@ fn constrain_regions_in_type_of_node(
// report errors later on in the writeback phase.
let ty0 = rcx.resolve_node_type(id);
let adjustment = rcx.fcx.inh.adjustments.find_copy(&id);
let ty = ty::adjust_ty(tcx, span, ty0, adjustment);
let ty = ty::adjust_ty(tcx, origin.span(), ty0, adjustment);
debug!("constrain_regions_in_type_of_node(\
ty=%s, ty0=%s, id=%d, minimum_lifetime=%?, adjustment=%?)",
ty_to_str(tcx, ty), ty_to_str(tcx, ty0),
id, minimum_lifetime, adjustment);
constrain_regions_in_type(rcx, minimum_lifetime, span, ty)
constrain_regions_in_type(rcx, minimum_lifetime, origin, ty)
}
fn constrain_regions_in_type(
rcx: @mut Rcx,
minimum_lifetime: ty::Region,
span: span,
origin: infer::SubregionOrigin,
ty: ty::t) -> bool
{
/*!
......@@ -700,40 +659,14 @@ fn constrain_regions_in_type(
// (e.g., the `&` in `fn(&T)`). Such regions need not be
// constrained by `minimum_lifetime` as they are placeholders
// for regions that are as-yet-unknown.
} else if r_sub == minimum_lifetime {
rcx.fcx.mk_subr(
true, origin,
r_sub, r_sup);
} else {
match rcx.fcx.mk_subr(true, span, r_sub, r_sup) {
result::Err(_) => {
if r_sub == minimum_lifetime {
tcx.sess.span_err(
span,
fmt!("reference is not valid outside of its lifetime"));
note_and_explain_region(
tcx,
"the reference is only valid for ",
r_sup,
"");
} else {
tcx.sess.span_err(
span,
fmt!("in type `%s`, pointer has a longer lifetime than \
the data it references",
rcx.fcx.infcx().ty_to_str(ty)));
note_and_explain_region(
tcx,
"the pointer is valid for ",
r_sub,
"");
note_and_explain_region(
tcx,
"but the referenced data is only valid for ",
r_sup,
"");
}
rcx.errors_reported += 1u;
}
result::Ok(()) => {
}
}
rcx.fcx.mk_subr(
true, infer::ReferenceOutlivesReferent(ty, origin.span()),
r_sub, r_sup);
}
}
......@@ -788,8 +721,9 @@ pub mod guarantor {
*/
use middle::typeck::check::regionck::{Rcx, infallibly_mk_subr};
use middle::typeck::check::regionck::Rcx;
use middle::typeck::check::regionck::mk_subregion_due_to_derefence;
use middle::typeck::infer;
use middle::ty;
use syntax::ast;
use syntax::codemap::span;
......@@ -869,9 +803,11 @@ fn maybe_make_subregion(
rcx: @mut Rcx,
expr: @ast::expr,
sub_region: ty::Region,
sup_region: Option<ty::Region>) {
sup_region: Option<ty::Region>)
{
for sup_region.iter().advance |r| {
infallibly_mk_subr(rcx, true, expr.span, sub_region, *r);
rcx.fcx.mk_subr(true, infer::Reborrow(expr.span),
sub_region, *r);
}
}
}
......@@ -929,7 +865,7 @@ fn link(
let tcx = rcx.fcx.ccx.tcx;
debug!("rptr_ty=%s", ty_to_str(tcx, rptr_ty));
let r = ty::ty_region(tcx, span, rptr_ty);
infallibly_mk_subr(rcx, true, span, r, bound);
rcx.fcx.mk_subr(true, infer::Reborrow(span), r, bound);
}
}
......@@ -1259,27 +1195,3 @@ fn link_ref_bindings_in_pats(rcx: @mut Rcx,
}
}
pub fn infallibly_mk_subr(rcx: @mut Rcx,
a_is_expected: bool,
span: span,
a: ty::Region,
b: ty::Region) {
/*!
* Constrains `a` to be a subregion of `b`. In many cases, we
* know that this can never yield an error due to the way that
* region inferencing works. Therefore just report a bug if we
* ever see `Err(_)`.
*/
match rcx.fcx.mk_subr(a_is_expected, span, a, b) {
result::Ok(()) => {}
result::Err(e) => {
rcx.fcx.ccx.tcx.sess.span_bug(
span,
fmt!("Supposedly infallible attempt to \
make %? < %? failed: %?",
a, b, e));
}
}
}
......@@ -101,18 +101,18 @@ fn lookup_vtables(vcx: &VtableContext,
// Substitute the values of the type parameters that may
// appear in the bound.
let trait_ref = (*trait_ref).subst(tcx, substs);
let trait_ref = trait_ref.subst(tcx, substs);
debug!("after subst: %s", trait_ref.repr(tcx));
match lookup_vtable(vcx, location_info, *ty, &trait_ref, is_early) {
match lookup_vtable(vcx, location_info, *ty, trait_ref, is_early) {
Some(vtable) => param_result.push(vtable),
None => {
vcx.tcx().sess.span_fatal(
location_info.span,
fmt!("failed to find an implementation of \
trait %s for %s",
vcx.infcx.trait_ref_to_str(&trait_ref),
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.ty_to_str(*ty)));
}
}
......@@ -152,8 +152,8 @@ fn fixup_substs(vcx: &VtableContext, location_info: &LocationInfo,
fn relate_trait_refs(vcx: &VtableContext,
location_info: &LocationInfo,
act_trait_ref: &ty::TraitRef,
exp_trait_ref: &ty::TraitRef)
act_trait_ref: @ty::TraitRef,
exp_trait_ref: @ty::TraitRef)
{
/*!
*
......@@ -162,8 +162,11 @@ fn relate_trait_refs(vcx: &VtableContext,
* error otherwise.
*/
match infer::mk_sub_trait_refs(vcx.infcx, false, location_info.span,
act_trait_ref, exp_trait_ref)
match infer::mk_sub_trait_refs(vcx.infcx,
false,
infer::RelateTraitRefs(location_info.span),
act_trait_ref,
exp_trait_ref)
{
result::Ok(()) => {} // Ok.
result::Err(ref err) => {
......@@ -191,7 +194,7 @@ fn relate_trait_refs(vcx: &VtableContext,
fn lookup_vtable(vcx: &VtableContext,
location_info: &LocationInfo,
ty: ty::t,
trait_ref: &ty::TraitRef,
trait_ref: @ty::TraitRef,
is_early: bool)
-> Option<vtable_origin>
{
......@@ -304,7 +307,8 @@ fn lookup_vtable(vcx: &VtableContext,
} = impl_self_ty(vcx, location_info, im.did);
match infer::mk_subty(vcx.infcx,
false,
location_info.span,
infer::RelateSelfType(
location_info.span),
ty,
for_ty) {
result::Err(_) => loop,
......@@ -337,11 +341,10 @@ fn lookup_vtable(vcx: &VtableContext,
vcx.infcx.trait_ref_to_str(trait_ref),
vcx.infcx.trait_ref_to_str(of_trait_ref));
let of_trait_ref =
(*of_trait_ref).subst(tcx, &substs);
let of_trait_ref = of_trait_ref.subst(tcx, &substs);
relate_trait_refs(
vcx, location_info,
&of_trait_ref, trait_ref);
of_trait_ref, trait_ref);
// Recall that trait_ref -- the trait type
// we're casting to -- is the trait with
......@@ -450,7 +453,7 @@ fn fixup_ty(vcx: &VtableContext,
fn connect_trait_tps(vcx: &VtableContext,
location_info: &LocationInfo,
impl_substs: &ty::substs,
trait_ref: &ty::TraitRef,
trait_ref: @ty::TraitRef,
impl_did: ast::def_id)
{
let tcx = vcx.tcx();
......@@ -461,8 +464,8 @@ fn connect_trait_tps(vcx: &VtableContext,
"connect_trait_tps invoked on a type impl")
};
let impl_trait_ref = (*impl_trait_ref).subst(tcx, impl_substs);
relate_trait_refs(vcx, location_info, &impl_trait_ref, trait_ref);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
relate_trait_refs(vcx, location_info, impl_trait_ref, trait_ref);
}
fn insert_vtables(fcx: @mut FnCtxt,
......@@ -581,7 +584,7 @@ fn mutability_allowed(a_mutbl: ast::mutability,
ccx: fcx.ccx,
infcx: fcx.infcx()
};
let target_trait_ref = ty::TraitRef {
let target_trait_ref = @ty::TraitRef {
def_id: target_def_id,
substs: ty::substs {
tps: copy target_substs.tps,
......@@ -593,7 +596,7 @@ fn mutability_allowed(a_mutbl: ast::mutability,
lookup_vtable(&vcx,
location_info,
mt.ty,
&target_trait_ref,
target_trait_ref,
is_early);
match vtable_opt {
Some(vtable) => {
......@@ -622,7 +625,8 @@ fn mutability_allowed(a_mutbl: ast::mutability,
ty::RegionTraitStore(rb)) => {
infer::mk_subr(fcx.infcx(),
false,
ex.span,
infer::RelateObjectBound(
ex.span),
rb,
ra);
}
......
......@@ -36,6 +36,7 @@
use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::{new_infer_ctxt, resolve_ivar};
use middle::typeck::infer::{resolve_nested_tvar, resolve_type};
use middle::typeck::infer;
use syntax::ast::{crate, def_id, def_struct, def_ty};
use syntax::ast::{item, item_enum, item_impl, item_mod, item_struct};
use syntax::ast::{local_crate, method, trait_ref, ty_path};
......@@ -546,10 +547,10 @@ pub fn polytypes_unify(&self,
pub fn universally_quantify_polytype(&self,
polytype: ty_param_bounds_and_ty)
-> UniversalQuantificationResult {
// NDM--this span is bogus.
let self_region =
polytype.generics.region_param.map(
|_r| self.inference_context.next_region_var_nb(dummy_sp()));
|_| self.inference_context.next_region_var(
infer::BoundRegionInCoherence));
let bounds_count = polytype.generics.type_param_defs.len();
let type_parameters = self.inference_context.next_ty_vars(bounds_count);
......@@ -580,11 +581,9 @@ pub fn can_unify_universally_quantified<'a>(&self,
b: &'a
UniversalQuantificationResult)
-> bool {
let mut might_unify = true;
let _ = do self.inference_context.probe {
let result = self.inference_context.sub(true, dummy_sp())
.tys(a.monotype, b.monotype);
if result.is_ok() {
match infer::can_mk_subty(self.inference_context,
a.monotype, b.monotype) {
Ok(_) => {
// Check to ensure that each parameter binding respected its
// kind bounds.
let xs = [a, b];
......@@ -604,8 +603,7 @@ pub fn can_unify_universally_quantified<'a>(&self,
self.inference_context.tcx,
resolved_ty)
{
might_unify = false;
break;
return false;
}
}
Err(*) => {
......@@ -615,13 +613,13 @@ pub fn can_unify_universally_quantified<'a>(&self,
}
}
}
} else {
might_unify = false;
true
}
result
};
might_unify
Err(_) => {
false
}
}
}
pub fn get_self_type_for_implementation(&self, implementation: @Impl)
......
......@@ -615,7 +615,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
};
debug!("trait_fty (post-subst): %s", trait_fty.repr(tcx));
match infer::mk_subty(infcx, false, cm.span, impl_fty, trait_fty) {
match infer::mk_subty(infcx, false, infer::MethodCompatCheck(cm.span),
impl_fty, trait_fty) {
result::Ok(()) => {}
result::Err(ref terr) => {
tcx.sess.span_err(
......
......@@ -70,7 +70,7 @@ fn foo<A>(a: A, b: A) { ... }
use middle::ty::{vstore_slice, vstore_box, vstore_uniq};
use middle::ty::{mt};
use middle::ty;
use middle::typeck::infer::{CoerceResult, resolve_type};
use middle::typeck::infer::{CoerceResult, resolve_type, Coercion};
use middle::typeck::infer::combine::CombineFields;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
......@@ -165,7 +165,7 @@ pub fn unpack_actual_value(&self,
}
Err(e) => {
self.infcx.tcx.sess.span_bug(
self.span,
self.trace.origin.span(),
fmt!("Failed to resolve even without \
any force options: %?", e));
}
......@@ -189,7 +189,7 @@ pub fn coerce_borrowed_pointer(&self,
// yield.
let sub = Sub(**self);
let r_borrow = self.infcx.next_region_var_nb(self.span);
let r_borrow = self.infcx.next_region_var(Coercion(self.trace));
let inner_ty = match *sty_a {
ty::ty_box(mt_a) => mt_a.ty,
......@@ -227,7 +227,7 @@ pub fn coerce_borrowed_string(&self,
}
};
let r_a = self.infcx.next_region_var_nb(self.span);
let r_a = self.infcx.next_region_var(Coercion(self.trace));
let a_borrowed = ty::mk_estr(self.infcx.tcx, vstore_slice(r_a));
if_ok!(self.subtype(a_borrowed, b));
Ok(Some(@AutoDerefRef(AutoDerefRef {
......@@ -247,7 +247,7 @@ pub fn coerce_borrowed_vector(&self,
b.inf_str(self.infcx));
let sub = Sub(**self);
let r_borrow = self.infcx.next_region_var_nb(self.span);
let r_borrow = self.infcx.next_region_var(Coercion(self.trace));
let ty_inner = match *sty_a {
ty::ty_evec(mt, _) => mt.ty,
_ => {
......@@ -285,7 +285,7 @@ pub fn coerce_borrowed_fn(&self,
}
};
let r_borrow = self.infcx.next_region_var_nb(self.span);
let r_borrow = self.infcx.next_region_var(Coercion(self.trace));
let a_borrowed = ty::mk_closure(
self.infcx.tcx,
ty::ClosureTy {
......
......@@ -65,6 +65,7 @@
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::unify::{InferCtxtMethods};
use middle::typeck::infer::{InferCtxt, cres, ures};
use middle::typeck::infer::{TypeOrigin, TypeTrace};
use util::common::indent;
use std::result::{iter_vec2, map_vec2};
......@@ -79,7 +80,7 @@ pub trait Combine {
fn infcx(&self) -> @mut InferCtxt;
fn tag(&self) -> ~str;
fn a_is_expected(&self) -> bool;
fn span(&self) -> span;
fn trace(&self) -> TypeTrace;
fn sub(&self) -> Sub;
fn lub(&self) -> Lub;
......@@ -121,7 +122,7 @@ fn trait_stores(&self,
pub struct CombineFields {
infcx: @mut InferCtxt,
a_is_expected: bool,
span: span,
trace: TypeTrace,
}
pub fn expected_found<C:Combine,T>(
......
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Type inference engine
This is loosely based on standard HM-type inference, but with an
extension to try and accommodate subtyping. There is nothing
principled about this extension; it's sound---I hope!---but it's a
heuristic, ultimately, and does not guarantee that it finds a valid
typing even if one exists (in fact, there are known scenarios where it
fails, some of which may eventually become problematic).
## Key idea
The main change is that each type variable T is associated with a
lower-bound L and an upper-bound U. L and U begin as bottom and top,
respectively, but gradually narrow in response to new constraints
being introduced. When a variable is finally resolved to a concrete
type, it can (theoretically) select any type that is a supertype of L
and a subtype of U.
There are several critical invariants which we maintain:
- the upper-bound of a variable only becomes lower and the lower-bound
only becomes higher over time;
- the lower-bound L is always a subtype of the upper bound U;
- the lower-bound L and upper-bound U never refer to other type variables,
but only to types (though those types may contain type variables).
> An aside: if the terms upper- and lower-bound confuse you, think of
> "supertype" and "subtype". The upper-bound is a "supertype"
> (super=upper in Latin, or something like that anyway) and the lower-bound
> is a "subtype" (sub=lower in Latin). I find it helps to visualize
> a simple class hierarchy, like Java minus interfaces and
> primitive types. The class Object is at the root (top) and other
> types lie in between. The bottom type is then the Null type.
> So the tree looks like:
>
> Object
> / \
> String Other
> \ /
> (null)
>
> So the upper bound type is the "supertype" and the lower bound is the
> "subtype" (also, super and sub mean upper and lower in Latin, or something
> like that anyway).
## Satisfying constraints
At a primitive level, there is only one form of constraint that the
inference understands: a subtype relation. So the outside world can
say "make type A a subtype of type B". If there are variables
involved, the inferencer will adjust their upper- and lower-bounds as
needed to ensure that this relation is satisfied. (We also allow "make
type A equal to type B", but this is translated into "A <: B" and "B
<: A")
As stated above, we always maintain the invariant that type bounds
never refer to other variables. This keeps the inference relatively
simple, avoiding the scenario of having a kind of graph where we have
to pump constraints along and reach a fixed point, but it does impose
some heuristics in the case where the user is relating two type
variables A <: B.
Combining two variables such that variable A will forever be a subtype
of variable B is the trickiest part of the algorithm because there is
often no right choice---that is, the right choice will depend on
future constraints which we do not yet know. The problem comes about
because both A and B have bounds that can be adjusted in the future.
Let's look at some of the cases that can come up.
Imagine, to start, the best case, where both A and B have an upper and
lower bound (that is, the bounds are not top nor bot respectively). In
that case, if we're lucky, A.ub <: B.lb, and so we know that whatever
A and B should become, they will forever have the desired subtyping
relation. We can just leave things as they are.
### Option 1: Unify
However, suppose that A.ub is *not* a subtype of B.lb. In
that case, we must make a decision. One option is to unify A
and B so that they are one variable whose bounds are:
UB = GLB(A.ub, B.ub)
LB = LUB(A.lb, B.lb)
(Note that we will have to verify that LB <: UB; if it does not, the
types are not intersecting and there is an error) In that case, A <: B
holds trivially because A==B. However, we have now lost some
flexibility, because perhaps the user intended for A and B to end up
as different types and not the same type.
Pictorally, what this does is to take two distinct variables with
(hopefully not completely) distinct type ranges and produce one with
the intersection.
B.ub B.ub
/\ /
A.ub / \ A.ub /
/ \ / \ \ /
/ X \ UB
/ / \ \ / \
/ / / \ / /
\ \ / / \ /
\ X / LB
\ / \ / / \
\ / \ / / \
A.lb B.lb A.lb B.lb
### Option 2: Relate UB/LB
Another option is to keep A and B as distinct variables but set their
bounds in such a way that, whatever happens, we know that A <: B will hold.
This can be achieved by ensuring that A.ub <: B.lb. In practice there
are two ways to do that, depicted pictorally here:
Before Option #1 Option #2
B.ub B.ub B.ub
/\ / \ / \
A.ub / \ A.ub /(B')\ A.ub /(B')\
/ \ / \ \ / / \ / /
/ X \ __UB____/ UB /
/ / \ \ / | | /
/ / / \ / | | /
\ \ / / /(A')| | /
\ X / / LB ______LB/
\ / \ / / / \ / (A')/ \
\ / \ / \ / \ \ / \
A.lb B.lb A.lb B.lb A.lb B.lb
In these diagrams, UB and LB are defined as before. As you can see,
the new ranges `A'` and `B'` are quite different from the range that
would be produced by unifying the variables.
### What we do now
Our current technique is to *try* (transactionally) to relate the
existing bounds of A and B, if there are any (i.e., if `UB(A) != top
&& LB(B) != bot`). If that succeeds, we're done. If it fails, then
we merge A and B into same variable.
This is not clearly the correct course. For example, if `UB(A) !=
top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)`
and leave the variables unmerged. This is sometimes the better
course, it depends on the program.
The main case which fails today that I would like to support is:
fn foo<T>(x: T, y: T) { ... }
fn bar() {
let x: @mut int = @mut 3;
let y: @int = @3;
foo(x, y);
}
In principle, the inferencer ought to find that the parameter `T` to
`foo(x, y)` is `@const int`. Today, however, it does not; this is
because the type variable `T` is merged with the type variable for
`X`, and thus inherits its UB/LB of `@mut int`. This leaves no
flexibility for `T` to later adjust to accommodate `@int`.
### What to do when not all bounds are present
In the prior discussion we assumed that A.ub was not top and B.lb was
not bot. Unfortunately this is rarely the case. Often type variables
have "lopsided" bounds. For example, if a variable in the program has
been initialized but has not been used, then its corresponding type
variable will have a lower bound but no upper bound. When that
variable is then used, we would like to know its upper bound---but we
don't have one! In this case we'll do different things depending on
how the variable is being used.
## Transactional support
Whenever we adjust merge variables or adjust their bounds, we always
keep a record of the old value. This allows the changes to be undone.
## Regions
I've only talked about type variables here, but region variables
follow the same principle. They have upper- and lower-bounds. A
region A is a subregion of a region B if A being valid implies that B
is valid. This basically corresponds to the block nesting structure:
the regions for outer block scopes are superregions of those for inner
block scopes.
## Integral and floating-point type variables
There is a third variety of type variable that we use only for
inferring the types of unsuffixed integer literals. Integral type
variables differ from general-purpose type variables in that there's
no subtyping relationship among the various integral types, so instead
of associating each variable with an upper and lower bound, we just
use simple unification. Each integer variable is associated with at
most one integer type. Floating point types are handled similarly to
integral types.
## GLB/LUB
Computing the greatest-lower-bound and least-upper-bound of two
types/regions is generally straightforward except when type variables
are involved. In that case, we follow a similar "try to use the bounds
when possible but otherwise merge the variables" strategy. In other
words, `GLB(A, B)` where `A` and `B` are variables will often result
in `A` and `B` being merged and the result being `A`.
## Type coercion
We have a notion of assignability which differs somewhat from
subtyping; in particular it may cause region borrowing to occur. See
the big comment later in this file on Type Coercion for specifics.
### In conclusion
I showed you three ways to relate `A` and `B`. There are also more,
of course, though I'm not sure if there are any more sensible options.
The main point is that there are various options, each of which
produce a distinct range of types for `A` and `B`. Depending on what
the correct values for A and B are, one of these options will be the
right choice: but of course we don't know the right values for A and B
yet, that's what we're trying to find! In our code, we opt to unify
(Option #1).
# Implementation details
We make use of a trait-like impementation strategy to consolidate
duplicated code between subtypes, GLB, and LUB computations. See the
section on "Type Combining" below for details.
*/
\ No newline at end of file
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Error Reporting Code for the inference engine
Because of the way inference, and in particular region inference,
works, it often happens that errors are not detected until far after
the relevant line of code has been type-checked. Therefore, there is
an elaborate system to track why a particular constraint in the
inference graph arose so that we can explain to the user what gave
rise to a patricular error.
The basis of the system are the "origin" types. An "origin" is the
reason that a constraint or inference variable arose. There are
different "origin" enums for different kinds of constraints/variables
(e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
a span, but also more information so that we can generate a meaningful
error message.
Having a catalogue of all the different reasons an error can arise is
also useful for other reasons, like cross-referencing FAQs etc, though
we are not really taking advantage of this yet.
# Region Inference
Region inference is particularly tricky because it always succeeds "in
the moment" and simply registers a constraint. Then, at the end, we
can compute the full graph and report errors, so we need to be able to
store and later report what gave rise to the conflicting constraints.
# Subtype Trace
Determing whether `T1 <: T2` often involves a number of subtypes and
subconstraints along the way. A "TypeTrace" is an extended version
of an origin that traces the types and other values that were being
compared. It is not necessarily comprehensive (in fact, at the time of
this writing it only tracks the root values being compared) but I'd
like to extend it to include significant "waypoints". For example, if
you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
<: T4` fails, I'd like the trace to include enough information to say
"in the 2nd element of the tuple". Similarly, failures when comparing
arguments or return types in fn types should be able to cite the
specific position, etc.
# Reality vs plan
Of course, there is still a LOT of code in typeck that has yet to be
ported to this system, and which relies on string concatenation at the
time of error detection.
*/
use std::prelude::*;
use middle::ty;
use middle::ty::Region;
use middle::typeck::infer;
use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::TypeTrace;
use middle::typeck::infer::TypeOrigin;
use middle::typeck::infer::SubregionOrigin;
use middle::typeck::infer::RegionVariableOrigin;
use middle::typeck::infer::Types;
use middle::typeck::infer::TraitRefs;
use middle::typeck::infer::ValuePairs;
use middle::typeck::infer::region_inference::RegionResolutionError;
use middle::typeck::infer::region_inference::ConcreteFailure;
use middle::typeck::infer::region_inference::SubSupConflict;
use middle::typeck::infer::region_inference::SupSupConflict;
use syntax::opt_vec;
use syntax::opt_vec::OptVec;
use util::ppaux::UserString;
use util::ppaux::note_and_explain_region;
pub trait ErrorReporting {
pub fn report_region_errors(@mut self,
errors: &OptVec<RegionResolutionError>);
pub fn report_and_explain_type_error(@mut self,
trace: TypeTrace,
terr: &ty::type_err);
fn values_str(@mut self, values: &ValuePairs) -> Option<~str>;
fn expected_found_str<T:UserString+Resolvable>(
@mut self,
exp_found: &ty::expected_found<T>)
-> Option<~str>;
fn report_concrete_failure(@mut self,
origin: SubregionOrigin,
sub: Region,
sup: Region);
fn report_sub_sup_conflict(@mut self,
var_origin: RegionVariableOrigin,
sub_origin: SubregionOrigin,
sub_region: Region,
sup_origin: SubregionOrigin,
sup_region: Region);
fn report_sup_sup_conflict(@mut self,
var_origin: RegionVariableOrigin,
origin1: SubregionOrigin,
region1: Region,
origin2: SubregionOrigin,
region2: Region);
}
impl ErrorReporting for InferCtxt {
pub fn report_region_errors(@mut self,
errors: &OptVec<RegionResolutionError>) {
for errors.iter().advance |error| {
match *error {
ConcreteFailure(origin, sub, sup) => {
self.report_concrete_failure(origin, sub, sup);
}
SubSupConflict(var_origin,
sub_origin, sub_r,
sup_origin, sup_r) => {
self.report_sub_sup_conflict(var_origin,
sub_origin, sub_r,
sup_origin, sup_r);
}
SupSupConflict(var_origin,
origin1, r1,
origin2, r2) => {
self.report_sup_sup_conflict(var_origin,
origin1, r1,
origin2, r2);
}
}
}
}
pub fn report_and_explain_type_error(@mut self,
trace: TypeTrace,
terr: &ty::type_err) {
let tcx = self.tcx;
let expected_found_str = match self.values_str(&trace.values) {
Some(v) => v,
None => {
return; /* derived error */
}
};
let message_root_str = match trace.origin {
infer::Misc(_) => "mismatched types",
infer::MethodCompatCheck(_) => "method not compatible with trait",
infer::ExprAssignable(_) => "mismatched types",
infer::RelateTraitRefs(_) => "mismatched traits",
infer::RelateSelfType(_) => "mismatched types",
infer::MatchExpression(_) => "match arms have incompatible types",
infer::IfExpression(_) => "if and else have incompatible types",
};
self.tcx.sess.span_err(
trace.origin.span(),
fmt!("%s: %s (%s)",
message_root_str,
expected_found_str,
ty::type_err_to_str(tcx, terr)));
ty::note_and_explain_type_err(self.tcx, terr);
}
fn values_str(@mut self, values: &ValuePairs) -> Option<~str> {
/*!
* Returns a string of the form "expected `%s` but found `%s`",
* or None if this is a derived error.
*/
match *values {
infer::Types(ref exp_found) => {
self.expected_found_str(exp_found)
}
infer::TraitRefs(ref exp_found) => {
self.expected_found_str(exp_found)
}
}
}
fn expected_found_str<T:UserString+Resolvable>(
@mut self,
exp_found: &ty::expected_found<T>)
-> Option<~str>
{
let expected = exp_found.expected.resolve(self);
if expected.contains_error() {
return None;
}
let found = exp_found.found.resolve(self);
if found.contains_error() {
return None;
}
Some(fmt!("expected `%s` but found `%s`",
expected.user_string(self.tcx),
found.user_string(self.tcx)))
}
fn report_concrete_failure(@mut self,
origin: SubregionOrigin,
sub: Region,
sup: Region) {
match origin {
infer::Subtype(trace) => {
let terr = ty::terr_regions_does_not_outlive(sup, sub);
self.report_and_explain_type_error(trace, &terr);
}
infer::Reborrow(span) => {
self.tcx.sess.span_err(
span,
"lifetime of borrowed pointer outlines \
lifetime of borrowed content...");
note_and_explain_region(
self.tcx,
"...the borrowed pointer is valid for ",
sub,
"...");
note_and_explain_region(
self.tcx,
"...but the borrowed content is only valid for ",
sup,
"");
}
infer::InvokeClosure(span) => {
self.tcx.sess.span_err(
span,
"cannot invoke closure outside of its lifetime");
note_and_explain_region(
self.tcx,
"the closure is only valid for ",
sup,
"");
}
infer::DerefPointer(span) => {
self.tcx.sess.span_err(
span,
"dereference of reference outside its lifetime");
note_and_explain_region(
self.tcx,
"the reference is only valid for ",
sup,
"");
}
infer::FreeVariable(span) => {
self.tcx.sess.span_err(
span,
"captured variable does not outlive the enclosing closure");
note_and_explain_region(
self.tcx,
"captured variable is valid for ",
sup,
"");
note_and_explain_region(
self.tcx,
"closure is valid for ",
sub,
"");
}
infer::IndexSlice(span) => {
self.tcx.sess.span_err(
span,
fmt!("index of slice outside its lifetime"));
note_and_explain_region(
self.tcx,
"the slice is only valid for ",
sup,
"");
}
infer::RelateObjectBound(span) => {
self.tcx.sess.span_err(
span,
"lifetime of the source pointer does not outlive \
lifetime bound of the object type");
note_and_explain_region(
self.tcx,
"object type is valid for ",
sub,
"");
note_and_explain_region(
self.tcx,
"source pointer is only valid for ",
sup,
"");
}
infer::CallRcvr(span) => {
self.tcx.sess.span_err(
span,
"lifetime of method receiver does not outlive \
the method call");
note_and_explain_region(
self.tcx,
"the receiver is only valid for ",
sup,
"");
}
infer::CallArg(span) => {
self.tcx.sess.span_err(
span,
"lifetime of function argument does not outlive \
the function call");
note_and_explain_region(
self.tcx,
"the function argument is only valid for ",
sup,
"");
}
infer::CallReturn(span) => {
self.tcx.sess.span_err(
span,
"lifetime of return value does not outlive \
the function call");
note_and_explain_region(
self.tcx,
"the return value is only valid for ",
sup,
"");
}
infer::AddrOf(span) => {
self.tcx.sess.span_err(
span,
"borrowed pointer is not valid \
at the time of borrow");
note_and_explain_region(
self.tcx,
"the borrow is only valid for ",
sup,
"");
}
infer::AutoBorrow(span) => {
self.tcx.sess.span_err(
span,
"automatically borrowed pointer is not valid \
at the time of borrow");
note_and_explain_region(
self.tcx,
"the automatic borrow is only valid for ",
sup,
"");
}
infer::BindingTypeIsNotValidAtDecl(span) => {
self.tcx.sess.span_err(
span,
"lifetime of variable does not enclose its declaration");
note_and_explain_region(
self.tcx,
"the variable is only valid for ",
sup,
"");
}
infer::ReferenceOutlivesReferent(ty, span) => {
self.tcx.sess.span_err(
origin.span(),
fmt!("in type `%s`, pointer has a longer lifetime than \
the data it references",
ty.user_string(self.tcx)));
note_and_explain_region(
self.tcx,
"the pointer is valid for ",
sub,
"");
note_and_explain_region(
self.tcx,
"but the referenced data is only valid for ",
sup,
"");
}
}
}
fn report_sub_sup_conflict(@mut self,
var_origin: RegionVariableOrigin,
sub_origin: SubregionOrigin,
sub_region: Region,
sup_origin: SubregionOrigin,
sup_region: Region) {
self.tcx.sess.span_err(
var_origin.span(),
fmt!("cannot infer an appropriate lifetime \
due to conflicting requirements"));
note_and_explain_region(
self.tcx,
"first, the lifetime cannot outlive ",
sup_region,
"...");
self.tcx.sess.span_note(
sup_origin.span(),
fmt!("...due to the following expression"));
note_and_explain_region(
self.tcx,
"but, the lifetime must be valid for ",
sub_region,
"...");
self.tcx.sess.span_note(
sub_origin.span(),
fmt!("...due to the following expression"));
}
fn report_sup_sup_conflict(@mut self,
var_origin: RegionVariableOrigin,
origin1: SubregionOrigin,
region1: Region,
origin2: SubregionOrigin,
region2: Region) {
self.tcx.sess.span_err(
var_origin.span(),
fmt!("cannot infer an appropriate lifetime \
due to conflicting requirements"));
note_and_explain_region(
self.tcx,
"first, the lifetime must be contained by ",
region1,
"...");
self.tcx.sess.span_note(
origin1.span(),
fmt!("...due to the following expression"));
note_and_explain_region(
self.tcx,
"but, the lifetime must also be contained by ",
region2,
"...");
self.tcx.sess.span_note(
origin2.span(),
fmt!("...due to the following expression"));
}
}
trait Resolvable {
fn resolve(&self, infcx: @mut InferCtxt) -> Self;
fn contains_error(&self) -> bool;
}
impl Resolvable for ty::t {
fn resolve(&self, infcx: @mut InferCtxt) -> ty::t {
infcx.resolve_type_vars_if_possible(*self)
}
fn contains_error(&self) -> bool {
ty::type_is_error(*self)
}
}
impl Resolvable for @ty::TraitRef {
fn resolve(&self, infcx: @mut InferCtxt) -> @ty::TraitRef {
@infcx.resolve_type_vars_in_trait_ref_if_possible(*self)
}
fn contains_error(&self) -> bool {
ty::trait_ref_contains_error(*self)
}
}
......@@ -18,6 +18,7 @@
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::isr_alist;
use syntax::ast;
......@@ -37,7 +38,7 @@ impl Combine for Glb {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"glb" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn span(&self) -> span { self.span }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }
......@@ -127,9 +128,7 @@ fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
a.inf_str(self.infcx),
b.inf_str(self.infcx));
do indent {
self.infcx.region_vars.glb_regions(self.span, a, b)
}
Ok(self.infcx.region_vars.glb_regions(Subtype(self.trace), a, b))
}
fn contraregions(&self, a: ty::Region, b: ty::Region)
......@@ -181,11 +180,11 @@ fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_isr) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, a);
self.trace, a);
let a_vars = var_ids(self, a_isr);
let (b_with_fresh, b_isr) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, b);
self.trace, b);
let b_vars = var_ids(self, b_isr);
// Collect constraints.
......@@ -277,7 +276,7 @@ fn rev_lookup(this: &Glb,
}
this.infcx.tcx.sess.span_bug(
this.span,
this.trace.origin.span(),
fmt!("could not find original bound region for %?", r));
}
......
......@@ -530,7 +530,7 @@ pub fn var_ids<T:Combine>(this: &T, isr: isr_alist) -> ~[RegionVid] {
ty::re_infer(ty::ReVar(r)) => { result.push(r); }
r => {
this.infcx().tcx.sess.span_bug(
this.span(),
this.trace().origin.span(),
fmt!("Found non-region-vid: %?", r));
}
}
......
......@@ -19,6 +19,7 @@
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::isr_alist;
use util::common::indent;
use util::ppaux::mt_to_str;
......@@ -44,7 +45,7 @@ impl Combine for Lub {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"lub" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn span(&self) -> span { self.span }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }
......@@ -119,9 +120,7 @@ fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
a.inf_str(self.infcx),
b.inf_str(self.infcx));
do indent {
self.infcx.region_vars.lub_regions(self.span, a, b)
}
Ok(self.infcx.region_vars.lub_regions(Subtype(self.trace), a, b))
}
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
......@@ -137,10 +136,10 @@ fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_isr) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, a);
self.trace, a);
let (b_with_fresh, _) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, b);
self.trace, b);
// Collect constraints.
let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
......@@ -196,7 +195,7 @@ fn generalize_region(this: &Lub,
}
this.infcx.tcx.sess.span_bug(
this.span,
this.trace.origin.span(),
fmt!("Region %? is not associated with \
any bound region from A!", r0));
}
......
......@@ -20,6 +20,7 @@
use middle::typeck::infer::lattice::CombineFieldsLatticeMethods;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{TypeTrace, Subtype};
use util::common::{indent, indenter};
use util::ppaux::bound_region_to_str;
......@@ -36,7 +37,7 @@ impl Combine for Sub {
fn infcx(&self) -> @mut InferCtxt { self.infcx }
fn tag(&self) -> ~str { ~"sub" }
fn a_is_expected(&self) -> bool { self.a_is_expected }
fn span(&self) -> span { self.span }
fn trace(&self) -> TypeTrace { self.trace }
fn sub(&self) -> Sub { Sub(**self) }
fn lub(&self) -> Lub { Lub(**self) }
......@@ -62,12 +63,8 @@ fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
self.tag(),
a.inf_str(self.infcx),
b.inf_str(self.infcx));
do indent {
match self.infcx.region_vars.make_subregion(self.span, a, b) {
Ok(()) => Ok(a),
Err(ref e) => Err((*e))
}
}
self.infcx.region_vars.make_subregion(Subtype(self.trace), a, b);
Ok(a)
}
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
......@@ -170,7 +167,7 @@ fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
// region variable.
let (a_sig, _) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, a);
self.trace, a);
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
......
......@@ -263,7 +263,7 @@ pub fn require_same_types(
}
}
match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
match infer::mk_eqty(l_infcx, t1_is_expected, infer::Misc(span), t1, t2) {
result::Ok(()) => true,
result::Err(ref terr) => {
l_tcx.sess.span_err(span, msg() + ": " +
......
......@@ -608,6 +608,12 @@ fn repr(&self, tcx: ctxt) -> ~str {
}
}
impl Repr for ty::bound_region {
fn repr(&self, tcx: ctxt) -> ~str {
bound_region_ptr_to_str(tcx, *self)
}
}
impl Repr for ty::Region {
fn repr(&self, tcx: ctxt) -> ~str {
region_to_str(tcx, "", false, *self)
......@@ -793,6 +799,19 @@ fn repr(&self, tcx: ctxt) -> ~str {
}
}
impl Repr for span {
fn repr(&self, tcx: ctxt) -> ~str {
tcx.sess.codemap.span_to_str(*self)
}
}
impl<A:UserString> UserString for @A {
fn user_string(&self, tcx: ctxt) -> ~str {
let this: &A = &**self;
this.user_string(tcx)
}
}
impl UserString for ty::BuiltinBounds {
fn user_string(&self, tcx: ctxt) -> ~str {
if self.is_empty() { ~"<no-bounds>" } else {
......
......@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: reference is not valid outside of its lifetime
// error-pattern: lifetime of return value does not outlive the function call
extern mod extra;
use extra::arc;
fn main() {
......
......@@ -8,14 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: reference is not valid outside of its lifetime
extern mod extra;
use extra::arc;
fn main() {
let x = ~arc::RWARC(1);
let mut y = None;
let mut y = None; //~ ERROR lifetime of variable does not enclose its declaration
do x.write |one| {
y = Some(one);
}
*y.unwrap() = 2;
//~^ ERROR lifetime of return value does not outlive the function call
//~^^ ERROR dereference of reference outside its lifetime
}
......@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: reference is not valid outside of its lifetime
// error-pattern: lifetime of variable does not enclose its declaration
extern mod extra;
use extra::arc;
fn main() {
......
......@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: reference is not valid outside of its lifetime
// error-pattern: lifetime of variable does not enclose its declaration
extern mod extra;
use extra::arc;
fn main() {
......
extern mod extra;
fn main() {
pub fn main() {
let foo = ~3;
let _pfoo = &foo;
let _f: @fn() -> int = || *foo + 5;
......
......@@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:mismatched types
fn main() { let x = if true { 10i } else { 10u }; }
fn main() {
let x = if true { 10i } else { 10u };
//~^ ERROR if and else have incompatible types: expected `int` but found `uint`
}
......@@ -23,13 +23,13 @@ fn main() {
// Error results because the type of is inferred to be
// @repeat<&'blk int> where blk is the lifetime of the block below.
let y = { //~ ERROR reference is not valid
let y = { //~ ERROR lifetime of variable does not enclose its declaration
let x: &'blk int = &3;
repeater(@x)
};
assert!(3 == *(y.get())); //~ ERROR dereference of reference outside its lifetime
//~^ ERROR reference is not valid outside of its lifetime
//~^^ ERROR reference is not valid outside of its lifetime
//~^^^ ERROR reference is not valid outside of its lifetime
assert!(3 == *(y.get()));
//~^ ERROR dereference of reference outside its lifetime
//~^^ ERROR automatically borrowed pointer is not valid at the time of borrow
//~^^^ ERROR lifetime of return value does not outlive the function call
//~^^^^ ERROR cannot infer an appropriate lifetime
}
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we correctly consider the type of `match` to be the LUB
// of the various arms, particularly in the case where regions are
// involved.
pub fn opt_str0<'a>(maybestr: &'a Option<~str>) -> &'a str {
if maybestr.is_none() {
"(none)"
} else {
let s: &'a str = *maybestr.get_ref();
s
}
}
pub fn opt_str1<'a>(maybestr: &'a Option<~str>) -> &'a str {
if maybestr.is_some() {
let s: &'a str = *maybestr.get_ref();
s
} else {
"(none)"
}
}
pub fn opt_str2<'a>(maybestr: &'a Option<~str>) -> &'static str {
if maybestr.is_none() { //~ ERROR mismatched types
"(none)"
} else {
let s: &'a str = *maybestr.get_ref();
s
}
}
pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
if maybestr.is_some() { //~ ERROR mismatched types
let s: &'a str = *maybestr.get_ref();
s
} else {
"(none)"
}
}
fn main() {}
\ No newline at end of file
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we correctly consider the type of `match` to be the LUB
// of the various arms, particularly in the case where regions are
// involved.
pub fn opt_str0<'a>(maybestr: &'a Option<~str>) -> &'a str {
match *maybestr {
Some(ref s) => {
let s: &'a str = *s;
s
}
None => "(none)",
}
}
pub fn opt_str1<'a>(maybestr: &'a Option<~str>) -> &'a str {
match *maybestr {
None => "(none)",
Some(ref s) => {
let s: &'a str = *s;
s
}
}
}
pub fn opt_str2<'a>(maybestr: &'a Option<~str>) -> &'static str {
match *maybestr { //~ ERROR mismatched types
None => "(none)",
Some(ref s) => {
let s: &'a str = *s;
s
}
}
}
pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
match *maybestr { //~ ERROR mismatched types
Some(ref s) => {
let s: &'a str = *s;
s
}
None => "(none)",
}
}
fn main() {}
\ No newline at end of file
......@@ -15,6 +15,6 @@ fn with_int(f: &fn(x: &int)) {
fn main() {
let mut x = None;
//~^ ERROR reference is not valid outside of its lifetime
//~^ ERROR lifetime of variable does not enclose its declaration
with_int(|y| x = Some(y));
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册