diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs new file mode 100644 index 0000000000000..be5314a2c170a --- /dev/null +++ b/src/librustc/infer/anon_types/mod.rs @@ -0,0 +1,626 @@ +// Copyright 2012-2015 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. + +use hir::def_id::DefId; +use infer::{self, InferCtxt, InferOk, TypeVariableOrigin}; +use infer::outlives::free_region_map::FreeRegionRelations; +use rustc_data_structures::fx::FxHashMap; +use syntax::ast; +use traits::{self, PredicateObligation}; +use ty::{self, Ty}; +use ty::fold::{BottomUpFolder, TypeFoldable}; +use ty::outlives::Component; +use ty::subst::{Kind, Substs}; +use util::nodemap::DefIdMap; + +pub type AnonTypeMap<'tcx> = DefIdMap>; + +/// Information about the anonymous, abstract types whose values we +/// are inferring in this function (these are the `impl Trait` that +/// appear in the return type). +#[derive(Copy, Clone, Debug)] +pub struct AnonTypeDecl<'tcx> { + /// The substitutions that we apply to the abstract that that this + /// `impl Trait` desugars to. e.g., if: + /// + /// fn foo<'a, 'b, T>() -> impl Trait<'a> + /// + /// winds up desugared to: + /// + /// abstract type Foo<'x, T>: Trait<'x> + /// fn foo<'a, 'b, T>() -> Foo<'a, T> + /// + /// then `substs` would be `['a, T]`. + pub substs: &'tcx Substs<'tcx>, + + /// The type variable that represents the value of the abstract type + /// that we require. In other words, after we compile this function, + /// we will be created a constraint like: + /// + /// Foo<'a, T> = ?C + /// + /// where `?C` is the value of this type variable. =) It may + /// naturally refer to the type and lifetime parameters in scope + /// in this function, though ultimately it should only reference + /// those that are arguments to `Foo` in the constraint above. (In + /// other words, `?C` should not include `'b`, even though it's a + /// lifetime parameter on `foo`.) + pub concrete_ty: Ty<'tcx>, + + /// True if the `impl Trait` bounds include region bounds. + /// For example, this would be true for: + /// + /// fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b + /// + /// but false for: + /// + /// fn foo<'c>() -> impl Trait<'c> + /// + /// unless `Trait` was declared like: + /// + /// trait Trait<'c>: 'c + /// + /// in which case it would be true. + /// + /// This is used during regionck to decide whether we need to + /// impose any additional constraints to ensure that region + /// variables in `concrete_ty` wind up being constrained to + /// something from `substs` (or, at minimum, things that outlive + /// the fn body). (Ultimately, writeback is responsible for this + /// check.) + pub has_required_region_bounds: bool, +} + +impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { + /// Replace all anonymized types in `value` with fresh inference variables + /// and creates appropriate obligations. For example, given the input: + /// + /// impl Iterator + /// + /// this method would create two type variables, `?0` and `?1`. It would + /// return the type `?0` but also the obligations: + /// + /// ?0: Iterator + /// ?1: Debug + /// + /// Moreover, it returns a `AnonTypeMap` that would map `?0` to + /// info about the `impl Iterator<..>` type and `?1` to info about + /// the `impl Debug` type. + /// + /// # Parameters + /// + /// - `parent_def_id` -- we will only instantiate anonymous types + /// with this parent. This is typically the def-id of the function + /// in whose return type anon types are being instantiated. + /// - `body_id` -- the body-id with which the resulting obligations should + /// be associated + /// - `param_env` -- the in-scope parameter environment to be used for + /// obligations + /// - `value` -- the value within which we are instantiating anon types + pub fn instantiate_anon_types>( + &self, + parent_def_id: DefId, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T, + ) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> { + debug!( + "instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})", + value, + parent_def_id, + body_id, + param_env, + ); + let mut instantiator = Instantiator { + infcx: self, + parent_def_id, + body_id, + param_env, + anon_types: DefIdMap(), + obligations: vec![], + }; + let value = instantiator.instantiate_anon_types_in_map(value); + InferOk { + value: (value, instantiator.anon_types), + obligations: instantiator.obligations, + } + } + + /// Given the map `anon_types` containing the existential `impl + /// Trait` types whose underlying, hidden types are being + /// inferred, this method adds constraints to the regions + /// appearing in those underlying hidden types to ensure that they + /// at least do not refer to random scopes within the current + /// function. These constraints are not (quite) sufficient to + /// guarantee that the regions are actually legal values; that + /// final condition is imposed after region inference is done. + /// + /// # The Problem + /// + /// Let's work through an example to explain how it works. Assume + /// the current function is as follows: + /// + /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) + /// + /// Here, we have two `impl Trait` types whose values are being + /// inferred (the `impl Bar<'a>` and the `impl + /// Bar<'b>`). Conceptually, this is sugar for a setup where we + /// define underlying abstract types (`Foo1`, `Foo2`) and then, in + /// the return type of `foo`, we *reference* those definitions: + /// + /// abstract type Foo1<'x>: Bar<'x>; + /// abstract type Foo2<'x>: Bar<'x>; + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// // ^^^^ ^^ + /// // | | + /// // | substs + /// // def_id + /// + /// As indicating in the comments above, each of those references + /// is (in the compiler) basically a substitution (`substs`) + /// applied to the type of a suitable `def_id` (which identifies + /// `Foo1` or `Foo2`). + /// + /// Now, at this point in compilation, what we have done is to + /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with + /// fresh inference variables C1 and C2. We wish to use the values + /// of these variables to infer the underlying types of `Foo1` and + /// `Foo2`. That is, this gives rise to higher-order (pattern) unification + /// constraints like: + /// + /// for<'a> (Foo1<'a> = C1) + /// for<'b> (Foo1<'b> = C2) + /// + /// For these equation to be satisfiable, the types `C1` and `C2` + /// can only refer to a limited set of regions. For example, `C1` + /// can only refer to `'static` and `'a`, and `C2` can only refer + /// to `'static` and `'b`. The job of this function is to impose that + /// constraint. + /// + /// Up to this point, C1 and C2 are basically just random type + /// inference variables, and hence they may contain arbitrary + /// regions. In fact, it is fairly likely that they do! Consider + /// this possible definition of `foo`: + /// + /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { + /// (&*x, &*y) + /// } + /// + /// Here, the values for the concrete types of the two impl + /// traits will include inference variables: + /// + /// &'0 i32 + /// &'1 i32 + /// + /// Ordinarily, the subtyping rules would ensure that these are + /// sufficiently large. But since `impl Bar<'a>` isn't a specific + /// type per se, we don't get such constraints by default. This + /// is where this function comes into play. It adds extra + /// constraints to ensure that all the regions which appear in the + /// inferred type are regions that could validly appear. + /// + /// This is actually a bit of a tricky constraint in general. We + /// want to say that each variable (e.g., `'0``) can only take on + /// values that were supplied as arguments to the abstract type + /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in + /// scope. We don't have a constraint quite of this kind in the current + /// region checker. + /// + /// # The Solution + /// + /// We make use of the constraint that we *do* have in the `<=` + /// relation. To do that, we find the "minimum" of all the + /// arguments that appear in the substs: that is, some region + /// which is less than all the others. In the case of `Foo1<'a>`, + /// that would be `'a` (it's the only choice, after all). Then we + /// apply that as a least bound to the variables (e.g., `'a <= + /// '0`). + /// + /// In some cases, there is no minimum. Consider this example: + /// + /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } + /// + /// Here we would report an error, because `'a` and `'b` have no + /// relation to one another. + /// + /// # The `free_region_relations` parameter + /// + /// The `free_region_relations` argument is used to find the + /// "minimum" of the regions supplied to a given abstract type. + /// It must be a relation that can answer whether `'a <= 'b`, + /// where `'a` and `'b` are regions that appear in the "substs" + /// for the abstract type references (the `<'a>` in `Foo1<'a>`). + /// + /// Note that we do not impose the constraints based on the + /// generic regions from the `Foo1` definition (e.g., `'x`). This + /// is because the constraints we are imposing here is basically + /// the concern of the one generating the constraining type C1, + /// which is the current function. It also means that we can + /// take "implied bounds" into account in some cases: + /// + /// trait SomeTrait<'a, 'b> { } + /// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. } + /// + /// Here, the fact that `'b: 'a` is known only because of the + /// implied bounds from the `&'a &'b u32` parameter, and is not + /// "inherent" to the abstract type definition. + /// + /// # Parameters + /// + /// - `anon_types` -- the map produced by `instantiate_anon_types` + /// - `free_region_relations` -- something that can be used to relate + /// the free regions (`'a`) that appear in the impl trait. + pub fn constrain_anon_types>( + &self, + anon_types: &AnonTypeMap<'tcx>, + free_region_relations: &FRR, + ) { + debug!("constrain_anon_types()"); + + for (&def_id, anon_defn) in anon_types { + self.constrain_anon_type(def_id, anon_defn, free_region_relations); + } + } + + fn constrain_anon_type>( + &self, + def_id: DefId, + anon_defn: &AnonTypeDecl<'tcx>, + free_region_relations: &FRR, + ) { + debug!("constrain_anon_type()"); + debug!("constrain_anon_type: def_id={:?}", def_id); + debug!("constrain_anon_type: anon_defn={:#?}", anon_defn); + + let concrete_ty = self.resolve_type_vars_if_possible(&anon_defn.concrete_ty); + + debug!("constrain_anon_type: concrete_ty={:?}", concrete_ty); + + let abstract_type_generics = self.tcx.generics_of(def_id); + + let span = self.tcx.def_span(def_id); + + // If there are required region bounds, we can just skip + // ahead. There will already be a registered region + // obligation related `concrete_ty` to those regions. + if anon_defn.has_required_region_bounds { + return; + } + + // There were no `required_region_bounds`, + // so we have to search for a `least_region`. + // Go through all the regions used as arguments to the + // abstract type. These are the parameters to the abstract + // type; so in our example above, `substs` would contain + // `['a]` for the first impl trait and `'b` for the + // second. + let mut least_region = None; + for region_def in &abstract_type_generics.regions { + // Find the index of this region in the list of substitutions. + let index = region_def.index as usize; + + // Get the value supplied for this region from the substs. + let subst_arg = anon_defn.substs[index].as_region().unwrap(); + + // Compute the least upper bound of it with the other regions. + debug!("constrain_anon_types: least_region={:?}", least_region); + debug!("constrain_anon_types: subst_arg={:?}", subst_arg); + match least_region { + None => least_region = Some(subst_arg), + Some(lr) => { + if free_region_relations.sub_free_regions(lr, subst_arg) { + // keep the current least region + } else if free_region_relations.sub_free_regions(subst_arg, lr) { + // switch to `subst_arg` + least_region = Some(subst_arg); + } else { + // There are two regions (`lr` and + // `subst_arg`) which are not relatable. We can't + // find a best choice. + self.tcx + .sess + .struct_span_err(span, "ambiguous lifetime bound in `impl Trait`") + .span_label( + span, + format!("neither `{}` nor `{}` outlives the other", lr, subst_arg), + ) + .emit(); + + least_region = Some(self.tcx.mk_region(ty::ReEmpty)); + break; + } + } + } + } + + let least_region = least_region.unwrap_or(self.tcx.types.re_static); + debug!("constrain_anon_types: least_region={:?}", least_region); + + // Require that the type `concrete_ty` outlives + // `least_region`, modulo any type parameters that appear + // in the type, which we ignore. This is because impl + // trait values are assumed to capture all the in-scope + // type parameters. This little loop here just invokes + // `outlives` repeatedly, draining all the nested + // obligations that result. + let mut types = vec![concrete_ty]; + let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r); + while let Some(ty) = types.pop() { + let mut components = self.tcx.outlives_components(ty); + while let Some(component) = components.pop() { + match component { + Component::Region(r) => { + bound_region(r); + } + + Component::Param(_) => { + // ignore type parameters like `T`, they are captured + // implicitly by the `impl Trait` + } + + Component::UnresolvedInferenceVariable(_) => { + // we should get an error that more type + // annotations are needed in this case + self.tcx + .sess + .delay_span_bug(span, "unresolved inf var in anon"); + } + + Component::Projection(ty::ProjectionTy { + substs, + item_def_id: _, + }) => { + for r in substs.regions() { + bound_region(r); + } + types.extend(substs.types()); + } + + Component::EscapingProjection(more_components) => { + components.extend(more_components); + } + } + } + } + } + + /// Given the fully resolved, instantiated type for an anonymous + /// type, i.e., the value of an inference variable like C1 or C2 + /// (*), computes the "definition type" for an abstract type + /// definition -- that is, the inferred value of `Foo1<'x>` or + /// `Foo2<'x>` that we would conceptually use in its definition: + /// + /// abstract type Foo1<'x>: Bar<'x> = AAA; <-- this type AAA + /// abstract type Foo2<'x>: Bar<'x> = BBB; <-- or this type BBB + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// + /// Note that these values are defined in terms of a distinct set of + /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main + /// purpose of this function is to do that translation. + /// + /// (*) C1 and C2 were introduced in the comments on + /// `constrain_anon_types`. Read that comment for more context. + /// + /// # Parameters + /// + /// - `def_id`, the `impl Trait` type + /// - `anon_defn`, the anonymous definition created in `instantiate_anon_types` + /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of + /// `anon_defn.concrete_ty` + pub fn infer_anon_definition_from_instantiation( + &self, + def_id: DefId, + anon_defn: &AnonTypeDecl<'tcx>, + instantiated_ty: Ty<'gcx>, + ) -> Ty<'gcx> { + debug!( + "infer_anon_definition_from_instantiation(instantiated_ty={:?})", + instantiated_ty + ); + + let gcx = self.tcx.global_tcx(); + + // Use substs to build up a reverse map from regions to their + // identity mappings. This is necessary because of `impl + // Trait` lifetimes are computed by replacing existing + // lifetimes with 'static and remapping only those used in the + // `impl Trait` return type, resulting in the parameters + // shifting. + let id_substs = Substs::identity_for_item(gcx, def_id); + let map: FxHashMap, Kind<'gcx>> = anon_defn + .substs + .iter() + .enumerate() + .map(|(index, subst)| (*subst, id_substs[index])) + .collect(); + + // Convert the type from the function into a type valid outside + // the function, by replacing invalid regions with 'static, + // after producing an error for each of them. + let definition_ty = gcx.fold_regions(&instantiated_ty, &mut false, |r, _| { + match *r { + // 'static and early-bound regions are valid. + ty::ReStatic | ty::ReEmpty => r, + + // All other regions, we map them appropriately to their adjusted + // indices, erroring if we find any lifetimes that were not mapped + // into the new set. + _ => if let Some(r1) = map.get(&Kind::from(r)).and_then(|k| k.as_region()) { + r1 + } else { + // No mapping was found. This means that + // it is either a disallowed lifetime, + // which will be caught by regionck, or it + // is a region in a non-upvar closure + // generic, which is explicitly + // allowed. If that surprises you, read + // on. + // + // The case of closure is a somewhat + // subtle (read: hacky) consideration. The + // problem is that our closure types + // currently include all the lifetime + // parameters declared on the enclosing + // function, even if they are unused by + // the closure itself. We can't readily + // filter them out, so here we replace + // those values with `'empty`. This can't + // really make a difference to the rest of + // the compiler; those regions are ignored + // for the outlives relation, and hence + // don't affect trait selection or auto + // traits, and they are erased during + // trans. + gcx.types.re_empty + }, + } + }); + + debug!( + "infer_anon_definition_from_instantiation: definition_ty={:?}", + definition_ty + ); + + definition_ty + } +} + +struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + parent_def_id: DefId, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + anon_types: AnonTypeMap<'tcx>, + obligations: Vec>, +} + +impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { + fn instantiate_anon_types_in_map>(&mut self, value: &T) -> T { + debug!("instantiate_anon_types_in_map(value={:?})", value); + let tcx = self.infcx.tcx; + value.fold_with(&mut BottomUpFolder { + tcx, + fldop: |ty| { + if let ty::TyAnon(def_id, substs) = ty.sty { + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named abstract types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using `abstract type` that indicates + // the distinction we are checking for: + // + // ```rust + // mod a { + // pub abstract type Foo: Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references a + // `TyAnon` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Anon assoc with `foo` + // } + // ``` + if let Some(anon_node_id) = tcx.hir.as_local_node_id(def_id) { + let anon_parent_node_id = tcx.hir.get_parent(anon_node_id); + let anon_parent_def_id = tcx.hir.local_def_id(anon_parent_node_id); + if self.parent_def_id == anon_parent_def_id { + return self.fold_anon_ty(ty, def_id, substs); + } + + debug!("instantiate_anon_types_in_map: \ + encountered anon with wrong parent \ + def_id={:?} \ + anon_parent_def_id={:?}", + def_id, + anon_parent_def_id); + } + } + + ty + }, + }) + } + + fn fold_anon_ty( + &mut self, + ty: Ty<'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>, + ) -> Ty<'tcx> { + let infcx = self.infcx; + let tcx = infcx.tcx; + + debug!( + "instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", + def_id, + substs + ); + + // Use the same type variable if the exact same TyAnon appears more + // than once in the return type (e.g. if it's passed to a type alias). + if let Some(anon_defn) = self.anon_types.get(&def_id) { + return anon_defn.concrete_ty; + } + let span = tcx.def_span(def_id); + let ty_var = infcx.next_ty_var(TypeVariableOrigin::TypeInference(span)); + + let predicates_of = tcx.predicates_of(def_id); + let bounds = predicates_of.instantiate(tcx, substs); + debug!("instantiate_anon_types: bounds={:?}", bounds); + + let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone()); + debug!( + "instantiate_anon_types: required_region_bounds={:?}", + required_region_bounds + ); + + self.anon_types.insert( + def_id, + AnonTypeDecl { + substs, + concrete_ty: ty_var, + has_required_region_bounds: !required_region_bounds.is_empty(), + }, + ); + debug!("instantiate_anon_types: ty_var={:?}", ty_var); + + for predicate in bounds.predicates { + // Change the predicate to refer to the type variable, + // which will be the concrete type, instead of the TyAnon. + // This also instantiates nested `impl Trait`. + let predicate = self.instantiate_anon_types_in_map(&predicate); + + let cause = traits::ObligationCause::new(span, self.body_id, traits::SizedReturnType); + + // Require that the predicate holds for the concrete type. + debug!("instantiate_anon_types: predicate={:?}", predicate); + self.obligations + .push(traits::Obligation::new(cause, self.param_env, predicate)); + } + + ty_var + } +} diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs deleted file mode 100644 index cade67a44bb0e..0000000000000 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ /dev/null @@ -1,415 +0,0 @@ -// 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 for Anonymous Region Lifetime Errors -//! where both the regions are anonymous. -use hir; -use infer::InferCtxt; -use ty::{self, Region}; -use infer::lexical_region_resolve::RegionResolutionError::*; -use infer::lexical_region_resolve::RegionResolutionError; -use hir::map as hir_map; -use middle::resolve_lifetime as rl; -use hir::intravisit::{self, Visitor, NestedVisitorMap}; -use infer::error_reporting::util::AnonymousArgInfo; - -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - /// Print the error message for lifetime errors when both the concerned regions are anonymous. - /// - /// Consider a case where we have - /// - /// ```no_run - /// fn foo(x: &mut Vec<&u8>, y: &u8) { - /// x.push(y); - /// } - /// ``` - /// - /// The example gives - /// - /// ```text - /// fn foo(x: &mut Vec<&u8>, y: &u8) { - /// --- --- these references are declared with different lifetimes... - /// x.push(y); - /// ^ ...but data from `y` flows into `x` here - /// ``` - /// - /// It has been extended for the case of structs too. - /// - /// Consider the example - /// - /// ```no_run - /// struct Ref<'a> { x: &'a u32 } - /// ``` - /// - /// ```text - /// fn foo(mut x: Vec, y: Ref) { - /// --- --- these structs are declared with different lifetimes... - /// x.push(y); - /// ^ ...but data from `y` flows into `x` here - /// } - /// ```` - /// - /// It will later be extended to trait objects. - pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool { - let (span, sub, sup) = match *error { - ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), - SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup), - _ => return false, // inapplicable - }; - - // Determine whether the sub and sup consist of both anonymous (elided) regions. - let anon_reg_sup = or_false!(self.is_suitable_region(sup)); - - let anon_reg_sub = or_false!(self.is_suitable_region(sub)); - let scope_def_id_sup = anon_reg_sup.def_id; - let bregion_sup = anon_reg_sup.boundregion; - let scope_def_id_sub = anon_reg_sub.def_id; - let bregion_sub = anon_reg_sub.boundregion; - - let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup)); - - let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub)); - - debug!("try_report_anon_anon_conflict: found_arg1={:?} sup={:?} br1={:?}", - ty_sub, - sup, - bregion_sup); - debug!("try_report_anon_anon_conflict: found_arg2={:?} sub={:?} br2={:?}", - ty_sup, - sub, - bregion_sub); - - let (ty_sup, ty_fndecl_sup) = ty_sup; - let (ty_sub, ty_fndecl_sub) = ty_sub; - - let AnonymousArgInfo { arg: anon_arg_sup, .. } = - or_false!(self.find_arg_with_region(sup, sup)); - let AnonymousArgInfo { arg: anon_arg_sub, .. } = - or_false!(self.find_arg_with_region(sub, sub)); - - let sup_is_ret_type = - self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); - let sub_is_ret_type = - self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - - let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() { - format!(" from `{}`", simple_name) - } else { - format!("") - }; - - let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() { - format!(" into `{}`", simple_name) - } else { - format!("") - }; - - - let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) { - (None, None) => { - let (main_label_1, span_label_1) = if ty_sup == ty_sub { - - (format!("this type is declared with multiple lifetimes..."), - format!("...but data{} flows{} here", - format!(" with one lifetime"), - format!(" into the other"))) - } else { - (format!("these two types are declared with different lifetimes..."), - format!("...but data{} flows{} here", - span_label_var1, - span_label_var2)) - }; - (ty_sup.span, ty_sub.span, main_label_1, span_label_1) - } - - (Some(ret_span), _) => { - (ty_sub.span, - ret_span, - format!("this parameter and the return type are declared \ - with different lifetimes...",), - format!("...but data{} is returned here", span_label_var1)) - } - (_, Some(ret_span)) => { - (ty_sup.span, - ret_span, - format!("this parameter and the return type are declared \ - with different lifetimes...",), - format!("...but data{} is returned here", span_label_var1)) - } - }; - - - struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch") - .span_label(span_1, main_label) - .span_label(span_2, format!("")) - .span_label(span, span_label) - .emit(); - return true; - } - - /// This function calls the `visit_ty` method for the parameters - /// corresponding to the anonymous regions. The `nested_visitor.found_type` - /// contains the anonymous type. - /// - /// # Arguments - /// region - the anonymous region corresponding to the anon_anon conflict - /// br - the bound region corresponding to the above region which is of type `BrAnon(_)` - /// - /// # Example - /// ``` - /// fn foo(x: &mut Vec<&u8>, y: &u8) - /// { x.push(y); } - /// ``` - /// The function returns the nested type corresponding to the anonymous region - /// for e.g. `&u8` and Vec<`&u8`. - pub fn find_anon_type(&self, - region: Region<'tcx>, - br: &ty::BoundRegion) - -> Option<(&hir::Ty, &hir::FnDecl)> { - if let Some(anon_reg) = self.is_suitable_region(region) { - let def_id = anon_reg.def_id; - if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { - let fndecl = match self.tcx.hir.get(node_id) { - hir_map::NodeItem(&hir::Item { node: hir::ItemFn(ref fndecl, ..), .. }) => { - &fndecl - } - hir_map::NodeTraitItem(&hir::TraitItem { - node: hir::TraitItemKind::Method(ref m, ..), .. - }) | - hir_map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(ref m, ..), .. - }) => &m.decl, - _ => return None, - }; - - return fndecl - .inputs - .iter() - .filter_map(|arg| self.find_component_for_bound_region(arg, br)) - .next() - .map(|ty| (ty, &**fndecl)); - } - } - None - } - - // This method creates a FindNestedTypeVisitor which returns the type corresponding - // to the anonymous region. - fn find_component_for_bound_region(&self, - arg: &'gcx hir::Ty, - br: &ty::BoundRegion) - -> Option<(&'gcx hir::Ty)> { - let mut nested_visitor = FindNestedTypeVisitor { - infcx: &self, - hir_map: &self.tcx.hir, - bound_region: *br, - found_type: None, - depth: 1, - }; - nested_visitor.visit_ty(arg); - nested_visitor.found_type - } -} - -// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the -// anonymous region. The example above would lead to a conflict between -// the two anonymous lifetimes for &u8 in x and y respectively. This visitor -// would be invoked twice, once for each lifetime, and would -// walk the types like &mut Vec<&u8> and &u8 looking for the HIR -// where that lifetime appears. This allows us to highlight the -// specific part of the type in the error message. -struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - hir_map: &'a hir::map::Map<'gcx>, - // The bound_region corresponding to the Refree(freeregion) - // associated with the anonymous region we are looking for. - bound_region: ty::BoundRegion, - // The type where the anonymous lifetime appears - // for e.g. Vec<`&u8`> and <`&u8`> - found_type: Option<&'gcx hir::Ty>, - depth: u32, -} - -impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { - NestedVisitorMap::OnlyBodies(&self.hir_map) - } - - fn visit_ty(&mut self, arg: &'gcx hir::Ty) { - match arg.node { - hir::TyBareFn(_) => { - self.depth += 1; - intravisit::walk_ty(self, arg); - self.depth -= 1; - return; - } - - hir::TyTraitObject(ref bounds, _) => { - for bound in bounds { - self.depth += 1; - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - self.depth -= 1; - } - } - - hir::TyRptr(ref lifetime, _) => { - // the lifetime of the TyRptr - let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id); - match (self.infcx.tcx.named_region(hir_id), self.bound_region) { - // Find the index of the anonymous region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), - ty::BrAnon(br_index)) => { - debug!("LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", - debruijn_index.depth, - anon_index, - br_index); - if debruijn_index.depth == self.depth && anon_index == br_index { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - - // Find the index of the named region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { - debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ - def_id={:?}", id, def_id); - if id == def_id { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - - // Find the index of the named region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - ( - Some(rl::Region::LateBound(debruijn_index, id, _)), - ty::BrNamed(def_id, _) - ) => { - debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", - debruijn_index.depth); - debug!("self.infcx.tcx.hir.local_def_id(id)={:?}", id); - debug!("def_id={:?}", def_id); - if debruijn_index.depth == self.depth && id == def_id { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - - (Some(rl::Region::Static), _) | - (Some(rl::Region::Free(_, _)), _) | - (Some(rl::Region::EarlyBound(_, _, _)), _) | - (Some(rl::Region::LateBound(_, _, _)), _) | - (Some(rl::Region::LateBoundAnon(_, _)), _) | - (None, _) => { - debug!("no arg found"); - } - } - } - // Checks if it is of type `hir::TyPath` which corresponds to a struct. - hir::TyPath(_) => { - let subvisitor = &mut TyPathVisitor { - infcx: self.infcx, - found_it: false, - bound_region: self.bound_region, - hir_map: self.hir_map, - depth: self.depth, - }; - intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty, - // this will visit only outermost type - if subvisitor.found_it { - self.found_type = Some(arg); - } - } - _ => {} - } - // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, - // go on to visit `&Foo` - intravisit::walk_ty(self, arg); - } -} - -// The visitor captures the corresponding `hir::Ty` of the anonymous region -// in the case of structs ie. `hir::TyPath`. -// This visitor would be invoked for each lifetime corresponding to a struct, -// and would walk the types like Vec in the above example and Ref looking for the HIR -// where that lifetime appears. This allows us to highlight the -// specific part of the type in the error message. -struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - hir_map: &'a hir::map::Map<'gcx>, - found_it: bool, - bound_region: ty::BoundRegion, - depth: u32, -} - -impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> { - fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { - NestedVisitorMap::OnlyBodies(&self.hir_map) - } - - fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { - - let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id); - match (self.infcx.tcx.named_region(hir_id), self.bound_region) { - // the lifetime of the TyPath! - (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), ty::BrAnon(br_index)) => { - if debruijn_index.depth == self.depth && anon_index == br_index { - self.found_it = true; - return; - } - } - - (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { - debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ - def_id={:?}", id, def_id); - if id == def_id { - self.found_it = true; - return; // we can stop visiting now - } - } - - (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => { - debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", - debruijn_index.depth); - debug!("id={:?}", id); - debug!("def_id={:?}", def_id); - if debruijn_index.depth == self.depth && id == def_id { - self.found_it = true; - return; // we can stop visiting now - } - } - - (Some(rl::Region::Static), _) | - (Some(rl::Region::EarlyBound(_, _, _)), _) | - (Some(rl::Region::LateBound(_, _, _)), _) | - (Some(rl::Region::LateBoundAnon(_, _)), _) | - (Some(rl::Region::Free(_, _)), _) | - (None, _) => { - debug!("no arg found"); - } - } - } - - fn visit_ty(&mut self, arg: &'gcx hir::Ty) { - // ignore nested types - // - // If you have a type like `Foo<'a, &Ty>` we - // are only interested in the immediate lifetimes ('a). - // - // Making `visit_ty` empty will ignore the `&Ty` embedded - // inside, it will get reached by the outer visitor. - debug!("`Ty` corresponding to a struct is {:?}", arg); - } -} diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 3e3aea0256e72..cae7bf5c25c81 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -78,10 +78,7 @@ mod note; mod need_type_info; -mod named_anon_conflict; -#[macro_use] -mod util; -mod different_lifetimes; +pub mod nice_region_error; impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn note_and_explain_region(self, @@ -261,10 +258,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn report_region_errors(&self, region_scope_tree: ®ion::ScopeTree, - errors: &Vec>) { + errors: &Vec>, + will_later_be_reported_by_nll: bool) { debug!("report_region_errors(): {} errors to start", errors.len()); - if self.tcx.sess.opts.debugging_opts.nll { + if will_later_be_reported_by_nll && self.tcx.sess.nll() { + // With `#![feature(nll)]`, we want to present a nice user + // experience, so don't even mention the errors from the + // AST checker. + if self.tcx.sess.features.borrow().nll { + return; + } + + // But with -Znll, it's nice to have some note for later. for error in errors { match *error { RegionResolutionError::ConcreteFailure(ref origin, ..) | @@ -294,9 +300,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { for error in errors { debug!("report_region_errors: error = {:?}", error); - if !self.try_report_named_anon_conflict(&error) && - !self.try_report_anon_anon_conflict(&error) - { + if !self.try_report_nice_region_error(&error) { match error.clone() { // These errors could indicate all manner of different // problems with many different solutions. Rather @@ -309,8 +313,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); } - RegionResolutionError::GenericBoundFailure(kind, param_ty, sub) => { - self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); + RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { + self.report_generic_bound_failure( + region_scope_tree, + origin.span(), + Some(origin), + param_ty, + sub, + ); } RegionResolutionError::SubSupConflict(var_origin, @@ -906,11 +916,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { DiagnosticStyledString::highlighted(format!("{}", exp_found.found)))) } - fn report_generic_bound_failure(&self, - region_scope_tree: ®ion::ScopeTree, - origin: SubregionOrigin<'tcx>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>) + pub fn report_generic_bound_failure(&self, + region_scope_tree: ®ion::ScopeTree, + span: Span, + origin: Option>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>) { // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. @@ -958,9 +969,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { format!("the associated type `{}`", p), }; - if let SubregionOrigin::CompareImplMethodObligation { + if let Some(SubregionOrigin::CompareImplMethodObligation { span, item_name, impl_item_def_id, trait_item_def_id, - } = origin { + }) = origin { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, @@ -995,7 +1006,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => { // Does the required lifetime have a nice name we can print? let mut err = struct_span_err!(self.tcx.sess, - origin.span(), + span, E0309, "{} may not live long enough", labeled_user_string); @@ -1006,7 +1017,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::ReStatic => { // Does the required lifetime have a nice name we can print? let mut err = struct_span_err!(self.tcx.sess, - origin.span(), + span, E0310, "{} may not live long enough", labeled_user_string); @@ -1017,7 +1028,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { _ => { // If not, be less specific. let mut err = struct_span_err!(self.tcx.sess, - origin.span(), + span, E0311, "{} may not live long enough", labeled_user_string); @@ -1033,7 +1044,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } }; - self.note_region_origin(&mut err, &origin); + if let Some(origin) = origin { + self.note_region_origin(&mut err, &origin); + } err.emit(); } diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs deleted file mode 100644 index 6af7415ba5371..0000000000000 --- a/src/librustc/infer/error_reporting/named_anon_conflict.rs +++ /dev/null @@ -1,101 +0,0 @@ -// 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 for Anonymous Region Lifetime Errors -//! where one region is named and the other is anonymous. -use infer::InferCtxt; -use infer::lexical_region_resolve::RegionResolutionError::*; -use infer::lexical_region_resolve::RegionResolutionError; -use ty; - -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - /// When given a `ConcreteFailure` for a function with arguments containing a named region and - /// an anonymous region, emit an descriptive diagnostic error. - pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool { - let (span, sub, sup) = match *error { - ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), - SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup), - _ => return false, // inapplicable - }; - - debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})", sub, sup); - - // Determine whether the sub and sup consist of one named region ('a) - // and one anonymous (elided) region. If so, find the parameter arg - // where the anonymous region appears (there must always be one; we - // only introduced anonymous regions in parameters) as well as a - // version new_ty of its type where the anonymous region is replaced - // with the named one.//scope_def_id - let (named, anon, anon_arg_info, region_info) = - if self.is_named_region(sub) && self.is_suitable_region(sup).is_some() && - self.find_arg_with_region(sup, sub).is_some() { - (sub, - sup, - self.find_arg_with_region(sup, sub).unwrap(), - self.is_suitable_region(sup).unwrap()) - } else if self.is_named_region(sup) && self.is_suitable_region(sub).is_some() && - self.find_arg_with_region(sub, sup).is_some() { - (sup, - sub, - self.find_arg_with_region(sub, sup).unwrap(), - self.is_suitable_region(sub).unwrap()) - } else { - return false; // inapplicable - }; - - debug!("try_report_named_anon_conflict: named = {:?}", named); - debug!("try_report_named_anon_conflict: anon_arg_info = {:?}", anon_arg_info); - debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); - - let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg, - anon_arg_info.arg_ty, - anon_arg_info.bound_region, - anon_arg_info.is_first, - region_info.def_id, - region_info.is_impl_item); - match br { - ty::BrAnon(_) => {} - _ => { - /* not an anonymous region */ - debug!("try_report_named_anon_conflict: not an anonymous region"); - return false; - } - } - - if is_impl_item { - debug!("try_report_named_anon_conflict: impl item, bail out"); - return false; - } - - if let Some((_, fndecl)) = self.find_anon_type(anon, &br) { - if self.is_return_type_anon(scope_def_id, br, fndecl).is_some() || - self.is_self_anon(is_first, scope_def_id) { - return false; - } - } - - let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() { - (format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name)) - } else { - ("parameter type".to_owned(), "type".to_owned()) - }; - - struct_span_err!(self.tcx.sess, - span, - E0621, - "explicit lifetime required in {}", - error_var) - .span_label(arg.pat.span, - format!("consider changing {} to `{}`", span_label_var, new_ty)) - .span_label(span, format!("lifetime `{}` required", named)) - .emit(); - return true; - } -} diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs new file mode 100644 index 0000000000000..d4ea899dc747f --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -0,0 +1,164 @@ +// 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 for Anonymous Region Lifetime Errors +//! where both the regions are anonymous. + +use infer::error_reporting::nice_region_error::NiceRegionError; +use infer::error_reporting::nice_region_error::util::AnonymousArgInfo; +use util::common::ErrorReported; + +impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { + /// Print the error message for lifetime errors when both the concerned regions are anonymous. + /// + /// Consider a case where we have + /// + /// ```no_run + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// x.push(y); + /// } + /// ``` + /// + /// The example gives + /// + /// ```text + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// --- --- these references are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// ``` + /// + /// It has been extended for the case of structs too. + /// + /// Consider the example + /// + /// ```no_run + /// struct Ref<'a> { x: &'a u32 } + /// ``` + /// + /// ```text + /// fn foo(mut x: Vec, y: Ref) { + /// --- --- these structs are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// } + /// ```` + /// + /// It will later be extended to trait objects. + pub(super) fn try_report_anon_anon_conflict(&self) -> Option { + let NiceRegionError { span, sub, sup, .. } = *self; + + // Determine whether the sub and sup consist of both anonymous (elided) regions. + let anon_reg_sup = self.is_suitable_region(sup)?; + + let anon_reg_sub = self.is_suitable_region(sub)?; + let scope_def_id_sup = anon_reg_sup.def_id; + let bregion_sup = anon_reg_sup.boundregion; + let scope_def_id_sub = anon_reg_sub.def_id; + let bregion_sub = anon_reg_sub.boundregion; + + let ty_sup = self.find_anon_type(sup, &bregion_sup)?; + + let ty_sub = self.find_anon_type(sub, &bregion_sub)?; + + debug!( + "try_report_anon_anon_conflict: found_arg1={:?} sup={:?} br1={:?}", + ty_sub, + sup, + bregion_sup + ); + debug!( + "try_report_anon_anon_conflict: found_arg2={:?} sub={:?} br2={:?}", + ty_sup, + sub, + bregion_sub + ); + + let (ty_sup, ty_fndecl_sup) = ty_sup; + let (ty_sub, ty_fndecl_sub) = ty_sub; + + let AnonymousArgInfo { + arg: anon_arg_sup, .. + } = self.find_arg_with_region(sup, sup)?; + let AnonymousArgInfo { + arg: anon_arg_sub, .. + } = self.find_arg_with_region(sub, sub)?; + + let sup_is_ret_type = + self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); + let sub_is_ret_type = + self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); + + let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() { + format!(" from `{}`", simple_name) + } else { + format!("") + }; + + let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() { + format!(" into `{}`", simple_name) + } else { + format!("") + }; + + + let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) { + (None, None) => { + let (main_label_1, span_label_1) = if ty_sup == ty_sub { + ( + format!("this type is declared with multiple lifetimes..."), + format!( + "...but data{} flows{} here", + format!(" with one lifetime"), + format!(" into the other") + ), + ) + } else { + ( + format!("these two types are declared with different lifetimes..."), + format!( + "...but data{} flows{} here", + span_label_var1, + span_label_var2 + ), + ) + }; + (ty_sup.span, ty_sub.span, main_label_1, span_label_1) + } + + (Some(ret_span), _) => ( + ty_sub.span, + ret_span, + format!( + "this parameter and the return type are declared \ + with different lifetimes...", + ), + format!("...but data{} is returned here", span_label_var1), + ), + (_, Some(ret_span)) => ( + ty_sup.span, + ret_span, + format!( + "this parameter and the return type are declared \ + with different lifetimes...", + ), + format!("...but data{} is returned here", span_label_var1), + ), + }; + + + struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch") + .span_label(span_1, main_label) + .span_label(span_2, format!("")) + .span_label(span, span_label) + .emit(); + return Some(ErrorReported); + } +} diff --git a/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs new file mode 100644 index 0000000000000..dc53c1db06f14 --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -0,0 +1,291 @@ +// 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. + +use hir; +use ty::{self, Region, TyCtxt}; +use hir::map as hir_map; +use middle::resolve_lifetime as rl; +use hir::intravisit::{self, NestedVisitorMap, Visitor}; +use infer::error_reporting::nice_region_error::NiceRegionError; + +impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { + /// This function calls the `visit_ty` method for the parameters + /// corresponding to the anonymous regions. The `nested_visitor.found_type` + /// contains the anonymous type. + /// + /// # Arguments + /// region - the anonymous region corresponding to the anon_anon conflict + /// br - the bound region corresponding to the above region which is of type `BrAnon(_)` + /// + /// # Example + /// ``` + /// fn foo(x: &mut Vec<&u8>, y: &u8) + /// { x.push(y); } + /// ``` + /// The function returns the nested type corresponding to the anonymous region + /// for e.g. `&u8` and Vec<`&u8`. + pub(super) fn find_anon_type( + &self, + region: Region<'tcx>, + br: &ty::BoundRegion, + ) -> Option<(&hir::Ty, &hir::FnDecl)> { + if let Some(anon_reg) = self.is_suitable_region(region) { + let def_id = anon_reg.def_id; + if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + let fndecl = match self.tcx.hir.get(node_id) { + hir_map::NodeItem(&hir::Item { + node: hir::ItemFn(ref fndecl, ..), + .. + }) => &fndecl, + hir_map::NodeTraitItem(&hir::TraitItem { + node: hir::TraitItemKind::Method(ref m, ..), + .. + }) + | hir_map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(ref m, ..), + .. + }) => &m.decl, + _ => return None, + }; + + return fndecl + .inputs + .iter() + .filter_map(|arg| self.find_component_for_bound_region(arg, br)) + .next() + .map(|ty| (ty, &**fndecl)); + } + } + None + } + + // This method creates a FindNestedTypeVisitor which returns the type corresponding + // to the anonymous region. + fn find_component_for_bound_region( + &self, + arg: &'gcx hir::Ty, + br: &ty::BoundRegion, + ) -> Option<(&'gcx hir::Ty)> { + let mut nested_visitor = FindNestedTypeVisitor { + tcx: self.tcx, + bound_region: *br, + found_type: None, + depth: 1, + }; + nested_visitor.visit_ty(arg); + nested_visitor.found_type + } +} + +// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the +// anonymous region. The example above would lead to a conflict between +// the two anonymous lifetimes for &u8 in x and y respectively. This visitor +// would be invoked twice, once for each lifetime, and would +// walk the types like &mut Vec<&u8> and &u8 looking for the HIR +// where that lifetime appears. This allows us to highlight the +// specific part of the type in the error message. +struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + // The bound_region corresponding to the Refree(freeregion) + // associated with the anonymous region we are looking for. + bound_region: ty::BoundRegion, + // The type where the anonymous lifetime appears + // for e.g. Vec<`&u8`> and <`&u8`> + found_type: Option<&'gcx hir::Ty>, + depth: u32, +} + +impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { + NestedVisitorMap::OnlyBodies(&self.tcx.hir) + } + + fn visit_ty(&mut self, arg: &'gcx hir::Ty) { + match arg.node { + hir::TyBareFn(_) => { + self.depth += 1; + intravisit::walk_ty(self, arg); + self.depth -= 1; + return; + } + + hir::TyTraitObject(ref bounds, _) => for bound in bounds { + self.depth += 1; + self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.depth -= 1; + }, + + hir::TyRptr(ref lifetime, _) => { + // the lifetime of the TyRptr + let hir_id = self.tcx.hir.node_to_hir_id(lifetime.id); + match (self.tcx.named_region(hir_id), self.bound_region) { + // Find the index of the anonymous region that was part of the + // error. We will then search the function parameters for a bound + // region at the right depth with the same index + ( + Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), + ty::BrAnon(br_index), + ) => { + debug!( + "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", + debruijn_index.depth, + anon_index, + br_index + ); + if debruijn_index.depth == self.depth && anon_index == br_index { + self.found_type = Some(arg); + return; // we can stop visiting now + } + } + + // Find the index of the named region that was part of the + // error. We will then search the function parameters for a bound + // region at the right depth with the same index + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { + debug!( + "EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ + def_id={:?}", + id, + def_id + ); + if id == def_id { + self.found_type = Some(arg); + return; // we can stop visiting now + } + } + + // Find the index of the named region that was part of the + // error. We will then search the function parameters for a bound + // region at the right depth with the same index + ( + Some(rl::Region::LateBound(debruijn_index, id, _)), + ty::BrNamed(def_id, _), + ) => { + debug!( + "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", + debruijn_index.depth + ); + debug!("self.infcx.tcx.hir.local_def_id(id)={:?}", id); + debug!("def_id={:?}", def_id); + if debruijn_index.depth == self.depth && id == def_id { + self.found_type = Some(arg); + return; // we can stop visiting now + } + } + + (Some(rl::Region::Static), _) + | (Some(rl::Region::Free(_, _)), _) + | (Some(rl::Region::EarlyBound(_, _, _)), _) + | (Some(rl::Region::LateBound(_, _, _)), _) + | (Some(rl::Region::LateBoundAnon(_, _)), _) + | (None, _) => { + debug!("no arg found"); + } + } + } + // Checks if it is of type `hir::TyPath` which corresponds to a struct. + hir::TyPath(_) => { + let subvisitor = &mut TyPathVisitor { + tcx: self.tcx, + found_it: false, + bound_region: self.bound_region, + depth: self.depth, + }; + intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty, + // this will visit only outermost type + if subvisitor.found_it { + self.found_type = Some(arg); + } + } + _ => {} + } + // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, + // go on to visit `&Foo` + intravisit::walk_ty(self, arg); + } +} + +// The visitor captures the corresponding `hir::Ty` of the anonymous region +// in the case of structs ie. `hir::TyPath`. +// This visitor would be invoked for each lifetime corresponding to a struct, +// and would walk the types like Vec in the above example and Ref looking for the HIR +// where that lifetime appears. This allows us to highlight the +// specific part of the type in the error message. +struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + tcx: TyCtxt<'a, 'gcx, 'tcx>, + found_it: bool, + bound_region: ty::BoundRegion, + depth: u32, +} + +impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> { + NestedVisitorMap::OnlyBodies(&self.tcx.hir) + } + + fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { + let hir_id = self.tcx.hir.node_to_hir_id(lifetime.id); + match (self.tcx.named_region(hir_id), self.bound_region) { + // the lifetime of the TyPath! + (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), ty::BrAnon(br_index)) => { + if debruijn_index.depth == self.depth && anon_index == br_index { + self.found_it = true; + return; + } + } + + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { + debug!( + "EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ + def_id={:?}", + id, + def_id + ); + if id == def_id { + self.found_it = true; + return; // we can stop visiting now + } + } + + (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => { + debug!( + "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", + debruijn_index.depth + ); + debug!("id={:?}", id); + debug!("def_id={:?}", def_id); + if debruijn_index.depth == self.depth && id == def_id { + self.found_it = true; + return; // we can stop visiting now + } + } + + (Some(rl::Region::Static), _) + | (Some(rl::Region::EarlyBound(_, _, _)), _) + | (Some(rl::Region::LateBound(_, _, _)), _) + | (Some(rl::Region::LateBoundAnon(_, _)), _) + | (Some(rl::Region::Free(_, _)), _) + | (None, _) => { + debug!("no arg found"); + } + } + } + + fn visit_ty(&mut self, arg: &'gcx hir::Ty) { + // ignore nested types + // + // If you have a type like `Foo<'a, &Ty>` we + // are only interested in the immediate lifetimes ('a). + // + // Making `visit_ty` empty will ignore the `&Ty` embedded + // inside, it will get reached by the outer visitor. + debug!("`Ty` corresponding to a struct is {:?}", arg); + } +} diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs new file mode 100644 index 0000000000000..edc38b6bb14ee --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -0,0 +1,63 @@ +// 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. + +use infer::InferCtxt; +use infer::lexical_region_resolve::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use syntax::codemap::Span; +use ty::{self, TyCtxt}; +use util::common::ErrorReported; + +mod different_lifetimes; +mod find_anon_type; +mod named_anon_conflict; +mod util; + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { + let (span, sub, sup) = match *error { + ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup), + SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup), + _ => return false, // inapplicable + }; + + if let Some(tables) = self.in_progress_tables { + let tables = tables.borrow(); + NiceRegionError::new(self.tcx, span, sub, sup, Some(&tables)).try_report().is_some() + } else { + NiceRegionError::new(self.tcx, span, sub, sup, None).try_report().is_some() + } + } +} + +pub struct NiceRegionError<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + span: Span, + sub: ty::Region<'tcx>, + sup: ty::Region<'tcx>, + tables: Option<&'cx ty::TypeckTables<'tcx>>, +} + +impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> { + pub fn new( + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + span: Span, + sub: ty::Region<'tcx>, + sup: ty::Region<'tcx>, + tables: Option<&'cx ty::TypeckTables<'tcx>>, + ) -> Self { + Self { tcx, span, sub, sup, tables } + } + + pub fn try_report(&self) -> Option { + self.try_report_named_anon_conflict() + .or_else(|| self.try_report_anon_anon_conflict()) + } +} diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs new file mode 100644 index 0000000000000..9d0ddfd4be04b --- /dev/null +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -0,0 +1,121 @@ +// 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 for Anonymous Region Lifetime Errors +//! where one region is named and the other is anonymous. +use infer::error_reporting::nice_region_error::NiceRegionError; +use ty; +use util::common::ErrorReported; + +impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { + /// When given a `ConcreteFailure` for a function with arguments containing a named region and + /// an anonymous region, emit an descriptive diagnostic error. + pub(super) fn try_report_named_anon_conflict(&self) -> Option { + let NiceRegionError { span, sub, sup, .. } = *self; + + debug!( + "try_report_named_anon_conflict(sub={:?}, sup={:?})", + sub, + sup + ); + + // Determine whether the sub and sup consist of one named region ('a) + // and one anonymous (elided) region. If so, find the parameter arg + // where the anonymous region appears (there must always be one; we + // only introduced anonymous regions in parameters) as well as a + // version new_ty of its type where the anonymous region is replaced + // with the named one.//scope_def_id + let (named, anon, anon_arg_info, region_info) = if self.is_named_region(sub) + && self.is_suitable_region(sup).is_some() + && self.find_arg_with_region(sup, sub).is_some() + { + ( + sub, + sup, + self.find_arg_with_region(sup, sub).unwrap(), + self.is_suitable_region(sup).unwrap(), + ) + } else if self.is_named_region(sup) && self.is_suitable_region(sub).is_some() + && self.find_arg_with_region(sub, sup).is_some() + { + ( + sup, + sub, + self.find_arg_with_region(sub, sup).unwrap(), + self.is_suitable_region(sub).unwrap(), + ) + } else { + return None; // inapplicable + }; + + debug!("try_report_named_anon_conflict: named = {:?}", named); + debug!( + "try_report_named_anon_conflict: anon_arg_info = {:?}", + anon_arg_info + ); + debug!( + "try_report_named_anon_conflict: region_info = {:?}", + region_info + ); + + let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = ( + anon_arg_info.arg, + anon_arg_info.arg_ty, + anon_arg_info.bound_region, + anon_arg_info.is_first, + region_info.def_id, + region_info.is_impl_item, + ); + match br { + ty::BrAnon(_) => {} + _ => { + /* not an anonymous region */ + debug!("try_report_named_anon_conflict: not an anonymous region"); + return None; + } + } + + if is_impl_item { + debug!("try_report_named_anon_conflict: impl item, bail out"); + return None; + } + + if let Some((_, fndecl)) = self.find_anon_type(anon, &br) { + if self.is_return_type_anon(scope_def_id, br, fndecl).is_some() + || self.is_self_anon(is_first, scope_def_id) + { + return None; + } + } + + let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() { + ( + format!("the type of `{}`", simple_name), + format!("the type of `{}`", simple_name), + ) + } else { + ("parameter type".to_owned(), "type".to_owned()) + }; + + struct_span_err!( + self.tcx.sess, + span, + E0621, + "explicit lifetime required in {}", + error_var + ).span_label( + arg.pat.span, + format!("consider changing {} to `{}`", span_label_var, new_ty), + ) + .span_label(span, format!("lifetime `{}` required", named)) + .emit(); + return Some(ErrorReported); + } +} diff --git a/src/librustc/infer/error_reporting/util.rs b/src/librustc/infer/error_reporting/nice_region_error/util.rs similarity index 68% rename from src/librustc/infer/error_reporting/util.rs rename to src/librustc/infer/error_reporting/nice_region_error/util.rs index 6d198601447b0..f8b6f7d0afa93 100644 --- a/src/librustc/infer/error_reporting/util.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/util.rs @@ -11,28 +11,16 @@ //! Helper functions corresponding to lifetime errors due to //! anonymous regions. use hir; -use infer::InferCtxt; +use infer::error_reporting::nice_region_error::NiceRegionError; use ty::{self, Region, Ty}; use hir::def_id::DefId; use hir::map as hir_map; use syntax_pos::Span; -macro_rules! or_false { - ($v:expr) => { - match $v { - Some(v) => v, - None => { - debug!("or_false failed: {}", stringify!($v)); - return false; - } - } - } -} - // The struct contains the information about the anonymous region // we are searching for. #[derive(Debug)] -pub struct AnonymousArgInfo<'tcx> { +pub(super) struct AnonymousArgInfo<'tcx> { // the argument corresponding to the anonymous region pub arg: &'tcx hir::Arg, // the type corresponding to the anonymopus region argument @@ -47,7 +35,7 @@ pub struct AnonymousArgInfo<'tcx> { // This struct contains information regarding the // Refree((FreeRegion) corresponding to lifetime conflict #[derive(Debug)] -pub struct FreeRegionInfo { +pub(super) struct FreeRegionInfo { // def id corresponding to FreeRegion pub def_id: DefId, // the bound region corresponding to FreeRegion @@ -56,7 +44,7 @@ pub struct FreeRegionInfo { pub is_impl_item: bool, } -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { +impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { // This method walks the Type of the function body arguments using // `fold_regions()` function and returns the // &hir::Arg of the function argument corresponding to the anonymous @@ -68,17 +56,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // i32, which is the type of y but with the anonymous region replaced // with 'a, the corresponding bound region and is_first which is true if // the hir::Arg is the first argument in the function declaration. - pub fn find_arg_with_region(&self, - anon_region: Region<'tcx>, - replace_region: Region<'tcx>) - -> Option { - + pub(super) fn find_arg_with_region( + &self, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, + ) -> Option { let (id, bound_region) = match *anon_region { ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), - ty::ReEarlyBound(ref ebr) => { - (self.tcx.parent_def_id(ebr.def_id).unwrap(), - ty::BoundRegion::BrNamed(ebr.def_id, ebr.name)) - } + ty::ReEarlyBound(ref ebr) => ( + self.tcx.parent_def_id(ebr.def_id).unwrap(), + ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), + ), _ => return None, // not a free region }; @@ -86,29 +74,30 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { if let Some(node_id) = hir.as_local_node_id(id) { if let Some(body_id) = hir.maybe_body_owned_by(node_id) { let body = hir.body(body_id); - if let Some(tables) = self.in_progress_tables { + if let Some(tables) = self.tables { body.arguments .iter() .enumerate() .filter_map(|(index, arg)| { // May return None; sometimes the tables are not yet populated. - let ty = tables.borrow().node_id_to_type_opt(arg.hir_id)?; + let ty = tables.node_id_to_type_opt(arg.hir_id)?; let mut found_anon_region = false; - let new_arg_ty = self.tcx - .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region { + let new_arg_ty = self.tcx.fold_regions(&ty, &mut false, |r, _| { + if *r == *anon_region { found_anon_region = true; replace_region } else { r - }); + } + }); if found_anon_region { let is_first = index == 0; Some(AnonymousArgInfo { - arg: arg, - arg_ty: new_arg_ty, - bound_region: bound_region, - is_first: is_first, - }) + arg: arg, + arg_ty: new_arg_ty, + bound_region: bound_region, + is_first: is_first, + }) } else { None } @@ -126,14 +115,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } // This method returns the DefId and the BoundRegion corresponding to the given region. - pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option { - + pub(super) fn is_suitable_region(&self, region: Region<'tcx>) -> Option { let (suitable_region_binding_scope, bound_region) = match *region { ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), - ty::ReEarlyBound(ref ebr) => { - (self.tcx.parent_def_id(ebr.def_id).unwrap(), - ty::BoundRegion::BrNamed(ebr.def_id, ebr.name)) - } + ty::ReEarlyBound(ref ebr) => ( + self.tcx.parent_def_id(ebr.def_id).unwrap(), + ty::BoundRegion::BrNamed(ebr.def_id, ebr.name), + ), _ => return None, // not a free region }; @@ -142,9 +130,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .as_local_node_id(suitable_region_binding_scope) .unwrap(); let is_impl_item = match self.tcx.hir.find(node_id) { - - Some(hir_map::NodeItem(..)) | - Some(hir_map::NodeTraitItem(..)) => false, + Some(hir_map::NodeItem(..)) | Some(hir_map::NodeTraitItem(..)) => false, Some(hir_map::NodeImplItem(..)) => { self.is_bound_region_in_impl_item(suitable_region_binding_scope) } @@ -152,21 +138,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }; return Some(FreeRegionInfo { - def_id: suitable_region_binding_scope, - boundregion: bound_region, - is_impl_item: is_impl_item, - }); - + def_id: suitable_region_binding_scope, + boundregion: bound_region, + is_impl_item: is_impl_item, + }); } // Here, we check for the case where the anonymous region // is in the return type. // FIXME(#42703) - Need to handle certain cases here. - pub fn is_return_type_anon(&self, - scope_def_id: DefId, - br: ty::BoundRegion, - decl: &hir::FnDecl) - -> Option { + pub(super) fn is_return_type_anon( + &self, + scope_def_id: DefId, + br: ty::BoundRegion, + decl: &hir::FnDecl, + ) -> Option { let ret_ty = self.tcx.type_of(scope_def_id); match ret_ty.sty { ty::TyFnDef(_, _) => { @@ -185,15 +171,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // corresponds to self and if yes, we display E0312. // FIXME(#42700) - Need to format self properly to // enable E0621 for it. - pub fn is_self_anon(&self, is_first: bool, scope_def_id: DefId) -> bool { - is_first && - self.tcx - .opt_associated_item(scope_def_id) - .map(|i| i.method_has_self_argument) == Some(true) + pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: DefId) -> bool { + is_first + && self.tcx + .opt_associated_item(scope_def_id) + .map(|i| i.method_has_self_argument) == Some(true) } // Here we check if the bound region is in Impl Item. - pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: DefId) -> bool { + pub(super) fn is_bound_region_in_impl_item( + &self, + suitable_region_binding_scope: DefId, + ) -> bool { let container_id = self.tcx .associated_item(suitable_region_binding_scope) .container @@ -211,14 +200,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } // This method returns whether the given Region is Named - pub fn is_named_region(&self, region: Region<'tcx>) -> bool { + pub(super) fn is_named_region(&self, region: Region<'tcx>) -> bool { match *region { - ty::ReFree(ref free_region) => { - match free_region.bound_region { - ty::BrNamed(..) => true, - _ => false, - } - } + ty::ReFree(ref free_region) => match free_region.bound_region { + ty::BrNamed(..) => true, + _ => false, + }, ty::ReEarlyBound(_) => true, _ => false, } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 9788ec2a5e453..07c5b319970f8 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -48,6 +48,7 @@ use self::outlives::env::OutlivesEnvironment; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; +pub mod anon_types; pub mod at; mod combine; mod equate; @@ -1158,10 +1159,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_type_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors(&self, - region_context: DefId, - region_map: ®ion::ScopeTree, - outlives_env: &OutlivesEnvironment<'tcx>) { + pub fn resolve_regions_and_report_errors( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.resolve_regions_and_report_errors_inner( + region_context, + region_map, + outlives_env, + false, + ) + } + + /// Like `resolve_regions_and_report_errors`, but skips error + /// reporting if NLL is enabled. This is used for fn bodies where + /// the same error may later be reported by the NLL-based + /// inference. + pub fn resolve_regions_and_report_errors_unless_nll( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.resolve_regions_and_report_errors_inner( + region_context, + region_map, + outlives_env, + true, + ) + } + + fn resolve_regions_and_report_errors_inner( + &self, + region_context: DefId, + region_map: ®ion::ScopeTree, + outlives_env: &OutlivesEnvironment<'tcx>, + will_later_be_reported_by_nll: bool, + ) { assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), "region_obligations not empty: {:#?}", self.region_obligations.borrow()); @@ -1186,7 +1222,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors); // see error_reporting module + self.report_region_errors(region_map, &errors, will_later_be_reported_by_nll); } } diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs index 2127c4714aef0..6163ec1642001 100644 --- a/src/librustc/infer/outlives/free_region_map.rs +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -38,20 +38,6 @@ impl<'tcx> FreeRegionMap<'tcx> { } } - /// Tests whether `r_a <= r_b`. Both must be free regions or - /// `'static`. - pub fn sub_free_regions<'a, 'gcx>(&self, - r_a: Region<'tcx>, - r_b: Region<'tcx>) - -> bool { - assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); - if let ty::ReStatic = r_b { - true // `'a <= 'static` is just always true, and not stored in the relation explicitly - } else { - r_a == r_b || self.relation.contains(&r_a, &r_b) - } - } - /// Compute the least-upper-bound of two free regions. In some /// cases, this is more conservative than necessary, in order to /// avoid making arbitrary choices. See @@ -75,6 +61,29 @@ impl<'tcx> FreeRegionMap<'tcx> { } } +/// The NLL region handling code represents free region relations in a +/// slightly different way; this trait allows functions to be abstract +/// over which version is in use. +pub trait FreeRegionRelations<'tcx> { + /// Tests whether `r_a <= r_b`. Both must be free regions or + /// `'static`. + fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool; +} + +impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { + fn sub_free_regions(&self, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> bool { + assert!(is_free_or_static(r_a) && is_free_or_static(r_b)); + if let ty::ReStatic = r_b { + true // `'a <= 'static` is just always true, and not stored in the relation explicitly + } else { + r_a == r_b || self.relation.contains(&r_a, &r_b) + } + } +} + fn is_free(r: Region) -> bool { match *r { ty::ReEarlyBound(_) | ty::ReFree(_) => true, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index f00830f9ec992..becaf78f7eca5 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -46,6 +46,7 @@ #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(drain_filter)] +#![feature(dyn_trait)] #![feature(from_ref)] #![feature(i128)] #![feature(i128_type)] diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index ca6a5dd7f5b0b..1341e3515d571 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -15,7 +15,7 @@ //! `TransitiveRelation` type and use that to decide when one free //! region outlives another and so forth. -use infer::outlives::free_region_map::FreeRegionMap; +use infer::outlives::free_region_map::{FreeRegionMap, FreeRegionRelations}; use hir::def_id::DefId; use middle::region; use ty::{self, TyCtxt, Region}; diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index efde224c3e120..f410865a6cd7f 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -21,6 +21,7 @@ use rustc_data_structures::control_flow_graph::ControlFlowGraph; use rustc_serialize as serialize; use hir::def::CtorKind; use hir::def_id::DefId; +use mir::visit::MirVisitable; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; @@ -868,6 +869,14 @@ impl<'tcx> BasicBlockData<'tcx> { } } } + + pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { + if index < self.statements.len() { + &self.statements[index] + } else { + &self.terminator + } + } } impl<'tcx> Debug for TerminatorKind<'tcx> { diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index a50a9c819f6ec..0e6c14af1ecfa 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -811,6 +811,31 @@ macro_rules! make_mir_visitor { make_mir_visitor!(Visitor,); make_mir_visitor!(MutVisitor,mut); +pub trait MirVisitable<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>); +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) + { + visitor.visit_statement(location.block, self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Terminator<'tcx> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) + { + visitor.visit_terminator(location.block, self, location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Option> { + fn apply(&self, location: Location, visitor: &mut dyn Visitor<'tcx>) + { + visitor.visit_terminator(location.block, self.as_ref().unwrap(), location) + } +} + /// Extra information passed to `visit_ty` and friends to give context /// about where the type etc appears. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index a1cf38ae336d2..6e0372f009ece 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1190,6 +1190,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "choose which RELRO level to use"), nll: bool = (false, parse_bool, [UNTRACKED], "run the non-lexical lifetimes MIR pass"), + nll_dump_cause: bool = (false, parse_bool, [UNTRACKED], + "dump cause information when reporting errors from NLL"), trans_time_graph: bool = (false, parse_bool, [UNTRACKED], "generate a graphical HTML report of time spent in trans and LLVM"), thinlto: Option = (None, parse_opt_bool, [TRACKED], diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index a9200a3c8059a..60a218500ca78 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -18,7 +18,7 @@ use lint; use middle::allocator::AllocatorKind; use middle::dependency_format; use session::search_paths::PathKind; -use session::config::DebugInfoLevel; +use session::config::{BorrowckMode, DebugInfoLevel}; use ty::tls; use util::nodemap::{FxHashMap, FxHashSet}; use util::common::{duration_to_secs_str, ErrorReported}; @@ -437,11 +437,62 @@ impl Session { pub fn print_llvm_passes(&self) -> bool { self.opts.debugging_opts.print_llvm_passes } + + /// If true, we should use NLL-style region checking instead of + /// lexical style. + pub fn nll(&self) -> bool { + self.features.borrow().nll || self.opts.debugging_opts.nll + } + + /// If true, we should use the MIR-based borrowck (we may *also* use + /// the AST-based borrowck). + pub fn use_mir(&self) -> bool { + self.borrowck_mode().use_mir() + } + + /// If true, we should gather causal information during NLL + /// checking. This will eventually be the normal thing, but right + /// now it is too unoptimized. + pub fn nll_dump_cause(&self) -> bool { + self.opts.debugging_opts.nll_dump_cause + } + + /// If true, we should enable two-phase borrows checks. This is + /// done with either `-Ztwo-phase-borrows` or with + /// `#![feature(nll)]`. + pub fn two_phase_borrows(&self) -> bool { + self.features.borrow().nll || self.opts.debugging_opts.two_phase_borrows + } + + /// What mode(s) of borrowck should we run? AST? MIR? both? + /// (Also considers the `#![feature(nll)]` setting.) + pub fn borrowck_mode(&self) -> BorrowckMode { + match self.opts.borrowck_mode { + mode @ BorrowckMode::Mir | + mode @ BorrowckMode::Compare => mode, + + mode @ BorrowckMode::Ast => { + if self.nll() { + BorrowckMode::Mir + } else { + mode + } + } + + } + } + + /// Should we emit EndRegion MIR statements? These are consumed by + /// MIR borrowck, but not when NLL is used. They are also consumed + /// by the validation stuff. pub fn emit_end_regions(&self) -> bool { + // FIXME(#46875) -- we should not emit end regions when NLL is enabled, + // but for now we can't stop doing so because it causes false positives self.opts.debugging_opts.emit_end_regions || - (self.opts.debugging_opts.mir_emit_validate > 0) || - self.opts.borrowck_mode.use_mir() + self.opts.debugging_opts.mir_emit_validate > 0 || + self.use_mir() } + pub fn lto(&self) -> bool { self.opts.cg.lto || self.target.target.options.requires_lto } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5bfa646456857..51841836698eb 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -865,13 +865,17 @@ impl fmt::Debug for ty::RegionVid { define_print! { () ty::InferTy, (self, f, cx) { display { - match *self { - ty::TyVar(_) => write!(f, "_"), - ty::IntVar(_) => write!(f, "{}", "{integer}"), - ty::FloatVar(_) => write!(f, "{}", "{float}"), - ty::FreshTy(v) => write!(f, "FreshTy({})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) + if cx.is_verbose { + print!(f, cx, print_debug(self)) + } else { + match *self { + ty::TyVar(_) => write!(f, "_"), + ty::IntVar(_) => write!(f, "{}", "{integer}"), + ty::FloatVar(_) => write!(f, "{}", "{float}"), + ty::FreshTy(v) => write!(f, "FreshTy({})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) + } } } debug { diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 62736db9260ac..b124872ba12ca 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -275,7 +275,7 @@ impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.tcx.sess.opts.borrowck_mode) { + if !o.should_emit_errors(self.tcx.sess.borrowck_mode()) { self.tcx.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index c144962fb2d88..bc1b3edbb6ad9 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -88,7 +88,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { pub(super) fn report_move_out_while_borrowed( &mut self, - _context: Context, + context: Context, (place, span): (&Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) { @@ -100,23 +100,23 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Some(name) => format!("`{}`", name), None => "value".to_owned(), }; - self.tcx - .cannot_move_when_borrowed( - span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ) - .span_label( - self.retrieve_borrow_span(borrow), - format!("borrow of {} occurs here", borrow_msg), - ) - .span_label(span, format!("move out of {} occurs here", value_msg)) - .emit(); + let mut err = self.tcx.cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + err.span_label( + self.retrieve_borrow_span(borrow), + format!("borrow of {} occurs here", borrow_msg), + ); + err.span_label(span, format!("move out of {} occurs here", value_msg)); + self.explain_why_borrow_contains_point(context, borrow, &mut err); + err.emit(); } pub(super) fn report_use_while_mutably_borrowed( &mut self, - _context: Context, + context: Context, (place, span): (&Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) { @@ -128,6 +128,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Origin::Mir, ); + self.explain_why_borrow_contains_point(context, borrow, &mut err); + err.emit(); } @@ -313,12 +315,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); } + self.explain_why_borrow_contains_point(context, issued_borrow, &mut err); + err.emit(); } pub(super) fn report_borrowed_value_does_not_live_long_enough( &mut self, - _: Context, + context: Context, borrow: &BorrowData<'tcx>, drop_span: Span, borrows: &ActiveBorrows<'cx, 'gcx, 'tcx> @@ -357,11 +361,26 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match (borrow.region, &self.describe_place(&borrow.borrowed_place)) { (RegionKind::ReScope(_), Some(name)) => { self.report_scoped_local_value_does_not_live_long_enough( - name, &scope_tree, &borrow, drop_span, borrow_span, proper_span, end_span); + context, + name, + &scope_tree, + &borrow, + drop_span, + borrow_span, + proper_span, + end_span + ); }, (RegionKind::ReScope(_), None) => { self.report_scoped_temporary_value_does_not_live_long_enough( - &scope_tree, &borrow, drop_span, borrow_span, proper_span, end_span); + context, + &scope_tree, + &borrow, + drop_span, + borrow_span, + proper_span, + end_span + ); }, (RegionKind::ReEarlyBound(_), Some(name)) | (RegionKind::ReFree(_), Some(name)) | @@ -369,7 +388,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (RegionKind::ReEmpty, Some(name)) | (RegionKind::ReVar(_), Some(name)) => { self.report_unscoped_local_value_does_not_live_long_enough( - name, &scope_tree, &borrow, drop_span, borrow_span, proper_span, end_span); + context, + name, + &scope_tree, + &borrow, + drop_span, + borrow_span, + proper_span, + end_span, + ); }, (RegionKind::ReEarlyBound(_), None) | (RegionKind::ReFree(_), None) | @@ -377,7 +404,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (RegionKind::ReEmpty, None) | (RegionKind::ReVar(_), None) => { self.report_unscoped_temporary_value_does_not_live_long_enough( - &scope_tree, &borrow, drop_span, borrow_span, proper_span, end_span); + context, + &scope_tree, + &borrow, + drop_span, + borrow_span, + proper_span, + end_span, + ); }, (RegionKind::ReLateBound(_, _), _) | (RegionKind::ReSkolemized(_, _), _) | @@ -389,8 +423,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } fn report_scoped_local_value_does_not_live_long_enough( - &mut self, name: &String, _scope_tree: &Rc, _borrow: &BorrowData<'tcx>, - drop_span: Span, borrow_span: Span, _proper_span: Span, end_span: Option + &mut self, + context: Context, + name: &String, + _scope_tree: &Rc, + borrow: &BorrowData<'tcx>, + drop_span: Span, + borrow_span: Span, + _proper_span: Span, + end_span: Option, ) { let mut err = self.tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), @@ -400,12 +441,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Some(end) = end_span { err.span_label(end, "borrowed value needs to live until here"); } + self.explain_why_borrow_contains_point(context, borrow, &mut err); err.emit(); } fn report_scoped_temporary_value_does_not_live_long_enough( - &mut self, _scope_tree: &Rc, _borrow: &BorrowData<'tcx>, - drop_span: Span, _borrow_span: Span, proper_span: Span, end_span: Option + &mut self, + context: Context, + _scope_tree: &Rc, + borrow: &BorrowData<'tcx>, + drop_span: Span, + _borrow_span: Span, + proper_span: Span, + end_span: Option, ) { let mut err = self.tcx.path_does_not_live_long_enough(proper_span, "borrowed value", @@ -416,12 +464,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Some(end) = end_span { err.span_label(end, "temporary value needs to live until here"); } + self.explain_why_borrow_contains_point(context, borrow, &mut err); err.emit(); } fn report_unscoped_local_value_does_not_live_long_enough( - &mut self, name: &String, scope_tree: &Rc, borrow: &BorrowData<'tcx>, - drop_span: Span, borrow_span: Span, _proper_span: Span, _end_span: Option + &mut self, + context: Context, + name: &String, + scope_tree: &Rc, + borrow: &BorrowData<'tcx>, + drop_span: Span, + borrow_span: Span, + _proper_span: Span, + _end_span: Option, ) { let mut err = self.tcx.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name), @@ -431,12 +487,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.tcx.note_and_explain_region(scope_tree, &mut err, "borrowed value must be valid for ", borrow.region, "..."); + self.explain_why_borrow_contains_point(context, borrow, &mut err); err.emit(); } fn report_unscoped_temporary_value_does_not_live_long_enough( - &mut self, scope_tree: &Rc, borrow: &BorrowData<'tcx>, - drop_span: Span, _borrow_span: Span, proper_span: Span, _end_span: Option + &mut self, + context: Context, + scope_tree: &Rc, + borrow: &BorrowData<'tcx>, + drop_span: Span, + _borrow_span: Span, + proper_span: Span, + _end_span: Option ) { let mut err = self.tcx.path_does_not_live_long_enough(proper_span, "borrowed value", @@ -446,12 +509,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.tcx.note_and_explain_region(scope_tree, &mut err, "borrowed value must be valid for ", borrow.region, "..."); + self.explain_why_borrow_contains_point(context, borrow, &mut err); err.emit(); } pub(super) fn report_illegal_mutation_of_borrowed( &mut self, - _: Context, + context: Context, (place, span): (&Place<'tcx>, Span), loan: &BorrowData, ) { @@ -462,6 +526,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Origin::Mir, ); + self.explain_why_borrow_contains_point(context, loan, &mut err); + err.emit(); } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index e0b03aec69a3b..c907c97d6d3ed 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -10,6 +10,7 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. +use borrow_check::nll::region_infer::RegionInferenceContext; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::hir::map::definitions::DefPathData; @@ -25,6 +26,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_set::IdxSetBuf; use rustc_data_structures::indexed_vec::Idx; +use std::rc::Rc; + use syntax::ast; use syntax_pos::Span; @@ -34,9 +37,9 @@ use dataflow::MoveDataParamEnv; use dataflow::{DataflowAnalysis, DataflowResultsConsumer}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{EverInitializedLvals, MovingOutStatements}; -use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex}; +use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; use dataflow::{ActiveBorrows, Reservations}; -use dataflow::indexes::{BorrowIndex}; +use dataflow::indexes::BorrowIndex; use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; @@ -69,10 +72,7 @@ fn mir_borrowck<'a, 'tcx>( let input_mir = tcx.mir_validated(def_id); debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); - if { - !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir() - && !tcx.sess.opts.debugging_opts.nll - } { + if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.use_mir() { return None; } @@ -101,7 +101,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( // contain non-lexical lifetimes. It will have a lifetime tied // to the inference context. let mut mir: Mir<'tcx> = input_mir.clone(); - let free_regions = if !tcx.sess.opts.debugging_opts.nll { + let free_regions = if !tcx.sess.nll() { None } else { let mir = &mut mir; @@ -149,9 +149,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( param_env: param_env, }; let body_id = match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::StructCtor | - DefPathData::EnumVariant(_) => None, - _ => Some(tcx.hir.body_owned_by(id)) + DefPathData::StructCtor | DefPathData::EnumVariant(_) => None, + _ => Some(tcx.hir.body_owned_by(id)), }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); @@ -203,9 +202,9 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( &mut flow_inits, &mdpe.move_data, ); - (Some(regioncx), opt_closure_req) + (Some(Rc::new(regioncx)), opt_closure_req) } else { - assert!(!tcx.sess.opts.debugging_opts.nll); + assert!(!tcx.sess.nll()); (None, None) }; let flow_inits = flow_inits; // remove mut @@ -217,13 +216,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( move_data: &mdpe.move_data, param_env: param_env, locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { - hir::BodyOwnerKind::Const | - hir::BodyOwnerKind::Static(_) => false, + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, hir::BodyOwnerKind::Fn => true, }, storage_dead_or_drop_error_reported_l: FxHashSet(), storage_dead_or_drop_error_reported_s: FxHashSet(), reservation_error_reported: FxHashSet(), + nonlexical_regioncx: opt_regioncx.clone(), }; let borrows = Borrows::new(tcx, mir, opt_regioncx, def_id, body_id); @@ -241,18 +240,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( // In practice it is easier to be looser; in particular, // it is okay for the kill-sets to hold activation bits. DebugFormatted::new(&(i.kind(), rs.location(i))) - }); + }, + ); let flow_active_borrows = { let reservations_on_entry = flow_reservations.0.sets.entry_set_state(); let reservations = flow_reservations.0.operator; - let a = DataflowAnalysis::new_with_entry_sets(mir, - &dead_unwinds, - Cow::Borrowed(reservations_on_entry), - ActiveBorrows::new(reservations)); - let results = a.run(tcx, - id, - &attributes, - |ab, i| DebugFormatted::new(&(i.kind(), ab.location(i)))); + let a = DataflowAnalysis::new_with_entry_sets( + mir, + &dead_unwinds, + Cow::Borrowed(reservations_on_entry), + ActiveBorrows::new(reservations), + ); + let results = a.run(tcx, id, &attributes, |ab, i| { + DebugFormatted::new(&(i.kind(), ab.location(i))) + }); FlowAtLocation::new(results) }; @@ -297,6 +298,10 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { /// but it is currently inconvenient to track down the BorrowIndex /// at the time we detect and report a reservation error. reservation_error_reported: FxHashSet>, + /// Non-lexical region inference context, if NLL is enabled. This + /// contains the results from region inference and lets us e.g. + /// find out which CFG points are contained in each borrow region. + nonlexical_regioncx: Option>>, } // Check that: @@ -551,9 +556,9 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx } }); } - TerminatorKind::Goto { target: _ } | - TerminatorKind::Unreachable | - TerminatorKind::FalseEdges { .. } => { + TerminatorKind::Goto { target: _ } + | TerminatorKind::Unreachable + | TerminatorKind::FalseEdges { .. } => { // no data used, thus irrelevant to borrowck } } @@ -648,13 +653,12 @@ enum LocalMutationIsAllowed { /// We want use of immutable upvars to cause a "write to immutable upvar" /// error, not an "reassignment" error. ExceptUpvars, - No + No, } struct AccessErrorsReported { mutability_error: bool, - #[allow(dead_code)] - conflict_error: bool + #[allow(dead_code)] conflict_error: bool, } #[derive(Copy, Clone)] @@ -704,9 +708,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Activation(_, borrow_index) = rw { if self.reservation_error_reported.contains(&place_span.0) { - debug!("skipping access_place for activation of invalid reservation \ - place: {:?} borrow_index: {:?}", place_span.0, borrow_index); - return AccessErrorsReported { mutability_error: false, conflict_error: true }; + debug!( + "skipping access_place for activation of invalid reservation \ + place: {:?} borrow_index: {:?}", + place_span.0, + borrow_index + ); + return AccessErrorsReported { + mutability_error: false, + conflict_error: true, + }; } } @@ -715,7 +726,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let conflict_error = self.check_access_for_conflict(context, place_span, sd, rw, flow_state); - AccessErrorsReported { mutability_error, conflict_error } + AccessErrorsReported { + mutability_error, + conflict_error, + } } fn check_access_for_conflict( @@ -738,22 +752,26 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // // NOTE: *reservations* do conflict with themselves; // thus aren't injecting unsoundenss w/ this check.) - (Activation(_, activating), _) if activating == index.borrow_index() => - { - debug!("check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ - skipping {:?} b/c activation of same borrow_index: {:?}", - place_span, sd, rw, (index, borrow), index.borrow_index()); + (Activation(_, activating), _) if activating == index.borrow_index() => { + debug!( + "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ + skipping {:?} b/c activation of same borrow_index: {:?}", + place_span, + sd, + rw, + (index, borrow), + index.borrow_index() + ); Control::Continue } - (Read(_), BorrowKind::Shared) | - (Reservation(..), BorrowKind::Shared) => Control::Continue, + (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => { + Control::Continue + } - (Read(kind), BorrowKind::Unique) | - (Read(kind), BorrowKind::Mut) => { + (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { // Reading from mere reservations of mutable-borrows is OK. - if this.tcx.sess.opts.debugging_opts.two_phase_borrows && - index.is_reservation() + if this.tcx.sess.two_phase_borrows() && index.is_reservation() { return Control::Continue; } @@ -781,20 +799,25 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Control::Break } - (Reservation(kind), BorrowKind::Unique) | - (Reservation(kind), BorrowKind::Mut) | - (Activation(kind, _), _) | - (Write(kind), _) => { - + (Reservation(kind), BorrowKind::Unique) + | (Reservation(kind), BorrowKind::Mut) + | (Activation(kind, _), _) + | (Write(kind), _) => { match rw { Reservation(_) => { - debug!("recording invalid reservation of \ - place: {:?}", place_span.0); + debug!( + "recording invalid reservation of \ + place: {:?}", + place_span.0 + ); this.reservation_error_reported.insert(place_span.0.clone()); } Activation(_, activating) => { - debug!("observing check_place for activation of \ - borrow_index: {:?}", activating); + debug!( + "observing check_place for activation of \ + borrow_index: {:?}", + activating + ); } Read(..) | Write(..) => {} } @@ -818,8 +841,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { WriteKind::StorageDeadOrDrop => { error_reported = true; this.report_borrowed_value_does_not_live_long_enough( - context, borrow, place_span.1, - flow_state.borrows.operator()); + context, + borrow, + place_span.1, + flow_state.borrows.operator(), + ); } WriteKind::Mutate => { error_reported = true; @@ -893,7 +919,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Unique | BorrowKind::Mut => { let wk = WriteKind::MutableBorrow(bk); - if self.tcx.sess.opts.debugging_opts.two_phase_borrows { + if self.tcx.sess.two_phase_borrows() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -917,10 +943,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); } - Rvalue::Use(ref operand) | - Rvalue::Repeat(ref operand, _) | - Rvalue::UnaryOp(_ /*un_op*/, ref operand) | - Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { + Rvalue::Use(ref operand) + | Rvalue::Repeat(ref operand, _) + | Rvalue::UnaryOp(_ /*un_op*/, ref operand) + | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { self.consume_operand(context, (operand, span), flow_state) } @@ -945,8 +971,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | - Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { + Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) + | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => { self.consume_operand(context, (operand1, span), flow_state); self.consume_operand(context, (operand2, span), flow_state); } @@ -1015,12 +1041,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Returns whether a borrow of this place is invalidated when the function /// exits - fn check_for_invalidation_at_exit(&mut self, - context: Context, - borrow: &BorrowData<'tcx>, - span: Span, - flow_state: &Flows<'cx, 'gcx, 'tcx>) - { + fn check_for_invalidation_at_exit( + &mut self, + context: Context, + borrow: &BorrowData<'tcx>, + span: Span, + flow_state: &Flows<'cx, 'gcx, 'tcx>, + ) { debug!("check_for_invalidation_at_exit({:?})", borrow); let place = &borrow.borrowed_place; let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); @@ -1061,11 +1088,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // FIXME: replace this with a proper borrow_conflicts_with_place when // that is merged. - let sd = if might_be_alive { - Deep - } else { - Shallow(None) - }; + let sd = if might_be_alive { Deep } else { Shallow(None) }; if self.places_conflict(place, root_place, sd) { debug!("check_for_invalidation_at_exit({:?}): INVALID", place); @@ -1075,17 +1098,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { context, borrow, span.end_point(), - flow_state.borrows.operator() + flow_state.borrows.operator(), ) } } - fn check_activations(&mut self, - location: Location, - span: Span, - flow_state: &Flows<'cx, 'gcx, 'tcx>) - { - if !self.tcx.sess.opts.debugging_opts.two_phase_borrows { + fn check_activations( + &mut self, + location: Location, + span: Span, + flow_state: &Flows<'cx, 'gcx, 'tcx>, + ) { + if !self.tcx.sess.two_phase_borrows() { return; } @@ -1095,8 +1119,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let domain = flow_state.borrows.operator(); let data = domain.borrows(); flow_state.borrows.each_gen_bit(|gen| { - if gen.is_activation() - { + if gen.is_activation() { let borrow_index = gen.borrow_index(); let borrow = &data[borrow_index]; // currently the flow analysis registers @@ -1105,16 +1128,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // mutable borrow before we check it. match borrow.kind { BorrowKind::Shared => return, - BorrowKind::Unique | - BorrowKind::Mut => {} + BorrowKind::Unique | BorrowKind::Mut => {} } - self.access_place(ContextKind::Activation.new(location), - (&borrow.borrowed_place, span), - (Deep, Activation(WriteKind::MutableBorrow(borrow.kind), - borrow_index)), - LocalMutationIsAllowed::No, - flow_state); + self.access_place( + ContextKind::Activation.new(location), + (&borrow.borrowed_place, span), + ( + Deep, + Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index), + ), + LocalMutationIsAllowed::No, + flow_state, + ); // We do not need to call `check_if_path_is_moved` // again, as we already called it when we made the // initial reservation. @@ -1135,7 +1161,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) { return; } - debug!("check_if_reassignment_to_immutable_state({:?}) - is an imm local", place); + debug!( + "check_if_reassignment_to_immutable_state({:?}) - is an imm local", + place + ); for i in flow_state.ever_inits.elems_incoming() { let init = self.move_data.inits[i]; @@ -1364,14 +1393,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); let mut error_reported = false; match kind { - Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) | - Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { + Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) + | Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { if let Err(_place_err) = self.is_mutable(place, LocalMutationIsAllowed::Yes) { span_bug!(span, "&unique borrow for {:?} should not fail", place); } } - Reservation(WriteKind::MutableBorrow(BorrowKind::Mut)) | - Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) = + Reservation(WriteKind::MutableBorrow(BorrowKind::Mut)) + | Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { error_reported = true; @@ -1393,8 +1422,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); }, - Reservation(WriteKind::Mutate) | - Write(WriteKind::Mutate) => { + Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => { if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { error_reported = true; @@ -1415,12 +1443,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } } - Reservation(WriteKind::Move) | - Reservation(WriteKind::StorageDeadOrDrop) | - Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) | - Write(WriteKind::Move) | - Write(WriteKind::StorageDeadOrDrop) | - Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { + Reservation(WriteKind::Move) + | Reservation(WriteKind::StorageDeadOrDrop) + | Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) + | Write(WriteKind::Move) + | Write(WriteKind::StorageDeadOrDrop) + | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { self.tcx.sess.delay_span_bug( span, @@ -1435,10 +1463,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Activation(..) => {} // permission checks are done at Reservation point. - Read(ReadKind::Borrow(BorrowKind::Unique)) | - Read(ReadKind::Borrow(BorrowKind::Mut)) | - Read(ReadKind::Borrow(BorrowKind::Shared)) | - Read(ReadKind::Copy) => {} // Access authorized + Read(ReadKind::Borrow(BorrowKind::Unique)) + | Read(ReadKind::Borrow(BorrowKind::Mut)) + | Read(ReadKind::Borrow(BorrowKind::Shared)) + | Read(ReadKind::Copy) => {} // Access authorized } error_reported @@ -1455,8 +1483,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let local = &self.mir.local_decls[local]; match local.mutability { Mutability::Not => match is_local_mutation_allowed { - LocalMutationIsAllowed::Yes | - LocalMutationIsAllowed::ExceptUpvars => Ok(()), + LocalMutationIsAllowed::Yes | LocalMutationIsAllowed::ExceptUpvars => { + Ok(()) + } LocalMutationIsAllowed::No => Err(place), }, Mutability::Mut => Ok(()), @@ -1481,13 +1510,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // Mutably borrowed data is mutable, but only if we have a // unique path to the `&mut` hir::MutMutable => { - let mode = match - self.is_upvar_field_projection(&proj.base) + let mode = match self.is_upvar_field_projection(&proj.base) { - Some(field) if { - self.mir.upvar_decls[field.index()].by_ref - } => is_local_mutation_allowed, - _ => LocalMutationIsAllowed::Yes + Some(field) + if { + self.mir.upvar_decls[field.index()].by_ref + } => + { + is_local_mutation_allowed + } + _ => LocalMutationIsAllowed::Yes, }; self.is_mutable(&proj.base, mode) @@ -1513,22 +1545,28 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // All other projections are owned by their base path, so mutable if // base path is mutable - ProjectionElem::Field(..) | - ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Subslice { .. } | - ProjectionElem::Downcast(..) => { + ProjectionElem::Field(..) + | ProjectionElem::Index(..) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(..) => { if let Some(field) = self.is_upvar_field_projection(place) { let decl = &self.mir.upvar_decls[field.index()]; - debug!("decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}", - decl, is_local_mutation_allowed, place); + debug!( + "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}", + decl, + is_local_mutation_allowed, + place + ); match (decl.mutability, is_local_mutation_allowed) { - (Mutability::Not, LocalMutationIsAllowed::No) | - (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) - => Err(place), - (Mutability::Not, LocalMutationIsAllowed::Yes) | - (Mutability::Mut, _) => + (Mutability::Not, LocalMutationIsAllowed::No) + | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => { + Err(place) + } + (Mutability::Not, LocalMutationIsAllowed::Yes) + | (Mutability::Mut, _) => { self.is_mutable(&proj.base, is_local_mutation_allowed) + } } } else { self.is_mutable(&proj.base, is_local_mutation_allowed) @@ -1593,11 +1631,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // Given that the bases of `elem1` and `elem2` are always either equal // or disjoint (and have the same type!), return the overlap situation // between `elem1` and `elem2`. - fn place_element_conflict(&self, - elem1: &Place<'tcx>, - elem2: &Place<'tcx>) - -> Overlap - { + fn place_element_conflict(&self, elem1: &Place<'tcx>, elem2: &Place<'tcx>) -> Overlap { match (elem1, elem2) { (Place::Local(l1), Place::Local(l2)) => { if l1 == l2 { @@ -1623,8 +1657,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Overlap::EqualOrDisjoint } } - (Place::Local(_), Place::Static(_)) | - (Place::Static(_), Place::Local(_)) => { + (Place::Local(_), Place::Static(_)) | (Place::Static(_), Place::Local(_)) => { debug!("place_element_conflict: DISJOINT-STATIC-LOCAL"); Overlap::Disjoint } @@ -1689,15 +1722,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Overlap::Disjoint } } - (ProjectionElem::Index(..), ProjectionElem::Index(..)) | - (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) | - (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) | - (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) | - (ProjectionElem::ConstantIndex { .. }, ProjectionElem::ConstantIndex { .. }) | - (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) | - (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) | - (ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) | - (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { + (ProjectionElem::Index(..), ProjectionElem::Index(..)) + | (ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) + | (ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) + | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) + | ( + ProjectionElem::ConstantIndex { .. }, + ProjectionElem::ConstantIndex { .. }, + ) + | (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) + | (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) + | (ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) + | (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { // Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint // (if the indexes differ) or equal (if they are the same), so this // is the recursive case that gives "equal *or* disjoint" its meaning. @@ -1714,41 +1750,45 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Overlap::EqualOrDisjoint } - (ProjectionElem::Deref, _) | - (ProjectionElem::Field(..), _) | - (ProjectionElem::Index(..), _) | - (ProjectionElem::ConstantIndex { .. }, _) | - (ProjectionElem::Subslice { .. }, _) | - (ProjectionElem::Downcast(..), _) => { - bug!("mismatched projections in place_element_conflict: {:?} and {:?}", - - elem1, elem2) - } + (ProjectionElem::Deref, _) + | (ProjectionElem::Field(..), _) + | (ProjectionElem::Index(..), _) + | (ProjectionElem::ConstantIndex { .. }, _) + | (ProjectionElem::Subslice { .. }, _) + | (ProjectionElem::Downcast(..), _) => bug!( + "mismatched projections in place_element_conflict: {:?} and {:?}", + elem1, + elem2 + ), } } - (Place::Projection(_), _) | - (_, Place::Projection(_)) => { - bug!("unexpected elements in place_element_conflict: {:?} and {:?}", - elem1, elem2) - } + (Place::Projection(_), _) | (_, Place::Projection(_)) => bug!( + "unexpected elements in place_element_conflict: {:?} and {:?}", + elem1, + elem2 + ), } } /// Returns whether an access of kind `access` to `access_place` conflicts with /// a borrow/full access to `borrow_place` (for deep accesses to mutable /// locations, this function is symmetric between `borrow_place` & `access_place`). - fn places_conflict(&mut self, - borrow_place: &Place<'tcx>, - access_place: &Place<'tcx>, - access: ShallowOrDeep) - -> bool - { - debug!("places_conflict({:?},{:?},{:?})", borrow_place, access_place, access); + fn places_conflict( + &mut self, + borrow_place: &Place<'tcx>, + access_place: &Place<'tcx>, + access: ShallowOrDeep, + ) -> bool { + debug!( + "places_conflict({:?},{:?},{:?})", + borrow_place, + access_place, + access + ); // Return all the prefixes of `place` in reverse order, including // downcasts. - fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>> - { + fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>> { let mut result = vec![]; let mut place = place; loop { @@ -1767,13 +1807,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let borrow_components = place_elements(borrow_place); let access_components = place_elements(access_place); - debug!("places_conflict: components {:?} / {:?}", - borrow_components, access_components); + debug!( + "places_conflict: components {:?} / {:?}", + borrow_components, + access_components + ); - let borrow_components = borrow_components.into_iter() - .map(Some).chain(iter::repeat(None)); - let access_components = access_components.into_iter() - .map(Some).chain(iter::repeat(None)); + let borrow_components = borrow_components + .into_iter() + .map(Some) + .chain(iter::repeat(None)); + let access_components = access_components + .into_iter() + .map(Some) + .chain(iter::repeat(None)); // The borrowck rules for proving disjointness are applied from the "root" of the // borrow forwards, iterating over "similar" projections in lockstep until // we can prove overlap one way or another. Essentially, we treat `Overlap` as @@ -1839,13 +1886,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let (base, elem) = match borrow_c { Place::Projection(box Projection { base, elem }) => (base, elem), - _ => bug!("place has no base?") + _ => bug!("place has no base?"), }; let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); match (elem, &base_ty.sty, access) { - (_, _, Shallow(Some(ArtificialField::Discriminant))) | - (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { + (_, _, Shallow(Some(ArtificialField::Discriminant))) + | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => { // The discriminant and array length are like // additional fields on the type; they do not // overlap any existing data there. Furthermore, @@ -1866,9 +1913,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { debug!("places_conflict: shallow access behind ptr"); return false; } - (ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut { - ty: _, mutbl: hir::MutImmutable - }), _) => { + ( + ProjectionElem::Deref, + ty::TyRef( + _, + ty::TypeAndMut { + ty: _, + mutbl: hir::MutImmutable, + }, + ), + _, + ) => { // the borrow goes through a dereference of a shared reference. // // I'm not sure why we are tracking these borrows - shared @@ -1878,12 +1933,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { return false; } - (ProjectionElem::Deref, _, Deep) | - (ProjectionElem::Field { .. }, _, _) | - (ProjectionElem::Index { ..}, _, _) | - (ProjectionElem::ConstantIndex { .. }, _, _) | - (ProjectionElem::Subslice { .. }, _, _) | - (ProjectionElem::Downcast { .. }, _, _) => { + (ProjectionElem::Deref, _, Deep) + | (ProjectionElem::Field { .. }, _, _) + | (ProjectionElem::Index { .. }, _, _) + | (ProjectionElem::ConstantIndex { .. }, _, _) + | (ProjectionElem::Subslice { .. }, _, _) + | (ProjectionElem::Downcast { .. }, _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and // there's a deref later on, e.g. a borrow @@ -1920,7 +1975,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { return false; } } - } } } @@ -1963,7 +2017,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if self.places_conflict(&borrowed.borrowed_place, place, access) { let ctrl = op(self, i, borrowed); - if ctrl == Control::Break { return; } + if ctrl == Control::Break { + return; + } } } } diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 673e85e6b61cd..bdacd831cb656 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -20,7 +20,7 @@ use rustc::ty::subst::Substs; use rustc::ty::fold::TypeFoldable; use super::ToRegionVid; -use super::region_infer::RegionInferenceContext; +use super::region_infer::{RegionInferenceContext, Cause}; pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, @@ -53,14 +53,14 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx /// We sometimes have `substs` within an rvalue, or within a /// call. Make them live at the location where they appear. fn visit_substs(&mut self, substs: &&'tcx Substs<'tcx>, location: Location) { - self.add_regular_live_constraint(*substs, location); + self.add_regular_live_constraint(*substs, location, Cause::LiveOther(location)); self.super_substs(substs); } /// We sometimes have `region` within an rvalue, or within a /// call. Make them live at the location where they appear. fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) { - self.add_regular_live_constraint(*region, location); + self.add_regular_live_constraint(*region, location, Cause::LiveOther(location)); self.super_region(region); } @@ -75,7 +75,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx ty_context); } TyContext::Location(location) => { - self.add_regular_live_constraint(*ty, location); + self.add_regular_live_constraint(*ty, location, Cause::LiveOther(location)); } } @@ -85,7 +85,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx /// We sometimes have `closure_substs` within an rvalue, or within a /// call. Make them live at the location where they appear. fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Location) { - self.add_regular_live_constraint(*substs, location); + self.add_regular_live_constraint(*substs, location, Cause::LiveOther(location)); self.super_closure_substs(substs); } @@ -112,7 +112,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at /// `location`. - fn add_regular_live_constraint(&mut self, live_ty: T, location: Location) + fn add_regular_live_constraint(&mut self, live_ty: T, location: Location, cause: Cause) where T: TypeFoldable<'tcx>, { @@ -126,7 +126,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { .tcx .for_each_free_region(&live_ty, |live_region| { let vid = live_region.to_region_vid(); - self.regioncx.add_live_point(vid, location); + self.regioncx.add_live_point(vid, location, &cause); }); } diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs new file mode 100644 index 0000000000000..948c1ac0b1362 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -0,0 +1,210 @@ +// Copyright 2017 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. + +use borrow_check::{Context, MirBorrowckCtxt}; +use borrow_check::nll::region_infer::{Cause, RegionInferenceContext}; +use dataflow::BorrowData; +use rustc::mir::{Local, Location, Mir}; +use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::DiagnosticBuilder; +use util::liveness::{self, DefUse, LivenessMode}; + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + pub(in borrow_check) fn explain_why_borrow_contains_point( + &self, + context: Context, + borrow: &BorrowData<'_>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let Some(regioncx) = &self.nonlexical_regioncx { + if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) { + let mir = self.mir; + + match *cause.root_cause() { + Cause::LiveVar(local, location) => { + match find_regular_use(&mir, regioncx, borrow, location, local) { + Some(p) => { + err.span_label( + mir.source_info(p).span, + format!("borrow later used here"), + ); + } + + None => { + span_bug!( + mir.source_info(context.loc).span, + "Cause should end in a LiveVar" + ); + } + } + } + + Cause::DropVar(local, location) => { + match find_drop_use(&mir, regioncx, borrow, location, local) { + Some(p) => { + let local_name = &mir.local_decls[local].name.unwrap(); + + err.span_label( + mir.source_info(p).span, + format!( + "borrow later used here, when `{}` is dropped", + local_name + ), + ); + } + + None => { + span_bug!( + mir.source_info(context.loc).span, + "Cause should end in a DropVar" + ); + } + } + } + + _ => { + cause.label_diagnostic(mir, err); + } + } + } + } + } +} + +fn find_regular_use<'gcx, 'tcx>( + mir: &'gcx Mir, + regioncx: &'tcx RegionInferenceContext, + borrow: &'tcx BorrowData, + start_point: Location, + local: Local, +) -> Option { + let mut uf = UseFinder { + mir, + regioncx, + borrow, + start_point, + local, + liveness_mode: LivenessMode { + include_regular_use: true, + include_drops: false, + }, + }; + + uf.find() +} + +fn find_drop_use<'gcx, 'tcx>( + mir: &'gcx Mir, + regioncx: &'tcx RegionInferenceContext, + borrow: &'tcx BorrowData, + start_point: Location, + local: Local, +) -> Option { + let mut uf = UseFinder { + mir, + regioncx, + borrow, + start_point, + local, + liveness_mode: LivenessMode { + include_regular_use: false, + include_drops: true, + }, + }; + + uf.find() +} + +struct UseFinder<'gcx, 'tcx> { + mir: &'gcx Mir<'gcx>, + regioncx: &'tcx RegionInferenceContext<'tcx>, + borrow: &'tcx BorrowData<'tcx>, + start_point: Location, + local: Local, + liveness_mode: LivenessMode, +} + +impl<'gcx, 'tcx> UseFinder<'gcx, 'tcx> { + fn find(&mut self) -> Option { + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(self.start_point); + while let Some(p) = stack.pop() { + if !self.regioncx.region_contains_point(self.borrow.region, p) { + continue; + } + + if !visited.insert(p) { + continue; + } + + let block_data = &self.mir[p.block]; + let (defined, used) = self.def_use(p, block_data.visitable(p.statement_index)); + + if used { + return Some(p); + } else if !defined { + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend( + block_data + .terminator() + .successors() + .iter() + .map(|&basic_block| Location { + statement_index: 0, + block: basic_block, + }), + ); + } + } + } + + None + } + + fn def_use(&self, location: Location, thing: &MirVisitable<'tcx>) -> (bool, bool) { + let mut visitor = DefUseVisitor { + defined: false, + used: false, + local: self.local, + liveness_mode: self.liveness_mode, + }; + + thing.apply(location, &mut visitor); + + (visitor.defined, visitor.used) + } +} + +struct DefUseVisitor { + defined: bool, + used: bool, + local: Local, + liveness_mode: LivenessMode, +} + +impl<'tcx> Visitor<'tcx> for DefUseVisitor { + fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { + if local == self.local { + match liveness::categorize(context, self.liveness_mode) { + Some(DefUse::Def) => self.defined = true, + Some(DefUse::Use) => self.used = true, + None => (), + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 6977d91d25a5d..f96e107efa38f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -27,6 +27,7 @@ use util::pretty::{self, ALIGN}; use self::mir_util::PassWhere; mod constraint_generation; +pub mod explain_borrow; pub(crate) mod region_infer; mod renumber; mod subtype_constraint_generation; @@ -77,17 +78,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( Option>, ) { // Run the MIR type-checker. - let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); let liveness = &LivenessResults::compute(mir); - let fr_fn_body = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let constraint_sets = &type_check::type_check( infcx, - mir_node_id, param_env, mir, - fr_fn_body, - universal_regions.input_tys, - universal_regions.output_ty, + def_id, + &universal_regions, &liveness, flow_inits, move_data, @@ -285,19 +282,25 @@ fn for_each_region_constraint( /// This is reasonable because in our MIR we replace all universal regions /// with inference variables. pub trait ToRegionVid { - fn to_region_vid(&self) -> RegionVid; + fn to_region_vid(self) -> RegionVid; } -impl ToRegionVid for RegionKind { - fn to_region_vid(&self) -> RegionVid { - if let &ty::ReVar(vid) = self { - vid +impl<'tcx> ToRegionVid for &'tcx RegionKind { + fn to_region_vid(self) -> RegionVid { + if let ty::ReVar(vid) = self { + *vid } else { bug!("region is not an ReVar: {:?}", self) } } } +impl ToRegionVid for RegionVid { + fn to_region_vid(self) -> RegionVid { + self + } +} + fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { // sort and deduplicate: let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect(); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs index 906efaef887c3..e8a23acd798de 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/annotation.rs @@ -15,32 +15,40 @@ //! handle the part about dumping the inference context internal //! state. -use rustc::ty; +use borrow_check::nll::region_infer::RegionInferenceContext; +use borrow_check::nll::universal_regions::DefiningTy; use rustc_errors::DiagnosticBuilder; -use super::RegionInferenceContext; impl<'gcx, 'tcx> RegionInferenceContext<'tcx> { /// Write out our state into the `.mir` files. pub(crate) fn annotate(&self, err: &mut DiagnosticBuilder<'_>) { - match self.universal_regions.defining_ty.sty { - ty::TyClosure(def_id, substs) => { + match self.universal_regions.defining_ty { + DefiningTy::Closure(def_id, substs) => { err.note(&format!( "defining type: {:?} with closure substs {:#?}", def_id, &substs.substs[..] )); } - ty::TyFnDef(def_id, substs) => { + DefiningTy::Generator(def_id, substs, interior) => { + err.note(&format!( + "defining type: {:?} with closure substs {:#?} and interior {:?}", + def_id, + &substs.substs[..], + interior + )); + } + DefiningTy::FnDef(def_id, substs) => { err.note(&format!( "defining type: {:?} with substs {:#?}", def_id, &substs[..] )); } - _ => { + DefiningTy::Const(ty) => { err.note(&format!( "defining type: {:?}", - self.universal_regions.defining_ty + ty )); } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs index 59860d61ab985..d55b601823245 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -15,6 +15,7 @@ use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements, RegionValues}; +use syntax::codemap::Span; use rustc::mir::{Location, Mir}; use rustc::ty::RegionVid; use rustc_data_structures::fx::FxHashSet; @@ -127,6 +128,7 @@ pub(super) struct CopyFromSourceToTarget<'v> { pub target_region: RegionVid, pub inferred_values: &'v mut RegionValues, pub constraint_point: Location, + pub constraint_span: Span, } impl<'v> DfsOp for CopyFromSourceToTarget<'v> { @@ -143,14 +145,22 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> { } fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result { - Ok(self.inferred_values.add(self.target_region, point_index)) + Ok(self.inferred_values.add_due_to_outlives( + self.source_region, + self.target_region, + point_index, + self.constraint_point, + self.constraint_span, + )) } fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result { - Ok( - self.inferred_values - .add_universal_regions_outlived_by(self.source_region, self.target_region), - ) + Ok(self.inferred_values.add_universal_regions_outlived_by( + self.source_region, + self.target_region, + self.constraint_point, + self.constraint_span, + )) } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 58e16e7673afc..da136a34b9971 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -15,12 +15,15 @@ use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::RegionObligation; use rustc::infer::RegionVariableOrigin; use rustc::infer::SubregionOrigin; +use rustc::infer::error_reporting::nice_region_error::NiceRegionError; use rustc::infer::region_constraints::{GenericKind, VarOrigins}; use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - Location, Mir}; + Local, Location, Mir}; use rustc::traits::ObligationCause; use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; +use rustc::util::common::ErrorReported; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_errors::DiagnosticBuilder; use std::fmt; use std::rc::Rc; use syntax::ast; @@ -34,6 +37,8 @@ mod graphviz; mod values; use self::values::{RegionValueElements, RegionValues}; +use super::ToRegionVid; + pub struct RegionInferenceContext<'tcx> { /// Contains the definition for every region variable. Region /// variables are identified by their index (`RegionVid`). The @@ -65,6 +70,8 @@ pub struct RegionInferenceContext<'tcx> { universal_regions: UniversalRegions<'tcx>, } +struct TrackCauses(bool); + struct RegionDefinition<'tcx> { /// Why we created this variable. Mostly these will be /// `RegionVariableOrigin::NLL`, but some variables get created @@ -83,6 +90,38 @@ struct RegionDefinition<'tcx> { external_name: Option>, } +/// NB: The variants in `Cause` are intentionally ordered. Lower +/// values are preferred when it comes to error messages. Do not +/// reorder willy nilly. +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +pub(crate) enum Cause { + /// point inserted because Local was live at the given Location + LiveVar(Local, Location), + + /// point inserted because Local was dropped at the given Location + DropVar(Local, Location), + + /// point inserted because the type was live at the given Location, + /// but not as part of some local variable + LiveOther(Location), + + /// part of the initial set of values for a universally quantified region + UniversalRegion(RegionVid), + + /// Element E was added to R because there was some + /// outlives obligation `R: R1 @ P` and `R1` contained `E`. + Outlives { + /// the reason that R1 had E + original_cause: Rc, + + /// the point P from the relation + constraint_location: Location, + + /// The span indicating why we added the outlives constraint. + constraint_span: Span, + }, +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { // NB. The ordering here is not significant for correctness, but @@ -193,7 +232,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free /// regions defined in `universal_regions`. - pub fn new( + pub(crate) fn new( var_origins: VarOrigins, universal_regions: UniversalRegions<'tcx>, mir: &Mir<'tcx>, @@ -209,10 +248,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|origin| RegionDefinition::new(origin)) .collect(); + let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.nll_dump_cause()); + let mut result = Self { definitions, elements: elements.clone(), - liveness_constraints: RegionValues::new(elements, num_region_variables), + liveness_constraints: RegionValues::new( + elements, + num_region_variables, + TrackCauses(nll_dump_cause), + ), inferred_values: None, constraints: Vec::new(), type_tests: Vec::new(), @@ -262,11 +307,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Add all nodes in the CFG to liveness constraints for point_index in self.elements.all_point_indices() { - self.liveness_constraints.add(variable, point_index); + self.liveness_constraints.add( + variable, + point_index, + &Cause::UniversalRegion(variable), + ); } // Add `end(X)` into the set for X. - self.liveness_constraints.add(variable, variable); + self.liveness_constraints + .add(variable, variable, &Cause::UniversalRegion(variable)); } } @@ -286,11 +336,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns true if the region `r` contains the point `p`. /// /// Panics if called before `solve()` executes, - pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { + pub fn region_contains_point(&self, r: R, p: Location) -> bool + where + R: ToRegionVid, + { let inferred_values = self.inferred_values .as_ref() .expect("region values not yet inferred"); - inferred_values.contains(r, p) + inferred_values.contains(r.to_region_vid(), p) + } + + /// Returns the *reason* that the region `r` contains the given point. + pub(crate) fn why_region_contains_point(&self, r: R, p: Location) -> Option> + where + R: ToRegionVid, + { + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + inferred_values.cause(r.to_region_vid(), p) } /// Returns access to the value of `r` for debugging purposes. @@ -306,13 +370,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Returns `true` if this constraint is new and `false` is the /// constraint was already present. - pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool { + pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location, cause: &Cause) -> bool { debug!("add_live_point({:?}, {:?})", v, point); assert!(self.inferred_values.is_none(), "values already inferred"); - debug!("add_live_point: @{:?}", point); + debug!("add_live_point: @{:?} Adding cause {:?}", point, cause); let element = self.elements.index(point); - if self.liveness_constraints.add(v, element) { + if self.liveness_constraints.add(v, element, &cause) { true } else { false @@ -366,9 +430,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { None }; - self.check_type_tests(infcx, mir, outlives_requirements.as_mut()); + self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut()); - self.check_universal_regions(infcx, outlives_requirements.as_mut()); + self.check_universal_regions(infcx, mir, mir_def_id, outlives_requirements.as_mut()); let outlives_requirements = outlives_requirements.unwrap_or(vec![]); @@ -416,6 +480,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { target_region: constraint.sup, inferred_values: &mut inferred_values, constraint_point: constraint.point, + constraint_span: constraint.span, }, ); @@ -439,6 +504,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, mir: &Mir<'tcx>, + mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, ) { let tcx = infcx.tcx; @@ -457,14 +523,56 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Oh the humanity. Obviously we will do better than this error eventually. - tcx.sess.span_err( - type_test.span, - &format!( - "`{}` does not outlive `{:?}`", + let lower_bound_region = self.to_error_region(type_test.lower_bound); + if let Some(lower_bound_region) = lower_bound_region { + let region_scope_tree = &tcx.region_scope_tree(mir_def_id); + infcx.report_generic_bound_failure( + region_scope_tree, + type_test.span, + None, type_test.generic_kind, - type_test.lower_bound, - ), - ); + lower_bound_region, + ); + } else { + // FIXME. We should handle this case better. It + // indicates that we have e.g. some region variable + // whose value is like `'a+'b` where `'a` and `'b` are + // distinct unrelated univesal regions that are not + // known to outlive one another. It'd be nice to have + // some examples where this arises to decide how best + // to report it; we could probably handle it by + // iterating over the universal regions and reporting + // an error that multiple bounds are required. + tcx.sess.span_err( + type_test.span, + &format!( + "`{}` does not live long enough", + type_test.generic_kind, + ), + ); + } + } + } + + /// Converts a region inference variable into a `ty::Region` that + /// we can use for error reporting. If `r` is universally bound, + /// then we use the name that we have on record for it. If `r` is + /// existentially bound, then we check its inferred value and try + /// to find a good name from that. Returns `None` if we can't find + /// one (e.g., this is just some random part of the CFG). + fn to_error_region(&self, r: RegionVid) -> Option> { + if self.universal_regions.is_universal_region(r) { + return self.definitions[r].external_name; + } else { + let inferred_values = self.inferred_values + .as_ref() + .expect("region values not yet inferred"); + let upper_bound = self.universal_upper_bound(r); + if inferred_values.contains(r, upper_bound) { + self.to_error_region(upper_bound) + } else { + None + } } } @@ -598,6 +706,36 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// encoding `T` as part of `try_promote_type_test_subject` (see /// that fn for details). /// + /// This is based on the result `'y` of `universal_upper_bound`, + /// except that it converts further takes the non-local upper + /// bound of `'y`, so that the final result is non-local. + fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { + let inferred_values = self.inferred_values.as_ref().unwrap(); + + debug!( + "non_local_universal_upper_bound(r={:?}={})", + r, + inferred_values.region_value_str(r) + ); + + let lub = self.universal_upper_bound(r); + + // Grow further to get smallest universal region known to + // creator. + let non_local_lub = self.universal_regions.non_local_upper_bound(lub); + + debug!( + "non_local_universal_upper_bound: non_local_lub={:?}", + non_local_lub + ); + + non_local_lub + } + + /// Returns a universally quantified region that outlives the + /// value of `r` (`r` may be existentially or universally + /// quantified). + /// /// Since `r` is (potentially) an existential region, it has some /// value which may include (a) any number of points in the CFG /// and (b) any number of `end('x)` elements of universally @@ -608,15 +746,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. - /// - Finally, we take the non-local upper bound of `'y`. - /// - This uses `UniversalRegions::non_local_upper_bound`, which - /// is similar to this method but only works on universal - /// regions). - fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { + fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { let inferred_values = self.inferred_values.as_ref().unwrap(); debug!( - "non_local_universal_upper_bound(r={:?}={})", + "universal_upper_bound(r={:?}={})", r, inferred_values.region_value_str(r) ); @@ -628,18 +762,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { lub = self.universal_regions.postdom_upper_bound(lub, ur); } - debug!("non_local_universal_upper_bound: lub={:?}", lub); - - // Grow further to get smallest universal region known to - // creator. - let non_local_lub = self.universal_regions.non_local_upper_bound(lub); + debug!("universal_upper_bound: r={:?} lub={:?}", r, lub); - debug!( - "non_local_universal_upper_bound: non_local_lub={:?}", - non_local_lub - ); - - non_local_lub + lub } /// Test if `test` is true when applied to `lower_bound` at @@ -743,6 +868,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_universal_regions<'gcx>( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, ) { // The universal regions are always found in a prefix of the @@ -755,7 +882,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. for (fr, _) in universal_definitions { - self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements); + self.check_universal_region( + infcx, + mir, + mir_def_id, + fr, + &mut propagated_outlives_requirements, + ); } } @@ -770,6 +903,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn check_universal_region<'gcx>( &self, infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mir_def_id: DefId, longer_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, ) { @@ -826,33 +961,62 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Note: in this case, we use the unapproximated regions // to report the error. This gives better error messages // in some cases. - self.report_error(infcx, longer_fr, shorter_fr, blame_span); + self.report_error(infcx, mir, mir_def_id, longer_fr, shorter_fr, blame_span); } } + /// Report an error because the universal region `fr` was required to outlive + /// `outlived_fr` but it is not known to do so. For example: + /// + /// ``` + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` + /// + /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`. fn report_error( &self, infcx: &InferCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + mir_def_id: DefId, fr: RegionVid, outlived_fr: RegionVid, blame_span: Span, ) { // Obviously uncool error reporting. - let fr_string = match self.definitions[fr].external_name { + let fr_name = self.to_error_region(fr); + let outlived_fr_name = self.to_error_region(outlived_fr); + + if let (Some(f), Some(o)) = (fr_name, outlived_fr_name) { + let tables = infcx.tcx.typeck_tables_of(mir_def_id); + let nice = NiceRegionError::new(infcx.tcx, blame_span, o, f, Some(tables)); + if let Some(ErrorReported) = nice.try_report() { + return; + } + } + + let fr_string = match fr_name { Some(r) => format!("free region `{}`", r), None => format!("free region `{:?}`", fr), }; - let outlived_fr_string = match self.definitions[outlived_fr].external_name { + let outlived_fr_string = match outlived_fr_name { Some(r) => format!("free region `{}`", r), None => format!("free region `{:?}`", outlived_fr), }; - infcx.tcx.sess.span_err( + let mut diag = infcx.tcx.sess.struct_span_err( blame_span, &format!("{} does not outlive {}", fr_string, outlived_fr_string,), ); + + // Find out why `fr` had to outlive `outlived_fr`... + let inferred_values = self.inferred_values.as_ref().unwrap(); + if let Some(cause) = inferred_values.cause(fr, outlived_fr) { + cause.label_diagnostic(mir, &mut diag); + } + + diag.emit(); } /// Tries to finds a good span to blame for the fact that `fr1` @@ -973,7 +1137,7 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi /// Given an instance T of the closure type, this method /// instantiates the "extra" requirements that we computed for the /// closure into the inference context. This has the effect of - /// adding new subregion obligations to existing variables. + /// adding new outlives obligations to existing variables. /// /// As described on `ClosureRegionRequirements`, the extra /// requirements are expressed in terms of regionvids that index @@ -1075,3 +1239,94 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi }) } } + +trait CauseExt { + fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause; +} + +impl CauseExt for Rc { + /// Creates a derived cause due to an outlives constraint. + fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause { + Cause::Outlives { + original_cause: self.clone(), + constraint_location, + constraint_span, + } + } +} + +impl Cause { + pub(crate) fn label_diagnostic(&self, mir: &Mir<'_>, diag: &mut DiagnosticBuilder<'_>) { + // The cause information is pretty messy. Only dump it as an + // internal debugging aid if -Znll-dump-cause is given. + let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.nll_dump_cause()); + if !nll_dump_cause { + return; + } + + let mut string = String::new(); + self.push_diagnostic_string(mir, &mut string); + diag.note(&string); + } + + fn push_diagnostic_string(&self, mir: &Mir<'_>, string: &mut String) { + match self { + Cause::LiveVar(local, location) => { + string.push_str(&format!("because `{:?}` is live at {:?}", local, location)); + } + + Cause::DropVar(local, location) => { + string.push_str(&format!( + "because `{:?}` is dropped at {:?}", + local, + location + )); + } + + Cause::LiveOther(location) => { + string.push_str(&format!( + "because of a general liveness constraint at {:?}", + location + )); + } + + Cause::UniversalRegion(region_vid) => { + string.push_str(&format!( + "because `{:?}` is universally quantified", + region_vid + )); + } + + Cause::Outlives { + original_cause, + constraint_location, + constraint_span: _, + } => { + string.push_str(&format!( + "because of an outlives relation created at `{:?}`\n", + constraint_location + )); + + original_cause.push_diagnostic_string(mir, string); + } + } + } + + pub(crate) fn root_cause(&self) -> &Cause { + match self { + Cause::LiveVar(..) | + Cause::DropVar(..) | + Cause::LiveOther(..) | + Cause::UniversalRegion(..) => { + self + } + + Cause::Outlives { + original_cause, + .. + } => { + original_cause.root_cause() + } + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 5f23a0e5790af..b2b2ca1182d03 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -10,10 +10,14 @@ use std::rc::Rc; use rustc_data_structures::bitvec::BitMatrix; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::ty::RegionVid; +use syntax::codemap::Span; + +use super::{Cause, CauseExt, TrackCauses}; /// Maps between the various kinds of elements of a region value to /// the internal indices that w use. @@ -27,18 +31,23 @@ pub(super) struct RegionValueElements { impl RegionValueElements { pub(super) fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self { let mut num_points = 0; - let statements_before_block = - mir.basic_blocks() - .iter() - .map(|block_data| { - let v = num_points; - num_points += block_data.statements.len() + 1; - v - }) - .collect(); - - debug!("RegionValueElements(num_universal_regions={:?})", num_universal_regions); - debug!("RegionValueElements: statements_before_block={:#?}", statements_before_block); + let statements_before_block = mir.basic_blocks() + .iter() + .map(|block_data| { + let v = num_points; + num_points += block_data.statements.len() + 1; + v + }) + .collect(); + + debug!( + "RegionValueElements(num_universal_regions={:?})", + num_universal_regions + ); + debug!( + "RegionValueElements: statements_before_block={:#?}", + statements_before_block + ); debug!("RegionValueElements: num_points={:#?}", num_points); Self { @@ -60,7 +69,9 @@ impl RegionValueElements { /// Iterates over the `RegionElementIndex` for all points in the CFG. pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator + 'a { - (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions)) + (0..self.num_points).map(move |i| { + RegionElementIndex::new(i + self.num_universal_regions) + }) } /// Iterates over the `RegionElementIndex` for all points in the CFG. @@ -95,12 +106,11 @@ impl RegionValueElements { // be (BB2, 20). // // Nit: we could do a binary search here but I'm too lazy. - let (block, &first_index) = - self.statements_before_block - .iter_enumerated() - .filter(|(_, first_index)| **first_index <= point_index) - .last() - .unwrap(); + let (block, &first_index) = self.statements_before_block + .iter_enumerated() + .filter(|(_, first_index)| **first_index <= point_index) + .last() + .unwrap(); RegionElement::Location(Location { block, @@ -151,7 +161,10 @@ pub(super) trait ToElementIndex { impl ToElementIndex for Location { fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex { - let Location { block, statement_index } = self; + let Location { + block, + statement_index, + } = self; let start_index = elements.statements_before_block[block]; RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index) } @@ -178,10 +191,22 @@ impl ToElementIndex for RegionElementIndex { pub(super) struct RegionValues { elements: Rc, matrix: BitMatrix, + + /// If cause tracking is enabled, maps from a pair (r, e) + /// consisting of a region `r` that contains some element `e` to + /// the reason that the element is contained. There should be an + /// entry for every bit set to 1 in `BitMatrix`. + causes: Option, } +type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc>; + impl RegionValues { - pub(super) fn new(elements: &Rc, num_region_variables: usize) -> Self { + pub(super) fn new( + elements: &Rc, + num_region_variables: usize, + track_causes: TrackCauses, + ) -> Self { assert!( elements.num_universal_regions <= num_region_variables, "universal regions are a subset of the region variables" @@ -190,31 +215,83 @@ impl RegionValues { Self { elements: elements.clone(), matrix: BitMatrix::new(num_region_variables, elements.num_elements()), + causes: if track_causes.0 { + Some(CauseMap::default()) + } else { + None + }, } } /// Adds the given element to the value for the given region. Returns true if /// the element is newly added (i.e., was not already present). - pub(super) fn add(&mut self, r: RegionVid, elem: E) -> bool { + pub(super) fn add(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool { let i = self.elements.index(elem); + self.add_internal(r, i, |_| cause.clone()) + } + + /// Internal method to add an element to a region. + /// + /// Takes a "lazy" cause -- this function will return the cause, but it will only + /// be invoked if cause tracking is enabled. + fn add_internal(&mut self, r: RegionVid, i: RegionElementIndex, make_cause: F) -> bool + where + F: FnOnce(&CauseMap) -> Cause, + { if self.matrix.add(r.index(), i.index()) { debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i)); + + if let Some(causes) = &mut self.causes { + let cause = Rc::new(make_cause(causes)); + causes.insert((r, i), cause); + } + true } else { + if let Some(causes) = &mut self.causes { + let cause = make_cause(causes); + let old_cause = causes.get_mut(&(r, i)).unwrap(); + if cause < **old_cause { + *old_cause = Rc::new(cause); + return true; + } + } + false } } + /// Adds `elem` to `to_region` because of a relation: + /// + /// to_region: from_region @ constraint_location + /// + /// that was added by the cod at `constraint_span`. + pub(super) fn add_due_to_outlives( + &mut self, + from_region: RegionVid, + to_region: RegionVid, + elem: T, + constraint_location: Location, + constraint_span: Span, + ) -> bool { + let elem = self.elements.index(elem); + self.add_internal(to_region, elem, |causes| { + causes[&(from_region, elem)].outlives(constraint_location, constraint_span) + }) + } + /// Adds all the universal regions outlived by `from_region` to /// `to_region`. pub(super) fn add_universal_regions_outlived_by( &mut self, from_region: RegionVid, to_region: RegionVid, + constraint_location: Location, + constraint_span: Span, ) -> bool { - // FIXME. We could optimize this by improving - // `BitMatrix::merge` so it does not always merge an entire - // row. + // We could optimize this by improving `BitMatrix::merge` so + // it does not always merge an entire row. That would + // complicate causal tracking though. debug!( "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})", from_region, @@ -223,7 +300,13 @@ impl RegionValues { let mut changed = false; for elem in self.elements.all_universal_region_indices() { if self.contains(from_region, elem) { - changed |= self.add(to_region, elem); + changed |= self.add_due_to_outlives( + from_region, + to_region, + elem, + constraint_location, + constraint_span, + ); } } changed @@ -338,4 +421,18 @@ impl RegionValues { )); } } + + /// Given a region `r` that contains the element `elem`, returns the `Cause` + /// that tells us *why* `elem` is found in that region. + /// + /// Returns None if cause tracking is disabled or `elem` is not + /// actually found in `r`. + pub(super) fn cause(&self, r: RegionVid, elem: T) -> Option> { + let index = self.elements.index(elem); + if let Some(causes) = &self.causes { + causes.get(&(r, index)).cloned() + } else { + None + } + } } diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 79505405692d8..4aee48b979d61 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -16,7 +16,7 @@ use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; /// Replaces all free regions appearing in the MIR with fresh /// inference variables, returning the number of variables created. -pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) { +pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>) { debug!("renumber_mir()"); debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); @@ -24,26 +24,36 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut visitor.visit_mir(mir); } +/// Replaces all regions appearing in `value` with fresh inference +/// variables. +pub fn renumber_regions<'tcx, T>( + infcx: &InferCtxt<'_, '_, 'tcx>, + ty_context: TyContext, + value: &T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("renumber_regions(value={:?})", value); + + infcx + .tcx + .fold_regions(value, &mut false, |_region, _depth| { + let origin = NLLRegionVariableOrigin::Inferred(ty_context); + infcx.next_nll_region_var(origin) + }) +} + struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, } impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { - /// Replaces all regions appearing in `value` with fresh inference - /// variables. This is what we do for almost the entire MIR, with - /// the exception of the declared types of our arguments. fn renumber_regions(&mut self, ty_context: TyContext, value: &T) -> T where T: TypeFoldable<'tcx>, { - debug!("renumber_regions(value={:?})", value); - - self.infcx - .tcx - .fold_regions(value, &mut false, |_region, _depth| { - let origin = NLLRegionVariableOrigin::Inferred(ty_context); - self.infcx.next_nll_region_var(origin) - }) + renumber_regions(self.infcx, ty_context, value) } } diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index e42302761bfa3..32728145b29f6 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -51,10 +51,10 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { outlives_sets.len() ); - for (region, location) in liveness_set { + for (region, location, cause) in liveness_set { debug!("generate: {:#?} is live at {:#?}", region, location); let region_vid = self.to_region_vid(region); - self.regioncx.add_live_point(region_vid, *location); + self.regioncx.add_live_point(region_vid, *location, &cause); } for OutlivesSet { locations, data } in outlives_sets { diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs new file mode 100644 index 0000000000000..9e88a632f5c3d --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -0,0 +1,183 @@ +// Copyright 2017 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. + +//! This module contains code to equate the input/output types appearing +//! in the MIR with the expected input/output types from the function +//! signature. This requires a bit of processing, as the expected types +//! are supplied to us before normalization and may contain existential +//! `impl Trait` instances. In contrast, the input/output types found in +//! the MIR (specifically, in the special local variables for the +//! `RETURN_PLACE` the MIR arguments) are always fully normalize (and +//! contain revealed `impl Trait` values). + +use borrow_check::nll::renumber; +use borrow_check::nll::universal_regions::UniversalRegions; +use rustc::hir::def_id::DefId; +use rustc::infer::InferOk; +use rustc::ty::Ty; +use rustc::ty::subst::Subst; +use rustc::mir::*; +use rustc::mir::visit::TyContext; +use rustc::traits::PredicateObligations; + +use rustc_data_structures::indexed_vec::Idx; + +use super::{AtLocation, TypeChecker}; + +impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { + pub(super) fn equate_inputs_and_outputs( + &mut self, + mir: &Mir<'tcx>, + mir_def_id: DefId, + universal_regions: &UniversalRegions<'tcx>, + ) { + let tcx = self.infcx.tcx; + + let &UniversalRegions { + unnormalized_output_ty, + unnormalized_input_tys, + .. + } = universal_regions; + let infcx = self.infcx; + + let start_position = Location { + block: START_BLOCK, + statement_index: 0, + }; + + // Equate expected input tys with those in the MIR. + let argument_locals = (1..).map(Local::new); + for (&unnormalized_input_ty, local) in unnormalized_input_tys.iter().zip(argument_locals) { + let input_ty = self.normalize(&unnormalized_input_ty, start_position); + let mir_input_ty = mir.local_decls[local].ty; + self.equate_normalized_input_or_output(start_position, input_ty, mir_input_ty); + } + + // Return types are a bit more complex. They may contain existential `impl Trait` + // types. + debug!( + "equate_inputs_and_outputs: unnormalized_output_ty={:?}", + unnormalized_output_ty + ); + let output_ty = self.normalize(&unnormalized_output_ty, start_position); + debug!( + "equate_inputs_and_outputs: normalized output_ty={:?}", + output_ty + ); + let mir_output_ty = mir.local_decls[RETURN_PLACE].ty; + let anon_type_map = self.fully_perform_op(start_position.at_self(), |cx| { + let mut obligations = ObligationAccumulator::default(); + + let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types( + mir_def_id, + cx.body_id, + cx.param_env, + &output_ty, + )); + debug!( + "equate_inputs_and_outputs: instantiated output_ty={:?}", + output_ty + ); + debug!( + "equate_inputs_and_outputs: anon_type_map={:#?}", + anon_type_map + ); + + debug!( + "equate_inputs_and_outputs: mir_output_ty={:?}", + mir_output_ty + ); + obligations.add(infcx + .at(&cx.misc(cx.last_span), cx.param_env) + .eq(output_ty, mir_output_ty)?); + + for (&anon_def_id, anon_decl) in &anon_type_map { + let anon_defn_ty = tcx.type_of(anon_def_id); + let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); + let anon_defn_ty = renumber::renumber_regions( + cx.infcx, + TyContext::Location(start_position), + &anon_defn_ty, + ); + debug!( + "equate_inputs_and_outputs: concrete_ty={:?}", + anon_decl.concrete_ty + ); + debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); + obligations.add(infcx + .at(&cx.misc(cx.last_span), cx.param_env) + .eq(anon_decl.concrete_ty, anon_defn_ty)?); + } + + debug!("equate_inputs_and_outputs: equated"); + + Ok(InferOk { + value: Some(anon_type_map), + obligations: obligations.into_vec(), + }) + }).unwrap_or_else(|terr| { + span_mirbug!( + self, + start_position, + "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`", + output_ty, + mir_output_ty, + terr + ); + None + }); + + // Finally, if we instantiated the anon types successfully, we + // have to solve any bounds (e.g., `-> impl Iterator` needs to + // prove that `T: Iterator` where `T` is the type we + // instantiated it with). + if let Some(anon_type_map) = anon_type_map { + self.fully_perform_op(start_position.at_self(), |_cx| { + infcx.constrain_anon_types(&anon_type_map, universal_regions); + Ok(InferOk { + value: (), + obligations: vec![], + }) + }).unwrap(); + } + } + + fn equate_normalized_input_or_output(&mut self, location: Location, a: Ty<'tcx>, b: Ty<'tcx>) { + debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b); + + if let Err(terr) = self.eq_types(a, b, location.at_self()) { + span_mirbug!( + self, + location, + "equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`", + a, + b, + terr + ); + } + } +} + +#[derive(Debug, Default)] +struct ObligationAccumulator<'tcx> { + obligations: PredicateObligations<'tcx>, +} + +impl<'tcx> ObligationAccumulator<'tcx> { + fn add(&mut self, value: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = value; + self.obligations.extend(obligations); + value + } + + fn into_vec(self) -> PredicateObligations<'tcx> { + self.obligations + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index e41bf7cda8e6c..50b38f9b46b61 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -9,6 +9,7 @@ // except according to those terms. use dataflow::{FlowAtLocation, FlowsAtLocation}; +use borrow_check::nll::region_infer::Cause; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::{HasMoveData, MoveData}; use rustc::mir::{BasicBlock, Location, Mir}; @@ -79,7 +80,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo .simulate_block(self.mir, bb, |location, live_locals| { for live_local in live_locals.iter() { let live_local_ty = self.mir.local_decls[live_local].ty; - self.push_type_live_constraint(live_local_ty, location); + let cause = Cause::LiveVar(live_local, location); + self.push_type_live_constraint(live_local_ty, location, cause); } }); @@ -121,7 +123,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo ); let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_live_constraint(live_local_ty, location); + self.add_drop_live_constraint(live_local, live_local_ty, location); } } @@ -146,7 +148,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at /// `location`. - fn push_type_live_constraint(&mut self, value: T, location: Location) + fn push_type_live_constraint(&mut self, value: T, location: Location, cause: Cause) where T: TypeFoldable<'tcx>, { @@ -160,7 +162,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo self.cx .constraints .liveness_set - .push((live_region, location)); + .push((live_region, location, cause.clone())); }); } @@ -169,9 +171,15 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo /// the regions in its type must be live at `location`. The /// precise set will depend on the dropck constraints, and in /// particular this takes `#[may_dangle]` into account. - fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) { + fn add_drop_live_constraint( + &mut self, + dropped_local: Local, + dropped_ty: Ty<'tcx>, + location: Location, + ) { debug!( - "add_drop_live_constraint(dropped_ty={:?}, location={:?})", + "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})", + dropped_local, dropped_ty, location ); @@ -196,7 +204,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for outlive in outlives { - self.push_type_live_constraint(outlive, location); + let cause = Cause::DropVar(dropped_local, location); + self.push_type_live_constraint(outlive, location, cause); } // However, there may also be some types that @@ -207,7 +216,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo let ty = self.cx.normalize(&ty, location); match ty.sty { ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - self.push_type_live_constraint(ty, location); + let cause = Cause::DropVar(dropped_local, location); + self.push_type_live_constraint(ty, location, cause); } _ => if known.insert(ty) { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 6cdd77048c998..4c8a171299f2c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -11,12 +11,15 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] +use borrow_check::nll::region_infer::Cause; use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use borrow_check::nll::universal_regions::UniversalRegions; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedLvals; use dataflow::move_paths::MoveData; +use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; -use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; @@ -34,7 +37,32 @@ use util::liveness::LivenessResults; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +macro_rules! span_mirbug { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + $crate::borrow_check::nll::type_check::mirbug( + $context.tcx(), + $context.last_span, + &format!( + "broken MIR in {:?} ({:?}): {}", + $context.body_id, + $elem, + format_args!($($message)*), + ), + ) + }) +} + +macro_rules! span_mirbug_and_err { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + { + span_mirbug!($context, $elem, $($message)*); + $context.error() + } + }) +} + mod liveness; +mod input_output; /// Type checks the given `mir` in the context of the inference /// context `infcx`. Returns any region constraints that have yet to @@ -50,9 +78,11 @@ mod liveness; /// # Parameters /// /// - `infcx` -- inference context to use -/// - `body_id` -- body-id of the MIR being checked /// - `param_env` -- parameter environment to use for trait solving /// - `mir` -- MIR to type-check +/// - `mir_def_id` -- DefId from which the MIR is derived (must be local) +/// - `region_bound_pairs` -- the implied outlives obligations between type parameters +/// and lifetimes (e.g., `&'a T` implies `T: 'a`) /// - `implicit_region_bound` -- a region which all generic parameters are assumed /// to outlive; should represent the fn body /// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; @@ -65,31 +95,27 @@ mod liveness; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis pub(crate) fn type_check<'gcx, 'tcx>( infcx: &InferCtxt<'_, 'gcx, 'tcx>, - body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, - implicit_region_bound: ty::Region<'tcx>, - input_tys: &[Ty<'tcx>], - output_ty: Ty<'tcx>, + mir_def_id: DefId, + universal_regions: &UniversalRegions<'tcx>, liveness: &LivenessResults, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { + let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); + let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); type_check_internal( infcx, body_id, param_env, mir, + &universal_regions.region_bound_pairs, Some(implicit_region_bound), &mut |cx| { liveness::generate(cx, mir, liveness, flow_inits, move_data); - // Equate the input and output tys given by the user with - // the ones found in the MIR. - cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty); - for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) { - cx.equate_input_or_output(input_ty, mir.local_decls[local].ty); - } + cx.equate_inputs_and_outputs(mir, mir_def_id, universal_regions); }, ) } @@ -99,10 +125,17 @@ fn type_check_internal<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound); + let mut checker = TypeChecker::new( + infcx, + body_id, + param_env, + region_bound_pairs, + implicit_region_bound, + ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); verifier.visit_mir(mir); @@ -119,7 +152,6 @@ fn type_check_internal<'gcx, 'tcx>( checker.constraints } - fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { // We sometimes see MIR failures (notably predicate failures) due to // the fact that we check rvalue sized predicates here. So use `delay_span_bug` @@ -127,25 +159,6 @@ fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { tcx.sess.diagnostic().delay_span_bug(span, msg); } -macro_rules! span_mirbug { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - mirbug($context.tcx(), $context.last_span, - &format!("broken MIR in {:?} ({:?}): {}", - $context.body_id, - $elem, - format_args!($($message)*))) - }) -} - -macro_rules! span_mirbug_and_err { - ($context:expr, $elem:expr, $($message:tt)*) => ({ - { - span_mirbug!($context, $elem, $($message)*); - $context.error() - } - }) -} - enum FieldAccessError { OutOfRange { field_count: usize }, } @@ -570,6 +583,7 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, @@ -578,7 +592,7 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. #[derive(Default)] -pub struct MirTypeckRegionConstraints<'tcx> { +pub(crate) struct MirTypeckRegionConstraints<'tcx> { /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, /// which performs a liveness analysis. However, in some limited @@ -586,7 +600,7 @@ pub struct MirTypeckRegionConstraints<'tcx> { /// not otherwise appear in the MIR -- in particular, the /// late-bound regions that it instantiates at call-sites -- and /// hence it must report on their liveness constraints. - pub liveness_set: Vec<(ty::Region<'tcx>, Location)>, + pub liveness_set: Vec<(ty::Region<'tcx>, Location, Cause)>, /// During the course of type-checking, we will accumulate region /// constraints due to performing subtyping operations or solving @@ -624,6 +638,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, ) -> Self { TypeChecker { @@ -631,6 +646,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { last_span: DUMMY_SP, body_id, param_env, + region_bound_pairs, implicit_region_bound, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), @@ -657,7 +673,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } self.infcx.process_registered_region_obligations( - &[], + self.region_bound_pairs, self.implicit_region_bound, self.param_env, self.body_id, @@ -694,25 +710,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }) } - fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) { - let start_position = Location { - block: START_BLOCK, - statement_index: 0, - }; - let a = self.normalize(&unnormalized_a, start_position); - if let Err(terr) = self.eq_types(a, b, start_position.at_self()) { - span_mirbug!( - self, - start_position, - "bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}", - unnormalized_a, - a, - b, - terr - ); - } - } - fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } @@ -763,12 +760,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }; } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) | - StatementKind::InlineAsm { .. } | - StatementKind::EndRegion(_) | - StatementKind::Validate(..) | - StatementKind::Nop => {} + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::InlineAsm { .. } + | StatementKind::EndRegion(_) + | StatementKind::Validate(..) + | StatementKind::Nop => {} } } @@ -781,13 +778,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::Drop { .. } | - TerminatorKind::FalseEdges { .. } => { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::FalseEdges { .. } => { // no checks needed for these } @@ -887,9 +884,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // output) types in the signature must be live, since // all the inputs that fed into it were live. for &late_bound_region in map.values() { - self.constraints - .liveness_set - .push((late_bound_region, term_location)); + self.constraints.liveness_set.push(( + late_bound_region, + term_location, + Cause::LiveOther(term_location), + )); } if self.is_box_free(func) { @@ -1099,9 +1098,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } | - TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { + TerminatorKind::Drop { target, unwind, .. } + | TerminatorKind::DropAndReplace { target, unwind, .. } + | TerminatorKind::Assert { target, cleanup: unwind, .. @@ -1357,13 +1356,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }, // FIXME: These other cases have to be implemented in future PRs - Rvalue::Use(..) | - Rvalue::Ref(..) | - Rvalue::Len(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) => {} + Rvalue::Use(..) + | Rvalue::Ref(..) + | Rvalue::Len(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::Discriminant(..) => {} } } @@ -1497,9 +1496,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let cause = this.misc(this.last_span); let obligations = predicates .iter() - .map(|&p| { - traits::Obligation::new(cause.clone(), this.param_env, p) - }) + .map(|&p| traits::Obligation::new(cause.clone(), this.param_env, p)) .collect(); Ok(InferOk { value: (), @@ -1563,7 +1560,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ()); + let _ = type_check_internal(&infcx, id, param_env, mir, &[], None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index a1e6ea135c68d..45604d52958c1 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -22,11 +22,12 @@ //! The code in this file doesn't *do anything* with those results; it //! just returns them for other code to use. -use rustc::hir::HirId; +use rustc::hir::{BodyOwnerKind, HirId}; use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; use rustc::infer::region_constraints::GenericKind; use rustc::infer::outlives::bounds::{self, OutlivesBound}; +use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::ty::{self, RegionVid, Ty, TyCtxt}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; @@ -67,19 +68,21 @@ pub struct UniversalRegions<'tcx> { /// The "defining" type for this function, with all universal /// regions instantiated. For a closure or generator, this is the /// closure type, but for a top-level function it's the `TyFnDef`. - pub defining_ty: Ty<'tcx>, + pub defining_ty: DefiningTy<'tcx>, /// The return type of this function, with all regions replaced by - /// their universal `RegionVid` equivalents. This type is **NOT - /// NORMALIZED** (i.e., it contains unnormalized associated type - /// projections). - pub output_ty: Ty<'tcx>, + /// their universal `RegionVid` equivalents. + /// + /// NB. Associated types in this type have not been normalized, + /// as the name suggests. =) + pub unnormalized_output_ty: Ty<'tcx>, /// The fully liberated input types of this function, with all /// regions replaced by their universal `RegionVid` equivalents. - /// This type is **NOT NORMALIZED** (i.e., it contains - /// unnormalized associated type projections). - pub input_tys: &'tcx [Ty<'tcx>], + /// + /// NB. Associated types in these types have not been normalized, + /// as the name suggests. =) + pub unnormalized_input_tys: &'tcx [Ty<'tcx>], /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to /// be true. These encode relationships like `T: 'a` that are @@ -96,6 +99,33 @@ pub struct UniversalRegions<'tcx> { relations: UniversalRegionRelations, } +/// The "defining type" for this MIR. The key feature of the "defining +/// type" is that it contains the information needed to derive all the +/// universal regions that are in scope as well as the types of the +/// inputs/output from the MIR. In general, early-bound universal +/// regions appear free in the defining type and late-bound regions +/// appear bound in the signature. +#[derive(Copy, Clone, Debug)] +pub enum DefiningTy<'tcx> { + /// The MIR is a closure. The signature is found via + /// `ClosureSubsts::closure_sig_ty`. + Closure(DefId, ty::ClosureSubsts<'tcx>), + + /// The MIR is a generator. The signature is that generators take + /// no parameters and return the result of + /// `ClosureSubsts::generator_return_ty`. + Generator(DefId, ty::ClosureSubsts<'tcx>, ty::GeneratorInterior<'tcx>), + + /// The MIR is a fn item with the given def-id and substs. The signature + /// of the function can be bound then with the `fn_sig` query. + FnDef(DefId, &'tcx Substs<'tcx>), + + /// The MIR represents some form of constant. The signature then + /// is that it has no inputs and a single return value, which is + /// the value of the constant. + Const(Ty<'tcx>), +} + #[derive(Debug)] struct UniversalRegionIndices<'tcx> { /// For those regions that may appear in the parameter environment @@ -415,7 +445,7 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { let defining_ty = self.defining_ty(); debug!("build: defining_ty={:?}", defining_ty); - let indices = self.compute_indices(fr_static, defining_ty); + let mut indices = self.compute_indices(fr_static, defining_ty); debug!("build: indices={:?}", indices); let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); @@ -423,8 +453,12 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { // "Liberate" the late-bound regions. These correspond to // "local" free regions. let first_local_index = self.infcx.num_region_vars(); - let inputs_and_output = self.infcx - .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output); + let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( + FR, + self.mir_def_id, + &bound_inputs_and_output, + &mut indices, + ); let fr_fn_body = self.infcx.next_nll_region_var(FR).to_region_vid(); let num_universals = self.infcx.num_region_vars(); @@ -452,10 +486,8 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { self.relations.relate_universal_regions(fr, fr_fn_body); } - let (output_ty, input_tys) = inputs_and_output.split_last().unwrap(); - - // we should not have created any more variables - assert_eq!(self.infcx.num_region_vars(), num_universals); + let (unnormalized_output_ty, unnormalized_input_tys) = + inputs_and_output.split_last().unwrap(); debug!( "build: global regions = {}..{}", @@ -481,30 +513,18 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { first_local_index, num_universals, defining_ty, - output_ty, - input_tys, + unnormalized_output_ty, + unnormalized_input_tys, region_bound_pairs: self.region_bound_pairs, relations: self.relations, } } - /// Returns the "defining type" of the current MIR: - /// - /// - for functions, this is the `TyFnDef`; - /// - for closures, this is the `TyClosure`; - /// - for generators, this is the `TyGenerator`; - /// - for constants, this is the type of value that gets produced. - /// - FIXME. Constants are handled somewhat inelegantly; this gets - /// patched in a later PR that has already landed on nll-master. - /// - /// The key feature of the "defining type" is that it contains the - /// information needed to derive all the universal regions that - /// are in scope as well as the types of the inputs/output from - /// the MIR. In general, early-bound universal regions appear free - /// in the defining type and late-bound regions appear bound in - /// the signature. - fn defining_ty(&self) -> ty::Ty<'tcx> { + /// Returns the "defining type" of the current MIR; + /// see `DefiningTy` for details. + fn defining_ty(&self) -> DefiningTy<'tcx> { let tcx = self.infcx.tcx; + let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id); let defining_ty = if self.mir_def_id == closure_base_def_id { @@ -514,8 +534,25 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { tables.node_id_to_type(self.mir_hir_id) }; - self.infcx - .replace_free_regions_with_nll_infer_vars(FR, &defining_ty) + let defining_ty = self.infcx + .replace_free_regions_with_nll_infer_vars(FR, &defining_ty); + + match tcx.hir.body_owner_kind(self.mir_node_id) { + BodyOwnerKind::Fn => match defining_ty.sty { + ty::TyClosure(def_id, substs) => DefiningTy::Closure(def_id, substs), + ty::TyGenerator(def_id, substs, interior) => { + DefiningTy::Generator(def_id, substs, interior) + } + ty::TyFnDef(def_id, substs) => DefiningTy::FnDef(def_id, substs), + _ => span_bug!( + tcx.def_span(self.mir_def_id), + "expected defining type for `{:?}`: `{:?}`", + self.mir_def_id, + defining_ty + ), + }, + BodyOwnerKind::Const | BodyOwnerKind::Static(..) => DefiningTy::Const(defining_ty), + } } /// Builds a hashmap that maps from the universal regions that are @@ -525,14 +562,14 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { fn compute_indices( &self, fr_static: RegionVid, - defining_ty: Ty<'tcx>, + defining_ty: DefiningTy<'tcx>, ) -> UniversalRegionIndices<'tcx> { let tcx = self.infcx.tcx; let gcx = tcx.global_tcx(); let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id); let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id); - let fr_substs = match defining_ty.sty { - ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => { + let fr_substs = match defining_ty { + DefiningTy::Closure(_, substs) | DefiningTy::Generator(_, substs, _) => { // In the case of closures, we rely on the fact that // the first N elements in the ClosureSubsts are // inherited from the `closure_base_def_id`. @@ -544,28 +581,18 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { assert_eq!(substs.substs.regions().count(), identity_substs.regions().count()); substs.substs } - ty::TyFnDef(_, substs) => substs, - // FIXME. When we encounter other sorts of constant + DefiningTy::FnDef(_, substs) => substs, + + // When we encounter other sorts of constant // expressions, such as the `22` in `[foo; 22]`, we can // get the type `usize` here. For now, just return an // empty vector of substs in this case, since there are no // generics in scope in such expressions right now. - // - // Eventually I imagine we could get a wider range of - // types. What is the best way to handle this? Should we - // be checking something other than the type of the def-id - // to figure out what to do (e.g. the def-key?). - ty::TyUint(..) => { + DefiningTy::Const(_) => { assert!(identity_substs.is_empty()); identity_substs } - - _ => span_bug!( - tcx.def_span(self.mir_def_id), - "unknown defining type: {:?}", - defining_ty - ), }; let global_mapping = iter::once((gcx.types.re_static, fr_static)); @@ -581,11 +608,11 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { fn compute_inputs_and_output( &self, indices: &UniversalRegionIndices<'tcx>, - defining_ty: Ty<'tcx>, + defining_ty: DefiningTy<'tcx>, ) -> ty::Binder<&'tcx ty::Slice>> { let tcx = self.infcx.tcx; - match defining_ty.sty { - ty::TyClosure(def_id, substs) => { + match defining_ty { + DefiningTy::Closure(def_id, substs) => { assert_eq!(self.mir_def_id, def_id); let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx); let inputs_and_output = closure_sig.inputs_and_output(); @@ -613,32 +640,24 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> { ) } - ty::TyGenerator(def_id, substs, ..) => { + DefiningTy::Generator(def_id, substs, interior) => { assert_eq!(self.mir_def_id, def_id); let output = substs.generator_return_ty(def_id, tcx); - let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]); + let generator_ty = tcx.mk_generator(def_id, substs, interior); + let inputs_and_output = self.infcx.tcx.intern_type_list(&[generator_ty, output]); ty::Binder::dummy(inputs_and_output) } - ty::TyFnDef(def_id, _) => { + DefiningTy::FnDef(def_id, _) => { let sig = tcx.fn_sig(def_id); let sig = indices.fold_to_region_vids(tcx, &sig); sig.inputs_and_output() } - // FIXME: as above, this happens on things like `[foo; - // 22]`. For now, no inputs, one output, but it seems like - // we need a more general way to handle this category of - // MIR. - ty::TyUint(..) => { - ty::Binder::dummy(tcx.mk_type_list(iter::once(defining_ty))) - } - - _ => span_bug!( - tcx.def_span(self.mir_def_id), - "unexpected defining type: {:?}", - defining_ty - ), + // This happens on things like `[foo; 22]`. Hence, no + // inputs, one output, but it seems like we need a more + // general way to handle this category of MIR. + DefiningTy::Const(ty) => ty::Binder::dummy(tcx.mk_type_list(iter::once(ty))), } } @@ -702,7 +721,7 @@ impl UniversalRegionRelations { } } -pub(crate) trait InferCtxtExt<'tcx> { +trait InferCtxtExt<'tcx> { fn replace_free_regions_with_nll_infer_vars( &self, origin: NLLRegionVariableOrigin, @@ -714,7 +733,9 @@ pub(crate) trait InferCtxtExt<'tcx> { fn replace_bound_regions_with_nll_infer_vars( &self, origin: NLLRegionVariableOrigin, + all_outlive_scope: DefId, value: &ty::Binder, + indices: &mut UniversalRegionIndices<'tcx>, ) -> T where T: TypeFoldable<'tcx>; @@ -729,28 +750,46 @@ impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { - self.tcx.fold_regions( - value, - &mut false, - |_region, _depth| self.next_nll_region_var(origin), - ) + self.tcx.fold_regions(value, &mut false, |_region, _depth| { + self.next_nll_region_var(origin) + }) } fn replace_bound_regions_with_nll_infer_vars( &self, origin: NLLRegionVariableOrigin, + all_outlive_scope: DefId, value: &ty::Binder, + indices: &mut UniversalRegionIndices<'tcx>, ) -> T where T: TypeFoldable<'tcx>, { - let (value, _map) = self.tcx - .replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin)); + let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| { + let liberated_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion { + scope: all_outlive_scope, + bound_region: br, + })); + let region_vid = self.next_nll_region_var(origin); + indices.insert_late_bound_region(liberated_region, region_vid.to_region_vid()); + region_vid + }); value } } impl<'tcx> UniversalRegionIndices<'tcx> { + /// Initially, the `UniversalRegionIndices` map contains only the + /// early-bound regions in scope. Once that is all setup, we come + /// in later and instantiate the late-bound regions, and then we + /// insert the `ReFree` version of those into the map as + /// well. These are used for error reporting. + fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, + vid: ty::RegionVid) + { + self.indices.insert(r, vid); + } + /// Converts `r` into a local inference variable: `r` can either /// by a `ReVar` (i.e., already a reference to an inference /// variable) or it can be `'static` or some early-bound @@ -773,10 +812,21 @@ impl<'tcx> UniversalRegionIndices<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions( - value, - &mut false, - |region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))), - ) + tcx.fold_regions(value, &mut false, |region, _| { + tcx.mk_region(ty::ReVar(self.to_region_vid(region))) + }) + } +} + +/// This trait is used by the `impl-trait` constraint code to abstract +/// over the `FreeRegionMap` from lexical regions and +/// `UniversalRegions` (from NLL)`. +impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegions<'tcx> { + fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool { + let shorter = shorter.to_region_vid(); + assert!(self.is_universal_region(shorter)); + let longer = longer.to_region_vid(); + assert!(self.is_universal_region(longer)); + self.outlives(longer, shorter) } } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 2504aa5ff378a..25e4b30da8095 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -66,7 +66,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { region_map: FxHashMap, FxHashSet>, local_map: FxHashMap>, region_span_map: FxHashMap, - nonlexical_regioncx: Option>, + nonlexical_regioncx: Option>>, } // Two-phase borrows actually requires two flow analyses; they need @@ -147,7 +147,7 @@ impl ReserveOrActivateIndex { impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - nonlexical_regioncx: Option>, + nonlexical_regioncx: Option>>, def_id: DefId, body_id: Option) -> Self { diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 4d26230e0612b..a7efdaf08264a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] +#![feature(dyn_trait)] #![feature(i128_type)] #![feature(inclusive_range_syntax)] #![feature(inclusive_range)] diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 00248400c553c..38227bd713322 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -75,7 +75,7 @@ pub trait BorrowckErrors { -> DiagnosticBuilder<'a>; fn cannot_move_when_borrowed(&self, span: Span, desc: &str, o: Origin) - -> DiagnosticBuilder + -> DiagnosticBuilder<'_> { let err = struct_span_err!(self, span, E0505, "cannot move out of `{}` because it is borrowed{OGN}", @@ -89,7 +89,7 @@ pub trait BorrowckErrors { borrow_span: Span, borrow_desc: &str, o: Origin) - -> DiagnosticBuilder + -> DiagnosticBuilder<'_> { let mut err = struct_span_err!(self, span, E0503, "cannot use `{}` because it was mutably borrowed{OGN}", @@ -509,7 +509,7 @@ impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> { o: Origin) -> DiagnosticBuilder<'a> { - if !o.should_emit_errors(self.sess.opts.borrowck_mode) { + if !o.should_emit_errors(self.sess.borrowck_mode()) { self.sess.diagnostic().cancel(&mut diag); } diag diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 45c9c350e7e43..765d50b400613 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -39,6 +39,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::indexed_set::IdxSetBuf; use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; use rustc::ty::item_path; +use rustc::mir::visit::MirVisitable; use std::path::{Path, PathBuf}; use std::fs; use rustc::ty::TyCtxt; @@ -219,6 +220,81 @@ impl LivenessResult { } } +#[derive(Eq, PartialEq, Clone)] +pub enum DefUse { + Def, + Use, +} + +pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Option { + match context { + /////////////////////////////////////////////////////////////////////////// + // DEFS + + PlaceContext::Store | + + // This is potentially both a def and a use... + PlaceContext::AsmOutput | + + // We let Call define the result in both the success and + // unwind cases. This is not really correct, however it + // does not seem to be observable due to the way that we + // generate MIR. See the test case + // `mir-opt/nll/liveness-call-subtlety.rs`. To do things + // properly, we would apply the def in call only to the + // input from the success path and not the unwind + // path. -nmatsakis + PlaceContext::Call | + + // Storage live and storage dead aren't proper defines, but we can ignore + // values that come before them. + PlaceContext::StorageLive | + PlaceContext::StorageDead => Some(DefUse::Def), + + /////////////////////////////////////////////////////////////////////////// + // REGULAR USES + // + // These are uses that occur *outside* of a drop. For the + // purposes of NLL, these are special in that **all** the + // lifetimes appearing in the variable must be live for each regular use. + + PlaceContext::Projection(..) | + + // Borrows only consider their local used at the point of the borrow. + // This won't affect the results since we use this analysis for generators + // and we only care about the result at suspension points. Borrows cannot + // cross suspension points so this behavior is unproblematic. + PlaceContext::Borrow { .. } | + + PlaceContext::Inspect | + PlaceContext::Copy | + PlaceContext::Move | + PlaceContext::Validate => { + if mode.include_regular_use { + Some(DefUse::Use) + } else { + None + } + } + + /////////////////////////////////////////////////////////////////////////// + // DROP USES + // + // These are uses that occur in a DROP (a MIR drop, not a + // call to `std::mem::drop()`). For the purposes of NLL, + // uses in drop are special because `#[may_dangle]` + // attributes can affect whether lifetimes must be live. + + PlaceContext::Drop => { + if mode.include_drops { + Some(DefUse::Use) + } else { + None + } + } + } +} + struct DefsUsesVisitor { mode: LivenessMode, defs_uses: DefsUses, @@ -267,69 +343,16 @@ impl DefsUses { impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { - match context { - /////////////////////////////////////////////////////////////////////////// - // DEFS - - PlaceContext::Store | - - // This is potentially both a def and a use... - PlaceContext::AsmOutput | - - // We let Call define the result in both the success and - // unwind cases. This is not really correct, however it - // does not seem to be observable due to the way that we - // generate MIR. See the test case - // `mir-opt/nll/liveness-call-subtlety.rs`. To do things - // properly, we would apply the def in call only to the - // input from the success path and not the unwind - // path. -nmatsakis - PlaceContext::Call | - - // Storage live and storage dead aren't proper defines, but we can ignore - // values that come before them. - PlaceContext::StorageLive | - PlaceContext::StorageDead => { + match categorize(context, self.mode) { + Some(DefUse::Def) => { self.defs_uses.add_def(local); } - /////////////////////////////////////////////////////////////////////////// - // REGULAR USES - // - // These are uses that occur *outside* of a drop. For the - // purposes of NLL, these are special in that **all** the - // lifetimes appearing in the variable must be live for each regular use. - - PlaceContext::Projection(..) | - - // Borrows only consider their local used at the point of the borrow. - // This won't affect the results since we use this analysis for generators - // and we only care about the result at suspension points. Borrows cannot - // cross suspension points so this behavior is unproblematic. - PlaceContext::Borrow { .. } | - - PlaceContext::Inspect | - PlaceContext::Copy | - PlaceContext::Move | - PlaceContext::Validate => { - if self.mode.include_regular_use { - self.defs_uses.add_use(local); - } + Some(DefUse::Use) => { + self.defs_uses.add_use(local); } - /////////////////////////////////////////////////////////////////////////// - // DROP USES - // - // These are uses that occur in a DROP (a MIR drop, not a - // call to `std::mem::drop()`). For the purposes of NLL, - // uses in drop are special because `#[may_dangle]` - // attributes can affect whether lifetimes must be live. - - PlaceContext::Drop => { - if self.mode.include_drops { - self.defs_uses.add_use(local); - } - } + None => {} } } } @@ -358,30 +381,6 @@ fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> D visitor.defs_uses } -trait MirVisitable<'tcx> { - fn apply(&self, location: Location, visitor: &mut V) - where - V: Visitor<'tcx>; -} - -impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { - fn apply(&self, location: Location, visitor: &mut V) - where - V: Visitor<'tcx>, - { - visitor.visit_statement(location.block, self, location) - } -} - -impl<'tcx> MirVisitable<'tcx> for Option> { - fn apply(&self, location: Location, visitor: &mut V) - where - V: Visitor<'tcx>, - { - visitor.visit_terminator(location.block, self.as_ref().unwrap(), location) - } -} - pub fn dump_mir<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7ebdb876ed02f..14296e78ddd1c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,6 +90,7 @@ use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use std::slice; use namespace::Namespace; use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; +use rustc::infer::anon_types::AnonTypeDecl; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::middle::region; use rustc::ty::subst::{Kind, Subst, Substs}; @@ -97,7 +98,7 @@ use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCo use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue}; use rustc::ty::{self, Ty, TyCtxt, Visibility}; use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; +use rustc::ty::fold::TypeFoldable; use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt}; use errors::{DiagnosticBuilder, DiagnosticId}; @@ -225,43 +226,6 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { body_id: Option, } -/// Information about the anonymous, abstract types whose values we -/// are inferring in this function (these are the `impl Trait` that -/// appear in the return type). -#[derive(Debug)] -struct AnonTypeDecl<'tcx> { - /// The substitutions that we apply to the abstract that that this - /// `impl Trait` desugars to. e.g., if: - /// - /// fn foo<'a, 'b, T>() -> impl Trait<'a> - /// - /// winds up desugared to: - /// - /// abstract type Foo<'x, T>: Trait<'x> - /// fn foo<'a, 'b, T>() -> Foo<'a, T> - /// - /// then `substs` would be `['a, T]`. - substs: &'tcx Substs<'tcx>, - - /// The type variable that represents the value of the abstract type - /// that we require. In other words, after we compile this function, - /// we will be created a constraint like: - /// - /// Foo<'a, T> = ?C - /// - /// where `?C` is the value of this type variable. =) It may - /// naturally refer to the type and lifetime parameters in scope - /// in this function, though ultimately it should only reference - /// those that are arguments to `Foo` in the constraint above. (In - /// other words, `?C` should not include `'b`, even though it's a - /// lifetime parameter on `foo`.) - concrete_ty: Ty<'tcx>, - - /// A list of all required region bounds on the impl Trait type, - /// e.g. `'a` and `'b` in `fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b`. - required_region_bounds: Vec>, -} - impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> { type Target = InferCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -873,8 +837,6 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, &fn_sig); let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0; - // Ensure anon_types have been instantiated prior to entering regionck - fcx.instantiate_anon_types(&fn_sig.output()); fcx } else { let fcx = FnCtxt::new(&inh, param_env, body.value.id); @@ -1023,7 +985,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, let ret_ty = fn_sig.output(); fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::SizedReturnType); - let ret_ty = fcx.instantiate_anon_types(&ret_ty); + let ret_ty = fcx.instantiate_anon_types_from_return_value(fn_id, &ret_ty); fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), @@ -1914,60 +1876,38 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { result } - /// Replace all anonymized types with fresh inference variables - /// and record them for writeback. - fn instantiate_anon_types>(&self, value: &T) -> T { - debug!("instantiate_anon_types(value={:?})", value); - value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| { - if let ty::TyAnon(def_id, substs) = ty.sty { - debug!("instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", def_id, substs); + /// Replace the anonymized types from the return value of the + /// function with type variables and records the `AnonTypeMap` for + /// later use during writeback. See + /// `InferCtxt::instantiate_anon_types` for more details. + fn instantiate_anon_types_from_return_value>( + &self, + fn_id: ast::NodeId, + value: &T, + ) -> T { + let fn_def_id = self.tcx.hir.local_def_id(fn_id); + debug!( + "instantiate_anon_types_from_return_value(fn_def_id={:?}, value={:?})", + fn_def_id, + value + ); - // Use the same type variable if the exact same TyAnon appears more - // than once in the return type (e.g. if it's passed to a type alias). - if let Some(anon_defn) = self.anon_types.borrow().get(&def_id) { - return anon_defn.concrete_ty; - } - let span = self.tcx.def_span(def_id); - let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span)); - - let predicates_of = self.tcx.predicates_of(def_id); - let bounds = predicates_of.instantiate(self.tcx, substs); - debug!("instantiate_anon_types: bounds={:?}", bounds); - - let required_region_bounds = - self.tcx.required_region_bounds(ty, bounds.predicates.clone()); - debug!("instantiate_anon_types: required_region_bounds={:?}", - required_region_bounds); - - self.anon_types.borrow_mut().insert(def_id, AnonTypeDecl { - substs, - concrete_ty: ty_var, - required_region_bounds, - }); - debug!("instantiate_anon_types: ty_var={:?}", ty_var); - - for predicate in bounds.predicates { - // Change the predicate to refer to the type variable, - // which will be the concrete type, instead of the TyAnon. - // This also instantiates nested `impl Trait`. - let predicate = self.instantiate_anon_types(&predicate); - - // Require that the predicate holds for the concrete type. - let cause = traits::ObligationCause::new(span, - self.body_id, - traits::SizedReturnType); - - debug!("instantiate_anon_types: predicate={:?}", predicate); - self.register_predicate(traits::Obligation::new(cause, - self.param_env, - predicate)); - } + let (value, anon_type_map) = self.register_infer_ok_obligations( + self.instantiate_anon_types( + fn_def_id, + self.body_id, + self.param_env, + value, + ) + ); - ty_var - } else { - ty - } - }}) + let mut anon_types = self.anon_types.borrow_mut(); + for (ty, decl) in anon_type_map { + let old_value = anon_types.insert(ty, decl); + assert!(old_value.is_none(), "instantiated twice: {:?}/{:?}", ty, decl); + } + + value } fn normalize_associated_types_in(&self, span: Span, value: &T) -> T diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 7ef6027772be2..64063ec5beda9 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -93,7 +93,6 @@ use rustc::ty::{self, Ty}; use rustc::infer; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; -use rustc::ty::outlives::Component; use std::mem; use std::ops::Deref; @@ -125,7 +124,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors_unless_nll(); assert!(self.tables.borrow().free_region_map.is_empty()); self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); @@ -174,7 +173,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors_unless_nll(); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes @@ -344,7 +343,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_hir_id, call_site_region); - self.constrain_anon_types(); + self.constrain_anon_types( + &self.fcx.anon_types.borrow(), + self.outlives_environment.free_region_map(), + ); } fn visit_region_obligations(&mut self, node_id: ast::NodeId) @@ -363,200 +365,18 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.body_id); } - /// Go through each of the existential `impl Trait` types that - /// appear in the function signature. For example, if the current - /// function is as follows: - /// - /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) - /// - /// we would iterate through the `impl Bar<'a>` and the - /// `impl Bar<'b>` here. Remember that each of them has - /// their own "abstract type" definition created for them. As - /// we iterate, we have a `def_id` that corresponds to this - /// definition, and a set of substitutions `substs` that are - /// being supplied to this abstract typed definition in the - /// signature: - /// - /// abstract type Foo1<'x>: Bar<'x>; - /// abstract type Foo2<'x>: Bar<'x>; - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// ^^^^ ^^ substs - /// def_id - /// - /// In addition, for each of the types we will have a type - /// variable `concrete_ty` containing the concrete type that - /// this function uses for `Foo1` and `Foo2`. That is, - /// conceptually, there is a constraint like: - /// - /// for<'a> (Foo1<'a> = C) - /// - /// where `C` is `concrete_ty`. For this equation to be satisfiable, - /// the type `C` can only refer to two regions: `'static` and `'a`. - /// - /// The problem is that this type `C` may contain arbitrary - /// region variables. In fact, it is fairly likely that it - /// does! Consider this possible definition of `foo`: - /// - /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { - /// (&*x, &*y) - /// } - /// - /// Here, the values for the concrete types of the two impl - /// traits will include inference variables: - /// - /// &'0 i32 - /// &'1 i32 - /// - /// Ordinarily, the subtyping rules would ensure that these are - /// sufficiently large. But since `impl Bar<'a>` isn't a specific - /// type per se, we don't get such constraints by default. This - /// is where this function comes into play. It adds extra - /// constraints to ensure that all the regions which appear in the - /// inferred type are regions that could validly appear. - /// - /// This is actually a bit of a tricky constraint in general. We - /// want to say that each variable (e.g., `'0``) can only take on - /// values that were supplied as arguments to the abstract type - /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in - /// scope. We don't have a constraint quite of this kind in the current - /// region checker. - /// - /// What we *do* have is the `<=` relation. So what we do is to - /// find the LUB of all the arguments that appear in the substs: - /// in this case, that would be `LUB('a) = 'a`, and then we apply - /// that as a least bound to the variables (e.g., `'a <= '0`). - /// - /// In some cases this is pretty suboptimal. Consider this example: - /// - /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } - /// - /// Here, the regions `'a` and `'b` appear in the substitutions, - /// so we would generate `LUB('a, 'b)` as a kind of "minimal upper - /// bound", but that turns out be `'static` -- which is clearly - /// too strict! - fn constrain_anon_types(&mut self) { - debug!("constrain_anon_types()"); - - for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { - let concrete_ty = self.resolve_type(anon_defn.concrete_ty); - - debug!("constrain_anon_types: def_id={:?}", def_id); - debug!("constrain_anon_types: anon_defn={:#?}", anon_defn); - debug!("constrain_anon_types: concrete_ty={:?}", concrete_ty); - - let abstract_type_generics = self.tcx.generics_of(def_id); - - let span = self.tcx.def_span(def_id); - - // If there are required region bounds, we can just skip - // ahead. There will already be a registered region - // obligation related `concrete_ty` to those regions. - if anon_defn.required_region_bounds.len() != 0 { - continue; - } - - // There were no `required_region_bounds`, - // so we have to search for a `least_region`. - // Go through all the regions used as arguments to the - // abstract type. These are the parameters to the abstract - // type; so in our example above, `substs` would contain - // `['a]` for the first impl trait and `'b` for the - // second. - let mut least_region = None; - for region_def in &abstract_type_generics.regions { - // Find the index of this region in the list of substitutions. - let index = region_def.index as usize; - - // Get the value supplied for this region from the substs. - let subst_arg = anon_defn.substs[index].as_region().unwrap(); - - // Compute the least upper bound of it with the other regions. - debug!("constrain_anon_types: least_region={:?}", least_region); - debug!("constrain_anon_types: subst_arg={:?}", subst_arg); - match least_region { - None => least_region = Some(subst_arg), - Some(lr) => { - if self.outlives_environment - .free_region_map() - .sub_free_regions(lr, subst_arg) { - // keep the current least region - } else if self.outlives_environment - .free_region_map() - .sub_free_regions(subst_arg, lr) { - // switch to `subst_arg` - least_region = Some(subst_arg); - } else { - // There are two regions (`lr` and - // `subst_arg`) which are not relatable. We can't - // find a best choice. - self.tcx - .sess - .struct_span_err(span, "ambiguous lifetime bound in `impl Trait`") - .span_label(span, - format!("neither `{}` nor `{}` outlives the other", - lr, subst_arg)) - .emit(); - - least_region = Some(self.tcx.mk_region(ty::ReEmpty)); - break; - } - } - } - } - - let least_region = least_region.unwrap_or(self.tcx.types.re_static); - debug!("constrain_anon_types: least_region={:?}", least_region); - - // Require that the type `concrete_ty` outlives - // `least_region`, modulo any type parameters that appear - // in the type, which we ignore. This is because impl - // trait values are assumed to capture all the in-scope - // type parameters. This little loop here just invokes - // `outlives` repeatedly, draining all the nested - // obligations that result. - let mut types = vec![concrete_ty]; - let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r); - while let Some(ty) = types.pop() { - let mut components = self.tcx.outlives_components(ty); - while let Some(component) = components.pop() { - match component { - Component::Region(r) => { - bound_region(r); - } - - Component::Param(_) => { - // ignore type parameters like `T`, they are captured - // implicitly by the `impl Trait` - } - - Component::UnresolvedInferenceVariable(_) => { - // we should get an error that more type - // annotations are needed in this case - self.tcx.sess.delay_span_bug(span, "unresolved inf var in anon"); - } - - Component::Projection(ty::ProjectionTy { substs, item_def_id: _ }) => { - for r in substs.regions() { - bound_region(r); - } - types.extend(substs.types()); - } - - Component::EscapingProjection(more_components) => { - components.extend(more_components); - } - } - } - } - } - } - fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, &self.outlives_environment); } + fn resolve_regions_and_report_errors_unless_nll(&self) { + self.fcx.resolve_regions_and_report_errors_unless_nll(self.subject_def_id, + &self.region_scope_tree, + &self.outlives_environment); + } + fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { debug!("regionck::visit_pat(pat={:?})", pat); pat.each_binding(|_, id, span, _| { diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 1052f031bbf14..29dc983ab560b 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -15,12 +15,11 @@ use check::FnCtxt; use rustc::hir; use rustc::hir::def_id::{DefId, DefIndex}; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use rustc::infer::{InferCtxt}; +use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc::infer::InferCtxt; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::fold::{TypeFolder, TypeFoldable}; -use rustc::ty::subst::{Kind, Substs}; -use rustc::util::nodemap::{DefIdSet, FxHashMap}; +use rustc::ty::fold::{TypeFoldable, TypeFolder}; +use rustc::util::nodemap::DefIdSet; use syntax::ast; use syntax_pos::Span; use std::mem; @@ -30,8 +29,7 @@ use std::rc::Rc; // Entry point impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn resolve_type_vars_in_body(&self, body: &'gcx hir::Body) - -> &'gcx ty::TypeckTables<'gcx> { + pub fn resolve_type_vars_in_body(&self, body: &'gcx hir::Body) -> &'gcx ty::TypeckTables<'gcx> { let item_id = self.tcx.hir.body_owner(body.id()); let item_def_id = self.tcx.hir.local_def_id(item_id); @@ -48,14 +46,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wbcx.visit_cast_types(); wbcx.visit_free_region_map(); - let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports, - Rc::new(DefIdSet())); - debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); + let used_trait_imports = mem::replace( + &mut self.tables.borrow_mut().used_trait_imports, + Rc::new(DefIdSet()), + ); + debug!( + "used_trait_imports({:?}) = {:?}", + item_def_id, + used_trait_imports + ); wbcx.tables.used_trait_imports = used_trait_imports; wbcx.tables.tainted_by_errors = self.is_tainted_by_errors(); - debug!("writeback: tables for {:?} are {:#?}", item_def_id, wbcx.tables); + debug!( + "writeback: tables for {:?} are {:#?}", + item_def_id, + wbcx.tables + ); self.tcx.alloc_tables(wbcx.tables) } @@ -69,7 +77,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // there, it applies a few ad-hoc checks that were not convenient to // do elsewhere. -struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { +struct WritebackCx<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> { fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, tables: ty::TypeckTables<'gcx>, @@ -78,9 +86,10 @@ struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { } impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { - fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, body: &'gcx hir::Body) - -> WritebackCx<'cx, 'gcx, 'tcx> - { + fn new( + fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, + body: &'gcx hir::Body, + ) -> WritebackCx<'cx, 'gcx, 'tcx> { let owner = fcx.tcx.hir.definitions().node_to_hir_id(body.id().node_id); WritebackCx { @@ -95,7 +104,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } fn write_ty_to_tables(&mut self, hir_id: hir::HirId, ty: Ty<'gcx>) { - debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); + debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); assert!(!ty.needs_infer()); self.tables.node_types_mut().insert(hir_id, ty); } @@ -106,8 +115,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { // operating on scalars, we clear the overload. fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr) { match e.node { - hir::ExprUnary(hir::UnNeg, ref inner) | - hir::ExprUnary(hir::UnNot, ref inner) => { + hir::ExprUnary(hir::UnNeg, ref inner) | hir::ExprUnary(hir::UnNot, ref inner) => { let inner_ty = self.fcx.node_ty(inner.hir_id); let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty); @@ -117,8 +125,8 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { tables.node_substs_mut().remove(e.hir_id); } } - hir::ExprBinary(ref op, ref lhs, ref rhs) | - hir::ExprAssignOp(ref op, ref lhs, ref rhs) => { + hir::ExprBinary(ref op, ref lhs, ref rhs) + | hir::ExprAssignOp(ref op, ref lhs, ref rhs) => { let lhs_ty = self.fcx.node_ty(lhs.hir_id); let lhs_ty = self.fcx.resolve_type_vars_if_possible(&lhs_ty); @@ -137,15 +145,18 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { adjustments.get_mut(lhs.hir_id).map(|a| a.pop()); adjustments.get_mut(rhs.hir_id).map(|a| a.pop()); } - }, + } hir::ExprAssignOp(..) => { - tables.adjustments_mut().get_mut(lhs.hir_id).map(|a| a.pop()); - }, - _ => {}, + tables + .adjustments_mut() + .get_mut(lhs.hir_id) + .map(|a| a.pop()); + } + _ => {} } } } - _ => {}, + _ => {} } } } @@ -189,11 +200,11 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { match p.node { hir::PatKind::Binding(..) => { let bm = *self.fcx - .tables - .borrow() - .pat_binding_modes() - .get(p.hir_id) - .expect("missing binding mode"); + .tables + .borrow() + .pat_binding_modes() + .get(p.hir_id) + .expect("missing binding mode"); self.tables.pat_binding_modes_mut().insert(p.hir_id, bm); } _ => {} @@ -228,14 +239,20 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { ty::UpvarCapture::ByRef(ref upvar_borrow) => { let r = upvar_borrow.region; let r = self.resolve(&r, &upvar_id.var_id); - ty::UpvarCapture::ByRef( - ty::UpvarBorrow { kind: upvar_borrow.kind, region: r }) + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: upvar_borrow.kind, + region: r, + }) } }; - debug!("Upvar capture for {:?} resolved to {:?}", - upvar_id, - new_upvar_capture); - self.tables.upvar_capture_map.insert(*upvar_id, new_upvar_capture); + debug!( + "Upvar capture for {:?} resolved to {:?}", + upvar_id, + new_upvar_capture + ); + self.tables + .upvar_capture_map + .insert(*upvar_id, new_upvar_capture); } } @@ -249,7 +266,9 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { owner: common_local_id_root.index, local_id: id, }; - self.tables.closure_kind_origins_mut().insert(hir_id, origin); + self.tables + .closure_kind_origins_mut() + .insert(hir_id, origin); } } @@ -270,7 +289,8 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } fn visit_free_region_map(&mut self) { - let free_region_map = self.tcx().lift_to_global(&self.fcx.tables.borrow().free_region_map); + let free_region_map = self.tcx() + .lift_to_global(&self.fcx.tables.borrow().free_region_map); let free_region_map = free_region_map.expect("all regions in free-region-map are global"); self.tables.free_region_map = free_region_map; } @@ -279,77 +299,25 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { let gcx = self.tcx().global_tcx(); for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { let node_id = gcx.hir.as_local_node_id(def_id).unwrap(); - let inside_ty = self.resolve(&anon_defn.concrete_ty, &node_id); - - // Use substs to build up a reverse map from regions - // to their identity mappings. - // This is necessary because of `impl Trait` lifetimes - // are computed by replacing existing lifetimes with 'static - // and remapping only those used in the `impl Trait` return type, - // resulting in the parameters shifting. - let id_substs = Substs::identity_for_item(gcx, def_id); - let map: FxHashMap, Kind<'gcx>> = - anon_defn.substs - .iter() - .enumerate() - .map(|(index, subst)| (*subst, id_substs[index])) - .collect(); - - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - let outside_ty = gcx.fold_regions(&inside_ty, &mut false, |r, _| { - match *r { - // 'static and early-bound regions are valid. - ty::ReStatic | - ty::ReEmpty => r, - - // All other regions, we map them appropriately to their adjusted - // indices, erroring if we find any lifetimes that were not mapped - // into the new set. - _ => if let Some(r1) = - map.get(&Kind::from(r)).and_then(|k| k.as_region()) { r1 } else - { - // No mapping was found. This means that - // it is either a disallowed lifetime, - // which will be caught by regionck, or it - // is a region in a non-upvar closure - // generic, which is explicitly - // allowed. If that surprises you, read - // on. - // - // The case of closure is a somewhat - // subtle (read: hacky) consideration. The - // problem is that our closure types - // currently include all the lifetime - // parameters declared on the enclosing - // function, even if they are unused by - // the closure itself. We can't readily - // filter them out, so here we replace - // those values with `'empty`. This can't - // really make a difference to the rest of - // the compiler; those regions are ignored - // for the outlives relation, and hence - // don't affect trait selection or auto - // traits, and they are erased during - // trans. - gcx.types.re_empty - }, - } - }); - + let instantiated_ty = self.resolve(&anon_defn.concrete_ty, &node_id); + let definition_ty = self.fcx.infer_anon_definition_from_instantiation( + def_id, + anon_defn, + instantiated_ty, + ); let hir_id = self.tcx().hir.node_to_hir_id(node_id); - self.tables.node_types_mut().insert(hir_id, outside_ty); + self.tables.node_types_mut().insert(hir_id, definition_ty); } } fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) { // Export associated path extensions and method resultions. if let Some(def) = self.fcx - .tables - .borrow_mut() - .type_dependent_defs_mut() - .remove(hir_id) { + .tables + .borrow_mut() + .type_dependent_defs_mut() + .remove(hir_id) + { self.tables.type_dependent_defs_mut().insert(hir_id, def); } @@ -373,10 +341,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) { let adjustment = self.fcx - .tables - .borrow_mut() - .adjustments_mut() - .remove(hir_id); + .tables + .borrow_mut() + .adjustments_mut() + .remove(hir_id); match adjustment { None => { debug!("No adjustments for node {:?}", hir_id); @@ -384,18 +352,24 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { Some(adjustment) => { let resolved_adjustment = self.resolve(&adjustment, &span); - debug!("Adjustments for node {:?}: {:?}", hir_id, resolved_adjustment); - self.tables.adjustments_mut().insert(hir_id, resolved_adjustment); + debug!( + "Adjustments for node {:?}: {:?}", + hir_id, + resolved_adjustment + ); + self.tables + .adjustments_mut() + .insert(hir_id, resolved_adjustment); } } } fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) { let adjustment = self.fcx - .tables - .borrow_mut() - .pat_adjustments_mut() - .remove(hir_id); + .tables + .borrow_mut() + .pat_adjustments_mut() + .remove(hir_id); match adjustment { None => { debug!("No pat_adjustments for node {:?}", hir_id); @@ -403,8 +377,14 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { Some(adjustment) => { let resolved_adjustment = self.resolve(&adjustment, &span); - debug!("pat_adjustments for node {:?}: {:?}", hir_id, resolved_adjustment); - self.tables.pat_adjustments_mut().insert(hir_id, resolved_adjustment); + debug!( + "pat_adjustments for node {:?}: {:?}", + hir_id, + resolved_adjustment + ); + self.tables + .pat_adjustments_mut() + .insert(hir_id, resolved_adjustment); } } } @@ -420,7 +400,9 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { local_id, }; let fn_sig = self.resolve(fn_sig, &hir_id); - self.tables.liberated_fn_sigs_mut().insert(hir_id, fn_sig.clone()); + self.tables + .liberated_fn_sigs_mut() + .insert(hir_id, fn_sig.clone()); } } @@ -440,15 +422,18 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } fn resolve(&self, x: &T, span: &Locatable) -> T::Lifted - where T: TypeFoldable<'tcx> + ty::Lift<'gcx> + where + T: TypeFoldable<'tcx> + ty::Lift<'gcx>, { let x = x.fold_with(&mut Resolver::new(self.fcx, span, self.body)); if let Some(lifted) = self.tcx().lift_to_global(&x) { lifted } else { - span_bug!(span.to_span(&self.fcx.tcx), - "writeback: `{:?}` missing from the global type context", - x); + span_bug!( + span.to_span(&self.fcx.tcx), + "writeback: `{:?}` missing from the global type context", + x + ); } } } @@ -458,11 +443,15 @@ trait Locatable { } impl Locatable for Span { - fn to_span(&self, _: &TyCtxt) -> Span { *self } + fn to_span(&self, _: &TyCtxt) -> Span { + *self + } } impl Locatable for ast::NodeId { - fn to_span(&self, tcx: &TyCtxt) -> Span { tcx.hir.span(*self) } + fn to_span(&self, tcx: &TyCtxt) -> Span { + tcx.hir.span(*self) + } } impl Locatable for DefIndex { @@ -483,7 +472,7 @@ impl Locatable for hir::HirId { // The Resolver. This is the type folding engine that detects // unresolved types and so forth. -struct Resolver<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { +struct Resolver<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> { tcx: TyCtxt<'cx, 'gcx, 'tcx>, infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, span: &'cx Locatable, @@ -491,9 +480,11 @@ struct Resolver<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { } impl<'cx, 'gcx, 'tcx> Resolver<'cx, 'gcx, 'tcx> { - fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, span: &'cx Locatable, body: &'gcx hir::Body) - -> Resolver<'cx, 'gcx, 'tcx> - { + fn new( + fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>, + span: &'cx Locatable, + body: &'gcx hir::Body, + ) -> Resolver<'cx, 'gcx, 'tcx> { Resolver { tcx: fcx.tcx, infcx: fcx, @@ -504,7 +495,8 @@ impl<'cx, 'gcx, 'tcx> Resolver<'cx, 'gcx, 'tcx> { fn report_error(&self, t: Ty<'tcx>) { if !self.tcx.sess.has_errors() { - self.infcx.need_type_info(Some(self.body.id()), self.span.to_span(&self.tcx), t); + self.infcx + .need_type_info(Some(self.body.id()), self.span.to_span(&self.tcx), t); } } } @@ -518,8 +510,10 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Resolver<'cx, 'gcx, 'tcx> { match self.infcx.fully_resolve(&t) { Ok(t) => t, Err(_) => { - debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", - t); + debug!( + "Resolver::fold_ty: input type `{:?}` not fully resolvable", + t + ); self.report_error(t); self.tcx().types.err } @@ -531,9 +525,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Resolver<'cx, 'gcx, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match self.infcx.fully_resolve(&r) { Ok(r) => r, - Err(_) => { - self.tcx.types.re_static - } + Err(_) => self.tcx.types.re_static, } } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index bf8f9d8b24a0d..e8d669838d3fb 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -81,6 +81,7 @@ This API is completely unstable and subject to change. #![feature(match_default_bindings)] #![feature(never_type)] #![feature(quote)] +#![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ba534676324a9..379881302eef7 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -186,6 +186,9 @@ declare_features! ( // Allows the use of rustc_* attributes; RFC 572 (active, rustc_attrs, "1.0.0", Some(29642)), + // Allows the use of non lexical lifetimes; RFC 2094 + (active, nll, "1.0.0", Some(44928)), + // Allows the use of #[allow_internal_unstable]. This is an // attribute on macro_rules! and can't use the attribute handling // below (it has to be checked before expansion possibly makes @@ -798,6 +801,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG libcore functions that are inlined \ across crates and will never be stable", cfg_fn!(rustc_attrs))), + + // RFC #2094 + ("nll", Whitelisted, Gated(Stability::Unstable, + "nll", + "Non lexical lifetimes", + cfg_fn!(nll))), ("compiler_builtins", Whitelisted, Gated(Stability::Unstable, "compiler_builtins", "the `#[compiler_builtins]` attribute is used to \ diff --git a/src/test/compile-fail/mir_check_cast_reify.rs b/src/test/compile-fail/mir_check_cast_reify.rs index 1736aea2d6de7..f6ad2820d17ce 100644 --- a/src/test/compile-fail/mir_check_cast_reify.rs +++ b/src/test/compile-fail/mir_check_cast_reify.rs @@ -45,7 +45,7 @@ fn bar<'a>(x: &'a u32) -> &'static u32 { // as part of checking the `ReifyFnPointer`. let f: fn(_) -> _ = foo; //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `'_#1r` does not outlive free region `'static` + //~| ERROR free region `'a` does not outlive free region `'static` f(x) } diff --git a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs index 39eafa1004026..c9b378dacd540 100644 --- a/src/test/compile-fail/mir_check_cast_unsafe_fn.rs +++ b/src/test/compile-fail/mir_check_cast_unsafe_fn.rs @@ -17,7 +17,7 @@ fn bar<'a>(input: &'a u32, f: fn(&'a u32) -> &'a u32) -> &'static u32 { // in `g`. These are related via the `UnsafeFnPointer` cast. let g: unsafe fn(_) -> _ = f; //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `'_#1r` does not outlive free region `'static` + //~| ERROR free region `'a` does not outlive free region `'static` unsafe { g(input) } } diff --git a/src/test/compile-fail/mir_check_cast_unsize.rs b/src/test/compile-fail/mir_check_cast_unsize.rs index bc867047ab5b3..1df56793f73bd 100644 --- a/src/test/compile-fail/mir_check_cast_unsize.rs +++ b/src/test/compile-fail/mir_check_cast_unsize.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; fn bar<'a>(x: &'a u32) -> &'static dyn Debug { - //~^ ERROR free region `'_#1r` does not outlive free region `'static` + //~^ ERROR free region `'a` does not outlive free region `'static` x //~^ WARNING not reporting region error due to -Znll } diff --git a/src/test/compile-fail/nll/where_clauses_in_functions.rs b/src/test/compile-fail/nll/where_clauses_in_functions.rs index a13360aeca7f5..ecea8756903ae 100644 --- a/src/test/compile-fail/nll/where_clauses_in_functions.rs +++ b/src/test/compile-fail/nll/where_clauses_in_functions.rs @@ -21,7 +21,7 @@ where fn bar<'a, 'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { foo(x, y) - //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` + //~^ ERROR lifetime mismatch [E0623] //~| WARNING not reporting region error due to -Znll } diff --git a/src/test/compile-fail/nll/where_clauses_in_structs.rs b/src/test/compile-fail/nll/where_clauses_in_structs.rs index 0c4fd5dead3f9..f1a6dc48e13b8 100644 --- a/src/test/compile-fail/nll/where_clauses_in_structs.rs +++ b/src/test/compile-fail/nll/where_clauses_in_structs.rs @@ -21,7 +21,7 @@ struct Foo<'a: 'b, 'b> { fn bar<'a, 'b>(x: Cell<&'a u32>, y: Cell<&'b u32>) { Foo { x, y }; - //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` + //~^ ERROR lifetime mismatch [E0623] //~| WARNING not reporting region error due to -Znll } diff --git a/src/test/compile-fail/regions-static-bound.rs b/src/test/compile-fail/regions-static-bound.rs index 678da45fce41f..a217cc9ebfa52 100644 --- a/src/test/compile-fail/regions-static-bound.rs +++ b/src/test/compile-fail/regions-static-bound.rs @@ -24,10 +24,10 @@ fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { fn error(u: &(), v: &()) { static_id(&u); //[ll]~ ERROR cannot infer an appropriate lifetime //[nll]~^ WARNING not reporting region error due to -Znll - //[nll]~| ERROR free region `'_#1r` does not outlive free region `'static` + //[nll]~| ERROR free region `` does not outlive free region `'static` static_id_indirect(&v); //[ll]~ ERROR cannot infer an appropriate lifetime //[nll]~^ WARNING not reporting region error due to -Znll - //[nll]~| ERROR free region `'_#2r` does not outlive free region `'static` + //[nll]~| ERROR free region `` does not outlive free region `'static` } fn main() {} diff --git a/src/test/compile-fail/regions-struct-not-wf.rs b/src/test/compile-fail/regions-struct-not-wf.rs index a7f1828970324..9106f1f0ba69c 100644 --- a/src/test/compile-fail/regions-struct-not-wf.rs +++ b/src/test/compile-fail/regions-struct-not-wf.rs @@ -10,11 +10,15 @@ // Various examples of structs whose fields are not well-formed. +// revisions:lexical nll + #![allow(dead_code)] +#![cfg_attr(nll, feature(nll))] struct Ref<'a, T> { field: &'a T - //~^ ERROR the parameter type `T` may not live long enough + //[lexical]~^ ERROR the parameter type `T` may not live long enough + //[nll]~^^ ERROR the parameter type `T` may not live long enough } struct RefOk<'a, T:'a> { @@ -23,12 +27,14 @@ struct RefOk<'a, T:'a> { struct RefIndirect<'a, T> { field: RefOk<'a, T> - //~^ ERROR the parameter type `T` may not live long enough + //[lexical]~^ ERROR the parameter type `T` may not live long enough + //[nll]~^^ ERROR the parameter type `T` may not live long enough } struct DoubleRef<'a, 'b, T> { field: &'a &'b T - //~^ ERROR reference has a longer lifetime than the data it references + //[lexical]~^ ERROR reference has a longer lifetime than the data it references + //[nll]~^^ ERROR reference has a longer lifetime than the data it references } fn main() { } diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs deleted file mode 100644 index 058a57fe612cf..0000000000000 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2012-2016 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. - -// Basic test for liveness constraints: the region (`R1`) that appears -// in the type of `p` includes the points after `&v[0]` up to (but not -// including) the call to `use_x`. The `else` branch is not included. - -// ignore-tidy-linelength -// compile-flags:-Znll -Zverbose -// ^^^^^^^^^ force compiler to dump more region information - -#![allow(warnings)] - -fn use_x(_: usize) -> bool { true } - -fn main() { - let mut v = [1, 2, 3]; - let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] }; - if true { - use_x(*p.value); - } else { - use_x(22); - } - - // `p` will get dropped here. Because the `#[may_dangle]` - // attribute is not present on `Wrap`, we must conservatively - // assume that the dtor may access the `value` field, and hence we - // must consider R1 to be live. -} - -struct Wrap { - value: T -} - -// Look ma, no `#[may_dangle]` attribute here. -impl Drop for Wrap { - fn drop(&mut self) { } -} - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#6r | {bb2[3..=5], bb3[0..=2], bb4[0], bb5[0..=2], bb6[0], bb7[0..=1], bb8[0]} -// ... -// let _2: Wrap<&'_#6r usize>; -// END rustc.main.nll.0.mir diff --git a/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs b/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs index a891e6072a7a6..35a5422040a17 100644 --- a/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs +++ b/src/test/run-pass/borrowck/two-phase-control-flow-split-before-activation.rs @@ -10,7 +10,8 @@ // revisions: lxl nll //[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows -//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll + +#![cfg_attr(nll, feature(nll))] fn main() { let mut a = 0; diff --git a/src/test/run-pass/impl-trait/example-calendar.rs b/src/test/run-pass/impl-trait/example-calendar.rs index 0b612c2d3ff34..8d035bafab78d 100644 --- a/src/test/run-pass/impl-trait/example-calendar.rs +++ b/src/test/run-pass/impl-trait/example-calendar.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: normal nll +//[nll] compile-flags: -Znll -Zborrowck=mir + #![feature(conservative_impl_trait, universal_impl_trait, fn_traits, diff --git a/src/test/run-pass/nll/get_default.rs b/src/test/run-pass/nll/get_default.rs new file mode 100644 index 0000000000000..13ef907d8d008 --- /dev/null +++ b/src/test/run-pass/nll/get_default.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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. + +#![feature(nll)] + +use std::collections::HashMap; + +fn get_default(map: &mut HashMap, key: usize) -> &mut String { + match map.get_mut(&key) { + Some(value) => value, + None => { + map.insert(key, "".to_string()); + map.get_mut(&key).unwrap() + } + } +} + +fn main() { + let map = &mut HashMap::new(); + map.insert(22, format!("Hello, world")); + map.insert(44, format!("Goodbye, world")); + assert_eq!(&*get_default(map, 22), "Hello, world"); + assert_eq!(&*get_default(map, 66), ""); +} diff --git a/src/test/run-pass/nll/process_or_insert_default.rs b/src/test/run-pass/nll/process_or_insert_default.rs new file mode 100644 index 0000000000000..a3a484402cc14 --- /dev/null +++ b/src/test/run-pass/nll/process_or_insert_default.rs @@ -0,0 +1,37 @@ +// Copyright 2016 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. + +#![feature(nll)] + +use std::collections::HashMap; + +fn process_or_insert_default(map: &mut HashMap, key: usize) { + match map.get_mut(&key) { + Some(value) => { + process(value); + } + None => { + map.insert(key, "".to_string()); + } + } +} + +fn process(x: &str) { + assert_eq!(x, "Hello, world"); +} + +fn main() { + let map = &mut HashMap::new(); + map.insert(22, format!("Hello, world")); + map.insert(44, format!("Goodbye, world")); + process_or_insert_default(map, 22); + process_or_insert_default(map, 66); + assert_eq!(map[&66], ""); +} diff --git a/src/test/run-pass/nll/rc-loop.rs b/src/test/run-pass/nll/rc-loop.rs new file mode 100644 index 0000000000000..2b746fac4d426 --- /dev/null +++ b/src/test/run-pass/nll/rc-loop.rs @@ -0,0 +1,40 @@ +// Copyright 2016 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. + +// A test for something that NLL enables. It sometimes happens that +// the `while let` pattern makes some borrows from a variable (in this +// case, `x`) that you need in order to compute the next value for +// `x`. The lexical checker makes this very painful. The NLL checker +// does not. + +#![feature(match_default_bindings)] +#![feature(nll)] + +use std::rc::Rc; + +#[derive(Debug, PartialEq, Eq)] +enum Foo { + Base(usize), + Next(Rc), +} + +fn find_base(mut x: Rc) -> Rc { + while let Foo::Next(n) = &*x { + x = n.clone(); + } + x +} + +fn main() { + let chain = Rc::new(Foo::Next(Rc::new(Foo::Base(44)))); + let base = find_base(chain); + assert_eq!(&*base, &Foo::Base(44)); +} + diff --git a/src/test/ui/feature-gate-nll.rs b/src/test/ui/feature-gate-nll.rs new file mode 100644 index 0000000000000..f34a9cddf98e4 --- /dev/null +++ b/src/test/ui/feature-gate-nll.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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. + +#![allow(dead_code)] + +fn main() { + let mut x = 33; + + let p = &x; + x = 22; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +} diff --git a/src/test/ui/feature-gate-nll.stderr b/src/test/ui/feature-gate-nll.stderr new file mode 100644 index 0000000000000..4135462305a89 --- /dev/null +++ b/src/test/ui/feature-gate-nll.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `x` because it is borrowed + --> $DIR/feature-gate-nll.rs:17:5 + | +16 | let p = &x; + | - borrow of `x` occurs here +17 | x = 22; //~ ERROR cannot assign to `x` because it is borrowed [E0506] + | ^^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/capture-ref-in-struct.rs b/src/test/ui/nll/capture-ref-in-struct.rs index 5eae5d924712b..74b086ab18a58 100644 --- a/src/test/ui/nll/capture-ref-in-struct.rs +++ b/src/test/ui/nll/capture-ref-in-struct.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Znll -Zborrowck=mir +// compile-flags:-Znll-dump-cause // Test that a structure which tries to store a pointer to `y` into // `p` (indirectly) fails to compile. #![feature(rustc_attrs)] +#![feature(nll)] struct SomeStruct<'a, 'b: 'a> { p: &'a mut &'b i32, diff --git a/src/test/ui/nll/capture-ref-in-struct.stderr b/src/test/ui/nll/capture-ref-in-struct.stderr index 3812ff4040799..7e7487daa67a3 100644 --- a/src/test/ui/nll/capture-ref-in-struct.stderr +++ b/src/test/ui/nll/capture-ref-in-struct.stderr @@ -1,11 +1,14 @@ error[E0597]: `y` does not live long enough - --> $DIR/capture-ref-in-struct.rs:32:16 + --> $DIR/capture-ref-in-struct.rs:33:16 | -32 | y: &y, +33 | y: &y, | ^^ borrowed value does not live long enough ... -37 | } +38 | } | - borrowed value only lives until here +39 | +40 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#5r... diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs index 1e34aaf1ea030..41c744fec6e76 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.rs +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.rs @@ -34,7 +34,7 @@ fn test() { { let y = 22; let mut closure = expect_sig(|p, y| *p = y); - //~^ ERROR free region `'_#4r` does not outlive free region `'_#3r` + //~^ ERROR does not outlive free region //~| WARNING not reporting region error due to -Znll closure(&mut p, &y); } diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index 2dfafd8f1725b..3bd02f308c883 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 36 | let mut closure = expect_sig(|p, y| *p = y); | ^ -error: free region `'_#4r` does not outlive free region `'_#3r` +error: free region `ReFree(DefId(0/1:9 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]), BrAnon(3))` does not outlive free region `ReFree(DefId(0/1:9 ~ escape_argument_callee[317d]::test[0]::{{closure}}[0]), BrAnon(2))` --> $DIR/escape-argument-callee.rs:36:45 | 36 | let mut closure = expect_sig(|p, y| *p = y); diff --git a/src/test/ui/nll/closure-requirements/escape-argument.rs b/src/test/ui/nll/closure-requirements/escape-argument.rs index 7e918c6431de4..17fadf0a2978b 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.rs +++ b/src/test/ui/nll/closure-requirements/escape-argument.rs @@ -22,7 +22,7 @@ // basically checking that the MIR type checker correctly enforces the // closure signature. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 940197986001a..09d5617b08ef5 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -31,6 +31,9 @@ error[E0597]: `y` does not live long enough 38 | //~^ ERROR `y` does not live long enough [E0597] 39 | } | - borrowed value only lives until here +40 | +41 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#6r... diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs index 05700ae00ad4f..984c9fe7c34bd 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.rs @@ -15,7 +15,7 @@ // // except that the closure does so via a second closure. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 277e5201416ad..430fb711c635d 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -58,6 +58,9 @@ error[E0597]: `y` does not live long enough ... 36 | } | - borrowed value only lives until here +37 | +38 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#4r... diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs index 93d8bfafcbaa4..499ebd659556c 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.rs @@ -19,7 +19,7 @@ // `'b`. This relationship is propagated to the closure creator, // which reports an error. -// compile-flags:-Znll -Zborrowck=mir -Zverbose +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose #![feature(rustc_attrs)] diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 709e6bc2de159..090bacbc17d07 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -35,6 +35,9 @@ error[E0597]: `y` does not live long enough ... 36 | } | - borrowed value only lives until here +37 | +38 | deref(p); + | - borrow later used here | = note: borrowed value must be valid for lifetime '_#4r... diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs index 50d7877de50d7..30a6dfc5b3edd 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs @@ -54,7 +54,7 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell // Only works if 'x: 'y: let p = x.get(); //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` + //~| ERROR does not outlive free region demand_y(x, y, p) }, ); diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index f90bc7c175a96..7e48c0fc5842a 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 55 | let p = x.get(); | ^^^^^^^ -error: free region `'_#5r` does not outlive free region `'_#6r` +error: free region `ReFree(DefId(0/1:20 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]), BrAnon(1))` does not outlive free region `ReFree(DefId(0/1:20 ~ propagate_approximated_fail_no_postdom[317d]::supply[0]::{{closure}}[0]), BrAnon(2))` --> $DIR/propagate-approximated-fail-no-postdom.rs:55:17 | 55 | let p = x.get(); @@ -17,7 +17,7 @@ note: No external requirements 54 | | // Only works if 'x: 'y: 55 | | let p = x.get(); 56 | | //~^ WARN not reporting region error due to -Znll -57 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#6r` +57 | | //~| ERROR does not outlive free region 58 | | demand_y(x, y, p) 59 | | }, | |_________^ diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs index 80a40581b8957..91128035f3d95 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -51,7 +51,7 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` + //~^ ERROR lifetime mismatch // Only works if 'x: 'y: demand_y(x, y, x.get()) //~ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 4bae29ad32617..f9a6999243a9c 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -9,7 +9,7 @@ note: External requirements | 53 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { | _______________________________________________^ -54 | | //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` +54 | | //~^ ERROR lifetime mismatch 55 | | 56 | | // Only works if 'x: 'y: 57 | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to -Znll @@ -23,18 +23,22 @@ note: External requirements = note: number of external vids: 3 = note: where '_#1r: '_#2r -error: free region `'_#1r` does not outlive free region `'_#2r` +error[E0623]: lifetime mismatch --> $DIR/propagate-approximated-ref.rs:53:29 | +52 | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | ------- ------- + | | + | these two types are declared with different lifetimes... 53 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ^^^^^^^ + | ^^^^^^^ ...but data from `cell_a` flows into `cell_b` here note: No external requirements --> $DIR/propagate-approximated-ref.rs:52:1 | 52 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { 53 | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { -54 | | //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` +54 | | //~^ ERROR lifetime mismatch 55 | | ... | 58 | | }); diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs index 244929d71dba8..f210346a82a67 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.rs @@ -31,7 +31,7 @@ fn case1() { foo(cell, |cell_a, cell_x| { //~^ WARNING not reporting region error due to -Znll cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure - //~^ ERROR free region `'_#2r` does not outlive free region `'_#1r` + //~^ ERROR does not outlive free region }) } diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 45dc5d913ee13..290377996c942 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 31 | foo(cell, |cell_a, cell_x| { | ^^^ -error: free region `'_#2r` does not outlive free region `'_#1r` +error: free region `ReFree(DefId(0/1:12 ~ propagate_approximated_shorter_to_static_comparing_against_free[317d]::case1[0]::{{closure}}[0]), BrAnon(1))` does not outlive free region `'_#1r` --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:9 | 33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure @@ -17,7 +17,7 @@ note: No external requirements | _______________^ 32 | | //~^ WARNING not reporting region error due to -Znll 33 | | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure -34 | | //~^ ERROR free region `'_#2r` does not outlive free region `'_#1r` +34 | | //~^ ERROR does not outlive free region 35 | | }) | |_____^ | diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs index 54007f0191da1..c66472d5ce9b1 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -43,7 +43,7 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` + //~^ ERROR does not outlive free region // Only works if 'x: 'y: demand_y(x, y, x.get()) //~ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 86b9fecb80e2e..13aedc408cf06 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -9,7 +9,7 @@ note: External requirements | 45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | _______________________________________________^ -46 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +46 | | //~^ ERROR does not outlive free region 47 | | 48 | | // Only works if 'x: 'y: 49 | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to -Znll @@ -23,12 +23,12 @@ note: External requirements = note: number of external vids: 2 = note: where '_#1r: '_#0r -error: free region `'_#1r` does not outlive free region `ReStatic` +error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47 | 45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | _______________________________________________^ -46 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +46 | | //~^ ERROR does not outlive free region 47 | | 48 | | // Only works if 'x: 'y: 49 | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to -Znll @@ -40,7 +40,7 @@ note: No external requirements | 44 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { 45 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { -46 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +46 | | //~^ ERROR does not outlive free region 47 | | ... | 50 | | }); diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs index 68d51e2b7d105..f4011a0e5335e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs @@ -46,7 +46,7 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` + //~^ ERROR does not outlive free region // Only works if 'x: 'y: demand_y(x, y, x.get()) //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index adc6b1ac595e9..947ed650e6bca 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -9,7 +9,7 @@ note: External requirements | 48 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { | _______________________________________________^ -49 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +49 | | //~^ ERROR does not outlive free region 50 | | // Only works if 'x: 'y: 51 | | demand_y(x, y, x.get()) 52 | | //~^ WARNING not reporting region error due to -Znll @@ -23,12 +23,12 @@ note: External requirements = note: number of external vids: 3 = note: where '_#1r: '_#0r -error: free region `'_#1r` does not outlive free region `ReStatic` +error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47 | 48 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { | _______________________________________________^ -49 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +49 | | //~^ ERROR does not outlive free region 50 | | // Only works if 'x: 'y: 51 | | demand_y(x, y, x.get()) 52 | | //~^ WARNING not reporting region error due to -Znll @@ -40,7 +40,7 @@ note: No external requirements | 47 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { 48 | | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { -49 | | //~^ ERROR free region `'_#1r` does not outlive free region `ReStatic` +49 | | //~^ ERROR does not outlive free region 50 | | // Only works if 'x: 'y: ... | 53 | | }); diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs index b4a759d5e705d..d163f304ae5b1 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.rs @@ -44,7 +44,7 @@ fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: #[rustc_regions] fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` + //~^ ERROR lifetime mismatch // Only works if 'x: 'y: demand_y(outlives1, outlives2, x.get()) //~ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 43d61fdf1b5f8..64766296e65a5 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -9,7 +9,7 @@ note: External requirements | 46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { | _____________________________________________^ -47 | | //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` +47 | | //~^ ERROR lifetime mismatch 48 | | 49 | | // Only works if 'x: 'y: 50 | | demand_y(outlives1, outlives2, x.get()) //~ WARNING not reporting region error due to -Znll @@ -23,18 +23,22 @@ note: External requirements = note: number of external vids: 3 = note: where '_#1r: '_#2r -error: free region `'_#1r` does not outlive free region `'_#2r` +error[E0623]: lifetime mismatch --> $DIR/propagate-approximated-val.rs:46:29 | +45 | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | ------- ------- + | | + | these two types are declared with different lifetimes... 46 | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { - | ^^^^^^ + | ^^^^^^ ...but data from `cell_a` flows into `cell_b` here note: No external requirements --> $DIR/propagate-approximated-val.rs:45:1 | 45 | / fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { 46 | | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { -47 | | //~^ ERROR free region `'_#1r` does not outlive free region `'_#2r` +47 | | //~^ ERROR lifetime mismatch 48 | | ... | 51 | | }); diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs index 4bbdcc4494486..eb512a3b9b1fb 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.rs @@ -46,7 +46,7 @@ fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { // Only works if 'x: 'y: demand_y(x, y, x.get()) //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` + //~| ERROR does not outlive free region }); } diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index f3c40c838fb3e..08dcfb042b5f4 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 47 | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ -error: free region `'_#6r` does not outlive free region `'_#4r` +error: free region `ReFree(DefId(0/1:18 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]), BrAnon(4))` does not outlive free region `ReFree(DefId(0/1:18 ~ propagate_fail_to_approximate_longer_no_bounds[317d]::supply[0]::{{closure}}[0]), BrAnon(2))` --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18 | 47 | demand_y(x, y, x.get()) @@ -18,7 +18,7 @@ note: No external requirements 46 | | // Only works if 'x: 'y: 47 | | demand_y(x, y, x.get()) 48 | | //~^ WARN not reporting region error due to -Znll -49 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r` +49 | | //~| ERROR does not outlive free region 50 | | }); | |_____^ | diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs index 69fad354792f0..9307424642972 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs @@ -50,7 +50,7 @@ fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { // Only works if 'x: 'y: demand_y(x, y, x.get()) //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#5r` does not outlive free region `'_#7r` + //~| ERROR does not outlive free region }); } diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index a66c2a7897024..502f565024970 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 51 | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ -error: free region `'_#5r` does not outlive free region `'_#7r` +error: free region `ReFree(DefId(0/1:18 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]), BrAnon(2))` does not outlive free region `ReFree(DefId(0/1:18 ~ propagate_fail_to_approximate_longer_wrong_bounds[317d]::supply[0]::{{closure}}[0]), BrAnon(4))` --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18 | 51 | demand_y(x, y, x.get()) @@ -18,7 +18,7 @@ note: No external requirements 50 | | // Only works if 'x: 'y: 51 | | demand_y(x, y, x.get()) 52 | | //~^ WARN not reporting region error due to -Znll -53 | | //~| ERROR free region `'_#5r` does not outlive free region `'_#7r` +53 | | //~| ERROR does not outlive free region 54 | | }); | |_____^ | diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs index 604c81da49db3..91796355752a5 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.rs @@ -40,7 +40,7 @@ where T: Trait<'a>, { establish_relationships(value, |value| { - //~^ ERROR `T` does not outlive + //~^ ERROR the parameter type `T` may not live long enough // This function call requires that // diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index efac55f2beeac..aefa160fcbc96 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -9,7 +9,7 @@ note: External requirements | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR `T` does not outlive +43 | | //~^ ERROR the parameter type `T` may not live long enough 44 | | 45 | | // This function call requires that ... | @@ -26,18 +26,20 @@ note: External requirements = note: number of external vids: 2 = note: where T: '_#1r -error: `T` does not outlive `'_#3r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/propagate-from-trait-match.rs:42:36 | 42 | establish_relationships(value, |value| { | ____________________________________^ -43 | | //~^ ERROR `T` does not outlive +43 | | //~^ ERROR the parameter type `T` may not live long enough 44 | | 45 | | // This function call requires that ... | 56 | | //~^ WARNING not reporting region error due to -Znll 57 | | }); | |_____^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/propagate-from-trait-match.rs:38:1 diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs index c61cf8a940f95..3f56dfe5af48d 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.rs @@ -18,7 +18,7 @@ fn foo(x: &u32) -> &'static u32 { &*x //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#1r` does not outlive free region `ReStatic` + //~| ERROR does not outlive free region } fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr index ef7ea9239127b..6648e38e7dea8 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr +++ b/src/test/ui/nll/closure-requirements/region-lbr-anon-does-not-outlive-static.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 19 | &*x | ^^^ -error: free region `'_#1r` does not outlive free region `ReStatic` +error: free region `ReFree(DefId(0/0:3 ~ region_lbr_anon_does_not_outlive_static[317d]::foo[0]), BrAnon(0))` does not outlive free region `ReStatic` --> $DIR/region-lbr-anon-does-not-outlive-static.rs:19:5 | 19 | &*x diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs index fcda5c5420bfe..a1be8e8518515 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.rs @@ -18,7 +18,7 @@ fn foo<'a>(x: &'a u32) -> &'static u32 { &*x //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#1r` does not outlive free region `ReStatic` + //~| ERROR does not outlive free region } fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr index 6dcb8e7cf122d..1edceba7b0916 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr +++ b/src/test/ui/nll/closure-requirements/region-lbr-named-does-not-outlive-static.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 19 | &*x | ^^^ -error: free region `'_#1r` does not outlive free region `ReStatic` +error: free region `ReFree(DefId(0/0:3 ~ region_lbr_named_does_not_outlive_static[317d]::foo[0]), BrNamed(crate0:DefIndex(1:9), 'a))` does not outlive free region `ReStatic` --> $DIR/region-lbr-named-does-not-outlive-static.rs:19:5 | 19 | &*x diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs index c1e4dee00656b..00b09e2ab21ad 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.rs @@ -18,7 +18,7 @@ fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { &*x //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#1r` does not outlive free region `'_#2r` + //~| ERROR lifetime mismatch } fn main() { } diff --git a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr index c1b2f44030949..efe0b73f195a9 100644 --- a/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr +++ b/src/test/ui/nll/closure-requirements/region-lbr1-does-not-outlive-ebr2.stderr @@ -4,11 +4,15 @@ warning: not reporting region error due to -Znll 19 | &*x | ^^^ -error: free region `'_#1r` does not outlive free region `'_#2r` +error[E0623]: lifetime mismatch --> $DIR/region-lbr1-does-not-outlive-ebr2.rs:19:5 | +18 | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 { + | ------- ------- + | | + | this parameter and the return type are declared with different lifetimes... 19 | &*x - | ^^^ + | ^^^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs index 9314bbf943222..754df4f2c5db6 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.rs @@ -20,7 +20,7 @@ fn test() { expect_sig(|a, b| b); // ought to return `a` //~^ WARN not reporting region error due to -Znll - //~| ERROR free region `'_#3r` does not outlive free region `'_#2r` + //~| ERROR does not outlive free region } fn expect_sig(f: F) -> F diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index cb2b2e2f11860..58a26e61e5767 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -4,7 +4,7 @@ warning: not reporting region error due to -Znll 21 | expect_sig(|a, b| b); // ought to return `a` | ^ -error: free region `'_#3r` does not outlive free region `'_#2r` +error: free region `ReFree(DefId(0/1:9 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]), BrAnon(2))` does not outlive free region `ReFree(DefId(0/1:9 ~ return_wrong_bound_region[317d]::test[0]::{{closure}}[0]), BrAnon(1))` --> $DIR/return-wrong-bound-region.rs:21:23 | 21 | expect_sig(|a, b| b); // ought to return `a` @@ -27,7 +27,7 @@ note: No external requirements 20 | / fn test() { 21 | | expect_sig(|a, b| b); // ought to return `a` 22 | | //~^ WARN not reporting region error due to -Znll -23 | | //~| ERROR free region `'_#3r` does not outlive free region `'_#2r` +23 | | //~| ERROR does not outlive free region 24 | | } | |_^ | diff --git a/src/test/ui/nll/constant.rs b/src/test/ui/nll/constant.rs new file mode 100644 index 0000000000000..ced06e5ea0504 --- /dev/null +++ b/src/test/ui/nll/constant.rs @@ -0,0 +1,21 @@ +// Copyright 2017 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. + +// Test that MIR borrowck and NLL analysis can handle constants of +// arbitrary types without ICEs. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose +// must-compile-successfully + +const HI: &str = "hi"; + +fn main() { + assert_eq!(HI, "hi"); +} diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/ui/nll/drop-may-dangle.rs similarity index 65% rename from src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs rename to src/test/ui/nll/drop-may-dangle.rs index c14ce6bb581de..2780b34746378 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/ui/nll/drop-may-dangle.rs @@ -12,8 +12,8 @@ // in the type of `p` includes the points after `&v[0]` up to (but not // including) the call to `use_x`. The `else` branch is not included. -// compile-flags:-Znll -Zverbose -// ^^^^^^^^^ force compiler to dump more region information +// compile-flags:-Znll -Zborrowck=mir +// must-compile-successfully #![allow(warnings)] #![feature(dropck_eyepatch)] @@ -23,28 +23,24 @@ fn use_x(_: usize) -> bool { true } fn main() { let mut v = [1, 2, 3]; - let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] }; + let p: WrapMayDangle<& /* R4 */ usize> = WrapMayDangle { value: &v[0] }; if true { + // `p` will get dropped at end of this block. However, because of + // the `#[may_dangle]` attribute, we do not need to consider R4 + // live after this point. use_x(*p.value); } else { + v[0] += 1; use_x(22); } - // `p` will get dropped here. However, because of the - // `#[may_dangle]` attribute, we do not need to consider R4 live. + v[0] += 1; } -struct Wrap { +struct WrapMayDangle { value: T } -unsafe impl<#[may_dangle] T> Drop for Wrap { +unsafe impl<#[may_dangle] T> Drop for WrapMayDangle { fn drop(&mut self) { } } - -// END RUST SOURCE -// START rustc.main.nll.0.mir -// | '_#6r | {bb2[3..=5], bb3[0..=1]} -// ... -// let _2: Wrap<&'_#6r usize>; -// END rustc.main.nll.0.mir diff --git a/src/test/ui/nll/drop-may-dangle.stderr b/src/test/ui/nll/drop-may-dangle.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/nll/drop-no-may-dangle.rs b/src/test/ui/nll/drop-no-may-dangle.rs new file mode 100644 index 0000000000000..0220858a0d59e --- /dev/null +++ b/src/test/ui/nll/drop-no-may-dangle.rs @@ -0,0 +1,43 @@ +// Copyright 2012-2016 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. + +// Basic test for liveness constraints: the region (`R1`) that appears +// in the type of `p` must include everything until `p` is dropped +// because of destructor. (Note that the stderr also identifies this +// destructor in the error message.) + +// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause + +#![allow(warnings)] +#![feature(dropck_eyepatch)] +#![feature(generic_param_attrs)] + +fn use_x(_: usize) -> bool { true } + +fn main() { + let mut v = [1, 2, 3]; + let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + if true { + use_x(*p.value); + } else { + use_x(22); + v[0] += 1; //~ ERROR cannot assign to `v[..]` because it is borrowed + } + + v[0] += 1; //~ ERROR cannot assign to `v[..]` because it is borrowed +} + +struct WrapMayNotDangle { + value: T +} + +impl Drop for WrapMayNotDangle { + fn drop(&mut self) { } +} diff --git a/src/test/ui/nll/drop-no-may-dangle.stderr b/src/test/ui/nll/drop-no-may-dangle.stderr new file mode 100644 index 0000000000000..ef850f3a568c0 --- /dev/null +++ b/src/test/ui/nll/drop-no-may-dangle.stderr @@ -0,0 +1,25 @@ +error[E0506]: cannot assign to `v[..]` because it is borrowed + --> $DIR/drop-no-may-dangle.rs:31:9 + | +26 | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + | ----- borrow of `v[..]` occurs here +... +31 | v[0] += 1; //~ ERROR cannot assign to `v[..]` because it is borrowed + | ^^^^^^^^^ assignment to borrowed `v[..]` occurs here +... +35 | } + | - borrow later used here, when `p` is dropped + +error[E0506]: cannot assign to `v[..]` because it is borrowed + --> $DIR/drop-no-may-dangle.rs:34:5 + | +26 | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; + | ----- borrow of `v[..]` occurs here +... +34 | v[0] += 1; //~ ERROR cannot assign to `v[..]` because it is borrowed + | ^^^^^^^^^ assignment to borrowed `v[..]` occurs here +35 | } + | - borrow later used here, when `p` is dropped + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs index e5944e75e4241..7c52a0c87af95 100644 --- a/src/test/ui/nll/get_default.rs +++ b/src/test/ui/nll/get_default.rs @@ -13,7 +13,7 @@ // a variety of errors from the older, AST-based machinery (notably // borrowck), and then we get the NLL error at the end. -// compile-flags:-Znll -Zborrowck=compare +// compile-flags:-Znll -Zborrowck=compare -Znll-dump-cause struct Map { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr index fff2684af1399..ed2c305090ccc 100644 --- a/src/test/ui/nll/get_default.stderr +++ b/src/test/ui/nll/get_default.stderr @@ -42,6 +42,9 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm 43 | Some(v) => { 44 | map.set(String::new()); // Both AST and MIR error here | ^^^ mutable borrow occurs here +... +47 | return v; + | - borrow later used here error: aborting due to 4 previous errors diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs index 0047f6d59237c..184dfe320d33d 100644 --- a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll -Znll-dump-cause + #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr index 389334f9c1d8d..3c685ce111a97 100644 --- a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -1,11 +1,14 @@ error[E0506]: cannot assign to `x` because it is borrowed - --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5 + --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:32:5 | -27 | let wrap = Wrap { p: &mut x }; +28 | let wrap = Wrap { p: &mut x }; | ------ borrow of `x` occurs here ... -31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] +32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +33 | // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones +34 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs index 3242136f005e7..beb2c87f8f3bd 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr index 9edeca2d18801..072818c7ce17b 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -6,6 +6,8 @@ error[E0506]: cannot assign to `x` because it is borrowed ... 31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +32 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs index 3e32818b8dcf3..39cad8acee181 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr index 24d0d6d04c8da..89117c2bfeafe 100644 --- a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -6,6 +6,9 @@ error[E0506]: cannot assign to `x` because it is borrowed ... 32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +33 | // FIXME ^ This currently errors and it should not. +34 | } + | - borrow later used here, when `foo` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs index 291fcbd73f3e1..767c5b9b8be8d 100644 --- a/src/test/ui/nll/maybe-initialized-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll +//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause #![allow(warnings)] diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr index 7b1b55d133ac5..626307a80ed57 100644 --- a/src/test/ui/nll/maybe-initialized-drop.stderr +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -5,6 +5,8 @@ error[E0506]: cannot assign to `x` because it is borrowed | ------ borrow of `x` occurs here 26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506] | ^^^^^ assignment to borrowed `x` occurs here +27 | } + | - borrow later used here, when `wrap` is dropped error: aborting due to previous error diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.rs b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs new file mode 100644 index 0000000000000..850cd1e7336d7 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.rs @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(conservative_impl_trait)] + +trait Foo<'a> { +} + +impl<'a, T> Foo<'a> for T { } + +fn foo<'a, T>(x: &T) -> impl Foo<'a> { + x + //~^ WARNING not reporting region error due to -Znll + //~| ERROR explicit lifetime required in the type of `x` [E0621] +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr new file mode 100644 index 0000000000000..4cfd12002e796 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -0,0 +1,16 @@ +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-captures.rs:22:5 + | +22 | x + | ^ + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/impl-trait-captures.rs:22:5 + | +21 | fn foo<'a, T>(x: &T) -> impl Foo<'a> { + | - consider changing the type of `x` to `&ReEarlyBound(0, 'a) T` +22 | x + | ^ lifetime `ReEarlyBound(0, 'a)` required + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs b/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs new file mode 100644 index 0000000000000..135805a733944 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.rs @@ -0,0 +1,51 @@ +// Copyright 2016 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +fn no_region<'a, T>(x: Box) -> impl Debug + 'a + //~^ WARNING not reporting region error due to -Znll +where + T: Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn correct_region<'a, T>(x: Box) -> impl Debug + 'a +where + T: 'a + Debug, +{ + x +} + +fn wrong_region<'a, 'b, T>(x: Box) -> impl Debug + 'a + //~^ WARNING not reporting region error due to -Znll +where + T: 'b + Debug, +{ + x + //~^ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn outlives_region<'a, 'b, T>(x: Box) -> impl Debug + 'a +where + T: 'b + Debug, + 'b: 'a, +{ + x +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr new file mode 100644 index 0000000000000..5916d0060a02a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/impl-trait-outlives.stderr @@ -0,0 +1,30 @@ +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-outlives.rs:18:35 + | +18 | fn no_region<'a, T>(x: Box) -> impl Debug + 'a + | ^^^^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/impl-trait-outlives.rs:34:42 + | +34 | fn wrong_region<'a, 'b, T>(x: Box) -> impl Debug + 'a + | ^^^^^^^^^^^^^^^ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/impl-trait-outlives.rs:23:5 + | +23 | x + | ^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/impl-trait-outlives.rs:39:5 + | +39 | x + | ^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs new file mode 100644 index 0000000000000..0ec6d7b74ad5a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs @@ -0,0 +1,56 @@ +// Copyright 2016 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +// Test that we can deduce when projections like `T::Item` outlive the +// function body. Test that this does not imply that `T: 'a` holds. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn twice(mut value: T, mut f: F) +where + F: FnMut(&T, Cell<&Option>), + T: Iterator, +{ + let mut n = value.next(); + f(&value, Cell::new(&n)); + f(&value, Cell::new(&n)); +} + +#[rustc_errors] +fn generic1(value: T) { + // No error here: + twice(value, |value_ref, item| invoke1(item)); +} + +fn invoke1<'a, T>(x: Cell<&'a Option>) +where + T: 'a, +{ +} + +#[rustc_errors] +fn generic2(value: T) { + twice(value, |value_ref, item| invoke2(value_ref, item)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR the parameter type `T` may not live long enough +} + +fn invoke2<'a, T, U>(a: &T, b: Cell<&'a Option>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr new file mode 100644 index 0000000000000..a49bdbbf09edf --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr @@ -0,0 +1,16 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-implied-bounds.rs:45:36 + | +45 | twice(value, |value_ref, item| invoke2(value_ref, item)); + | ^^^^^^^ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-implied-bounds.rs:45:18 + | +45 | twice(value, |value_ref, item| invoke2(value_ref, item)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'static`... + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs index 9451163ace993..0493bd1ea0d9c 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.rs @@ -35,7 +35,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `::Item` does not outlive + //~| ERROR the associated type `::Item` may not live long enough } #[rustc_regions] @@ -53,7 +53,7 @@ where { with_signature(x, |mut y| Box::new(y.next())) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `::Item` does not outlive + //~| ERROR the associated type `::Item` may not live long enough } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 9afd5d41182f4..b2e98b7c2f6a4 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -72,11 +72,13 @@ note: External requirements = note: number of external vids: 4 = note: where ::Item: '_#3r -error: `::Item` does not outlive `'_#4r` +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-closure.rs:36:23 | 36 | with_signature(x, |mut y| Box::new(y.next())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `::Item: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/projection-no-regions-closure.rs:32:1 @@ -86,7 +88,7 @@ note: No external requirements 34 | | T: Iterator, 35 | | { ... | -38 | | //~| ERROR `::Item` does not outlive +38 | | //~| ERROR the associated type `::Item` may not live long enough 39 | | } | |_^ | @@ -111,11 +113,13 @@ note: No external requirements T ] -error: `::Item` does not outlive `'_#6r` +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-closure.rs:54:23 | 54 | with_signature(x, |mut y| Box::new(y.next())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `::Item: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/projection-no-regions-closure.rs:50:1 @@ -125,7 +129,7 @@ note: No external requirements 52 | | T: 'b + Iterator, 53 | | { ... | -56 | | //~| ERROR `::Item` does not outlive +56 | | //~| ERROR the associated type `::Item` may not live long enough 57 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs index c815fdc1a0c5e..5f2e84e247a3c 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.rs @@ -23,7 +23,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `::Item` does not outlive + //~| the associated type `::Item` may not live long enough } fn correct_region<'a, T>(mut x: T) -> Box @@ -39,7 +39,7 @@ where { Box::new(x.next()) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `::Item` does not outlive + //~| the associated type `::Item` may not live long enough } fn outlives_region<'a, 'b, T>(mut x: T) -> Box diff --git a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr index 4d13972641c16..d309bf2ce6ca4 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -10,17 +10,21 @@ warning: not reporting region error due to -Znll 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ -error: `::Item` does not outlive `'_#4r` +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-fn.rs:24:5 | 24 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `::Item: ReEarlyBound(0, 'a)`... -error: `::Item` does not outlive `'_#5r` +error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-fn.rs:40:5 | 40 | Box::new(x.next()) | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `::Item: ReEarlyBound(0, 'a)`... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs index e2a2d20d77d5c..9a5e04deddfc9 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -55,8 +55,8 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive - //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR does not outlive free region } #[rustc_regions] @@ -67,8 +67,8 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive - //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR does not outlive free region } #[rustc_regions] @@ -89,7 +89,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| ERROR the parameter type `T` may not live long enough //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` } diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index cbd80d70bf955..e57a39a9de963 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -83,13 +83,15 @@ note: External requirements = note: where T: '_#3r = note: where '_#2r: '_#3r -error: `T` does not outlive `'_#5r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:56:29 | 56 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(1:17), 'a))`... -error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `ReFree(DefId(0/0:8 ~ projection_one_region_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(1:17), 'a))` --> $DIR/projection-one-region-closure.rs:56:20 | 56 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -103,7 +105,7 @@ note: No external requirements 54 | | T: Anything<'b>, 55 | | { ... | -59 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +59 | | //~| ERROR does not outlive free region 60 | | } | |_^ | @@ -112,11 +114,13 @@ note: No external requirements T ] -error: `T` does not outlive `'_#6r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:68:29 | 68 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` --> $DIR/projection-one-region-closure.rs:68:20 @@ -132,7 +136,7 @@ note: No external requirements 65 | | T: Anything<'b>, 66 | | 'a: 'a, ... | -71 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +71 | | //~| ERROR does not outlive free region 72 | | } | |_^ | @@ -142,11 +146,13 @@ note: No external requirements T ] -error: `T` does not outlive `'_#6r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:90:29 | 90 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... error: free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` --> $DIR/projection-one-region-closure.rs:90:20 diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs index e179927dfb0b9..232025b57355c 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -47,7 +47,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + //~| ERROR does not outlive free region } #[rustc_regions] @@ -58,7 +58,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + //~| ERROR does not outlive free region } #[rustc_regions] @@ -79,7 +79,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` + //~| ERROR does not outlive free region } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 1088ae846fee5..5053b8486959a 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -94,7 +94,7 @@ note: External requirements = note: number of external vids: 3 = note: where '_#1r: '_#2r -error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `ReFree(DefId(0/0:8 ~ projection_one_region_trait_bound_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(1:17), 'a))` --> $DIR/projection-one-region-trait-bound-closure.rs:48:20 | 48 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -108,7 +108,7 @@ note: No external requirements 46 | | T: Anything<'b>, 47 | | { ... | -50 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +50 | | //~| ERROR does not outlive free region 51 | | } | |_^ | @@ -131,7 +131,7 @@ note: No external requirements 56 | | T: Anything<'b>, 57 | | 'a: 'a, ... | -61 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +61 | | //~| ERROR does not outlive free region 62 | | } | |_^ | @@ -155,7 +155,7 @@ note: No external requirements 67 | | T: Anything<'b>, 68 | | T::AssocType: 'a, ... | -82 | | //~| ERROR free region `ReEarlyBound(1, 'b)` does not outlive free region `ReEarlyBound(0, 'a)` +82 | | //~| ERROR does not outlive free region 83 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs index 42bfdfcf9f91b..e3cee00ed4eb3 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -48,7 +48,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `>::AssocType` does not outlive `'_#7r` + //~| ERROR associated type `>::AssocType` may not live long enough } #[rustc_regions] @@ -59,7 +59,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `>::AssocType` does not outlive `'_#8r` + //~| ERROR associated type `>::AssocType` may not live long enough } #[rustc_regions] @@ -80,7 +80,7 @@ where with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR `>::AssocType` does not outlive `'_#8r` + //~| ERROR associated type `>::AssocType` may not live long enough } #[rustc_regions] @@ -108,7 +108,7 @@ where { with_signature(cell, t, |cell, t| require(cell, t)); //~^ WARNING not reporting region error due to -Znll - //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` + //~| ERROR does not outlive free region } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index 5b708a0d7e629..414ae38080f63 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -152,11 +152,13 @@ note: External requirements = note: number of external vids: 3 = note: where >::AssocType: '_#2r -error: `>::AssocType` does not outlive `'_#7r` +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:49:29 | 49 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::AssocType: ReFree(DefId(0/0:8 ~ projection_two_region_trait_bound_closure[317d]::no_relationships_late[0]), BrNamed(crate0:DefIndex(1:19), 'a))`... note: No external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:45:1 @@ -166,7 +168,7 @@ note: No external requirements 47 | | T: Anything<'b, 'c>, 48 | | { ... | -51 | | //~| ERROR `>::AssocType` does not outlive `'_#7r` +51 | | //~| ERROR associated type `>::AssocType` may not live long enough 52 | | } | |_^ | @@ -176,11 +178,13 @@ note: No external requirements T ] -error: `>::AssocType` does not outlive `'_#8r` +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:60:29 | 60 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:55:1 @@ -190,7 +194,7 @@ note: No external requirements 57 | | T: Anything<'b, 'c>, 58 | | 'a: 'a, ... | -62 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` +62 | | //~| ERROR associated type `>::AssocType` may not live long enough 63 | | } | |_^ | @@ -201,11 +205,13 @@ note: No external requirements T ] -error: `>::AssocType` does not outlive `'_#8r` +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:81:29 | 81 | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:66:1 @@ -215,7 +221,7 @@ note: No external requirements 68 | | T: Anything<'b, 'c>, 69 | | T::AssocType: 'a, ... | -83 | | //~| ERROR `>::AssocType` does not outlive `'_#8r` +83 | | //~| ERROR associated type `>::AssocType` may not live long enough 84 | | } | |_^ | @@ -264,7 +270,7 @@ note: No external requirements T ] -error: free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +error: free region `ReEarlyBound(0, 'b)` does not outlive free region `ReFree(DefId(0/0:13 ~ projection_two_region_trait_bound_closure[317d]::two_regions[0]), BrNamed(crate0:DefIndex(1:44), 'a))` --> $DIR/projection-two-region-trait-bound-closure.rs:109:20 | 109 | with_signature(cell, t, |cell, t| require(cell, t)); @@ -278,7 +284,7 @@ note: No external requirements 107 | | T: Anything<'b, 'b>, 108 | | { ... | -111 | | //~| ERROR free region `ReEarlyBound(0, 'b)` does not outlive free region `'_#2r` +111 | | //~| ERROR does not outlive free region 112 | | } | |_^ | diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs new file mode 100644 index 0000000000000..423747a6bd6cb --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs @@ -0,0 +1,55 @@ +// Copyright 2016 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Invoke in such a way that the callee knows: +// +// - 'a: 'x +// +// and it must prove that `T: 'x`. Callee passes along `T: 'a`. +fn twice<'a, F, T>(v: Cell<&'a ()>, value: T, mut f: F) +where + F: for<'x> FnMut(Option>, &T), +{ + f(None, &value); + f(None, &value); +} + +#[rustc_regions] +fn generic(value: T) { + let cell = Cell::new(&()); + twice(cell, value, |a, b| invoke(a, b)); + //~^ WARNING not reporting region error + // + // This error from the old region solver looks bogus. +} + +#[rustc_regions] +fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { + twice(cell, value, |a, b| invoke(a, b)); + //~^ WARNING not reporting region error + //~| WARNING not reporting region error + //~| ERROR the parameter type `T` may not live long enough +} + +fn invoke<'a, 'x, T>(x: Option>, y: &T) +where + T: 'x, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr new file mode 100644 index 0000000000000..ef564377e506e --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -0,0 +1,87 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:35:31 + | +35 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:31 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:31 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^ + +note: External requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:35:24 + | +35 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:14 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]::{{closure}}[0]) with closure substs [ + T, + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: External requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:24 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:17 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]::{{closure}}[0]) with closure substs [ + T, + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: No external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:33:1 + | +33 | / fn generic(value: T) { +34 | | let cell = Cell::new(&()); +35 | | twice(cell, value, |a, b| invoke(a, b)); +36 | | //~^ WARNING not reporting region error +37 | | // +38 | | // This error from the old region solver looks bogus. +39 | | } + | |_^ + | + = note: defining type: DefId(0/0:5 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]) with substs [ + T + ] + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:24 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0/0:6 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]), BrNamed(crate0:DefIndex(1:16), 'a))`... + +note: No external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:42:1 + | +42 | / fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { +43 | | twice(cell, value, |a, b| invoke(a, b)); +44 | | //~^ WARNING not reporting region error +45 | | //~| WARNING not reporting region error +46 | | //~| ERROR the parameter type `T` may not live long enough +47 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]) with substs [ + T + ] + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs index 14e2eb26976ab..95a483b3c355d 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.rs @@ -36,7 +36,7 @@ where with_signature(x, |y| y) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| ERROR the parameter type `T` may not live long enough } fn correct_region<'a, T>(x: Box) -> Box @@ -52,7 +52,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| ERROR the parameter type `T` may not live long enough } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 37ebc38da4dae..b7120017a2c0e 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -25,11 +25,13 @@ note: External requirements = note: number of external vids: 3 = note: where T: '_#2r -error: `T` does not outlive `'_#4r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-closure-outlives-from-return-type.rs:37:23 | 37 | with_signature(x, |y| y) | ^^^^^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... note: No external requirements --> $DIR/ty-param-closure-outlives-from-return-type.rs:26:1 @@ -39,7 +41,7 @@ note: No external requirements 28 | | T: Debug, 29 | | { ... | -39 | | //~| ERROR `T` does not outlive +39 | | //~| ERROR the parameter type `T` may not live long enough 40 | | } | |_^ | @@ -48,11 +50,13 @@ note: No external requirements T ] -error: `T` does not outlive `'_#4r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-closure-outlives-from-return-type.rs:53:5 | 53 | x | ^ + | + = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs index beed1a740eac3..1149f250a46e2 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.rs @@ -36,7 +36,7 @@ where #[rustc_regions] fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { with_signature(a, b, |x, y| { - //~^ ERROR `T` does not outlive + //~^ ERROR the parameter type `T` may not live long enough // // See `correct_region`, which explains the point of this // test. The only difference is that, in the case of this @@ -74,7 +74,7 @@ where T: 'b, { with_signature(a, b, |x, y| { - //~^ ERROR `T` does not outlive + //~^ ERROR the parameter type `T` may not live long enough // See `correct_region` require(&x, &y) //~^ WARNING not reporting region error due to -Znll diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr index 78445eb47c328..d47f506cd2043 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -15,7 +15,7 @@ note: External requirements | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR `T` does not outlive +39 | | //~^ ERROR the parameter type `T` may not live long enough 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | @@ -58,7 +58,7 @@ note: External requirements | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR `T` does not outlive +77 | | //~^ ERROR the parameter type `T` may not live long enough 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll @@ -94,25 +94,27 @@ note: External requirements = note: number of external vids: 4 = note: where T: '_#3r -error: `T` does not outlive `'_#3r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-closure-outlives-from-where-clause.rs:38:26 | 38 | with_signature(a, b, |x, y| { | __________________________^ -39 | | //~^ ERROR `T` does not outlive +39 | | //~^ ERROR the parameter type `T` may not live long enough 40 | | // 41 | | // See `correct_region`, which explains the point of this ... | 46 | | //~^ WARNING not reporting region error due to -Znll 47 | | }) | |_____^ + | + = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0/0:6 ~ ty_param_closure_outlives_from_where_clause[317d]::no_region[0]), BrNamed(crate0:DefIndex(1:15), 'a))`... note: No external requirements --> $DIR/ty-param-closure-outlives-from-where-clause.rs:37:1 | 37 | / fn no_region<'a, T>(a: Cell<&'a ()>, b: T) { 38 | | with_signature(a, b, |x, y| { -39 | | //~^ ERROR `T` does not outlive +39 | | //~^ ERROR the parameter type `T` may not live long enough 40 | | // ... | 47 | | }) @@ -140,17 +142,19 @@ note: No external requirements T ] -error: `T` does not outlive `'_#5r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-closure-outlives-from-where-clause.rs:76:26 | 76 | with_signature(a, b, |x, y| { | __________________________^ -77 | | //~^ ERROR `T` does not outlive +77 | | //~^ ERROR the parameter type `T` may not live long enough 78 | | // See `correct_region` 79 | | require(&x, &y) 80 | | //~^ WARNING not reporting region error due to -Znll 81 | | }) | |_____^ + | + = help: consider adding an explicit lifetime bound `T: ReFree(DefId(0/0:8 ~ ty_param_closure_outlives_from_where_clause[317d]::wrong_region[0]), BrNamed(crate0:DefIndex(1:21), 'a))`... note: No external requirements --> $DIR/ty-param-closure-outlives-from-where-clause.rs:72:1 diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs new file mode 100644 index 0000000000000..babe608354fb9 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +// Test that we assume that universal types like `T` outlive the +// function body. Same as ty-param-fn-body, but uses `feature(nll)`, +// which affects error reporting. + +#![feature(nll)] + +#![allow(warnings)] +#![feature(dyn_trait)] + +use std::cell::Cell; + +// No errors here, because `'a` is local to the body. +fn region_within_body(t: T) { + let some_int = 22; + let cell = Cell::new(&some_int); + outlives(cell, t) +} + +// Error here, because T: 'a is not satisfied. +fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { + outlives(cell, t) + //~^ ERROR the parameter type `T` may not live long enough +} + +fn outlives<'a, T>(x: Cell<&'a usize>, y: T) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr new file mode 100644 index 0000000000000..fa9105df07027 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body-nll-feature.stderr @@ -0,0 +1,10 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/ty-param-fn-body-nll-feature.rs:31:5 + | +31 | outlives(cell, t) + | ^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs index a1e636cbc444b..e66c1853b64b9 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.rs @@ -29,7 +29,7 @@ fn region_within_body(t: T) { fn region_static<'a, T>(cell: Cell<&'a usize>, t: T) { outlives(cell, t) //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| ERROR the parameter type `T` may not live long enough } fn outlives<'a, T>(x: Cell<&'a usize>, y: T) diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr index bbe55c52b6ed5..3334f4ecc7c86 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn-body.stderr @@ -4,11 +4,13 @@ warning: not reporting region error due to -Znll 30 | outlives(cell, t) | ^^^^^^^^ -error: `T` does not outlive `'_#4r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-fn-body.rs:30:5 | 30 | outlives(cell, t) | ^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... error: aborting due to previous error diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.rs b/src/test/ui/nll/ty-outlives/ty-param-fn.rs index 76783af4ceb0e..aa3a03afa35cd 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.rs +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.rs @@ -21,7 +21,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| the parameter type `T` may not live long enough } fn correct_region<'a, T>(x: Box) -> Box @@ -37,7 +37,7 @@ where { x //~^ WARNING not reporting region error due to -Znll - //~| ERROR `T` does not outlive + //~| the parameter type `T` may not live long enough } fn outlives_region<'a, 'b, T>(x: Box) -> Box diff --git a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr index 02c4ebbd5aca9..1e659e2e9f073 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-fn.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-fn.stderr @@ -10,17 +10,21 @@ warning: not reporting region error due to -Znll 38 | x | ^ -error: `T` does not outlive `'_#3r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-fn.rs:22:5 | 22 | x | ^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... -error: `T` does not outlive `'_#4r` +error[E0309]: the parameter type `T` may not live long enough --> $DIR/ty-param-fn.rs:38:5 | 38 | x | ^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... error: aborting due to 2 previous errors diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs new file mode 100644 index 0000000000000..cab7ba7a50525 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs @@ -0,0 +1,42 @@ +// Copyright 2016 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. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose +// must-compile-successfully + +// Test that we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn twice(value: T, mut f: F) +where + F: FnMut(Cell<&T>), +{ + f(Cell::new(&value)); + f(Cell::new(&value)); +} + +#[rustc_errors] +fn generic(value: T) { + // No error here: + twice(value, |r| invoke(r)); +} + +fn invoke<'a, T>(x: Cell<&'a T>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d