From 9e6d5e152ee30c306afccfe9ede105acc6a0a278 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 23 May 2013 21:37:37 -0400 Subject: [PATCH] Defer reasoning about region relationships until after regionck. This patch makes error handling for region inference failures more uniform by not reporting *any* region errors until the reigon inference step. This requires threading through more information about what caused a region constraint, so that we can still give informative error messages. I have only taken partial advantage of this information: when region inference fails, we still report the same error we always did, despite the fact that we now know precisely what caused the various constriants and what the region variable represents, which we did not know before. This change is required not only to improve error messages but because the region hierarchy is not in fact fully known until regionck, because it is not clear where closure bodies fit in (our current treatment is unsound). Moreover, the relationships between free variables cannot be fully determined until type inference is otherwise complete. cc #3238. --- src/librustc/middle/typeck/check/_match.rs | 14 +- src/librustc/middle/typeck/check/demand.rs | 4 +- src/librustc/middle/typeck/check/method.rs | 17 +- src/librustc/middle/typeck/check/mod.rs | 64 ++- src/librustc/middle/typeck/check/regionck.rs | 182 ++----- src/librustc/middle/typeck/check/vtable.rs | 40 +- src/librustc/middle/typeck/coherence.rs | 26 +- src/librustc/middle/typeck/collect.rs | 3 +- src/librustc/middle/typeck/infer/coercion.rs | 12 +- src/librustc/middle/typeck/infer/combine.rs | 5 +- .../middle/typeck/infer/error_reporting.rs | 434 +++++++++++++++++ src/librustc/middle/typeck/infer/glb.rs | 13 +- src/librustc/middle/typeck/infer/lattice.rs | 2 +- src/librustc/middle/typeck/infer/lub.rs | 13 +- src/librustc/middle/typeck/infer/mod.rs | 365 +++++++++++--- .../middle/typeck/infer/region_inference.rs | 445 ++++++++++-------- src/librustc/middle/typeck/infer/sub.rs | 13 +- src/librustc/middle/typeck/mod.rs | 2 +- src/librustc/util/ppaux.rs | 19 + .../arc-rw-cond-shouldnt-escape.rs | 2 +- .../arc-rw-state-shouldnt-escape.rs | 5 +- .../arc-rw-write-mode-cond-shouldnt-escape.rs | 2 +- .../arc-rw-write-mode-shouldnt-escape.rs | 2 +- .../compile-fail/borrowck-move-by-capture.rs | 4 +- .../kindck-owned-trait-contains.rs | 10 +- src/test/compile-fail/regions-bounds.rs | 6 +- .../compile-fail/regions-escape-bound-fn-2.rs | 2 +- .../regions-escape-via-trait-or-not.rs | 2 +- .../regions-infer-at-fn-not-param.rs | 5 +- .../regions-infer-covariance-due-to-arg.rs | 1 + .../compile-fail/regions-infer-not-param.rs | 5 + .../regions-infer-paramd-indirect.rs | 1 + src/test/compile-fail/regions-nested-fns.rs | 1 + .../compile-fail/regions-ret-borrowed-1.rs | 2 + src/test/compile-fail/regions-ret-borrowed.rs | 2 + .../compile-fail/sync-cond-shouldnt-escape.rs | 2 +- .../sync-rwlock-cond-shouldnt-escape.rs | 2 +- ...-rwlock-write-mode-cond-shouldnt-escape.rs | 2 +- .../sync-rwlock-write-mode-shouldnt-escape.rs | 2 +- 39 files changed, 1211 insertions(+), 522 deletions(-) create mode 100644 src/librustc/middle/typeck/infer/error_reporting.rs diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index c2bf4594d30e..351d09b7ce5c 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -15,6 +15,7 @@ use middle::typeck::check::demand; 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}; @@ -38,8 +39,6 @@ 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);} @@ -93,8 +92,6 @@ pub fn check_match(fcx: @mut FnCtxt, 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 +439,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 +541,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 diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index 7ca78068f070..cf29d3f7f1f5 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -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); diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 95584889218a..ee61399113a4 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -619,14 +619,18 @@ impl<'self> LookupContext<'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 @@ impl<'self> LookupContext<'self> { -> Option { // 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 @@ impl<'self> LookupContext<'self> { 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 @@ impl<'self> LookupContext<'self> { // 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(_) => { diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index a6bc335bcdbc..b397181ddca6 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -467,8 +467,6 @@ pub fn check_fn(ccx: @mut CrateCtxt, 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 @@ impl FnCtxt { 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_to_str(self.tcx(), br)) + }, + replacement: { + self.infcx().next_region_var( + infer::BoundRegionError(span)) + } }) } } @@ -698,7 +701,7 @@ impl FnCtxt { impl region_scope for FnCtxt { fn anon_region(&self, span: span) -> Result { - 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 { self.search_in_scope_regions(span, ty::br_self) @@ -845,11 +848,11 @@ impl FnCtxt { pub fn mk_subty(&self, a_is_expected: bool, - span: span, + origin: infer::SubtypeOrigin, 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 @@ impl FnCtxt { 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 @@ impl FnCtxt { pub fn mk_eqty(&self, a_is_expected: bool, - span: span, + origin: infer::SubtypeOrigin, 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(@mut self, lb: ast::node_id, f: &fn() -> R) @@ -905,7 +914,9 @@ impl FnCtxt { rp: Option, span: span) -> Option { - 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 @@ pub fn check_expr_with_unifier(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, @@ -2085,7 +2098,7 @@ pub fn check_expr_with_unifier(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 +2408,8 @@ pub fn check_expr_with_unifier(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 +2451,7 @@ pub fn check_expr_with_unifier(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(_) => { @@ -2686,7 +2700,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, 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 +2921,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 +3424,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) } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 80faad15695c..2e41649e4dbb 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -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::FnCtxt; 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 @@ pub mod guarantor { rcx: @mut Rcx, expr: @ast::expr, sub_region: ty::Region, - sup_region: Option) { + sup_region: Option) + { 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 @@ pub mod guarantor { 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 @@ pub mod guarantor { } } - -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)); - } - } -} diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 7a3c02efebec..d90863344396 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -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 { @@ -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 @@ pub fn early_resolve_expr(ex: @ast::expr, 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 @@ pub fn early_resolve_expr(ex: @ast::expr, lookup_vtable(&vcx, location_info, mt.ty, - &target_trait_ref, + target_trait_ref, is_early); match vtable_opt { Some(vtable) => { @@ -622,7 +625,8 @@ pub fn early_resolve_expr(ex: @ast::expr, ty::RegionTraitStore(rb)) => { infer::mk_subr(fcx.infcx(), false, - ex.span, + infer::RelateObjectBound( + ex.span), rb, ra); } diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 8748d3dcd23b..24ac63ac7b07 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -36,6 +36,7 @@ use middle::typeck::infer::combine::Combine; 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 @@ impl CoherenceChecker { 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 @@ impl CoherenceChecker { 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 @@ impl CoherenceChecker { self.inference_context.tcx, resolved_ty) { - might_unify = false; - break; + return false; } } Err(*) => { @@ -615,13 +613,13 @@ impl CoherenceChecker { } } } - } else { - might_unify = false; + true } - result - }; - might_unify + Err(_) => { + false + } + } } pub fn get_self_type_for_implementation(&self, implementation: @Impl) diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 85bd2bc2d75e..de05aca61caf 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -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( diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 63f882f5e541..03d243797b32 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -70,7 +70,7 @@ use middle::ty::{AutoDerefRef}; 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 @@ impl Coerce { } 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 @@ impl Coerce { // 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 @@ impl Coerce { } }; - 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 @@ impl Coerce { 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 @@ impl Coerce { } }; - 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 { diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index adc263cbc4d8..8af454774b82 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -65,6 +65,7 @@ use middle::typeck::infer::sub::Sub; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::unify::{InferCtxtMethods}; use middle::typeck::infer::{InferCtxt, cres, ures}; +use middle::typeck::infer::{SubtypeOrigin, SubtypeTrace}; 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) -> SubtypeTrace; fn sub(&self) -> Sub; fn lub(&self) -> Lub; @@ -121,7 +122,7 @@ pub trait Combine { pub struct CombineFields { infcx: @mut InferCtxt, a_is_expected: bool, - span: span, + trace: SubtypeTrace, } pub fn expected_found( diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs new file mode 100644 index 000000000000..079a01beaa14 --- /dev/null +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -0,0 +1,434 @@ +// 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 or the MIT license +// , 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., `SubtypeOrigin`, `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 "SubtypeTrace" 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 core::prelude::*; +use middle::ty; +use middle::ty::Region; +use middle::typeck::infer; +use middle::typeck::infer::InferCtxt; +use middle::typeck::infer::SubtypeTrace; +use middle::typeck::infer::SubtypeOrigin; +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; + +impl InferCtxt { + pub fn report_region_errors(@mut self, + errors: &OptVec) { + for errors.each |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); + } + } + } + } + + fn report_and_explain_type_error(@mut self, + trace: SubtypeTrace, + 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" + }; + + 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( + @mut self, + exp_found: &ty::expected_found) + -> 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(sub, sup); + 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) + } +} + diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 0dd45919be14..46ccfd24eb56 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -18,6 +18,7 @@ use middle::typeck::infer::lub::Lub; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::{cres, InferCtxt}; +use middle::typeck::infer::{SubtypeTrace, 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) -> SubtypeTrace { self.trace } fn sub(&self) -> Sub { Sub(**self) } fn lub(&self) -> Lub { Lub(**self) } @@ -127,9 +128,7 @@ impl Combine for Glb { 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 @@ impl Combine for Glb { // 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 @@ impl Combine for Glb { } this.infcx.tcx.sess.span_bug( - this.span, + this.trace.origin.span(), fmt!("could not find original bound region for %?", r)); } diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 73e43c6c0762..b1a6aefd1794 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -530,7 +530,7 @@ pub fn var_ids(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)); } } diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index ad063be86146..5d896bdadba7 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -19,6 +19,7 @@ use middle::typeck::infer::sub::Sub; 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::{SubtypeTrace, 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) -> SubtypeTrace { self.trace } fn sub(&self) -> Sub { Sub(**self) } fn lub(&self) -> Lub { Lub(**self) } @@ -119,9 +120,7 @@ impl Combine for Lub { 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 { @@ -137,10 +136,10 @@ impl Combine for Lub { // 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 @@ impl Combine for 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)); } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 28d943b58079..29f24f2ce9a7 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -264,7 +264,8 @@ use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::unify::{ValsAndBindings, Root}; use middle::typeck::isr_alist; use util::common::indent; -use util::ppaux::{bound_region_to_str, ty_to_str, trait_ref_to_str}; +use util::ppaux::{bound_region_to_str, ty_to_str, trait_ref_to_str, Repr, + UserString}; use std::result; use std::vec; @@ -286,6 +287,7 @@ pub mod sub; pub mod to_str; pub mod unify; pub mod coercion; +pub mod error_reporting; pub type Bound = Option; pub struct Bounds { @@ -319,6 +321,127 @@ pub struct InferCtxt { region_vars: RegionVarBindings, } +/// Why did we require that the two types be related? +/// +/// See `error_reporting.rs` for more details +pub enum SubtypeOrigin { + // Not yet categorized in a better way + Misc(span), + + // Checking that method of impl is compatible with trait + MethodCompatCheck(span), + + // Checking that this expression can be assigned where it needs to be + ExprAssignable(@ast::expr), + + // Relating trait refs when resolving vtables + RelateTraitRefs(span), + + // Relating trait refs when resolving vtables + RelateSelfType(span), +} + +/// See `error_reporting.rs` for more details +pub enum ValuePairs { + Types(ty::expected_found), + TraitRefs(ty::expected_found<@ty::TraitRef>), +} + +/// The trace designates the path through inference that we took to +/// encounter an error or subtyping constraint. +/// +/// See `error_reporting.rs` for more details. +pub struct SubtypeTrace { + origin: SubtypeOrigin, + values: ValuePairs, +} + +/// The origin of a `r1 <= r2` constraint. +/// +/// See `error_reporting.rs` for more details +pub enum SubregionOrigin { + // Arose from a subtyping relation + Subtype(SubtypeTrace), + + // Invocation of closure must be within its lifetime + InvokeClosure(span), + + // Dereference of borrowed pointer must be within its lifetime + DerefPointer(span), + + // Closure bound must not outlive captured free variables + FreeVariable(span), + + // Index into slice must be within its lifetime + IndexSlice(span), + + // When casting `&'a T` to an `&'b Trait` object, + // relating `'a` to `'b` + RelateObjectBound(span), + + // Creating a pointer `b` to contents of another borrowed pointer + Reborrow(span), + + // (&'a &'b T) where a >= b + ReferenceOutlivesReferent(ty::t, span), + + // A `ref b` whose region does not enclose the decl site + BindingTypeIsNotValidAtDecl(span), + + // Regions appearing in a method receiver must outlive method call + CallRcvr(span), + + // Regions appearing in a function argument must outlive func call + CallArg(span), + + // Region in return type of invoked fn must enclose call + CallReturn(span), + + // Region resulting from a `&` expr must enclose the `&` expr + AddrOf(span), + + // An auto-borrow that does not enclose the expr where it occurs + AutoBorrow(span), +} + +/// Reasons to create a region inference variable +/// +/// See `error_reporting.rs` for more details +pub enum RegionVariableOrigin { + // Region variables created for ill-categorized reasons, + // mostly indicates places in need of refactoring + MiscVariable(span), + + // Regions created by a `&P` or `[...]` pattern + PatternRegion(span), + + // Regions created by `&` operator + AddrOfRegion(span), + + // Regions created by `&[...]` literal + AddrOfSlice(span), + + // Regions created as part of an autoref of a method receiver + Autoref(span), + + // Regions created as part of an automatic coercion + Coercion(SubtypeTrace), + + // Region variables created for bound regions + // in a function or method that is called + BoundRegionInFnCall(span, ty::bound_region), + + // Region variables created for bound regions + // when doing subtyping/lub/glb computations + BoundRegionInFnType(span, ty::bound_region), + + BoundRegionInTypeOrImpl(span), + + BoundRegionInCoherence, + + BoundRegionError(span), +} + pub enum fixup_err { unresolved_int_ty(IntVid), unresolved_ty(TyVid), @@ -366,14 +489,18 @@ pub fn new_infer_ctxt(tcx: ty::ctxt) -> @mut InferCtxt { pub fn mk_subty(cx: @mut InferCtxt, a_is_expected: bool, - span: span, + origin: SubtypeOrigin, a: ty::t, b: ty::t) -> ures { debug!("mk_subty(%s <: %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.commit { - cx.sub(a_is_expected, span).tys(a, b) + let trace = SubtypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + }; + cx.sub(a_is_expected, trace).tys(a, b) } }.to_ures() } @@ -382,35 +509,40 @@ pub fn can_mk_subty(cx: @mut InferCtxt, a: ty::t, b: ty::t) -> ures { debug!("can_mk_subty(%s <: %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.probe { - cx.sub(true, codemap::dummy_sp()).tys(a, b) + let trace = SubtypeTrace { + origin: Misc(codemap::dummy_sp()), + values: Types(expected_found(true, a, b)) + }; + cx.sub(true, trace).tys(a, b) } }.to_ures() } pub fn mk_subr(cx: @mut InferCtxt, a_is_expected: bool, - span: span, + origin: SubregionOrigin, a: ty::Region, - b: ty::Region) - -> ures { + b: ty::Region) { debug!("mk_subr(%s <: %s)", a.inf_str(cx), b.inf_str(cx)); - do indent { - do cx.commit { - cx.sub(a_is_expected, span).regions(a, b) - } - }.to_ures() + cx.region_vars.start_snapshot(); + cx.region_vars.make_subregion(origin, a, b); + cx.region_vars.commit(); } pub fn mk_eqty(cx: @mut InferCtxt, a_is_expected: bool, - span: span, + origin: SubtypeOrigin, a: ty::t, b: ty::t) -> ures { debug!("mk_eqty(%s <: %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.commit { - let suber = cx.sub(a_is_expected, span); + let trace = SubtypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + }; + let suber = cx.sub(a_is_expected, trace); eq_tys(&suber, a, b) } }.to_ures() @@ -418,31 +550,49 @@ pub fn mk_eqty(cx: @mut InferCtxt, pub fn mk_sub_trait_refs(cx: @mut InferCtxt, a_is_expected: bool, - span: span, - a: &ty::TraitRef, - b: &ty::TraitRef) + origin: SubtypeOrigin, + a: @ty::TraitRef, + b: @ty::TraitRef) -> ures { debug!("mk_sub_trait_refs(%s <: %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.commit { - let suber = cx.sub(a_is_expected, span); + let trace = SubtypeTrace { + origin: origin, + values: TraitRefs(expected_found(a_is_expected, a, b)) + }; + let suber = cx.sub(a_is_expected, trace); suber.trait_refs(a, b) } }.to_ures() } +fn expected_found(a_is_expected: bool, + a: T, + b: T) -> ty::expected_found { + if a_is_expected { + ty::expected_found {expected: a, found: b} + } else { + ty::expected_found {expected: b, found: a} + } +} + pub fn mk_coercety(cx: @mut InferCtxt, a_is_expected: bool, - span: span, + origin: SubtypeOrigin, a: ty::t, b: ty::t) -> CoerceResult { debug!("mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.commit { - Coerce(cx.combine_fields(a_is_expected, span)).tys(a, b) + let trace = SubtypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + }; + Coerce(cx.combine_fields(a_is_expected, trace)).tys(a, b) } } } @@ -451,8 +601,11 @@ pub fn can_mk_coercety(cx: @mut InferCtxt, a: ty::t, b: ty::t) -> ures { debug!("can_mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.probe { - let span = codemap::dummy_sp(); - Coerce(cx.combine_fields(true, span)).tys(a, b) + let trace = SubtypeTrace { + origin: Misc(codemap::dummy_sp()), + values: Types(expected_found(true, a, b)) + }; + Coerce(cx.combine_fields(true, trace)).tys(a, b) } }.to_ures() } @@ -535,15 +688,17 @@ struct Snapshot { } impl InferCtxt { - pub fn combine_fields(@mut self, a_is_expected: bool, span: span) + pub fn combine_fields(@mut self, + a_is_expected: bool, + trace: SubtypeTrace) -> CombineFields { CombineFields {infcx: self, a_is_expected: a_is_expected, - span: span} + trace: trace} } - pub fn sub(@mut self, a_is_expected: bool, span: span) -> Sub { - Sub(self.combine_fields(a_is_expected, span)) + pub fn sub(@mut self, a_is_expected: bool, trace: SubtypeTrace) -> Sub { + Sub(self.combine_fields(a_is_expected, trace)) } pub fn in_snapshot(&self) -> bool { @@ -663,31 +818,13 @@ impl InferCtxt { ty::mk_float_var(self.tcx, self.next_float_var_id()) } - pub fn next_region_var_nb(&mut self, span: span) -> ty::Region { - ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span))) + pub fn next_region_var(&mut self, origin: RegionVariableOrigin) -> ty::Region { + ty::re_infer(ty::ReVar(self.region_vars.new_region_var(origin))) } - pub fn next_region_var_with_lb(&mut self, - span: span, - lb_region: ty::Region) - -> ty::Region { - let region_var = self.next_region_var_nb(span); - - // add lb_region as a lower bound on the newly built variable - assert!(self.region_vars.make_subregion(span, - lb_region, - region_var).is_ok()); - - return region_var; - } - - pub fn next_region_var(&mut self, span: span, scope_id: ast::node_id) - -> ty::Region { - self.next_region_var_with_lb(span, ty::re_scope(scope_id)) - } - - pub fn resolve_regions(&mut self) { - self.region_vars.resolve_regions(); + pub fn resolve_regions(@mut self) { + let errors = self.region_vars.resolve_regions(); + self.report_region_errors(&errors); // see error_reporting.rs } pub fn ty_to_str(@mut self, t: ty::t) -> ~str { @@ -809,17 +946,13 @@ impl InferCtxt { } pub fn replace_bound_regions_with_fresh_regions(&mut self, - span: span, + trace: SubtypeTrace, fsig: &ty::FnSig) - -> (ty::FnSig, - isr_alist) { + -> (ty::FnSig, isr_alist) { let(isr, _, fn_sig) = replace_bound_regions_in_fn_sig(self.tcx, @Nil, None, fsig, |br| { - // N.B.: The name of the bound region doesn't have anything to - // do with the region variable that's created for it. The - // only thing we're doing with `br` here is using it in the - // debug message. - let rvar = self.next_region_var_nb(span); + let rvar = self.next_region_var( + BoundRegionInFnType(trace.origin.span(), br)); debug!("Bound region %s maps to %?", bound_region_to_str(self.tcx, "", false, br), rvar); @@ -838,3 +971,121 @@ pub fn fold_regions_in_sig( ty::fold_regions(tcx, t, |r, in_fn| fldr(r, in_fn)) } } + +impl SubtypeTrace { + pub fn span(&self) -> span { + self.origin.span() + } +} + +impl Repr for SubtypeTrace { + fn repr(&self, tcx: ty::ctxt) -> ~str { + fmt!("SubtypeTrace(%s)", self.origin.repr(tcx)) + } +} + +impl SubtypeOrigin { + pub fn span(&self) -> span { + match *self { + MethodCompatCheck(span) => span, + ExprAssignable(expr) => expr.span, + Misc(span) => span, + RelateTraitRefs(span) => span, + RelateSelfType(span) => span, + } + } +} + +impl Repr for SubtypeOrigin { + fn repr(&self, tcx: ty::ctxt) -> ~str { + match *self { + MethodCompatCheck(a) => fmt!("MethodCompatCheck(%s)", a.repr(tcx)), + ExprAssignable(a) => fmt!("ExprAssignable(%s)", a.repr(tcx)), + Misc(a) => fmt!("Misc(%s)", a.repr(tcx)), + RelateTraitRefs(a) => fmt!("RelateTraitRefs(%s)", a.repr(tcx)), + RelateSelfType(a) => fmt!("RelateSelfType(%s)", a.repr(tcx)), + } + } +} + +impl SubregionOrigin { + pub fn span(&self) -> span { + match *self { + Subtype(a) => a.span(), + InvokeClosure(a) => a, + DerefPointer(a) => a, + FreeVariable(a) => a, + IndexSlice(a) => a, + RelateObjectBound(a) => a, + Reborrow(a) => a, + ReferenceOutlivesReferent(_, a) => a, + BindingTypeIsNotValidAtDecl(a) => a, + CallRcvr(a) => a, + CallArg(a) => a, + CallReturn(a) => a, + AddrOf(a) => a, + AutoBorrow(a) => a, + } + } +} + +impl Repr for SubregionOrigin { + fn repr(&self, tcx: ty::ctxt) -> ~str { + match *self { + Subtype(a) => fmt!("Subtype(%s)", a.repr(tcx)), + InvokeClosure(a) => fmt!("InvokeClosure(%s)", a.repr(tcx)), + DerefPointer(a) => fmt!("DerefPointer(%s)", a.repr(tcx)), + FreeVariable(a) => fmt!("FreeVariable(%s)", a.repr(tcx)), + IndexSlice(a) => fmt!("IndexSlice(%s)", a.repr(tcx)), + RelateObjectBound(a) => fmt!("RelateObjectBound(%s)", a.repr(tcx)), + Reborrow(a) => fmt!("Reborrow(%s)", a.repr(tcx)), + ReferenceOutlivesReferent(_, a) => fmt!("ReferenceOutlivesReferent(%s)", a.repr(tcx)), + BindingTypeIsNotValidAtDecl(a) => fmt!("BindingTypeIsNotValidAtDecl(%s)", a.repr(tcx)), + CallRcvr(a) => fmt!("CallRcvr(%s)", a.repr(tcx)), + CallArg(a) => fmt!("CallArg(%s)", a.repr(tcx)), + CallReturn(a) => fmt!("CallReturn(%s)", a.repr(tcx)), + AddrOf(a) => fmt!("AddrOf(%s)", a.repr(tcx)), + AutoBorrow(a) => fmt!("AutoBorrow(%s)", a.repr(tcx)), + } + } +} + +impl RegionVariableOrigin { + pub fn span(&self) -> span { + match *self { + MiscVariable(a) => a, + PatternRegion(a) => a, + AddrOfRegion(a) => a, + AddrOfSlice(a) => a, + Autoref(a) => a, + Coercion(a) => a.span(), + BoundRegionInFnCall(a, _) => a, + BoundRegionInFnType(a, _) => a, + BoundRegionInTypeOrImpl(a) => a, + BoundRegionInCoherence => codemap::dummy_sp(), + BoundRegionError(a) => a, + } + } +} + +impl Repr for RegionVariableOrigin { + fn repr(&self, tcx: ty::ctxt) -> ~str { + match *self { + MiscVariable(a) => fmt!("MiscVariable(%s)", a.repr(tcx)), + PatternRegion(a) => fmt!("PatternRegion(%s)", a.repr(tcx)), + AddrOfRegion(a) => fmt!("AddrOfRegion(%s)", a.repr(tcx)), + AddrOfSlice(a) => fmt!("AddrOfSlice(%s)", a.repr(tcx)), + Autoref(a) => fmt!("Autoref(%s)", a.repr(tcx)), + Coercion(a) => fmt!("Coercion(%s)", a.repr(tcx)), + BoundRegionInFnCall(a, b) => fmt!("BoundRegionInFnCall(%s,%s)", + a.repr(tcx), b.repr(tcx)), + BoundRegionInFnType(a, b) => fmt!("BoundRegionInFnType(%s,%s)", + a.repr(tcx), b.repr(tcx)), + BoundRegionInTypeOrImpl(a) => fmt!("BoundRegionInTypeOrImpl(%s)", + a.repr(tcx)), + BoundRegionInCoherence => fmt!("BoundRegionInCoherence"), + BoundRegionError(a) => fmt!("BoundRegionError(%s)", a.repr(tcx)), + } + } +} + diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs index db17405fc266..82fbb6b8ce7e 100644 --- a/src/librustc/middle/typeck/infer/region_inference.rs +++ b/src/librustc/middle/typeck/infer/region_inference.rs @@ -542,8 +542,10 @@ use middle::ty::{FreeRegion, Region, RegionVid}; use middle::ty::{re_empty, re_static, re_infer, re_free, re_bound}; use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh}; use middle::typeck::infer::cres; +use middle::typeck::infer::{RegionVariableOrigin, SubregionOrigin}; +use middle::typeck::infer; use util::common::indenter; -use util::ppaux::note_and_explain_region; +use util::ppaux::{note_and_explain_region, Repr, UserString}; use std::cell::Cell; use std::hashmap::{HashMap, HashSet}; @@ -551,6 +553,8 @@ use std::uint; use std::vec; use syntax::codemap::span; use syntax::ast; +use syntax::opt_vec; +use syntax::opt_vec::OptVec; #[deriving(Eq,IterBytes)] enum Constraint { @@ -576,12 +580,37 @@ enum CombineMapType { Lub, Glb } +pub enum RegionResolutionError { + /// `ConcreteFailure(o, a, b)`: + /// + /// `o` requires that `a <= b`, but this does not hold + ConcreteFailure(SubregionOrigin, Region, Region), + + /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: + /// + /// Could not infer a value for `v` because `sub_r <= v` (due to + /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and + /// `sub_r <= sup_r` does not hold. + SubSupConflict(RegionVariableOrigin, + SubregionOrigin, Region, + SubregionOrigin, Region), + + /// `SupSupConflict(v, origin1, r1, origin2, r2)`: + /// + /// Could not infer a value for `v` because `v <= r1` (due to + /// `origin1`) and `v <= r2` (due to `origin2`) and + /// `r1` and `r2` have no intersection. + SupSupConflict(RegionVariableOrigin, + SubregionOrigin, Region, + SubregionOrigin, Region), +} + type CombineMap = HashMap; pub struct RegionVarBindings { tcx: ty::ctxt, - var_spans: ~[span], - constraints: HashMap, + var_origins: ~[RegionVariableOrigin], + constraints: HashMap, lubs: CombineMap, glbs: CombineMap, skolemization_count: uint, @@ -606,7 +635,7 @@ pub struct RegionVarBindings { pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings { RegionVarBindings { tcx: tcx, - var_spans: ~[], + var_origins: ~[], values: Cell::new_empty(), constraints: HashMap::new(), lubs: HashMap::new(), @@ -647,8 +676,8 @@ impl RegionVarBindings { match undo_item { Snapshot => {} AddVar(vid) => { - assert_eq!(self.var_spans.len(), vid.to_uint() + 1); - self.var_spans.pop(); + assert_eq!(self.var_origins.len(), vid.to_uint() + 1); + self.var_origins.pop(); } AddConstraint(ref constraint) => { self.constraints.remove(constraint); @@ -664,18 +693,18 @@ impl RegionVarBindings { } pub fn num_vars(&mut self) -> uint { - self.var_spans.len() + self.var_origins.len() } - pub fn new_region_var(&mut self, span: span) -> RegionVid { + pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { let id = self.num_vars(); - self.var_spans.push(span); + self.var_origins.push(origin); let vid = RegionVid { id: id }; if self.in_snapshot() { self.undo_log.push(AddVar(vid)); } - debug!("created new region variable %? with span %?", - vid, self.tcx.sess.codemap.span_to_str(span)); + debug!("created new region variable %? with origin %?", + vid, origin.repr(self.tcx)); return vid; } @@ -705,109 +734,106 @@ impl RegionVarBindings { re_bound(br_fresh(sc)) } - pub fn add_constraint(&mut self, constraint: Constraint, span: span) { + pub fn add_constraint(&mut self, + constraint: Constraint, + origin: SubregionOrigin) { // cannot add constraints once regions are resolved assert!(self.values.is_empty()); debug!("RegionVarBindings: add_constraint(%?)", constraint); - if self.constraints.insert(constraint, span) { + if self.constraints.insert(constraint, origin) { if self.in_snapshot() { self.undo_log.push(AddConstraint(constraint)); } } } - pub fn make_subregion(&mut self, span: span, sub: Region, sup: Region) - -> cres<()> { + pub fn make_subregion(&mut self, + origin: SubregionOrigin, + sub: Region, + sup: Region) { // cannot add constraints once regions are resolved assert!(self.values.is_empty()); debug!("RegionVarBindings: make_subregion(%?, %?)", sub, sup); match (sub, sup) { (re_infer(ReVar(sub_id)), re_infer(ReVar(sup_id))) => { - self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), span); - Ok(()) + self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin); } (r, re_infer(ReVar(sup_id))) => { - self.add_constraint(ConstrainRegSubVar(r, sup_id), span); - Ok(()) + self.add_constraint(ConstrainRegSubVar(r, sup_id), origin); } (re_infer(ReVar(sub_id)), r) => { - self.add_constraint(ConstrainVarSubReg(sub_id, r), span); - Ok(()) + self.add_constraint(ConstrainVarSubReg(sub_id, r), origin); } (re_bound(br), _) => { self.tcx.sess.span_bug( - span, + origin.span(), fmt!("Cannot relate bound region as subregion: %?", br)); } (_, re_bound(br)) => { self.tcx.sess.span_bug( - span, + origin.span(), fmt!("Cannot relate bound region as superregion: %?", br)); } _ => { - if self.is_subregion_of(sub, sup) { - Ok(()) - } else { - Err(ty::terr_regions_does_not_outlive(sub, sup)) - } + self.add_constraint(ConstrainRegSubReg(sub, sup), origin); } } } - pub fn lub_regions(&mut self, span: span, a: Region, b: Region) - -> cres { + pub fn lub_regions(&mut self, + origin: SubregionOrigin, + a: Region, + b: Region) + -> Region { // cannot add constraints once regions are resolved assert!(self.values.is_empty()); debug!("RegionVarBindings: lub_regions(%?, %?)", a, b); match (a, b) { - (re_static, _) | (_, re_static) => { - Ok(re_static) // nothing lives longer than static - } + (re_static, _) | (_, re_static) => { + re_static // nothing lives longer than static + } - (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { - self.combine_vars( - Lub, a, b, span, - |this, old_r, new_r| this.make_subregion(span, old_r, new_r)) - } - - _ => { - Ok(self.lub_concrete_regions(a, b)) - } + _ => { + self.combine_vars( + Lub, a, b, origin, + |this, old_r, new_r| + this.make_subregion(origin, old_r, new_r)) + } } } - pub fn glb_regions(&mut self, span: span, a: Region, b: Region) - -> cres { + pub fn glb_regions(&mut self, + origin: SubregionOrigin, + a: Region, + b: Region) + -> Region { // cannot add constraints once regions are resolved assert!(self.values.is_empty()); debug!("RegionVarBindings: glb_regions(%?, %?)", a, b); match (a, b) { - (re_static, r) | (r, re_static) => { - // static lives longer than everything else - Ok(r) - } + (re_static, r) | (r, re_static) => { + // static lives longer than everything else + r + } - (re_infer(ReVar(*)), _) | (_, re_infer(ReVar(*))) => { - self.combine_vars( - Glb, a, b, span, - |this, old_r, new_r| this.make_subregion(span, new_r, old_r)) - } - - _ => { - self.glb_concrete_regions(a, b) - } + _ => { + self.combine_vars( + Glb, a, b, origin, + |this, old_r, new_r| + this.make_subregion(origin, new_r, old_r)) + } } } pub fn resolve_var(&mut self, rid: RegionVid) -> ty::Region { if self.values.is_empty() { self.tcx.sess.span_bug( - self.var_spans[rid.to_uint()], + self.var_origins[rid.to_uint()].span(), fmt!("Attempt to resolve region variable before values have \ been computed!")); } @@ -830,46 +856,41 @@ impl RegionVarBindings { } } + fn combine_map<'a>(&'a mut self, + t: CombineMapType) + -> &'a mut CombineMap + { + match t { + Glb => &mut self.glbs, + Lub => &mut self.lubs, + } + } + pub fn combine_vars(&mut self, t: CombineMapType, a: Region, b: Region, - span: span, + origin: SubregionOrigin, relate: &fn(this: &mut RegionVarBindings, old_r: Region, - new_r: Region) -> cres<()>) - -> cres { + new_r: Region)) + -> Region { let vars = TwoRegions { a: a, b: b }; - let c; - { - // FIXME (#3850): shouldn't need a scope, nor should this need to be - // done twice to get the maps out - { - let combines = match t { - Glb => &self.glbs, Lub => &self.lubs - }; - match combines.find(&vars) { - Some(&c) => return Ok(re_infer(ReVar(c))), - None => () - } - } - c = self.new_region_var(span); - { - let combines = match t { - Glb => &mut self.glbs, Lub => &mut self.lubs - }; - combines.insert(vars, c); + match self.combine_map(t).find(&vars) { + Some(&c) => { + return re_infer(ReVar(c)); } + None => {} } + let c = self.new_region_var(infer::MiscVariable(origin.span())); + self.combine_map(t).insert(vars, c); if self.in_snapshot() { self.undo_log.push(AddCombination(t, vars)); } - do relate(self, a, re_infer(ReVar(c))).then { - do relate(self, b, re_infer(ReVar(c))).then { - debug!("combine_vars() c=%?", c); - Ok(re_infer(ReVar(c))) - } - } + relate(self, a, re_infer(ReVar(c))); + relate(self, b, re_infer(ReVar(c))); + debug!("combine_vars() c=%?", c); + re_infer(ReVar(c)) } pub fn vars_created_since_snapshot(&mut self, snapshot: uint) @@ -924,6 +945,9 @@ impl RegionVarBindings { AddConstraint(ConstrainVarSubReg(ref a, ref b)) => { Some((re_infer(ReVar(*a)), *b)) } + AddConstraint(ConstrainRegSubReg(a, b)) => { + Some((a, b)) + } _ => { None } @@ -931,11 +955,11 @@ impl RegionVarBindings { match regs { None => {} - Some((ref r1, ref r2)) => { + Some((r1, r2)) => { result_set = - consider_adding_edge(result_set, &r, r1, r2); + consider_adding_edge(result_set, r, r1, r2); result_set = - consider_adding_edge(result_set, &r, r2, r1); + consider_adding_edge(result_set, r, r2, r1); } } @@ -948,14 +972,14 @@ impl RegionVarBindings { return result_set; fn consider_adding_edge(result_set: ~[Region], - r: &Region, - r1: &Region, - r2: &Region) -> ~[Region] + r: Region, + r1: Region, + r2: Region) -> ~[Region] { let mut result_set = result_set; - if *r == *r1 { // Clearly, this is potentially inefficient. + if r == r1 { // Clearly, this is potentially inefficient. if !result_set.iter().any_(|x| x == r2) { - result_set.push(*r2); + result_set.push(r2); } } return result_set; @@ -969,10 +993,12 @@ impl RegionVarBindings { constraints, assuming such values can be found; if they cannot, errors are reported. */ - pub fn resolve_regions(&mut self) { + pub fn resolve_regions(&mut self) -> OptVec { debug!("RegionVarBindings: resolve_regions()"); - let v = self.infer_variable_values(); + let mut errors = opt_vec::Empty; + let v = self.infer_variable_values(&mut errors); self.values.put_back(v); + errors } } @@ -994,7 +1020,7 @@ impl RegionVarBindings { (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => { self.tcx.sess.span_bug( - self.var_spans[v_id.to_uint()], + self.var_origins[v_id.to_uint()].span(), fmt!("lub_concrete_regions invoked with \ non-concrete regions: %?, %?", a, b)); } @@ -1096,7 +1122,7 @@ impl RegionVarBindings { (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => { self.tcx.sess.span_bug( - self.var_spans[v_id.to_uint()], + self.var_origins[v_id.to_uint()].span(), fmt!("glb_concrete_regions invoked with \ non-concrete regions: %?, %?", a, b)); } @@ -1173,9 +1199,11 @@ impl RegionVarBindings { } } - fn report_type_error(&mut self, span: span, terr: &ty::type_err) { + fn report_type_error(&mut self, + origin: SubregionOrigin, + terr: &ty::type_err) { let terr_str = ty::type_err_to_str(self.tcx, terr); - self.tcx.sess.span_err(span, terr_str); + self.tcx.sess.span_err(origin.span(), terr_str); } fn intersect_scopes(&self, @@ -1210,7 +1238,7 @@ enum Classification { Expanding, Contracting } enum GraphNodeValue { NoValue, Value(Region), ErrorValue } struct GraphNode { - span: span, + origin: RegionVariableOrigin, classification: Classification, value: GraphNodeValue, head_edge: [uint, ..2], @@ -1219,7 +1247,6 @@ struct GraphNode { struct GraphEdge { next_edge: [uint, ..2], constraint: Constraint, - span: span, } struct Graph { @@ -1227,20 +1254,23 @@ struct Graph { edges: ~[GraphEdge], } -struct SpannedRegion { +struct RegionAndOrigin { region: Region, - span: span, + origin: SubregionOrigin, } impl RegionVarBindings { - pub fn infer_variable_values(&mut self) -> ~[GraphNodeValue] { + fn infer_variable_values(&mut self, + errors: &mut OptVec) + -> ~[GraphNodeValue] { let mut graph = self.construct_graph(); self.expansion(&mut graph); self.contraction(&mut graph); - self.extract_values_and_report_conflicts(&graph) + self.collect_concrete_region_errors(&graph, errors); + self.extract_values_and_collect_conflicts(&graph, errors) } - pub fn construct_graph(&mut self) -> Graph { + fn construct_graph(&mut self) -> Graph { let num_vars = self.num_vars(); let num_edges = self.constraints.len(); @@ -1251,7 +1281,7 @@ impl RegionVarBindings { // those nodes that have a concrete region predecessor to // Expanding. classification: Contracting, - span: self.var_spans[var_idx], + origin: self.var_origins[var_idx], value: NoValue, head_edge: [uint::max_value, uint::max_value] } @@ -1259,11 +1289,10 @@ impl RegionVarBindings { // It would be nice to write this using map(): let mut edges = vec::with_capacity(num_edges); - for self.constraints.iter().advance |(constraint, span)| { + for self.constraints.iter().advance |(constraint, _)| { edges.push(GraphEdge { next_edge: [uint::max_value, uint::max_value], constraint: *constraint, - span: *span }); } @@ -1284,6 +1313,10 @@ impl RegionVarBindings { ConstrainVarSubReg(a_id, _) => { insert_edge(&mut graph, a_id, Outgoing, edge_idx); } + ConstrainRegSubReg(*) => { + // Relations between two concrete regions do not + // require an edge in the graph. + } } } @@ -1305,7 +1338,7 @@ impl RegionVarBindings { } } - pub fn expansion(&mut self, graph: &mut Graph) { + fn expansion(&mut self, graph: &mut Graph) { do iterate_until_fixed_point(~"Expansion", graph) |nodes, edge| { match edge.constraint { ConstrainRegSubVar(a_region, b_vid) => { @@ -1325,15 +1358,19 @@ impl RegionVarBindings { // This is a contraction constraint. Ignore it. false } + ConstrainRegSubReg(*) => { + // No region variables involved. Ignore. + false + } } } } - pub fn expand_node(&mut self, - a_region: Region, - b_vid: RegionVid, - b_node: &mut GraphNode) - -> bool { + fn expand_node(&mut self, + a_region: Region, + b_vid: RegionVid, + b_node: &mut GraphNode) + -> bool { debug!("expand_node(%?, %? == %?)", a_region, b_vid, b_node.value); @@ -1365,7 +1402,8 @@ impl RegionVarBindings { } } - pub fn contraction(&mut self, graph: &mut Graph) { + fn contraction(&mut self, + graph: &mut Graph) { do iterate_until_fixed_point(~"Contraction", graph) |nodes, edge| { match edge.constraint { ConstrainRegSubVar(*) => { @@ -1385,15 +1423,19 @@ impl RegionVarBindings { let a_node = &mut nodes[a_vid.to_uint()]; self.contract_node(a_vid, a_node, b_region) } + ConstrainRegSubReg(*) => { + // No region variables involved. Ignore. + false + } } } } - pub fn contract_node(&mut self, - a_vid: RegionVid, - a_node: &mut GraphNode, - b_region: Region) - -> bool { + fn contract_node(&mut self, + a_vid: RegionVid, + a_node: &mut GraphNode, + b_region: Region) + -> bool { debug!("contract_node(%? == %?/%?, %?)", a_vid, a_node.value, a_node.classification, b_region); @@ -1461,9 +1503,42 @@ impl RegionVarBindings { } } - pub fn extract_values_and_report_conflicts(&mut self, graph: &Graph) - -> ~[GraphNodeValue] { - debug!("extract_values_and_report_conflicts()"); + fn collect_concrete_region_errors( + &mut self, + graph: &Graph, + errors: &mut OptVec) + { + let num_edges = graph.edges.len(); + for uint::range(0, num_edges) |edge_idx| { + let edge = &graph.edges[edge_idx]; + let origin = self.constraints.get_copy(&edge.constraint); + + let (sub, sup) = match edge.constraint { + ConstrainVarSubVar(*) | + ConstrainRegSubVar(*) | + ConstrainVarSubReg(*) => { + loop; + } + ConstrainRegSubReg(sub, sup) => { + (sub, sup) + } + }; + + if self.is_subregion_of(sub, sup) { + loop; + } + + errors.push(ConcreteFailure(origin, sub, sup)); + } + } + + fn extract_values_and_collect_conflicts( + &mut self, + graph: &Graph, + errors: &mut OptVec) + -> ~[GraphNodeValue] + { + debug!("extract_values_and_collect_conflicts()"); // This is the best way that I have found to suppress // duplicate and related errors. Basically we keep a set of @@ -1516,12 +1591,12 @@ impl RegionVarBindings { let node_vid = RegionVid { id: idx }; match node.classification { Expanding => { - self.report_error_for_expanding_node( - graph, dup_vec, node_vid); + self.collect_error_for_expanding_node( + graph, dup_vec, node_vid, errors); } Contracting => { - self.report_error_for_contracting_node( - graph, dup_vec, node_vid); + self.collect_error_for_contracting_node( + graph, dup_vec, node_vid, errors); } } } @@ -1531,10 +1606,13 @@ impl RegionVarBindings { }).collect() } - pub fn report_error_for_expanding_node(&mut self, - graph: &Graph, - dup_vec: &mut [uint], - node_idx: RegionVid) { + fn collect_error_for_expanding_node( + &mut self, + graph: &Graph, + dup_vec: &mut [uint], + node_idx: RegionVid, + errors: &mut OptVec) + { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. let (lower_bounds, lower_dup) = @@ -1550,50 +1628,33 @@ impl RegionVarBindings { for upper_bounds.iter().advance |upper_bound| { if !self.is_subregion_of(lower_bound.region, upper_bound.region) { - - self.tcx.sess.span_err( - self.var_spans[node_idx.to_uint()], - fmt!("cannot infer an appropriate lifetime \ - due to conflicting requirements")); - - note_and_explain_region( - self.tcx, - "first, the lifetime cannot outlive ", - upper_bound.region, - "..."); - - self.tcx.sess.span_note( - upper_bound.span, - fmt!("...due to the following expression")); - - note_and_explain_region( - self.tcx, - "but, the lifetime must be valid for ", + errors.push(SubSupConflict( + self.var_origins[node_idx.to_uint()], + lower_bound.origin, lower_bound.region, - "..."); - - self.tcx.sess.span_note( - lower_bound.span, - fmt!("...due to the following expression")); - + upper_bound.origin, + upper_bound.region)); return; } } } self.tcx.sess.span_bug( - self.var_spans[node_idx.to_uint()], - fmt!("report_error_for_expanding_node() could not find error \ + self.var_origins[node_idx.to_uint()].span(), + fmt!("collect_error_for_expanding_node() could not find error \ for var %?, lower_bounds=%s, upper_bounds=%s", node_idx, lower_bounds.map(|x| x.region).repr(self.tcx), upper_bounds.map(|x| x.region).repr(self.tcx))); } - pub fn report_error_for_contracting_node(&mut self, - graph: &Graph, - dup_vec: &mut [uint], - node_idx: RegionVid) { + fn collect_error_for_contracting_node( + &mut self, + graph: &Graph, + dup_vec: &mut [uint], + node_idx: RegionVid, + errors: &mut OptVec) + { // Errors in contracting nodes result from two upper-bounds // that have no intersection. let (upper_bounds, dup_found) = @@ -1609,32 +1670,12 @@ impl RegionVarBindings { upper_bound_2.region) { Ok(_) => {} Err(_) => { - - self.tcx.sess.span_err( - self.var_spans[node_idx.to_uint()], - fmt!("cannot infer an appropriate lifetime \ - due to conflicting requirements")); - - note_and_explain_region( - self.tcx, - "first, the lifetime must be contained by ", + errors.push(SupSupConflict( + self.var_origins[node_idx.to_uint()], + upper_bound_1.origin, upper_bound_1.region, - "..."); - - self.tcx.sess.span_note( - upper_bound_1.span, - fmt!("...due to the following expression")); - - note_and_explain_region( - self.tcx, - "but, the lifetime must also be contained by ", - upper_bound_2.region, - "..."); - - self.tcx.sess.span_note( - upper_bound_2.span, - fmt!("...due to the following expression")); - + upper_bound_2.origin, + upper_bound_2.region)); return; } } @@ -1642,23 +1683,23 @@ impl RegionVarBindings { } self.tcx.sess.span_bug( - self.var_spans[node_idx.to_uint()], - fmt!("report_error_for_contracting_node() could not find error \ + self.var_origins[node_idx.to_uint()].span(), + fmt!("collect_error_for_contracting_node() could not find error \ for var %?, upper_bounds=%s", node_idx, upper_bounds.map(|x| x.region).repr(self.tcx))); } - pub fn collect_concrete_regions(&mut self, - graph: &Graph, - orig_node_idx: RegionVid, - dir: Direction, - dup_vec: &mut [uint]) - -> (~[SpannedRegion], bool) { + fn collect_concrete_regions(&mut self, + graph: &Graph, + orig_node_idx: RegionVid, + dir: Direction, + dup_vec: &mut [uint]) + -> (~[RegionAndOrigin], bool) { struct WalkState { set: HashSet, stack: ~[RegionVid], - result: ~[SpannedRegion], + result: ~[RegionAndOrigin], dup_found: bool } let mut state = WalkState { @@ -1720,17 +1761,19 @@ impl RegionVarBindings { ConstrainRegSubVar(region, _) | ConstrainVarSubReg(_, region) => { - state.result.push(SpannedRegion { + state.result.push(RegionAndOrigin { region: region, - span: edge.span + origin: this.constraints.get_copy(&edge.constraint) }); } + + ConstrainRegSubReg(*) => {} } } } } - pub fn each_edge(&mut self, + pub fn each_edge(&self, graph: &Graph, node_idx: RegionVid, dir: Direction, diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 905e86a73f06..ea66f8601af4 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -20,6 +20,7 @@ use middle::typeck::infer::InferCtxt; use middle::typeck::infer::lattice::CombineFieldsLatticeMethods; use middle::typeck::infer::lub::Lub; use middle::typeck::infer::to_str::InferStr; +use middle::typeck::infer::{SubtypeTrace, 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) -> SubtypeTrace { self.trace } fn sub(&self) -> Sub { Sub(**self) } fn lub(&self) -> Lub { Lub(**self) } @@ -62,12 +63,8 @@ impl Combine for Sub { 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 { @@ -170,7 +167,7 @@ impl Combine for Sub { // 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. diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 4eaa0b69d9cc..bbcfc73853a1 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -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() + ": " + diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 80344a9894fb..1a0cdd6fa64f 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -608,6 +608,12 @@ impl Repr for @ast::pat { } } +impl Repr for ty::bound_region { + fn repr(&self, tcx: ctxt) -> ~str { + bound_region_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 @@ impl Repr for ty::BuiltinBounds { } } +impl Repr for span { + fn repr(&self, tcx: ctxt) -> ~str { + tcx.sess.codemap.span_to_str(*self) + } +} + +impl 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() { ~"" } else { diff --git a/src/test/compile-fail/arc-rw-cond-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-cond-shouldnt-escape.rs index 82868647e57d..b00b701191e2 100644 --- a/src/test/compile-fail/arc-rw-cond-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-cond-shouldnt-escape.rs @@ -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() { diff --git a/src/test/compile-fail/arc-rw-state-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-state-shouldnt-escape.rs index 6bd32866f898..001e6cf922f6 100644 --- a/src/test/compile-fail/arc-rw-state-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-state-shouldnt-escape.rs @@ -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 } diff --git a/src/test/compile-fail/arc-rw-write-mode-cond-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-write-mode-cond-shouldnt-escape.rs index 534475319033..59e899dbbf2e 100644 --- a/src/test/compile-fail/arc-rw-write-mode-cond-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-write-mode-cond-shouldnt-escape.rs @@ -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() { diff --git a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs index decb7b8af9f3..2599fb4dfa0c 100644 --- a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs @@ -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() { diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index 0efde1df6c22..4ee824d1d49a 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -1,6 +1,4 @@ -extern mod extra; - -fn main() { +pub fn main() { let foo = ~3; let _pfoo = &foo; let _f: @fn() -> int = || *foo + 5; diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index 6bb90bff228d..33e122867bb3 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -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 } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 35ba38624380..f92ea7f18cec 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -16,11 +16,13 @@ struct an_enum<'self>(&'self int); struct a_class<'self> { x:&'self int } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { - return e; //~ ERROR mismatched types: expected `an_enum<'b>` but found `an_enum<'a>` + return e; //~ ERROR mismatched types: expected `an_enum<'b> ` but found `an_enum<'a> ` + //~^ ERROR cannot infer an appropriate lifetime } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { - return e; //~ ERROR mismatched types: expected `a_class<'b>` but found `a_class<'a>` + return e; //~ ERROR mismatched types: expected `a_class<'b> ` but found `a_class<'a> ` + //~^ ERROR cannot infer an appropriate lifetime } fn a_fn4<'a,'b>() { diff --git a/src/test/compile-fail/regions-escape-bound-fn-2.rs b/src/test/compile-fail/regions-escape-bound-fn-2.rs index 9cee55643f89..305aa6852849 100644 --- a/src/test/compile-fail/regions-escape-bound-fn-2.rs +++ b/src/test/compile-fail/regions-escape-bound-fn-2.rs @@ -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)); } diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs index aa431d6b81c6..5b6dc1b2f4fb 100644 --- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs +++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs @@ -23,7 +23,7 @@ fn with(f: &fn(x: &int) -> R) -> int { } fn return_it() -> int { - with(|o| o) //~ ERROR reference is not valid outside of its lifetime + with(|o| o) //~ ERROR lifetime of function argument does not outlive the function call } fn main() { diff --git a/src/test/compile-fail/regions-infer-at-fn-not-param.rs b/src/test/compile-fail/regions-infer-at-fn-not-param.rs index c8813b73e6b3..488d1f3940d6 100644 --- a/src/test/compile-fail/regions-infer-at-fn-not-param.rs +++ b/src/test/compile-fail/regions-infer-at-fn-not-param.rs @@ -20,7 +20,10 @@ struct not_parameterized2 { g: @fn() } -fn take1(p: parameterized1) -> parameterized1 { p } //~ ERROR mismatched types +fn take1(p: parameterized1) -> parameterized1 { p } +//~^ ERROR mismatched types +//~^^ ERROR cannot infer an appropriate lifetime + fn take3(p: not_parameterized1) -> not_parameterized1 { p } fn take4(p: not_parameterized2) -> not_parameterized2 { p } diff --git a/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs b/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs index 85cc6e6ce248..c33ca2dab2ee 100644 --- a/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs +++ b/src/test/compile-fail/regions-infer-covariance-due-to-arg.rs @@ -22,6 +22,7 @@ fn to_same_lifetime<'r>(bi: covariant<'r>) { fn to_shorter_lifetime<'r>(bi: covariant<'r>) { let bj: covariant<'blk> = bi; //~ ERROR mismatched types + //~^ ERROR cannot infer an appropriate lifetime } fn to_longer_lifetime<'r>(bi: covariant<'r>) -> covariant<'static> { diff --git a/src/test/compile-fail/regions-infer-not-param.rs b/src/test/compile-fail/regions-infer-not-param.rs index a0ecb08a0895..fa853b82d9eb 100644 --- a/src/test/compile-fail/regions-infer-not-param.rs +++ b/src/test/compile-fail/regions-infer-not-param.rs @@ -23,6 +23,11 @@ struct indirect2<'self> { } fn take_direct(p: direct) -> direct { p } //~ ERROR mismatched types +//~^ ERROR cannot infer an appropriate lifetime + fn take_indirect1(p: indirect1) -> indirect1 { p } + fn take_indirect2(p: indirect2) -> indirect2 { p } //~ ERROR mismatched types +//~^ ERROR cannot infer an appropriate lifetime + fn main() {} diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index e8d66ab297b7..0b4aa44010bd 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -30,6 +30,7 @@ impl<'self> set_f<'self> for c<'self> { fn set_f_bad(&self, b: @b) { self.f = b; //~ ERROR mismatched types: expected `@@&'self int` but found `@@&int` + //~^ ERROR cannot infer an appropriate lifetime } } diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs index 74399967446e..244e9cc06a1a 100644 --- a/src/test/compile-fail/regions-nested-fns.rs +++ b/src/test/compile-fail/regions-nested-fns.rs @@ -22,6 +22,7 @@ fn nested<'x>(x: &'x int) { ignore::<&fn<'z>(&'z int) -> &'z int>(|z| { if false { return x; } //~ ERROR mismatched types + //~^ ERROR cannot infer an appropriate lifetime if false { return ay; } return z; }); diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index a572d90313b6..542711687190 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -18,6 +18,8 @@ fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~ ERROR mismatched types + //~^ ERROR lifetime of return value does not outlive the function call + //~^^ ERROR cannot infer an appropriate lifetime } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index ec9a908ba987..4d646aa364a4 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -21,6 +21,8 @@ fn with(f: &fn(x: &int) -> R) -> R { fn return_it() -> &int { with(|o| o) //~ ERROR mismatched types + //~^ ERROR lifetime of return value does not outlive the function call + //~^^ ERROR cannot infer an appropriate lifetime } fn main() { diff --git a/src/test/compile-fail/sync-cond-shouldnt-escape.rs b/src/test/compile-fail/sync-cond-shouldnt-escape.rs index b22d4d3b2e2e..2006027e7970 100644 --- a/src/test/compile-fail/sync-cond-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-cond-shouldnt-escape.rs @@ -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::sync; diff --git a/src/test/compile-fail/sync-rwlock-cond-shouldnt-escape.rs b/src/test/compile-fail/sync-rwlock-cond-shouldnt-escape.rs index 518e67800d76..4108201f9115 100644 --- a/src/test/compile-fail/sync-rwlock-cond-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-rwlock-cond-shouldnt-escape.rs @@ -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 method receiver does not outlive the method call extern mod extra; use extra::sync; fn main() { diff --git a/src/test/compile-fail/sync-rwlock-write-mode-cond-shouldnt-escape.rs b/src/test/compile-fail/sync-rwlock-write-mode-cond-shouldnt-escape.rs index 09b83887bcfd..43b4d9aabb87 100644 --- a/src/test/compile-fail/sync-rwlock-write-mode-cond-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-rwlock-write-mode-cond-shouldnt-escape.rs @@ -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::sync; fn main() { diff --git a/src/test/compile-fail/sync-rwlock-write-mode-shouldnt-escape.rs b/src/test/compile-fail/sync-rwlock-write-mode-shouldnt-escape.rs index 679c4a72598e..15af7be52468 100644 --- a/src/test/compile-fail/sync-rwlock-write-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-rwlock-write-mode-shouldnt-escape.rs @@ -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::sync; fn main() {