diff --git a/src/librustc/infer/README.md b/src/librustc/infer/README.md index 6c1478531f14..e7daff3e2c37 100644 --- a/src/librustc/infer/README.md +++ b/src/librustc/infer/README.md @@ -1,239 +1,227 @@ # Type inference engine -This is loosely based on standard HM-type inference, but with an -extension to try and accommodate subtyping. There is nothing -principled about this extension; it's sound---I hope!---but it's a -heuristic, ultimately, and does not guarantee that it finds a valid -typing even if one exists (in fact, there are known scenarios where it -fails, some of which may eventually become problematic). - -## Key idea - -The main change is that each type variable T is associated with a -lower-bound L and an upper-bound U. L and U begin as bottom and top, -respectively, but gradually narrow in response to new constraints -being introduced. When a variable is finally resolved to a concrete -type, it can (theoretically) select any type that is a supertype of L -and a subtype of U. - -There are several critical invariants which we maintain: - -- the upper-bound of a variable only becomes lower and the lower-bound - only becomes higher over time; -- the lower-bound L is always a subtype of the upper bound U; -- the lower-bound L and upper-bound U never refer to other type variables, - but only to types (though those types may contain type variables). - -> An aside: if the terms upper- and lower-bound confuse you, think of -> "supertype" and "subtype". The upper-bound is a "supertype" -> (super=upper in Latin, or something like that anyway) and the lower-bound -> is a "subtype" (sub=lower in Latin). I find it helps to visualize -> a simple class hierarchy, like Java minus interfaces and -> primitive types. The class Object is at the root (top) and other -> types lie in between. The bottom type is then the Null type. -> So the tree looks like: -> -> ```text -> Object -> / \ -> String Other -> \ / -> (null) -> ``` -> -> So the upper bound type is the "supertype" and the lower bound is the -> "subtype" (also, super and sub mean upper and lower in Latin, or something -> like that anyway). - -## Satisfying constraints - -At a primitive level, there is only one form of constraint that the -inference understands: a subtype relation. So the outside world can -say "make type A a subtype of type B". If there are variables -involved, the inferencer will adjust their upper- and lower-bounds as -needed to ensure that this relation is satisfied. (We also allow "make -type A equal to type B", but this is translated into "A <: B" and "B -<: A") - -As stated above, we always maintain the invariant that type bounds -never refer to other variables. This keeps the inference relatively -simple, avoiding the scenario of having a kind of graph where we have -to pump constraints along and reach a fixed point, but it does impose -some heuristics in the case where the user is relating two type -variables A <: B. - -Combining two variables such that variable A will forever be a subtype -of variable B is the trickiest part of the algorithm because there is -often no right choice---that is, the right choice will depend on -future constraints which we do not yet know. The problem comes about -because both A and B have bounds that can be adjusted in the future. -Let's look at some of the cases that can come up. - -Imagine, to start, the best case, where both A and B have an upper and -lower bound (that is, the bounds are not top nor bot respectively). In -that case, if we're lucky, A.ub <: B.lb, and so we know that whatever -A and B should become, they will forever have the desired subtyping -relation. We can just leave things as they are. - -### Option 1: Unify - -However, suppose that A.ub is *not* a subtype of B.lb. In -that case, we must make a decision. One option is to unify A -and B so that they are one variable whose bounds are: - - UB = GLB(A.ub, B.ub) - LB = LUB(A.lb, B.lb) - -(Note that we will have to verify that LB <: UB; if it does not, the -types are not intersecting and there is an error) In that case, A <: B -holds trivially because A==B. However, we have now lost some -flexibility, because perhaps the user intended for A and B to end up -as different types and not the same type. - -Pictorially, what this does is to take two distinct variables with -(hopefully not completely) distinct type ranges and produce one with -the intersection. - -```text - B.ub B.ub - /\ / - A.ub / \ A.ub / - / \ / \ \ / - / X \ UB - / / \ \ / \ - / / / \ / / - \ \ / / \ / - \ X / LB - \ / \ / / \ - \ / \ / / \ - A.lb B.lb A.lb B.lb -``` +The type inference is based on standard HM-type inference, but +extended in various way to accommodate subtyping, region inference, +and higher-ranked types. + +## A note on terminology + +We use the notation `?T` to refer to inference variables, also called +existential variables. + +We use the term "region" and "lifetime" interchangeably. Both refer to +the `'a` in `&'a T`. + +The term "bound region" refers to regions bound in a function +signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is +"free" if it is not bound. +## Creating an inference context -### Option 2: Relate UB/LB - -Another option is to keep A and B as distinct variables but set their -bounds in such a way that, whatever happens, we know that A <: B will hold. -This can be achieved by ensuring that A.ub <: B.lb. In practice there -are two ways to do that, depicted pictorially here: - -```text - Before Option #1 Option #2 - - B.ub B.ub B.ub - /\ / \ / \ - A.ub / \ A.ub /(B')\ A.ub /(B')\ - / \ / \ \ / / \ / / - / X \ __UB____/ UB / - / / \ \ / | | / - / / / \ / | | / - \ \ / / /(A')| | / - \ X / / LB ______LB/ - \ / \ / / / \ / (A')/ \ - \ / \ / \ / \ \ / \ - A.lb B.lb A.lb B.lb A.lb B.lb +You create and "enter" an inference context by doing something like +the following: + +```rust +tcx.infer_ctxt().enter(|infcx| { + // use the inference context `infcx` in here +}) ``` -In these diagrams, UB and LB are defined as before. As you can see, -the new ranges `A'` and `B'` are quite different from the range that -would be produced by unifying the variables. +Each inference context creates a short-lived type arena to store the +fresh types and things that it will create, as described in +[the README in the ty module][ty-readme]. This arena is created by the `enter` +function and disposed after it returns. -### What we do now +[ty-readme]: src/librustc/ty/README.md -Our current technique is to *try* (transactionally) to relate the -existing bounds of A and B, if there are any (i.e., if `UB(A) != top -&& LB(B) != bot`). If that succeeds, we're done. If it fails, then -we merge A and B into same variable. +Within the closure, the infcx will have the type `InferCtxt<'cx, 'gcx, +'tcx>` for some fresh `'cx` and `'tcx` -- the latter corresponds to +the lifetime of this temporary arena, and the `'cx` is the lifetime of +the `InferCtxt` itself. (Again, see [that ty README][ty-readme] for +more details on this setup.) -This is not clearly the correct course. For example, if `UB(A) != -top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)` -and leave the variables unmerged. This is sometimes the better -course, it depends on the program. +The `tcx.infer_ctxt` method actually returns a build, which means +there are some kinds of configuration you can do before the `infcx` is +created. See `InferCtxtBuilder` for more information. -The main case which fails today that I would like to support is: +## Inference variables -```rust -fn foo(x: T, y: T) { ... } +The main purpose of the inference context is to house a bunch of +**inference variables** -- these represent types or regions whose precise +value is not yet known, but will be uncovered as we perform type-checking. + +If you're familiar with the basic ideas of unification from H-M type +systems, or logic languages like Prolog, this is the same concept. If +you're not, you might want to read a tutorial on how H-M type +inference works, or perhaps this blog post on +[unification in the Chalk project]. -fn bar() { - let x: @mut int = @mut 3; - let y: @int = @3; - foo(x, y); -} +[Unification in the Chalk project]: http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/ + +All told, the inference context stores four kinds of inference variables as of this +writing: + +- Type variables, which come in three varieties: + - General type variables (the most common). These can be unified with any type. + - Integral type variables, which can only be unified with an integral type, and + arise from an integer literal expression like `22`. + - Float type variables, which can only be unified with a float type, and + arise from a float literal expression like `22.0`. +- Region variables, which represent lifetimes, and arise all over the dang place. + +All the type variables work in much the same way: you can create a new +type variable, and what you get is `Ty<'tcx>` representing an +unresolved type `?T`. Then later you can apply the various operations +that the inferencer supports, such as equality or subtyping, and it +will possibly **instantiate** (or **bind**) that `?T` to a specific +value as a result. + +The region variables work somewhat differently, and are described +below in a separate section. + +## Enforcing equality / subtyping + +The most basic operations you can perform in the type inferencer is +**equality**, which forces two types `T` and `U` to be the same. The +recommended way to add an equality constraint is using the `at` +method, roughly like so: + +``` +infcx.at(...).eq(t, u); ``` -In principle, the inferencer ought to find that the parameter `T` to -`foo(x, y)` is `@const int`. Today, however, it does not; this is -because the type variable `T` is merged with the type variable for -`X`, and thus inherits its UB/LB of `@mut int`. This leaves no -flexibility for `T` to later adjust to accommodate `@int`. - -Note: `@` and `@mut` are replaced with `Rc` and `Rc>` in current Rust. - -### What to do when not all bounds are present - -In the prior discussion we assumed that A.ub was not top and B.lb was -not bot. Unfortunately this is rarely the case. Often type variables -have "lopsided" bounds. For example, if a variable in the program has -been initialized but has not been used, then its corresponding type -variable will have a lower bound but no upper bound. When that -variable is then used, we would like to know its upper bound---but we -don't have one! In this case we'll do different things depending on -how the variable is being used. - -## Transactional support - -Whenever we adjust merge variables or adjust their bounds, we always -keep a record of the old value. This allows the changes to be undone. - -## Regions - -I've only talked about type variables here, but region variables -follow the same principle. They have upper- and lower-bounds. A -region A is a subregion of a region B if A being valid implies that B -is valid. This basically corresponds to the block nesting structure: -the regions for outer block scopes are superregions of those for inner -block scopes. - -## Integral and floating-point type variables - -There is a third variety of type variable that we use only for -inferring the types of unsuffixed integer literals. Integral type -variables differ from general-purpose type variables in that there's -no subtyping relationship among the various integral types, so instead -of associating each variable with an upper and lower bound, we just -use simple unification. Each integer variable is associated with at -most one integer type. Floating point types are handled similarly to -integral types. - -## GLB/LUB - -Computing the greatest-lower-bound and least-upper-bound of two -types/regions is generally straightforward except when type variables -are involved. In that case, we follow a similar "try to use the bounds -when possible but otherwise merge the variables" strategy. In other -words, `GLB(A, B)` where `A` and `B` are variables will often result -in `A` and `B` being merged and the result being `A`. - -## Type coercion - -We have a notion of assignability which differs somewhat from -subtyping; in particular it may cause region borrowing to occur. See -the big comment later in this file on Type Coercion for specifics. - -### In conclusion - -I showed you three ways to relate `A` and `B`. There are also more, -of course, though I'm not sure if there are any more sensible options. -The main point is that there are various options, each of which -produce a distinct range of types for `A` and `B`. Depending on what -the correct values for A and B are, one of these options will be the -right choice: but of course we don't know the right values for A and B -yet, that's what we're trying to find! In our code, we opt to unify -(Option #1). - -# Implementation details - -We make use of a trait-like implementation strategy to consolidate -duplicated code between subtypes, GLB, and LUB computations. See the -section on "Type Combining" in combine.rs for more details. +The first `at()` call provides a bit of context, i.e., why you are +doing this unification, and in what environment, and the `eq` method +performs the actual equality constraint. + +When you equate things, you force them to be precisely equal. Equating +returns a `InferResult` -- if it returns `Err(err)`, then equating +failed, and the enclosing `TypeError` will tell you what went wrong. + +The success case is perhaps more interesting. The "primary" return +type of `eq` is `()` -- that is, when it succeeds, it doesn't return a +value of any particular interest. Rather, it is executed for its +side-effects of constraining type variables and so forth. However, the +actual return type is not `()`, but rather `InferOk<()>`. The +`InferOk` type is used to carry extra trait obligations -- your job is +to ensure that these are fulfilled (typically by enrolling them in a +fulfillment context). See the [trait README] for more background here. + +[trait README]: ../traits/README.md + +You can also enforce subtyping through `infcx.at(..).sub(..)`. The same +basic concepts apply as above. + +## "Trying" equality + +Sometimes you would like to know if it is *possible* to equate two +types without error. You can test that with `infcx.can_eq` (or +`infcx.can_sub` for subtyping). If this returns `Ok`, then equality +is possible -- but in all cases, any side-effects are reversed. + +Be aware though that the success or failure of these methods is always +**modulo regions**. That is, two types `&'a u32` and `&'b u32` will +return `Ok` for `can_eq`, even if `'a != 'b`. This falls out from the +"two-phase" nature of how we solve region constraints. + +## Snapshots + +As described in the previous section on `can_eq`, often it is useful +to be able to do a series of operations and then roll back their +side-effects. This is done for various reasons: one of them is to be +able to backtrack, trying out multiple possibilities before settling +on which path to take. Another is in order to ensure that a series of +smaller changes take place atomically or not at all. + +To allow for this, the inference context supports a `snapshot` method. +When you call it, it will start recording changes that occur from the +operations you perform. When you are done, you can either invoke +`rollback_to`, which will undo those changes, or else `confirm`, which +will make the permanent. Snapshots can be nested as long as you follow +a stack-like discipline. + +Rather than use snapshots directly, it is often helpful to use the +methods like `commit_if_ok` or `probe` that encapsulte higher-level +patterns. + +## Subtyping obligations + +One thing worth discussing are subtyping obligations. When you force +two types to be a subtype, like `?T <: i32`, we can often convert those +into equality constraints. This follows from Rust's rather limited notion +of subtyping: so, in the above case, `?T <: i32` is equivalent to `?T = i32`. + +However, in some cases we have to be more careful. For example, when +regions are involved. So if you have `?T <: &'a i32`, what we would do +is to first "generalize" `&'a i32` into a type with a region variable: +`&'?b i32`, and then unify `?T` with that (`?T = &'?b i32`). We then +relate this new variable with the original bound: + + &'?b i32 <: &'a i32 + +This will result in a region constraint (see below) of `'?b: 'a`. + +One final interesting case is relating two unbound type variables, +like `?T <: ?U`. In that case, we can't make progress, so we enqueue +an obligation `Subtype(?T, ?U)` and return it via the `InferOk` +mechanism. You'll have to try again when more details about `?T` or +`?U` are known. + +## Region constraints + +Regions are inferred somewhat differently from types. Rather than +eagerly unifying things, we simply collect constraints as we go, but +make (almost) no attempt to solve regions. These constraints have the +form of an outlives constraint: + + 'a: 'b + +Actually the code tends to view them as a subregion relation, but it's the same +idea: + + 'b <= 'a + +(There are various other kinds of constriants, such as "verifys"; see +the `region_constraints` module for details.) + +There is one case where we do some amount of eager unification. If you have an equality constraint +between two regions + + 'a = 'b + +we will record that fact in a unification table. You can then use +`opportunistic_resolve_var` to convert `'b` to `'a` (or vice +versa). This is sometimes needed to ensure termination of fixed-point +algorithms. + +## Extracting region constraints + +Ultimately, region constraints are only solved at the very end of +type-checking, once all other constraints are known. There are two +ways to solve region constraints right now: lexical and +non-lexical. Eventually there will only be one. + +To solve **lexical** region constraints, you invoke +`resolve_regions_and_report_errors`. This will "close" the region +constraint process and invoke the `lexical_region_resolve` code. Once +this is done, any further attempt to equate or create a subtyping +relationship will yield an ICE. + +Non-lexical region constraints are not handled within the inference +context. Instead, the NLL solver (actually, the MIR type-checker) +invokes `take_and_reset_region_constraints` periodically. This +extracts all of the outlives constraints from the region solver, but +leaves the set of variables intact. This is used to get *just* the +region constraints that resulted from some particular point in the +program, since the NLL solver needs to know not just *what* regions +were subregions but *where*. Finally, the NLL solver invokes +`take_region_var_origins`, which "closes" the region constraint +process in the same way as normal solving. + +## Lexical region resolution + +Lexical region resolution is done by initially assigning each region +variable to an empty value. We then process each outlives constraint +repeatedly, growing region variables until a fixed-point is reached. +Region variables can be grown using a least-upper-bound relation on +the region lattice in a fairly straight-forward fashion. diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index f9ffaee81f15..2ae8f8ae9335 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -104,7 +104,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> a, b); let origin = Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_eqregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_eqregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs index 36370e23f216..c64bd610962e 100644 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/different_lifetimes.rs @@ -13,8 +13,8 @@ use hir; use infer::InferCtxt; use ty::{self, Region}; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +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}; diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index e9916bd77e75..d22eb20e70a8 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -57,8 +57,8 @@ use infer; use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs}; -use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict, - GenericBoundFailure, GenericKind}; +use super::region_constraints::GenericKind; +use super::lexical_region_resolve::RegionResolutionError; use std::fmt; use hir; @@ -177,13 +177,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReEarlyBound(_) | ty::ReFree(_) => { - let scope = match *region { - ty::ReEarlyBound(ref br) => { - self.parent_def_id(br.def_id).unwrap() - } - ty::ReFree(ref fr) => fr.scope, - _ => bug!() - }; + let scope = region.free_region_binding_scope(self); let prefix = match *region { ty::ReEarlyBound(ref br) => { format!("the lifetime {} as defined on", br.name) @@ -293,33 +287,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { debug!("report_region_errors: error = {:?}", error); if !self.try_report_named_anon_conflict(&error) && - !self.try_report_anon_anon_conflict(&error) { - - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - ConcreteFailure(origin, sub, sup) => { - self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); - } - - GenericBoundFailure(kind, param_ty, sub) => { - self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); - } - - SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => { + !self.try_report_anon_anon_conflict(&error) + { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + 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::SubSupConflict(var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r) => { self.report_sub_sup_conflict(region_scope_tree, var_origin, sub_origin, sub_r, sup_origin, sup_r); - } - } + } + } } } } @@ -351,9 +349,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // the only thing in the list. let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - ConcreteFailure(..) => false, - SubSupConflict(..) => false, - GenericBoundFailure(..) => true, + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) | + RegionResolutionError::SubSupConflict(..) => false, }; @@ -365,9 +363,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // sort the errors by span, for better error message stability. errors.sort_by_key(|u| match *u { - ConcreteFailure(ref sro, _, _) => sro.span(), - GenericBoundFailure(ref sro, _, _) => sro.span(), - SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), }); errors } @@ -880,14 +878,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }; if let SubregionOrigin::CompareImplMethodObligation { - span, item_name, impl_item_def_id, trait_item_def_id, lint_id + span, item_name, impl_item_def_id, trait_item_def_id, } = origin { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", bound_kind, sub), - lint_id) + &format!("`{}: {}`", bound_kind, sub)) .emit(); return; } @@ -1026,6 +1023,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let var_name = self.tcx.hir.name(var_node_id); format!(" for capture of `{}` by closure", var_name) } + infer::NLL(..) => bug!("NLL variable found in lexical phase"), }; struct_span_err!(self.tcx.sess, var_origin.span(), E0495, diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs index e0b8a193ede0..6af7415ba537 100644 --- a/src/librustc/infer/error_reporting/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs @@ -11,8 +11,8 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. use infer::InferCtxt; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use infer::lexical_region_resolve::RegionResolutionError; use ty; impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs index 1f0fd7b01d37..e46613b3e4da 100644 --- a/src/librustc/infer/error_reporting/note.rs +++ b/src/librustc/infer/error_reporting/note.rs @@ -445,14 +445,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { infer::CompareImplMethodObligation { span, item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", sup, sub), - lint_id) + &format!("`{}: {}`", sup, sub)) } } } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 9cad6ce6f9fa..756a6947ee3f 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -78,8 +78,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.type_variables.borrow_mut().types_created_since_snapshot( &snapshot.type_snapshot); let region_vars = - self.region_vars.vars_created_since_snapshot( - &snapshot.region_vars_snapshot); + self.borrow_region_constraints().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); Ok((type_variables, region_vars, value)) } diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index d7afeba7dc96..8b42314ed97c 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.glb_regions(origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 6736751a5a2c..c49b3b4b9c87 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -17,7 +17,7 @@ use super::{CombinedSnapshot, SubregionOrigin, SkolemizationMap}; use super::combine::CombineFields; -use super::region_inference::{TaintDirections}; +use super::region_constraints::{TaintDirections}; use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::error::TypeError; @@ -176,9 +176,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&r| r != representative) { let origin = SubregionOrigin::Subtype(self.trace.clone()); - self.infcx.region_vars.make_eqregion(origin, - *representative, - *region); + self.infcx.borrow_region_constraints() + .make_eqregion(origin, + *representative, + *region); } } @@ -427,7 +428,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_vars.new_bound(debruijn) + infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn) } } } @@ -481,7 +482,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions) + self.borrow_region_constraints().tainted( + self.tcx, + &snapshot.region_constraints_snapshot, + r, + directions) } fn region_vars_confined_to_snapshot(&self, @@ -539,7 +544,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { */ let mut region_vars = - self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot); + self.borrow_region_constraints().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); let escaping_types = self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot); @@ -581,7 +587,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot) + self.borrow_region_constraints() + .push_skolemized(self.tcx, br, &snapshot.region_constraints_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -766,7 +773,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot); + self.borrow_region_constraints() + .pop_skolemized(self.tcx, &skol_regions, &snapshot.region_constraints_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/region_inference/README.md b/src/librustc/infer/lexical_region_resolve/README.md similarity index 77% rename from src/librustc/infer/region_inference/README.md rename to src/librustc/infer/lexical_region_resolve/README.md index b564faf3d0c2..a90230870a6c 100644 --- a/src/librustc/infer/region_inference/README.md +++ b/src/librustc/infer/lexical_region_resolve/README.md @@ -1,10 +1,13 @@ -Region inference +# Region inference -# Terminology +## Terminology Note that we use the terms region and lifetime interchangeably. -# Introduction +## Introduction + +See the [general inference README](../README.md) for an overview of +how lexical-region-solving fits into the bigger picture. Region inference uses a somewhat more involved algorithm than type inference. It is not the most efficient thing ever written though it @@ -16,63 +19,6 @@ it's worth spending more time on a more involved analysis. Moreover, regions are a simpler case than types: they don't have aggregate structure, for example. -Unlike normal type inference, which is similar in spirit to H-M and thus -works progressively, the region type inference works by accumulating -constraints over the course of a function. Finally, at the end of -processing a function, we process and solve the constraints all at -once. - -The constraints are always of one of three possible forms: - -- `ConstrainVarSubVar(Ri, Rj)` states that region variable Ri must be - a subregion of Rj -- `ConstrainRegSubVar(R, Ri)` states that the concrete region R (which - must not be a variable) must be a subregion of the variable Ri -- `ConstrainVarSubReg(Ri, R)` states the variable Ri shoudl be less - than the concrete region R. This is kind of deprecated and ought to - be replaced with a verify (they essentially play the same role). - -In addition to constraints, we also gather up a set of "verifys" -(what, you don't think Verify is a noun? Get used to it my -friend!). These represent relations that must hold but which don't -influence inference proper. These take the form of: - -- `VerifyRegSubReg(Ri, Rj)` indicates that Ri <= Rj must hold, - where Rj is not an inference variable (and Ri may or may not contain - one). This doesn't influence inference because we will already have - inferred Ri to be as small as possible, so then we just test whether - that result was less than Rj or not. -- `VerifyGenericBound(R, Vb)` is a more complex expression which tests - that the region R must satisfy the bound `Vb`. The bounds themselves - may have structure like "must outlive one of the following regions" - or "must outlive ALL of the following regions. These bounds arise - from constraints like `T: 'a` -- if we know that `T: 'b` and `T: 'c` - (say, from where clauses), then we can conclude that `T: 'a` if `'b: - 'a` *or* `'c: 'a`. - -# Building up the constraints - -Variables and constraints are created using the following methods: - -- `new_region_var()` creates a new, unconstrained region variable; -- `make_subregion(Ri, Rj)` states that Ri is a subregion of Rj -- `lub_regions(Ri, Rj) -> Rk` returns a region Rk which is - the smallest region that is greater than both Ri and Rj -- `glb_regions(Ri, Rj) -> Rk` returns a region Rk which is - the greatest region that is smaller than both Ri and Rj - -The actual region resolution algorithm is not entirely -obvious, though it is also not overly complex. - -## Snapshotting - -It is also permitted to try (and rollback) changes to the graph. This -is done by invoking `start_snapshot()`, which returns a value. Then -later you can call `rollback_to()` which undoes the work. -Alternatively, you can call `commit()` which ends all snapshots. -Snapshots can be recursive---so you can start a snapshot when another -is in progress, but only the root snapshot can "commit". - ## The problem Basically our input is a directed graph where nodes can be divided @@ -109,9 +55,9 @@ step where we walk over the verify bounds and check that they are satisfied. These bounds represent the "maximal" values that a region variable can take on, basically. -# The Region Hierarchy +## The Region Hierarchy -## Without closures +### Without closures Let's first consider the region hierarchy without thinking about closures, because they add a lot of complications. The region @@ -141,7 +87,7 @@ Within that, there are sublifetimes for the assignment pattern and also the expression `x + y`. The expression itself has sublifetimes for evaluating `x` and `y`. -## Function calls +#s## Function calls Function calls are a bit tricky. I will describe how we handle them *now* and then a bit about how we can improve them (Issue #6268). @@ -259,7 +205,7 @@ there is a reference created whose lifetime does not enclose the borrow expression, we must issue sufficient restrictions to ensure that the pointee remains valid. -## Modeling closures +### Modeling closures Integrating closures properly into the model is a bit of work-in-progress. In an ideal world, we would model closures as @@ -314,8 +260,3 @@ handling of closures, there are no known cases where this leads to a type-checking accepting incorrect code (though it sometimes rejects what might be considered correct code; see rust-lang/rust#22557), but it still doesn't feel like the right approach. - -### Skolemization - -For a discussion on skolemization and higher-ranked subtyping, please -see the module `middle::infer::higher_ranked::doc`. diff --git a/src/librustc/infer/region_inference/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs similarity index 88% rename from src/librustc/infer/region_inference/graphviz.rs rename to src/librustc/infer/lexical_region_resolve/graphviz.rs index efe364166e4b..412094873957 100644 --- a/src/librustc/infer/region_inference/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -9,7 +9,7 @@ // except according to those terms. //! This module provides linkage between libgraphviz traits and -//! `rustc::middle::typeck::infer::region_inference`, generating a +//! `rustc::middle::typeck::infer::region_constraints`, generating a //! rendering of the graph represented by the list of `Constraint` //! instances (which make up the edges of the graph), as well as the //! origin for each constraint (which are attached to the labels on @@ -25,7 +25,7 @@ use middle::free_region::RegionRelations; use middle::region; use super::Constraint; use infer::SubregionOrigin; -use infer::region_inference::RegionVarBindings; +use infer::region_constraints::RegionConstraintData; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -57,12 +57,13 @@ graphs will be printed. \n\ } pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( - region_vars: &RegionVarBindings<'a, 'gcx, 'tcx>, + region_data: &RegionConstraintData<'tcx>, region_rels: &RegionRelations<'a, 'gcx, 'tcx>) { + let tcx = region_rels.tcx; let context = region_rels.context; - if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph { + if !tcx.sess.opts.debugging_opts.print_region_graph { return; } @@ -112,12 +113,11 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( } }; - let constraints = &*region_vars.constraints.borrow(); - match dump_region_constraints_to(region_rels, constraints, &output_path) { + match dump_region_data_to(region_rels, ®ion_data.constraints, &output_path) { Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); - region_vars.tcx.sess.err(&msg) + tcx.sess.err(&msg) } } } @@ -212,13 +212,13 @@ impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { fn constraint_to_nodes(c: &Constraint) -> (Node, Node) { match *c { - Constraint::ConstrainVarSubVar(rv_1, rv_2) => + Constraint::VarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1), Node::RegionVid(rv_2)), - Constraint::ConstrainRegSubVar(r_1, rv_2) => + Constraint::RegSubVar(r_1, rv_2) => (Node::Region(*r_1), Node::RegionVid(rv_2)), - Constraint::ConstrainVarSubReg(rv_1, r_2) => + Constraint::VarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), Node::Region(*r_2)), - Constraint::ConstrainRegSubReg(r_1, r_2) => + Constraint::RegSubReg(r_1, r_2) => (Node::Region(*r_1), Node::Region(*r_2)), } } @@ -267,15 +267,15 @@ impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { pub type ConstraintMap<'tcx> = BTreeMap, SubregionOrigin<'tcx>>; -fn dump_region_constraints_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - map: &ConstraintMap<'tcx>, - path: &str) - -> io::Result<()> { - debug!("dump_region_constraints map (len: {}) path: {}", +fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + map: &ConstraintMap<'tcx>, + path: &str) + -> io::Result<()> { + debug!("dump_region_data map (len: {}) path: {}", map.len(), path); - let g = ConstraintGraph::new(format!("region_constraints"), region_rels, map); - debug!("dump_region_constraints calling render"); + let g = ConstraintGraph::new(format!("region_data"), region_rels, map); + debug!("dump_region_data calling render"); let mut v = Vec::new(); dot::render(&g, &mut v).unwrap(); File::create(path).and_then(|mut f| f.write_all(&v)) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs new file mode 100644 index 000000000000..0692d284d7c1 --- /dev/null +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -0,0 +1,766 @@ +// Copyright 2012-2014 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. + +//! The code to do lexical region resolution. + +use infer::SubregionOrigin; +use infer::RegionVariableOrigin; +use infer::region_constraints::Constraint; +use infer::region_constraints::GenericKind; +use infer::region_constraints::RegionConstraintData; +use infer::region_constraints::VarOrigins; +use infer::region_constraints::VerifyBound; +use middle::free_region::RegionRelations; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; +use std::fmt; +use std::u32; +use ty::{self, TyCtxt}; +use ty::{Region, RegionVid}; +use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; + +mod graphviz; + +/// This function performs lexical region resolution given a complete +/// set of constraints and variable origins. It performs a fixed-point +/// iteration to find region values which satisfy all constraints, +/// assuming such values can be found. It returns the final values of +/// all the variables as well as a set of errors that must be reported. +pub fn resolve<'tcx>( + region_rels: &RegionRelations<'_, '_, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx>, +) -> ( + LexicalRegionResolutions<'tcx>, + Vec>, +) { + debug!("RegionConstraintData: resolve_regions()"); + let mut errors = vec![]; + let mut resolver = LexicalResolver { + region_rels, + var_origins, + data, + }; + let values = resolver.infer_variable_values(&mut errors); + (values, errors) +} + +/// Contains the result of lexical region resolution. Offers methods +/// to lookup up the final value of a region variable. +pub struct LexicalRegionResolutions<'tcx> { + values: IndexVec>, + error_region: ty::Region<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum VarValue<'tcx> { + Value(Region<'tcx>), + ErrorValue, +} + +#[derive(Clone, Debug)] +pub enum RegionResolutionError<'tcx> { + /// `ConcreteFailure(o, a, b)`: + /// + /// `o` requires that `a <= b`, but this does not hold + ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), + + /// `GenericBoundFailure(p, s, a) + /// + /// The parameter/associated-type `p` must be known to outlive the lifetime + /// `a` (but none of the known bounds are sufficient). + GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), + + /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: + /// + /// Could not infer a value for `v` because `sub_r <= v` (due to + /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and + /// `sub_r <= sup_r` does not hold. + SubSupConflict( + RegionVariableOrigin, + SubregionOrigin<'tcx>, + Region<'tcx>, + SubregionOrigin<'tcx>, + Region<'tcx>, + ), +} + +struct RegionAndOrigin<'tcx> { + region: Region<'tcx>, + origin: SubregionOrigin<'tcx>, +} + +type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; + +struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + region_rels: &'cx RegionRelations<'cx, 'gcx, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { + fn infer_variable_values( + &mut self, + errors: &mut Vec>, + ) -> LexicalRegionResolutions<'tcx> { + let mut var_data = self.construct_var_data(self.region_rels.tcx); + + // Dorky hack to cause `dump_constraints` to only get called + // if debug mode is enabled: + debug!( + "----() End constraint listing (context={:?}) {:?}---", + self.region_rels.context, + self.dump_constraints(self.region_rels) + ); + graphviz::maybe_print_constraints_for(&self.data, self.region_rels); + + let graph = self.construct_graph(); + self.expand_givens(&graph); + self.expansion(&mut var_data); + self.collect_errors(&mut var_data, errors); + self.collect_var_errors(&var_data, &graph, errors); + var_data + } + + fn num_vars(&self) -> usize { + self.var_origins.len() + } + + /// Initially, the value for all variables is set to `'empty`, the + /// empty region. The `expansion` phase will grow this larger. + fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: tcx.types.re_static, + values: (0..self.num_vars()) + .map(|_| VarValue::Value(tcx.types.re_empty)) + .collect(), + } + } + + fn dump_constraints(&self, free_regions: &RegionRelations<'_, '_, 'tcx>) { + debug!( + "----() Start constraint listing (context={:?}) ()----", + free_regions.context + ); + for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { + debug!("Constraint {} => {:?}", idx, constraint); + } + } + + fn expand_givens(&mut self, graph: &RegionGraph) { + // Givens are a kind of horrible hack to account for + // constraints like 'c <= '0 that are known to hold due to + // closure signatures (see the comment above on the `givens` + // field). They should go away. But until they do, the role + // of this fn is to account for the transitive nature: + // + // Given 'c <= '0 + // and '0 <= '1 + // then 'c <= '1 + + let seeds: Vec<_> = self.data.givens.iter().cloned().collect(); + for (r, vid) in seeds { + // While all things transitively reachable in the graph + // from the variable (`'0` in the example above). + let seed_index = NodeIndex(vid.index as usize); + for succ_index in graph.depth_traverse(seed_index, OUTGOING) { + let succ_index = succ_index.0; + + // The first N nodes correspond to the region + // variables. Other nodes correspond to constant + // regions. + if succ_index < self.num_vars() { + let succ_vid = RegionVid::new(succ_index); + + // Add `'c <= '1`. + self.data.givens.insert((r, succ_vid)); + } + } + } + } + + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { + self.iterate_until_fixed_point("Expansion", |constraint, origin| { + debug!("expansion: constraint={:?} origin={:?}", constraint, origin); + match *constraint { + Constraint::RegSubVar(a_region, b_vid) => { + let b_data = var_values.value_mut(b_vid); + self.expand_node(a_region, b_vid, b_data) + } + Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { + VarValue::ErrorValue => false, + VarValue::Value(a_region) => { + let b_node = var_values.value_mut(b_vid); + self.expand_node(a_region, b_vid, b_node) + } + }, + Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { + // These constraints are checked after expansion + // is done, in `collect_errors`. + false + } + } + }) + } + + fn expand_node( + &self, + a_region: Region<'tcx>, + b_vid: RegionVid, + b_data: &mut VarValue<'tcx>, + ) -> bool { + debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data); + + // Check if this relationship is implied by a given. + match *a_region { + ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid)) + { + debug!("given"); + return false; + }, + _ => {} + } + + match *b_data { + VarValue::Value(cur_region) => { + let lub = self.lub_concrete_regions(a_region, cur_region); + if lub == cur_region { + return false; + } + + debug!( + "Expanding value of {:?} from {:?} to {:?}", + b_vid, + cur_region, + lub + ); + + *b_data = VarValue::Value(lub); + return true; + } + + VarValue::ErrorValue => { + return false; + } + } + } + + + fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { + let tcx = self.region_rels.tcx; + match (a, b) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + bug!("cannot relate region: LUB({:?}, {:?})", a, b); + } + + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + (&ReEmpty, r) | (r, &ReEmpty) => { + r // everything lives longer than empty + } + + (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { + span_bug!( + self.var_origins[v_id].span(), + "lub_concrete_regions invoked with non-concrete \ + regions: {:?}, {:?}", + a, + b + ); + } + + (&ReEarlyBound(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReEarlyBound(_)) | + (&ReFree(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReFree(_)) => { + // A "free" region can be interpreted as "some region + // at least as big as fr.scope". So, we can + // reasonably compare free regions and scopes: + let fr_scope = match (a, b) { + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels + .region_scope_tree + .early_free_scope(self.region_rels.tcx, br), + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels + .region_scope_tree + .free_scope(self.region_rels.tcx, fr), + _ => bug!(), + }; + let r_id = self.region_rels + .region_scope_tree + .nearest_common_ancestor(fr_scope, s_id); + if r_id == fr_scope { + // if the free region's scope `fr.scope` is bigger than + // the scope region `s_id`, then the LUB is the free + // region itself: + match (a, b) { + (_, &ReScope(_)) => return a, + (&ReScope(_), _) => return b, + _ => bug!(), + } + } + + // otherwise, we don't know what the free region is, + // so we must conservatively say the LUB is static: + tcx.types.re_static + } + + (&ReScope(a_id), &ReScope(b_id)) => { + // The region corresponding to an outer block is a + // subtype of the region corresponding to an inner + // block. + let lub = self.region_rels + .region_scope_tree + .nearest_common_ancestor(a_id, b_id); + tcx.mk_region(ReScope(lub)) + } + + (&ReEarlyBound(_), &ReEarlyBound(_)) | + (&ReFree(_), &ReEarlyBound(_)) | + (&ReEarlyBound(_), &ReFree(_)) | + (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), + + // For these types, we cannot define any additional + // relationship: + (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { + a + } else { + tcx.types.re_static + }, + } + } + + /// After expansion is complete, go and check upper bounds (i.e., + /// cases where the region cannot grow larger than a fixed point) + /// and check that they are satisfied. + fn collect_errors( + &self, + var_data: &mut LexicalRegionResolutions<'tcx>, + errors: &mut Vec>, + ) { + for (constraint, origin) in &self.data.constraints { + debug!( + "collect_errors: constraint={:?} origin={:?}", + constraint, + origin + ); + match *constraint { + Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { + // Expansion will ensure that these constraints hold. Ignore. + } + + Constraint::RegSubReg(sub, sup) => { + if self.region_rels.is_subregion_of(sub, sup) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + origin, + sub, + sup + ); + + errors.push(RegionResolutionError::ConcreteFailure( + (*origin).clone(), + sub, + sup, + )); + } + + Constraint::VarSubReg(a_vid, b_region) => { + let a_data = var_data.value_mut(a_vid); + debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); + + let a_region = match *a_data { + VarValue::ErrorValue => continue, + VarValue::Value(a_region) => a_region, + }; + + // Do not report these errors immediately: + // instead, set the variable value to error and + // collect them later. + if !self.region_rels.is_subregion_of(a_region, b_region) { + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?}={:?} <= {:?}", + origin, + a_vid, + a_region, + b_region + ); + *a_data = VarValue::ErrorValue; + } + } + } + } + + for verify in &self.data.verifys { + debug!("collect_errors: verify={:?}", verify); + let sub = var_data.normalize(verify.region); + + // This was an inference variable which didn't get + // constrained, therefore it can be assume to hold. + if let ty::ReEmpty = *sub { + continue; + } + + if self.bound_is_met(&verify.bound, var_data, sub) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + verify.origin, + verify.region, + verify.bound + ); + + errors.push(RegionResolutionError::GenericBoundFailure( + verify.origin.clone(), + verify.kind.clone(), + sub, + )); + } + } + + /// Go over the variables that were declared to be error variables + /// and create a `RegionResolutionError` for each of them. + fn collect_var_errors( + &self, + var_data: &LexicalRegionResolutions<'tcx>, + graph: &RegionGraph<'tcx>, + errors: &mut Vec>, + ) { + debug!("collect_var_errors"); + + // This is the best way that I have found to suppress + // duplicate and related errors. Basically we keep a set of + // flags for every node. Whenever an error occurs, we will + // walk some portion of the graph looking to find pairs of + // conflicting regions to report to the user. As we walk, we + // trip the flags from false to true, and if we find that + // we've already reported an error involving any particular + // node we just stop and don't report the current error. The + // idea is to report errors that derive from independent + // regions of the graph, but not those that derive from + // overlapping locations. + let mut dup_vec = vec![u32::MAX; self.num_vars()]; + + for (node_vid, value) in var_data.values.iter_enumerated() { + match *value { + VarValue::Value(_) => { /* Inference successful */ } + VarValue::ErrorValue => { + /* Inference impossible, this value contains + inconsistent constraints. + + I think that in this case we should report an + error now---unlike the case above, we can't + wait to see whether the user needs the result + of this variable. The reason is that the mere + existence of this variable implies that the + region graph is inconsistent, whether or not it + is used. + + For example, we may have created a region + variable that is the GLB of two other regions + which do not have a GLB. Even if that variable + is not used, it implies that those two regions + *should* have a GLB. + + At least I think this is true. It may be that + the mere existence of a conflict in a region variable + that is not used is not a problem, so if this rule + starts to create problems we'll have to revisit + this portion of the code and think hard about it. =) */ + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + } + } + } + } + + fn construct_graph(&self) -> RegionGraph<'tcx> { + let num_vars = self.num_vars(); + + let mut graph = graph::Graph::new(); + + for _ in 0..num_vars { + graph.add_node(()); + } + + // Issue #30438: two distinct dummy nodes, one for incoming + // edges (dummy_source) and another for outgoing edges + // (dummy_sink). In `dummy -> a -> b -> dummy`, using one + // dummy node leads one to think (erroneously) there exists a + // path from `b` to `a`. Two dummy nodes sidesteps the issue. + let dummy_source = graph.add_node(()); + let dummy_sink = graph.add_node(()); + + for (constraint, _) in &self.data.constraints { + match *constraint { + Constraint::VarSubVar(a_id, b_id) => { + graph.add_edge( + NodeIndex(a_id.index as usize), + NodeIndex(b_id.index as usize), + *constraint, + ); + } + Constraint::RegSubVar(_, b_id) => { + graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); + } + Constraint::VarSubReg(a_id, _) => { + graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); + } + Constraint::RegSubReg(..) => { + // this would be an edge from `dummy_source` to + // `dummy_sink`; just ignore it. + } + } + } + + return graph; + } + + fn collect_error_for_expanding_node( + &self, + graph: &RegionGraph<'tcx>, + dup_vec: &mut [u32], + node_idx: RegionVid, + errors: &mut Vec>, + ) { + // Errors in expanding nodes result from a lower-bound that is + // not contained by an upper-bound. + let (mut lower_bounds, lower_dup) = + self.collect_concrete_regions(graph, node_idx, graph::INCOMING, dup_vec); + let (mut upper_bounds, upper_dup) = + self.collect_concrete_regions(graph, node_idx, graph::OUTGOING, dup_vec); + + if lower_dup || upper_dup { + return; + } + + // We place free regions first because we are special casing + // SubSupConflict(ReFree, ReFree) when reporting error, and so + // the user will more likely get a specific suggestion. + fn region_order_key(x: &RegionAndOrigin) -> u8 { + match *x.region { + ReEarlyBound(_) => 0, + ReFree(_) => 1, + _ => 2, + } + } + lower_bounds.sort_by_key(region_order_key); + upper_bounds.sort_by_key(region_order_key); + + for lower_bound in &lower_bounds { + for upper_bound in &upper_bounds { + if !self.region_rels + .is_subregion_of(lower_bound.region, upper_bound.region) + { + let origin = self.var_origins[node_idx].clone(); + debug!( + "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ + sup: {:?}", + origin, + node_idx, + lower_bound.region, + upper_bound.region + ); + errors.push(RegionResolutionError::SubSupConflict( + origin, + lower_bound.origin.clone(), + lower_bound.region, + upper_bound.origin.clone(), + upper_bound.region, + )); + return; + } + } + } + + span_bug!( + self.var_origins[node_idx].span(), + "collect_error_for_expanding_node() could not find \ + error for var {:?}, lower_bounds={:?}, \ + upper_bounds={:?}", + node_idx, + lower_bounds, + upper_bounds + ); + } + + fn collect_concrete_regions( + &self, + graph: &RegionGraph<'tcx>, + orig_node_idx: RegionVid, + dir: Direction, + dup_vec: &mut [u32], + ) -> (Vec>, bool) { + struct WalkState<'tcx> { + set: FxHashSet, + stack: Vec, + result: Vec>, + dup_found: bool, + } + let mut state = WalkState { + set: FxHashSet(), + stack: vec![orig_node_idx], + result: Vec::new(), + dup_found: false, + }; + state.set.insert(orig_node_idx); + + // to start off the process, walk the source node in the + // direction specified + process_edges(&self.data, &mut state, graph, orig_node_idx, dir); + + while !state.stack.is_empty() { + let node_idx = state.stack.pop().unwrap(); + + // check whether we've visited this node on some previous walk + if dup_vec[node_idx.index as usize] == u32::MAX { + dup_vec[node_idx.index as usize] = orig_node_idx.index; + } else if dup_vec[node_idx.index as usize] != orig_node_idx.index { + state.dup_found = true; + } + + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, + node_idx + ); + + process_edges(&self.data, &mut state, graph, node_idx, dir); + } + + let WalkState { + result, dup_found, .. + } = state; + return (result, dup_found); + + fn process_edges<'tcx>( + this: &RegionConstraintData<'tcx>, + state: &mut WalkState<'tcx>, + graph: &RegionGraph<'tcx>, + source_vid: RegionVid, + dir: Direction, + ) { + debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); + + let source_node_index = NodeIndex(source_vid.index as usize); + for (_, edge) in graph.adjacent_edges(source_node_index, dir) { + match edge.data { + Constraint::VarSubVar(from_vid, to_vid) => { + let opp_vid = if from_vid == source_vid { + to_vid + } else { + from_vid + }; + if state.set.insert(opp_vid) { + state.stack.push(opp_vid); + } + } + + Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { + state.result.push(RegionAndOrigin { + region, + origin: this.constraints.get(&edge.data).unwrap().clone(), + }); + } + + Constraint::RegSubReg(..) => panic!( + "cannot reach reg-sub-reg edge in region inference \ + post-processing" + ), + } + } + } + } + + fn iterate_until_fixed_point(&self, tag: &str, mut body: F) + where + F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool, + { + let mut iteration = 0; + let mut changed = true; + while changed { + changed = false; + iteration += 1; + debug!("---- {} Iteration {}{}", "#", tag, iteration); + for (constraint, origin) in &self.data.constraints { + let edge_changed = body(constraint, origin); + if edge_changed { + debug!("Updated due to constraint {:?}", constraint); + changed = true; + } + } + } + debug!("---- {} Complete after {} iteration(s)", tag, iteration); + } + + fn bound_is_met( + &self, + bound: &VerifyBound<'tcx>, + var_values: &LexicalRegionResolutions<'tcx>, + min: ty::Region<'tcx>, + ) -> bool { + match bound { + VerifyBound::AnyRegion(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) + .any(|r| self.region_rels.is_subregion_of(min, r)), + + VerifyBound::AllRegions(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) + .all(|r| self.region_rels.is_subregion_of(min, r)), + + VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)), + + VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)), + } + } +} + +impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) + } +} + + +impl<'tcx> LexicalRegionResolutions<'tcx> { + fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => self.resolve_var(rid), + _ => r, + } + } + + fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { + &self.values[rid] + } + + fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> { + &mut self.values[rid] + } + + pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + let result = match self.values[rid] { + VarValue::Value(r) => r, + VarValue::ErrorValue => self.error_region, + }; + debug!("resolve_var({:?}) = {:?}", rid, result); + result + } +} diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 04b470b29fc5..4a2a7a6bdfec 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.lub_regions(origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 79eeebfb2503..f734ff84f639 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -16,7 +16,6 @@ pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_inference::{GenericKind, VerifyBound}; use hir::def_id::DefId; use middle::free_region::{FreeRegionMap, RegionRelations}; @@ -31,7 +30,7 @@ use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::relate::RelateResult; use traits::{self, ObligationCause, PredicateObligations, Reveal}; use rustc_data_structures::unify::{self, UnificationTable}; -use std::cell::{Cell, RefCell, Ref}; +use std::cell::{Cell, RefCell, Ref, RefMut}; use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; @@ -41,7 +40,9 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; -use self::region_inference::{RegionVarBindings, RegionSnapshot}; +use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; +use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins}; +use self::lexical_region_resolve::LexicalRegionResolutions; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -54,13 +55,17 @@ mod glb; mod higher_ranked; pub mod lattice; mod lub; -pub mod region_inference; +pub mod region_constraints; +mod lexical_region_resolve; +mod outlives; pub mod resolve; mod freshen; mod sub; pub mod type_variable; pub mod unify_key; +pub use self::outlives::env::OutlivesEnvironment; + #[must_use] pub struct InferOk<'tcx, T> { pub value: T, @@ -98,8 +103,15 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // Map from floating variable to the kind of float it represents float_unification_table: RefCell>, - // For region variables. - region_vars: RegionVarBindings<'a, 'gcx, 'tcx>, + // Tracks the set of region variables and the constraints between + // them. This is initially `Some(_)` but when + // `resolve_regions_and_report_errors` is invoked, this gets set + // to `None` -- further attempts to perform unification etc may + // fail if new region constraints would've been added. + region_constraints: RefCell>>, + + // Once region inference is done, the values for each variable. + lexical_region_resolutions: RefCell>>, /// Caches the results of trait selection. This cache is used /// for things that have to do with the parameters in scope. @@ -135,6 +147,39 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // This flag is true while there is an active snapshot. in_snapshot: Cell, + + // A set of constraints that regionck must validate. Each + // constraint has the form `T:'a`, meaning "some type `T` must + // outlive the lifetime 'a". These constraints derive from + // instantiated type parameters. So if you had a struct defined + // like + // + // struct Foo { ... } + // + // then in some expression `let x = Foo { ... }` it will + // instantiate the type parameter `T` with a fresh type `$0`. At + // the same time, it will record a region obligation of + // `$0:'static`. This will get checked later by regionck. (We + // can't generally check these things right away because we have + // to wait until types are resolved.) + // + // These are stored in a map keyed to the id of the innermost + // enclosing fn body / static initializer expression. This is + // because the location where the obligation was incurred can be + // relevant with respect to which sublifetime assumptions are in + // place. The reason that we store under the fn-id, and not + // something more fine-grained, is so that it is easier for + // regionck to be sure that it has found *all* the region + // obligations (otherwise, it's easy to fail to walk to a + // particular node-id). + // + // Before running `resolve_regions_and_report_errors`, the creator + // of the inference context is expected to invoke + // `process_region_obligations` (defined in `self::region_obligations`) + // for each body-id in this map, which will process the + // obligations within. This is expected to be done 'late enough' + // that all type inference variables have been bound and so forth. + region_obligations: RefCell)>>, } /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized @@ -248,10 +293,6 @@ pub enum SubregionOrigin<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - - // this is `Some(_)` if this error arises from the bug fix for - // #18937. This is a temporary measure. - lint_id: Option, }, } @@ -280,7 +321,7 @@ pub enum LateBoundRegionConversionTime { /// Reasons to create a region inference variable /// /// See `error_reporting` module for more details -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RegionVariableOrigin { // Region variables created for ill-categorized reasons, // mostly indicates places in need of refactoring @@ -308,6 +349,20 @@ pub enum RegionVariableOrigin { UpvarRegion(ty::UpvarId, Span), BoundRegionInCoherence(ast::Name), + + // This origin is used for the inference variables that we create + // during NLL region processing. + NLL(NLLRegionVariableOrigin), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum NLLRegionVariableOrigin { + // During NLL region processing, we create variables for free + // regions that we encounter in the function signature and + // elsewhere. This origin indices we've got one of those. + FreeRegion, + + Inferred(::mir::visit::TyContext), } #[derive(Copy, Clone, Debug)] @@ -317,6 +372,14 @@ pub enum FixupError { UnresolvedTy(TyVid) } +/// See the `region_obligations` field for more information. +#[derive(Clone)] +pub struct RegionObligation<'tcx> { + pub sub_region: ty::Region<'tcx>, + pub sup_type: Ty<'tcx>, + pub cause: ObligationCause<'tcx>, +} + impl fmt::Display for FixupError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::FixupError::*; @@ -379,13 +442,15 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_vars: RegionVarBindings::new(tcx), + region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), + lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), reported_trait_errors: RefCell::new(FxHashMap()), tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), + region_obligations: RefCell::new(vec![]), })) } } @@ -412,7 +477,8 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { type_snapshot: type_variable::Snapshot, int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, - region_vars_snapshot: RegionSnapshot, + region_constraints_snapshot: RegionSnapshot, + region_obligations_snapshot: usize, was_in_snapshot: bool, _in_progress_tables: Option>>, } @@ -720,7 +786,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), - region_vars_snapshot: self.region_vars.start_snapshot(), + region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(), + region_obligations_snapshot: self.region_obligations.borrow().len(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -736,7 +803,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, + region_obligations_snapshot, was_in_snapshot, _in_progress_tables } = snapshot; @@ -754,8 +822,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .rollback_to(float_snapshot); - self.region_vars - .rollback_to(region_vars_snapshot); + self.region_obligations + .borrow_mut() + .truncate(region_obligations_snapshot); + self.borrow_region_constraints() + .rollback_to(region_constraints_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot) { @@ -764,7 +835,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, + region_obligations_snapshot: _, was_in_snapshot, _in_progress_tables } = snapshot; @@ -782,8 +854,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .commit(float_snapshot); - self.region_vars - .commit(region_vars_snapshot); + self.borrow_region_constraints() + .commit(region_constraints_snapshot); } /// Execute `f` and commit the bindings @@ -838,7 +910,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub: ty::Region<'tcx>, sup: ty::RegionVid) { - self.region_vars.add_given(sub, sup); + self.borrow_region_constraints().add_given(sub, sup); } pub fn can_sub(&self, @@ -878,7 +950,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>) { debug!("sub_regions({:?} <: {:?})", a, b); - self.region_vars.make_subregion(origin, a, b); + self.borrow_region_constraints().make_subregion(origin, a, b); } pub fn equality_predicate(&self, @@ -979,9 +1051,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .new_key(None) } + /// Create a fresh region variable with the next available index. + /// + /// # Parameters + /// + /// - `origin`: information about why we created this variable, for use + /// during diagnostics / error-reporting. pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { - self.tcx.mk_region(ty::ReVar(self.region_vars.new_region_var(origin))) + self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin))) + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) + -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::NLL(origin)) } /// Create a region inference variable for the given @@ -1040,10 +1124,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } - pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - self.region_vars.new_bound(debruijn) - } - /// True if errors have been reported since this infcx was /// created. This is sometimes used as a heuristic to skip /// reporting errors that often occur as a result of earlier @@ -1069,15 +1149,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tainted_by_errors_flag.set(true) } + /// Process the region constraints and report any errors that + /// 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, free_regions: &FreeRegionMap<'tcx>) { - let region_rels = RegionRelations::new(self.tcx, - region_context, - region_map, - free_regions); - let errors = self.region_vars.resolve_regions(®ion_rels); + assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), + "region_obligations not empty: {:#?}", + self.region_obligations.borrow()); + + let region_rels = &RegionRelations::new(self.tcx, + region_context, + region_map, + free_regions); + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + let (lexical_region_resolutions, errors) = + lexical_region_resolve::resolve(region_rels, var_origins, data); + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors @@ -1089,6 +1185,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + /// Obtains (and clears) the current set of region + /// constraints. The inference context is still usable: further + /// unifications will simply add new constraints. + /// + /// This method is not meant to be used with normal lexical region + /// resolution. Rather, it is used in the NLL mode as a kind of + /// interim hack: basically we run normal type-check and generate + /// region constraints as normal, but then we take them and + /// translate them into the form that the NLL solver + /// understands. See the NLL module for mode details. + pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { + self.borrow_region_constraints().take_and_reset_data() + } + + /// Takes ownership of the list of variable regions. This implies + /// that all the region constriants have already been taken, and + /// hence that `resolve_regions_and_report_errors` can never be + /// called. This is used only during NLL processing to "hand off" ownership + /// of the set of region vairables into the NLL region context. + pub fn take_region_var_origins(&self) -> VarOrigins { + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + assert!(data.is_empty()); + var_origins + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_type_vars_if_possible(&t).to_string() } @@ -1301,7 +1425,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { Ok(InferOk { value: result, obligations: combine.obligations }) } - /// See `verify_generic_bound` method in `region_inference` + /// See `verify_generic_bound` method in `region_constraints` pub fn verify_generic_bound(&self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, @@ -1312,7 +1436,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a, bound); - self.region_vars.verify_generic_bound(origin, kind, a, bound); + self.borrow_region_constraints().verify_generic_bound(origin, kind, a, bound); } pub fn type_moves_by_default(&self, @@ -1389,6 +1513,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.generator_sig(def_id) } + + /// Normalizes associated types in `value`, potentially returning + /// new obligations that must further be processed. + pub fn partially_normalize_associated_types_in(&self, + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> InferOk<'tcx, T> + where T : TypeFoldable<'tcx> + { + debug!("partially_normalize_associated_types_in(value={:?})", value); + let mut selcx = traits::SelectionContext::new(self); + let cause = ObligationCause::misc(span, body_id); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, param_env, cause, value); + debug!("partially_normalize_associated_types_in: result={:?} predicates={:?}", + value, + obligations); + InferOk { value, obligations } + } + + fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> { + RefMut::map( + self.region_constraints.borrow_mut(), + |c| c.as_mut().expect("region constraints already solved")) + } } impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { @@ -1466,14 +1617,12 @@ impl<'tcx> SubregionOrigin<'tcx> { traits::ObligationCauseCode::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => + trait_item_def_id, } => SubregionOrigin::CompareImplMethodObligation { span: cause.span, item_name, impl_item_def_id, trait_item_def_id, - lint_id, }, _ => default(), @@ -1492,7 +1641,8 @@ impl RegionVariableOrigin { EarlyBoundRegion(a, ..) => a, LateBoundRegion(a, ..) => a, BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP, - UpvarRegion(_, a) => a + UpvarRegion(_, a) => a, + NLL(..) => bug!("NLL variable used with `span`"), } } } @@ -1533,3 +1683,12 @@ impl<'tcx> TypeFoldable<'tcx> for TypeTrace<'tcx> { self.cause.visit_with(visitor) || self.values.visit_with(visitor) } } + +impl<'tcx> fmt::Debug for RegionObligation<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, + self.sup_type) + } +} + diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs new file mode 100644 index 000000000000..2099e923e095 --- /dev/null +++ b/src/librustc/infer/outlives/env.rs @@ -0,0 +1,355 @@ +// Copyright 2012-2014 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 middle::free_region::FreeRegionMap; +use infer::{InferCtxt, GenericKind}; +use traits::FulfillmentContext; +use ty::{self, Ty, TypeFoldable}; +use ty::outlives::Component; +use ty::wf; + +use syntax::ast; +use syntax_pos::Span; + +/// The `OutlivesEnvironment` collects information about what outlives +/// what in a given type-checking setting. For example, if we have a +/// where-clause like `where T: 'a` in scope, then the +/// `OutlivesEnvironment` would record that (in its +/// `region_bound_pairs` field). Similarly, it contains methods for +/// processing and adding implied bounds into the outlives +/// environment. +/// +/// Other code at present does not typically take a +/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g., +/// `process_registered_region_obligations` wants the +/// region-bound-pairs). There is no mistaking it: the current setup +/// of tracking region information is quite scattered! The +/// `OutlivesEnvironment`, for example, needs to sometimes be combined +/// with the `middle::RegionRelations`, to yield a full picture of how +/// (lexical) lifetimes interact. However, I'm reluctant to do more +/// refactoring here, since the setup with NLL is quite different. +/// For example, NLL has no need of `RegionRelations`, and is solely +/// interested in the `OutlivesEnvironment`. -nmatsakis +#[derive(Clone)] +pub struct OutlivesEnvironment<'tcx> { + param_env: ty::ParamEnv<'tcx>, + free_region_map: FreeRegionMap<'tcx>, + region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, +} + +/// Implied bounds are region relationships that we deduce +/// automatically. The idea is that (e.g.) a caller must check that a +/// function's argument types are well-formed immediately before +/// calling that fn, and hence the *callee* can assume that its +/// argument types are well-formed. This may imply certain relationships +/// between generic parameters. For example: +/// +/// fn foo<'a,T>(x: &'a T) +/// +/// can only be called with a `'a` and `T` such that `&'a T` is WF. +/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. +#[derive(Debug)] +enum ImpliedBound<'tcx> { + RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), + RegionSubParam(ty::Region<'tcx>, ty::ParamTy), + RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), +} + +impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { + pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { + let mut free_region_map = FreeRegionMap::new(); + free_region_map.relate_free_regions_from_predicates(¶m_env.caller_bounds); + + OutlivesEnvironment { + param_env, + free_region_map, + region_bound_pairs: vec![], + } + } + + /// Borrows current value of the `free_region_map`. + pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { + &self.free_region_map + } + + /// Borrows current value of the `region_bound_pairs`. + pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] { + &self.region_bound_pairs + } + + /// Returns ownership of the `free_region_map`. + pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> { + self.free_region_map + } + + /// This is a hack to support the old-skool regionck, which + /// processes region constraints from the main function and the + /// closure together. In that context, when we enter a closure, we + /// want to be able to "save" the state of the surrounding a + /// function. We can then add implied bounds and the like from the + /// closure arguments into the environment -- these should only + /// apply in the closure body, so once we exit, we invoke + /// `pop_snapshot_post_closure` to remove them. + /// + /// Example: + /// + /// ``` + /// fn foo() { + /// callback(for<'a> |x: &'a T| { + /// // ^^^^^^^ not legal syntax, but probably should be + /// // within this closure body, `T: 'a` holds + /// }) + /// } + /// ``` + /// + /// This "containment" of closure's effects only works so well. In + /// particular, we (intentionally) leak relationships between free + /// regions that are created by the closure's bounds. The case + /// where this is useful is when you have (e.g.) a closure with a + /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this + /// case, we want to keep the relationship `'b: 'a` in the + /// free-region-map, so that later if we have to take `LUB('b, + /// 'a)` we can get the result `'b`. + /// + /// I have opted to keep **all modifications** to the + /// free-region-map, however, and not just those that concern free + /// variables bound in the closure. The latter seems more correct, + /// but it is not the existing behavior, and I could not find a + /// case where the existing behavior went wrong. In any case, it + /// seems like it'd be readily fixed if we wanted. There are + /// similar leaks around givens that seem equally suspicious, to + /// be honest. --nmatsakis + pub fn push_snapshot_pre_closure(&self) -> usize { + self.region_bound_pairs.len() + } + + /// See `push_snapshot_pre_closure`. + pub fn pop_snapshot_post_closure(&mut self, len: usize) { + self.region_bound_pairs.truncate(len); + } + + /// This method adds "implied bounds" into the outlives environment. + /// Implied bounds are outlives relationships that we can deduce + /// on the basis that certain types must be well-formed -- these are + /// either the types that appear in the function signature or else + /// the input types to an impl. For example, if you have a function + /// like + /// + /// ``` + /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } + /// ``` + /// + /// we can assume in the caller's body that `'b: 'a` and that `T: + /// 'b` (and hence, transitively, that `T: 'a`). This method would + /// add those assumptions into the outlives-environment. + /// + /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + pub fn add_implied_bounds( + &mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + fn_sig_tys: &[Ty<'tcx>], + body_id: ast::NodeId, + span: Span, + ) { + debug!("add_implied_bounds()"); + + for &ty in fn_sig_tys { + let ty = infcx.resolve_type_vars_if_possible(&ty); + debug!("add_implied_bounds: ty = {}", ty); + let implied_bounds = self.implied_bounds(infcx, body_id, ty, span); + + // But also record other relationships, such as `T:'x`, + // that don't go into the free-region-map but which we use + // here. + for implication in implied_bounds { + debug!("add_implied_bounds: implication={:?}", implication); + match implication { + ImpliedBound::RegionSubRegion( + r_a @ &ty::ReEarlyBound(_), + &ty::ReVar(vid_b), + ) | + ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + infcx.add_given(r_a, vid_b); + } + ImpliedBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); + } + ImpliedBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Projection(projection_b))); + } + ImpliedBound::RegionSubRegion(r_a, r_b) => { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); + } + } + } + } + } + + /// Compute the implied bounds that a callee/impl can assume based on + /// the fact that caller/projector has ensured that `ty` is WF. See + /// the `ImpliedBound` type for more details. + fn implied_bounds( + &mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec> { + let tcx = infcx.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Currently (at least) these resulting + // predicates are always guaranteed to be a subset of the original + // type, so we need not fear non-termination. + let mut wf_types = vec![ty]; + + let mut implied_bounds = vec![]; + + let mut fulfill_cx = FulfillmentContext::new(); + + while let Some(ty) = wf_types.pop() { + // Compute the obligations for `ty` to be well-formed. If `ty` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + let obligations = + wf::obligations(infcx, self.param_env, body_id, ty, span).unwrap_or(vec![]); + + // NB: All of these predicates *ought* to be easily proven + // true. In fact, their correctness is (mostly) implied by + // other parts of the program. However, in #42552, we had + // an annoying scenario where: + // + // - Some `T::Foo` gets normalized, resulting in a + // variable `_1` and a `T: Trait` constraint + // (not sure why it couldn't immediately get + // solved). This result of `_1` got cached. + // - These obligations were dropped on the floor here, + // rather than being registered. + // - Then later we would get a request to normalize + // `T::Foo` which would result in `_1` being used from + // the cache, but hence without the `T: Trait` + // constraint. As a result, `_1` never gets resolved, + // and we get an ICE (in dropck). + // + // Therefore, we register any predicates involving + // inference variables. We restrict ourselves to those + // involving inference variables both for efficiency and + // to avoids duplicate errors that otherwise show up. + fulfill_cx.register_predicate_obligations( + infcx, + obligations + .iter() + .filter(|o| o.predicate.has_infer_types()) + .cloned()); + + // From the full set of obligations, just filter down to the + // region relationships. + implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { + assert!(!obligation.has_escaping_regions()); + match obligation.predicate { + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | + ty::Predicate::Projection(..) | + ty::Predicate::ClosureKind(..) | + ty::Predicate::ObjectSafe(..) | + ty::Predicate::ConstEvaluatable(..) => vec![], + + ty::Predicate::WellFormed(subty) => { + wf_types.push(subty); + vec![] + } + + ty::Predicate::RegionOutlives(ref data) => { + match tcx.no_late_bound_regions(data) { + None => vec![], + Some(ty::OutlivesPredicate(r_a, r_b)) => { + vec![ImpliedBound::RegionSubRegion(r_b, r_a)] + } + } + } + + ty::Predicate::TypeOutlives(ref data) => { + match tcx.no_late_bound_regions(data) { + None => vec![], + Some(ty::OutlivesPredicate(ty_a, r_b)) => { + let ty_a = infcx.resolve_type_vars_if_possible(&ty_a); + let components = tcx.outlives_components(ty_a); + self.implied_bounds_from_components(r_b, components) + } + } + } + } + })); + } + + // Ensure that those obligations that we had to solve + // get solved *here*. + match fulfill_cx.select_all_or_error(infcx) { + Ok(()) => (), + Err(errors) => infcx.report_fulfillment_errors(&errors, None), + } + + implied_bounds + } + + /// When we have an implied bound that `T: 'a`, we can further break + /// this down to determine what relationships would have to hold for + /// `T: 'a` to hold. We get to assume that the caller has validated + /// those relationships. + fn implied_bounds_from_components( + &self, + sub_region: ty::Region<'tcx>, + sup_components: Vec>, + ) -> Vec> { + sup_components + .into_iter() + .flat_map(|component| { + match component { + Component::Region(r) => + vec![ImpliedBound::RegionSubRegion(sub_region, r)], + Component::Param(p) => + vec![ImpliedBound::RegionSubParam(sub_region, p)], + Component::Projection(p) => + vec![ImpliedBound::RegionSubProjection(sub_region, p)], + Component::EscapingProjection(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + vec![], + Component::UnresolvedInferenceVariable(..) => + vec![], + } + }) + .collect() + } +} diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs new file mode 100644 index 000000000000..0976c5f1f2fc --- /dev/null +++ b/src/librustc/infer/outlives/mod.rs @@ -0,0 +1,12 @@ +// Copyright 2012-2014 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. + +pub mod env; +mod obligations; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs new file mode 100644 index 000000000000..c7081e59ec36 --- /dev/null +++ b/src/librustc/infer/outlives/obligations.rs @@ -0,0 +1,623 @@ +// Copyright 2012-2014 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. + +//! Code that handles "type-outlives" constraints like `T: 'a`. This +//! is based on the `outlives_components` function defined on the tcx, +//! but it adds a bit of heuristics on top, in particular to deal with +//! associated types and projections. +//! +//! When we process a given `T: 'a` obligation, we may produce two +//! kinds of constraints for the region inferencer: +//! +//! - Relationships between inference variables and other regions. +//! For example, if we have `&'?0 u32: 'a`, then we would produce +//! a constraint that `'a <= '?0`. +//! - "Verifys" that must be checked after inferencing is done. +//! For example, if we know that, for some type parameter `T`, +//! `T: 'a + 'b`, and we have a requirement that `T: '?1`, +//! then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`. +//! - Note the difference with the previous case: here, the region +//! variable must be less than something else, so this doesn't +//! affect how inference works (it finds the smallest region that +//! will do); it's just a post-condition that we have to check. +//! +//! **The key point is that once this function is done, we have +//! reduced all of our "type-region outlives" obligations into relationships +//! between individual regions.** +//! +//! One key input to this function is the set of "region-bound pairs". +//! These are basically the relationships between type parameters and +//! regions that are in scope at the point where the outlives +//! obligation was incurred. **When type-checking a function, +//! particularly in the face of closures, this is not known until +//! regionck runs!** This is because some of those bounds come +//! from things we have yet to infer. +//! +//! Consider: +//! +//! ``` +//! fn bar(a: T, b: impl for<'a> Fn(&'a T)); +//! fn foo(x: T) { +//! bar(x, |y| { ... }) +//! // ^ closure arg +//! } +//! ``` +//! +//! Here, the type of `y` may involve inference variables and the +//! like, and it may also contain implied bounds that are needed to +//! type-check the closure body (e.g., here it informs us that `T` +//! outlives the late-bound region `'a`). +//! +//! Note that by delaying the gathering of implied bounds until all +//! inference information is known, we may find relationships between +//! bound regions and other regions in the environment. For example, +//! when we first check a closure like the one expected as argument +//! to `foo`: +//! +//! ``` +//! fn foo FnMut(&'a U)>(_f: F) {} +//! ``` +//! +//! the type of the closure's first argument would be `&'a ?U`. We +//! might later infer `?U` to something like `&'b u32`, which would +//! imply that `'b: 'a`. + +use hir::def_id::DefId; +use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use traits; +use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::subst::{Subst, Substs}; +use ty::outlives::Component; +use syntax::ast; + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Registers that the given region obligation must be resolved + /// from within the scope of `body_id`. These regions are enqueued + /// and later processed by regionck, when full type information is + /// available (see `region_obligations` field for more + /// information). + pub fn register_region_obligation( + &self, + body_id: ast::NodeId, + obligation: RegionObligation<'tcx>, + ) { + self.region_obligations + .borrow_mut() + .push((body_id, obligation)); + } + + /// Process the region obligations that must be proven (during + /// `regionck`) for the given `body_id`, given information about + /// the region bounds in scope and so forth. This function must be + /// invoked for all relevant body-ids before region inference is + /// done (or else an assert will fire). + /// + /// See the `region_obligations` field of `InferCtxt` for some + /// comments about how this funtion fits into the overall expected + /// flow of the the inferencer. The key point is that it is + /// invoked after all type-inference variables have been bound -- + /// towards the end of regionck. This also ensures that the + /// region-bound-pairs are available (see comments above regarding + /// closures). + /// + /// # Parameters + /// + /// - `region_bound_pairs`: the set of region bounds implied by + /// the parameters and where-clauses. In particular, each pair + /// `('a, K)` in this list tells us that the bounds in scope + /// indicate that `K: 'a`, where `K` is either a generic + /// parameter like `T` or a projection like `T::Item`. + /// - `implicit_region_bound`: if some, this is a region bound + /// that is considered to hold for all type parameters (the + /// function body). + /// - `param_env` is the parameter environment for the enclosing function. + /// - `body_id` is the body-id whose region obligations are being + /// processed. + /// + /// # Returns + /// + /// This function may have to perform normalizations, and hence it + /// returns an `InferOk` with subobligations that must be + /// processed. + pub fn process_registered_region_obligations( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ) { + assert!( + !self.in_snapshot.get(), + "cannot process registered region obligations in a snapshot" + ); + + // pull out the region obligations with the given `body_id` (leaving the rest) + let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); + { + let mut r_o = self.region_obligations.borrow_mut(); + for (_, obligation) in r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id) { + my_region_obligations.push(obligation); + } + } + + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + + for RegionObligation { + sup_type, + sub_region, + cause, + } in my_region_obligations + { + let origin = SubregionOrigin::from_obligation_cause( + &cause, + || infer::RelateParamBound(cause.span, sup_type), + ); + + outlives.type_must_outlive(origin, sup_type, sub_region); + } + } + + /// Processes a single ad-hoc region obligation that was not + /// registered in advance. + pub fn type_must_outlive( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); + outlives.type_must_outlive(origin, ty, region); + } + + /// Ignore the region obligations, not bothering to prove + /// them. This function should not really exist; it is used to + /// accommodate some older code for the time being. + pub fn ignore_region_obligations(&self) { + assert!( + !self.in_snapshot.get(), + "cannot ignore registered region obligations in a snapshot" + ); + + self.region_obligations.borrow_mut().clear(); + } +} + +#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =) +struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + // See the comments on `process_registered_region_obligations` for the meaning + // of these fields. + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { + fn new( + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + Self { + infcx, + region_bound_pairs, + implicit_region_bound, + param_env, + } + } + + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + fn type_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let ty = self.infcx.resolve_type_vars_if_possible(&ty); + + debug!( + "type_must_outlive(ty={:?}, region={:?}, origin={:?})", + ty, + region, + origin + ); + + assert!(!ty.has_escaping_regions()); + + let components = self.tcx().outlives_components(ty); + self.components_must_outlive(origin, components, region); + } + + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn components_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + components: Vec>, + region: ty::Region<'tcx>, + ) { + for component in components { + let origin = origin.clone(); + match component { + Component::Region(region1) => { + self.infcx.sub_regions(origin, region, region1); + } + Component::Param(param_ty) => { + self.param_ty_must_outlive(origin, region, param_ty); + } + Component::Projection(projection_ty) => { + self.projection_must_outlive(origin, region, projection_ty); + } + Component::EscapingProjection(subcomponents) => { + self.components_must_outlive(origin, subcomponents, region); + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.infcx.tcx.sess.delay_span_bug( + origin.span(), + &format!("unresolved inference variable in outlives: {:?}", v), + ); + } + } + } + } + + fn param_ty_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + param_ty: ty::ParamTy, + ) { + debug!( + "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", + region, + param_ty, + origin + ); + + let verify_bound = self.param_bound(param_ty); + let generic = GenericKind::Param(param_ty); + self.infcx + .verify_generic_bound(origin, generic, region, verify_bound); + } + + fn projection_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + debug!( + "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", + region, + projection_ty, + origin + ); + + // This case is thorny for inference. The fundamental problem is + // that there are many cases where we have choice, and inference + // doesn't like choice (the current region inference in + // particular). :) First off, we have to choose between using the + // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and + // OutlivesProjectionComponent rules, any one of which is + // sufficient. If there are no inference variables involved, it's + // not hard to pick the right rule, but if there are, we're in a + // bit of a catch 22: if we picked which rule we were going to + // use, we could add constraints to the region inference graph + // that make it apply, but if we don't add those constraints, the + // rule might not apply (but another rule might). For now, we err + // on the side of adding too few edges into the graph. + + // Compute the bounds we can derive from the environment or trait + // definition. We know that the projection outlives all the + // regions in this list. + let env_bounds = self.projection_declared_bounds(projection_ty); + + debug!("projection_must_outlive: env_bounds={:?}", env_bounds); + + // If we know that the projection outlives 'static, then we're + // done here. + if env_bounds.contains(&&ty::ReStatic) { + debug!("projection_must_outlive: 'static as declared bound"); + return; + } + + // If declared bounds list is empty, the only applicable rule is + // OutlivesProjectionComponent. If there are inference variables, + // then, we can break down the outlives into more primitive + // components without adding unnecessary edges. + // + // If there are *no* inference variables, however, we COULD do + // this, but we choose not to, because the error messages are less + // good. For example, a requirement like `T::Item: 'r` would be + // translated to a requirement that `T: 'r`; when this is reported + // to the user, it will thus say "T: 'r must hold so that T::Item: + // 'r holds". But that makes it sound like the only way to fix + // the problem is to add `T: 'r`, which isn't true. So, if there are no + // inference variables, we use a verify constraint instead of adding + // edges, which winds up enforcing the same condition. + let needs_infer = projection_ty.needs_infer(); + if env_bounds.is_empty() && needs_infer { + debug!("projection_must_outlive: no declared bounds"); + + for component_ty in projection_ty.substs.types() { + self.type_must_outlive(origin.clone(), component_ty, region); + } + + for r in projection_ty.substs.regions() { + self.infcx.sub_regions(origin.clone(), region, r); + } + + return; + } + + // If we find that there is a unique declared bound `'b`, and this bound + // appears in the trait reference, then the best action is to require that `'b:'r`, + // so do that. This is best no matter what rule we use: + // + // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to + // the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` in addition to + // other conditions + if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { + let unique_bound = env_bounds[0]; + debug!( + "projection_must_outlive: unique declared bound = {:?}", + unique_bound + ); + if projection_ty + .substs + .regions() + .any(|r| env_bounds.contains(&r)) + { + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.infcx.sub_regions(origin.clone(), region, unique_bound); + return; + } + } + + // Fallback to verifying after the fact that there exists a + // declared bound, or that all the components appearing in the + // projection outlive; in some cases, this may add insufficient + // edges into the inference graph, leading to inference failures + // even though a satisfactory solution exists. + let verify_bound = self.projection_bound(env_bounds, projection_ty); + let generic = GenericKind::Projection(projection_ty); + self.infcx + .verify_generic_bound(origin, generic.clone(), region, verify_bound); + } + + fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + match ty.sty { + ty::TyParam(p) => self.param_bound(p), + ty::TyProjection(data) => { + let declared_bounds = self.projection_declared_bounds(data); + self.projection_bound(declared_bounds, data) + } + _ => self.recursive_type_bound(ty), + } + } + + fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + param_bounds.extend(self.implicit_region_bound); + + VerifyBound::AnyRegion(param_bounds) + } + + fn projection_declared_bounds( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + + declared_bounds + .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); + + declared_bounds + } + + fn projection_bound( + &self, + declared_bounds: Vec>, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> VerifyBound<'tcx> { + debug!( + "projection_bound(declared_bounds={:?}, projection_ty={:?})", + declared_bounds, + projection_ty + ); + + // see the extensive comment in projection_must_outlive + let ty = self.infcx + .tcx + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_type_bound(ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + } + + fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = vec![]; + + for subty in ty.walk_shallow() { + bounds.push(self.type_bound(subty)); + } + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(regions)); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } + } + + fn declared_generic_bounds_from_env( + &self, + generic: GenericKind<'tcx>, + ) -> Vec> { + let tcx = self.tcx(); + + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. Comparing using `==` is a bit + // dubious for projections, but it will work for simple cases + // like `T` and `T::Item`. It may not work as well for things + // like `>::Item`. + let generic_ty = generic.to_ty(tcx); + let c_b = self.param_env.caller_bounds; + let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-generic by `'a`, but we + // don't know that this holds from first principles. + for &(r, p) in self.region_bound_pairs { + debug!("generic={:?} p={:?}", generic, p); + if generic == p { + param_bounds.push(r); + } + } + + param_bounds + } + + /// Given a projection like `>::Bar`, returns any bounds + /// declared in the trait definition. For example, if the trait were + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// then this function would return `'x`. This is subject to the + /// limitations around higher-ranked bounds described in + /// `region_bounds_declared_on_associated_item`. + fn declared_projection_bounds_from_trait( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); + for r in &mut bounds { + *r = r.subst(self.tcx(), projection_ty.substs); + } + bounds + } + + /// Given the def-id of an associated item, returns any region + /// bounds attached to that associated item from the trait definition. + /// + /// For example: + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// If we were given the def-id of `Foo::Bar`, we would return + /// `'a`. You could then apply the substitutions from the + /// projection to convert this into your namespace. This also + /// works if the user writes `where >::Bar: 'a` on + /// the trait. In fact, it works by searching for just such a + /// where-clause. + /// + /// It will not, however, work for higher-ranked bounds like: + /// + /// ```rust + /// trait Foo<'a, 'b> + /// where for<'x> >::Bar: 'x + /// { + /// type Bar; + /// } + /// ``` + /// + /// This is for simplicity, and because we are not really smart + /// enough to cope with such bounds anywhere. + fn region_bounds_declared_on_associated_item( + &self, + assoc_item_def_id: DefId, + ) -> Vec> { + let tcx = self.tcx(); + let assoc_item = tcx.associated_item(assoc_item_def_id); + let trait_def_id = assoc_item.container.assert_trait(); + let trait_predicates = tcx.predicates_of(trait_def_id); + let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); + let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); + self.collect_outlives_from_predicate_list( + identity_proj, + traits::elaborate_predicates(tcx, trait_predicates.predicates), + ) + } + + /// Searches through a predicate list for a predicate `T: 'a`. + /// + /// Careful: does not elaborate predicates, and just uses `==` + /// when comparing `ty` for equality, so `ty` must be something + /// that does not involve inference variables and where you + /// otherwise want a precise match. + fn collect_outlives_from_predicate_list( + &self, + ty: Ty<'tcx>, + predicates: I, + ) -> Vec> + where + I: IntoIterator, + P: AsRef>, + { + predicates + .into_iter() + .filter_map(|p| p.as_ref().to_opt_type_outlives()) + .filter_map(|p| self.tcx().no_late_bound_regions(&p)) + .filter(|p| p.0 == ty) + .map(|p| p.1) + .collect() + } +} diff --git a/src/librustc/infer/region_constraints/README.md b/src/librustc/infer/region_constraints/README.md new file mode 100644 index 000000000000..67ad08c75303 --- /dev/null +++ b/src/librustc/infer/region_constraints/README.md @@ -0,0 +1,70 @@ +# Region constraint collection + +## Terminology + +Note that we use the terms region and lifetime interchangeably. + +## Introduction + +As described in the [inference README](../README.md), and unlike +normal type inference, which is similar in spirit to H-M and thus +works progressively, the region type inference works by accumulating +constraints over the course of a function. Finally, at the end of +processing a function, we process and solve the constraints all at +once. + +The constraints are always of one of three possible forms: + +- `ConstrainVarSubVar(Ri, Rj)` states that region variable Ri must be + a subregion of Rj +- `ConstrainRegSubVar(R, Ri)` states that the concrete region R (which + must not be a variable) must be a subregion of the variable Ri +- `ConstrainVarSubReg(Ri, R)` states the variable Ri shoudl be less + than the concrete region R. This is kind of deprecated and ought to + be replaced with a verify (they essentially play the same role). + +In addition to constraints, we also gather up a set of "verifys" +(what, you don't think Verify is a noun? Get used to it my +friend!). These represent relations that must hold but which don't +influence inference proper. These take the form of: + +- `VerifyRegSubReg(Ri, Rj)` indicates that Ri <= Rj must hold, + where Rj is not an inference variable (and Ri may or may not contain + one). This doesn't influence inference because we will already have + inferred Ri to be as small as possible, so then we just test whether + that result was less than Rj or not. +- `VerifyGenericBound(R, Vb)` is a more complex expression which tests + that the region R must satisfy the bound `Vb`. The bounds themselves + may have structure like "must outlive one of the following regions" + or "must outlive ALL of the following regions. These bounds arise + from constraints like `T: 'a` -- if we know that `T: 'b` and `T: 'c` + (say, from where clauses), then we can conclude that `T: 'a` if `'b: + 'a` *or* `'c: 'a`. + +## Building up the constraints + +Variables and constraints are created using the following methods: + +- `new_region_var()` creates a new, unconstrained region variable; +- `make_subregion(Ri, Rj)` states that Ri is a subregion of Rj +- `lub_regions(Ri, Rj) -> Rk` returns a region Rk which is + the smallest region that is greater than both Ri and Rj +- `glb_regions(Ri, Rj) -> Rk` returns a region Rk which is + the greatest region that is smaller than both Ri and Rj + +The actual region resolution algorithm is not entirely +obvious, though it is also not overly complex. + +## Snapshotting + +It is also permitted to try (and rollback) changes to the graph. This +is done by invoking `start_snapshot()`, which returns a value. Then +later you can call `rollback_to()` which undoes the work. +Alternatively, you can call `commit()` which ends all snapshots. +Snapshots can be recursive---so you can start a snapshot when another +is in progress, but only the root snapshot can "commit". + +## Skolemization + +For a discussion on skolemization and higher-ranked subtyping, please +see the module `middle::infer::higher_ranked::doc`. diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs new file mode 100644 index 000000000000..096037ebe880 --- /dev/null +++ b/src/librustc/infer/region_constraints/mod.rs @@ -0,0 +1,956 @@ +// Copyright 2012-2014 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. + +//! See README.md + +use self::UndoLogEntry::*; +use self::CombineMapType::*; + +use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; +use super::unify_key; + +use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unify::{self, UnificationTable}; +use ty::{self, Ty, TyCtxt}; +use ty::{Region, RegionVid}; +use ty::ReStatic; +use ty::{BrFresh, ReLateBound, ReSkolemized, ReVar}; + +use std::collections::BTreeMap; +use std::fmt; +use std::mem; +use std::u32; + +mod taint; + +pub struct RegionConstraintCollector<'tcx> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + var_origins: IndexVec, + + data: RegionConstraintData<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'tcx>, + + /// Number of skolemized variables currently active. + skolemization_count: u32, + + /// Global counter used during the GLB algorithm to create unique + /// names for fresh bound regions + bound_count: u32, + + /// The undo log records actions that might later be undone. + /// + /// Note: when the undo_log is empty, we are not actively + /// snapshotting. When the `start_snapshot()` method is called, we + /// push an OpenSnapshot entry onto the list to indicate that we + /// are now actively snapshotting. The reason for this is that + /// otherwise we end up adding entries for things like the lower + /// bound on a variable and so forth, which can never be rolled + /// back. + undo_log: Vec>, + + /// When we add a R1 == R2 constriant, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when dropck and other such code + /// is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that + /// have been equated but appear distinct. + unification_table: UnificationTable, +} + +pub type VarOrigins = IndexVec; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Default)] +pub struct RegionConstraintData<'tcx> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: BTreeMap, SubregionOrigin<'tcx>>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec>, + + /// A "given" is a relationship that is known to hold. In + /// particular, we often know from closure fn signatures that a + /// particular free region must be a subregion of a region + /// variable: + /// + /// foo.iter().filter(<'a> |x: &'a &'b T| ...) + /// + /// In situations like this, `'b` is in fact a region variable + /// introduced by the call to `iter()`, and `'a` is a bound region + /// on the closure (as indicated by the `<'a>` prefix). If we are + /// naive, we wind up inferring that `'b` must be `'static`, + /// because we require that it be greater than `'a` and we do not + /// know what `'a` is precisely. + /// + /// This hashmap is used to avoid that naive scenario. Basically + /// we record the fact that `'a <= 'b` is implied by the fn + /// signature, and then ignore the constraint when solving + /// equations. This is a bit of a hack but seems to work. + pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, +} + +/// A constraint that influences the inference process. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +pub enum Constraint<'tcx> { + /// One region variable is subregion of another + VarSubVar(RegionVid, RegionVid), + + /// Concrete region is subregion of region variable + RegSubVar(Region<'tcx>, RegionVid), + + /// Region variable is subregion of concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'tcx>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'tcx>, Region<'tcx>), +} + +/// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or +/// associated type) must outlive the region `R`. `T` is known to +/// outlive `RS`. Therefore verify that `R <= RS[i]` for some +/// `i`. Inference variables may be involved (but this verification +/// step doesn't influence inference). +#[derive(Debug)] +pub struct Verify<'tcx> { + pub kind: GenericKind<'tcx>, + pub origin: SubregionOrigin<'tcx>, + pub region: Region<'tcx>, + pub bound: VerifyBound<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum GenericKind<'tcx> { + Param(ty::ParamTy), + Projection(ty::ProjectionTy<'tcx>), +} + +/// When we introduce a verification step, we wish to test that a +/// particular region (let's call it `'min`) meets some bound. +/// The bound is described the by the following grammar: +#[derive(Debug)] +pub enum VerifyBound<'tcx> { + /// B = exists {R} --> some 'r in {R} must outlive 'min + /// + /// Put another way, the subject value is known to outlive all + /// regions in {R}, so if any of those outlives 'min, then the + /// bound is met. + AnyRegion(Vec>), + + /// B = forall {R} --> all 'r in {R} must outlive 'min + /// + /// Put another way, the subject value is known to outlive some + /// region in {R}, so if all of those outlives 'min, then the bound + /// is met. + AllRegions(Vec>), + + /// B = exists {B} --> 'min must meet some bound b in {B} + AnyBound(Vec>), + + /// B = forall {B} --> 'min must meet all bounds b in {B} + AllBounds(Vec>), +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct TwoRegions<'tcx> { + a: Region<'tcx>, + b: Region<'tcx>, +} + +#[derive(Copy, Clone, PartialEq)] +enum UndoLogEntry<'tcx> { + /// Pushed when we start a snapshot. + OpenSnapshot, + + /// Replaces an `OpenSnapshot` when a snapshot is committed, but + /// that snapshot is not the root. If the root snapshot is + /// unrolled, all nested snapshots must be committed. + CommitedSnapshot, + + /// We added `RegionVid` + AddVar(RegionVid), + + /// We added the given `constraint` + AddConstraint(Constraint<'tcx>), + + /// We added the given `verify` + AddVerify(usize), + + /// We added the given `given` + AddGiven(Region<'tcx>, ty::RegionVid), + + /// We added a GLB/LUB "combination variable" + AddCombination(CombineMapType, TwoRegions<'tcx>), + + /// During skolemization, we sometimes purge entries from the undo + /// log in a kind of minisnapshot (unlike other snapshots, this + /// purging actually takes place *on success*). In that case, we + /// replace the corresponding entry with `Noop` so as to avoid the + /// need to do a bunch of swapping. (We can't use `swap_remove` as + /// the order of the vector is important.) + Purged, +} + +#[derive(Copy, Clone, PartialEq)] +enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'tcx> = FxHashMap, RegionVid>; + +pub struct RegionSnapshot { + length: usize, + region_snapshot: unify::Snapshot, + skolemization_count: u32, +} + +/// When working with skolemized regions, we often wish to find all of +/// the regions that are either reachable from a skolemized region, or +/// which can reach a skolemized region, or both. We call such regions +/// *tained* regions. This struct allows you to decide what set of +/// tainted regions you want. +#[derive(Debug)] +pub struct TaintDirections { + incoming: bool, + outgoing: bool, +} + +impl TaintDirections { + pub fn incoming() -> Self { + TaintDirections { + incoming: true, + outgoing: false, + } + } + + pub fn outgoing() -> Self { + TaintDirections { + incoming: false, + outgoing: true, + } + } + + pub fn both() -> Self { + TaintDirections { + incoming: true, + outgoing: true, + } + } +} + +impl<'tcx> RegionConstraintCollector<'tcx> { + pub fn new() -> RegionConstraintCollector<'tcx> { + RegionConstraintCollector { + var_origins: VarOrigins::default(), + data: RegionConstraintData::default(), + lubs: FxHashMap(), + glbs: FxHashMap(), + skolemization_count: 0, + bound_count: 0, + undo_log: Vec::new(), + unification_table: UnificationTable::new(), + } + } + + pub fn var_origins(&self) -> &VarOrigins { + &self.var_origins + } + + /// Once all the constraints have been gathered, extract out the final data. + /// + /// Not legal during a snapshot. + pub fn into_origins_and_data(self) -> (VarOrigins, RegionConstraintData<'tcx>) { + assert!(!self.in_snapshot()); + (self.var_origins, self.data) + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + assert!(!self.in_snapshot()); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintCollector { + var_origins, + data, + lubs, + glbs, + skolemization_count, + bound_count: _, + undo_log: _, + unification_table, + } = self; + + assert_eq!(*skolemization_count, 0); + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + *unification_table = UnificationTable::new(); + for vid in var_origins.indices() { + unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); + } + + mem::replace(data, RegionConstraintData::default()) + } + + fn in_snapshot(&self) -> bool { + !self.undo_log.is_empty() + } + + pub fn start_snapshot(&mut self) -> RegionSnapshot { + let length = self.undo_log.len(); + debug!("RegionConstraintCollector: start_snapshot({})", length); + self.undo_log.push(OpenSnapshot); + RegionSnapshot { + length, + region_snapshot: self.unification_table.snapshot(), + skolemization_count: self.skolemization_count, + } + } + + pub fn commit(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: commit({})", snapshot.length); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + assert!( + self.skolemization_count == snapshot.skolemization_count, + "failed to pop skolemized regions: {} now vs {} at start", + self.skolemization_count, + snapshot.skolemization_count + ); + + if snapshot.length == 0 { + self.undo_log.truncate(0); + } else { + (*self.undo_log)[snapshot.length] = CommitedSnapshot; + } + self.unification_table.commit(snapshot.region_snapshot); + } + + pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + while self.undo_log.len() > snapshot.length + 1 { + let undo_entry = self.undo_log.pop().unwrap(); + self.rollback_undo_entry(undo_entry); + } + let c = self.undo_log.pop().unwrap(); + assert!(c == OpenSnapshot); + self.skolemization_count = snapshot.skolemization_count; + self.unification_table.rollback_to(snapshot.region_snapshot); + } + + fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { + match undo_entry { + OpenSnapshot => { + panic!("Failure to observe stack discipline"); + } + Purged | CommitedSnapshot => { + // nothing to do here + } + AddVar(vid) => { + self.var_origins.pop().unwrap(); + assert_eq!(self.var_origins.len(), vid.index as usize); + } + AddConstraint(ref constraint) => { + self.data.constraints.remove(constraint); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddGiven(sub, sup) => { + self.data.givens.remove(&(sub, sup)); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } + + pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { + let vid = self.var_origins.push(origin.clone()); + + let u_vid = self.unification_table + .new_key(unify_key::RegionVidKey { min_vid: vid }); + assert_eq!(vid, u_vid); + if self.in_snapshot() { + self.undo_log.push(AddVar(vid)); + } + debug!( + "created new region variable {:?} with origin {:?}", + vid, + origin + ); + return vid; + } + + /// Returns the origin for the given variable. + pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { + self.var_origins[vid].clone() + } + + /// Creates a new skolemized region. Skolemized regions are fresh + /// regions used when performing higher-ranked computations. They + /// must be used in a very particular way and are never supposed + /// to "escape" out into error messages or the code at large. + /// + /// The idea is to always create a snapshot. Skolemized regions + /// can be created in the context of this snapshot, but before the + /// snapshot is committed or rolled back, they must be popped + /// (using `pop_skolemized_regions`), so that their numbers can be + /// recycled. Normally you don't have to think about this: you use + /// the APIs in `higher_ranked/mod.rs`, such as + /// `skolemize_late_bound_regions` and `plug_leaks`, which will + /// guide you on this path (ensure that the `SkolemizationMap` is + /// consumed and you are good). There are also somewhat extensive + /// comments in `higher_ranked/README.md`. + /// + /// The `snapshot` argument to this function is not really used; + /// it's just there to make it explicit which snapshot bounds the + /// skolemized region that results. It should always be the top-most snapshot. + pub fn push_skolemized( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + br: ty::BoundRegion, + snapshot: &RegionSnapshot, + ) -> Region<'tcx> { + assert!(self.in_snapshot()); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + + let sc = self.skolemization_count; + self.skolemization_count = sc + 1; + tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) + } + + /// Removes all the edges to/from the skolemized regions that are + /// in `skols`. This is used after a higher-ranked operation + /// completes to remove all trace of the skolemized regions + /// created in that time. + pub fn pop_skolemized( + &mut self, + _tcx: TyCtxt<'_, '_, 'tcx>, + skols: &FxHashSet>, + snapshot: &RegionSnapshot, + ) { + debug!("pop_skolemized_regions(skols={:?})", skols); + + assert!(self.in_snapshot()); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + assert!( + self.skolemization_count as usize >= skols.len(), + "popping more skolemized variables than actually exist, \ + sc now = {}, skols.len = {}", + self.skolemization_count, + skols.len() + ); + + let last_to_pop = self.skolemization_count; + let first_to_pop = last_to_pop - (skols.len() as u32); + + assert!( + first_to_pop >= snapshot.skolemization_count, + "popping more regions than snapshot contains, \ + sc now = {}, sc then = {}, skols.len = {}", + self.skolemization_count, + snapshot.skolemization_count, + skols.len() + ); + debug_assert! { + skols.iter() + .all(|&k| match *k { + ty::ReSkolemized(index, _) => + index.index >= first_to_pop && + index.index < last_to_pop, + _ => + false + }), + "invalid skolemization keys or keys out of range ({}..{}): {:?}", + snapshot.skolemization_count, + self.skolemization_count, + skols + } + + let constraints_to_kill: Vec = self.undo_log + .iter() + .enumerate() + .rev() + .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) + .map(|(index, _)| index) + .collect(); + + for index in constraints_to_kill { + let undo_entry = mem::replace(&mut self.undo_log[index], Purged); + self.rollback_undo_entry(undo_entry); + } + + self.skolemization_count = snapshot.skolemization_count; + return; + + fn kill_constraint<'tcx>( + skols: &FxHashSet>, + undo_entry: &UndoLogEntry<'tcx>, + ) -> bool { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(..)) => false, + &AddConstraint(Constraint::RegSubVar(a, _)) => skols.contains(&a), + &AddConstraint(Constraint::VarSubReg(_, b)) => skols.contains(&b), + &AddConstraint(Constraint::RegSubReg(a, b)) => { + skols.contains(&a) || skols.contains(&b) + } + &AddGiven(..) => false, + &AddVerify(_) => false, + &AddCombination(_, ref two_regions) => { + skols.contains(&two_regions.a) || skols.contains(&two_regions.b) + } + &AddVar(..) | &OpenSnapshot | &Purged | &CommitedSnapshot => false, + } + } + } + + pub fn new_bound( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + debruijn: ty::DebruijnIndex, + ) -> Region<'tcx> { + // Creates a fresh bound variable for use in GLB computations. + // See discussion of GLB computation in the large comment at + // the top of this file for more details. + // + // This computation is potentially wrong in the face of + // rollover. It's conceivable, if unlikely, that one might + // wind up with accidental capture for nested functions in + // that case, if the outer function had bound regions created + // a very long time before and the inner function somehow + // wound up rolling over such that supposedly fresh + // identifiers were in fact shadowed. For now, we just assert + // that there is no rollover -- eventually we should try to be + // robust against this possibility, either by checking the set + // of bound identifiers that appear in a given expression and + // ensure that we generate one that is distinct, or by + // changing the representation of bound regions in a fn + // declaration + + let sc = self.bound_count; + self.bound_count = sc + 1; + + if sc >= self.bound_count { + bug!("rollover in RegionInference new_bound()"); + } + + tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) + } + + fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { + // cannot add constraints once regions are resolved + debug!( + "RegionConstraintCollector: add_constraint({:?})", + constraint + ); + + // never overwrite an existing (constraint, origin) - only insert one if it isn't + // present in the map yet. This prevents origins from outside the snapshot being + // replaced with "less informative" origins e.g. during calls to `can_eq` + let in_snapshot = self.in_snapshot(); + let undo_log = &mut self.undo_log; + self.data.constraints.entry(constraint).or_insert_with(|| { + if in_snapshot { + undo_log.push(AddConstraint(constraint)); + } + origin + }); + } + + fn add_verify(&mut self, verify: Verify<'tcx>) { + // cannot add verifys once regions are resolved + debug!("RegionConstraintCollector: add_verify({:?})", verify); + + // skip no-op cases known to be satisfied + match verify.bound { + VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { + return; + } + _ => {} + } + + let index = self.data.verifys.len(); + self.data.verifys.push(verify); + if self.in_snapshot() { + self.undo_log.push(AddVerify(index)); + } + } + + pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { + // cannot add givens once regions are resolved + if self.data.givens.insert((sub, sup)) { + debug!("add_given({:?} <= {:?})", sub, sup); + + if self.in_snapshot() { + self.undo_log.push(AddGiven(sub, sup)); + } + } + } + + pub fn make_eqregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + if sub != sup { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(origin.clone(), sub, sup); + self.make_subregion(origin, sup, sub); + + if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { + self.unification_table.union(sub, sup); + } + } + } + + pub fn make_subregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + // cannot add constraints once regions are resolved + debug!( + "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}", + sub, + sup, + origin + ); + + match (sub, sup) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) => { + span_bug!( + origin.span(), + "cannot relate bound region: {:?} <= {:?}", + sub, + sup + ); + } + (_, &ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (&ReVar(sub_id), &ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin); + } + (_, &ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin); + } + (&ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup), origin); + } + } + } + + /// See `Verify::VerifyGenericBound` + pub fn verify_generic_bound( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + sub: Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.add_verify(Verify { + kind, + origin, + region: sub, + bound, + }); + } + + pub fn lub_regions( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); + match (a, b) { + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + _ if a == b => { + a // LUB(a,a) = a + } + + _ => self.combine_vars(tcx, Lub, a, b, origin.clone()), + } + } + + pub fn glb_regions( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); + match (a, b) { + (&ReStatic, r) | (r, &ReStatic) => { + r // static lives longer than everything else + } + + _ if a == b => { + a // GLB(a,a) = a + } + + _ => self.combine_vars(tcx, Glb, a, b, origin.clone()), + } + } + + pub fn opportunistic_resolve_var( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + rid: RegionVid, + ) -> ty::Region<'tcx> { + let vid = self.unification_table.find_value(rid).min_vid; + tcx.mk_region(ty::ReVar(vid)) + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> { + match t { + Glb => &mut self.glbs, + Lub => &mut self.lubs, + } + } + + fn combine_vars( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + t: CombineMapType, + a: Region<'tcx>, + b: Region<'tcx>, + origin: SubregionOrigin<'tcx>, + ) -> Region<'tcx> { + let vars = TwoRegions { a: a, b: b }; + if let Some(&c) = self.combine_map(t).get(&vars) { + return tcx.mk_region(ReVar(c)); + } + let c = self.new_region_var(MiscVariable(origin.span())); + self.combine_map(t).insert(vars, c); + if self.in_snapshot() { + self.undo_log.push(AddCombination(t, vars)); + } + let new_r = tcx.mk_region(ReVar(c)); + for &old_r in &[a, b] { + match t { + Glb => self.make_subregion(origin.clone(), new_r, old_r), + Lub => self.make_subregion(origin.clone(), old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { + self.undo_log[mark.length..] + .iter() + .filter_map(|&elt| match elt { + AddVar(vid) => Some(vid), + _ => None, + }) + .collect() + } + + /// Computes all regions that have been related to `r0` since the + /// mark `mark` was made---`r0` itself will be the first + /// entry. The `directions` parameter controls what kind of + /// relations are considered. For example, one can say that only + /// "incoming" edges to `r0` are desired, in which case one will + /// get the set of regions `{r|r <= r0}`. This is used when + /// checking whether skolemized regions are being improperly + /// related to other regions. + pub fn tainted( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + mark: &RegionSnapshot, + r0: Region<'tcx>, + directions: TaintDirections, + ) -> FxHashSet> { + debug!( + "tainted(mark={:?}, r0={:?}, directions={:?})", + mark, + r0, + directions + ); + + // `result_set` acts as a worklist: we explore all outgoing + // edges and add any new regions we find to result_set. This + // is not a terribly efficient implementation. + let mut taint_set = taint::TaintSet::new(directions, r0); + taint_set.fixed_point(tcx, &self.undo_log[mark.length..], &self.data.verifys); + debug!("tainted: result={:?}", taint_set); + return taint_set.into_set(); + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "RegionSnapshot(length={},skolemization={})", + self.length, + self.skolemization_count + ) + } +} + +impl<'tcx> fmt::Debug for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{:?}", p), + GenericKind::Projection(ref p) => write!(f, "{:?}", p), + } + } +} + +impl<'tcx> fmt::Display for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{}", p), + GenericKind::Projection(ref p) => write!(f, "{}", p), + } + } +} + +impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { + pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { + match *self { + GenericKind::Param(ref p) => p.to_ty(tcx), + GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), + } + } +} + +impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { + fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) { + match self { + &VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs { + f(r); + }, + + &VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs { + b.for_each_region(f); + }, + } + } + + pub fn must_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), + &VerifyBound::AllRegions(ref bs) => bs.is_empty(), + &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), + &VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty), + &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } + + pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { + if self.must_hold() && vb.must_hold() { + self + } else if self.cannot_hold() && vb.cannot_hold() { + self + } else { + VerifyBound::AllBounds(vec![self, vb]) + } + } +} + +impl<'tcx> RegionConstraintData<'tcx> { + /// True if this region constraint data contains no constraints. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { + constraints, + verifys, + givens, + } = self; + constraints.is_empty() && verifys.is_empty() && givens.is_empty() + } +} diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs new file mode 100644 index 000000000000..ee45f7bd8280 --- /dev/null +++ b/src/librustc/infer/region_constraints/taint.rs @@ -0,0 +1,96 @@ +// Copyright 2012-2014 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 super::*; + +#[derive(Debug)] +pub(super) struct TaintSet<'tcx> { + directions: TaintDirections, + regions: FxHashSet> +} + +impl<'tcx> TaintSet<'tcx> { + pub(super) fn new(directions: TaintDirections, + initial_region: ty::Region<'tcx>) + -> Self { + let mut regions = FxHashSet(); + regions.insert(initial_region); + TaintSet { directions: directions, regions: regions } + } + + pub(super) fn fixed_point(&mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + undo_log: &[UndoLogEntry<'tcx>], + verifys: &[Verify<'tcx>]) { + let mut prev_len = 0; + while prev_len < self.len() { + debug!("tainted: prev_len = {:?} new_len = {:?}", + prev_len, self.len()); + + prev_len = self.len(); + + for undo_entry in undo_log { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), + tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::RegSubVar(a, b)) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::VarSubReg(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), b); + } + &AddConstraint(Constraint::RegSubReg(a, b)) => { + self.add_edge(a, b); + } + &AddGiven(a, b) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddVerify(i) => { + verifys[i].bound.for_each_region(&mut |b| { + self.add_edge(verifys[i].region, b); + }); + } + &Purged | + &AddCombination(..) | + &AddVar(..) | + &OpenSnapshot | + &CommitedSnapshot => {} + } + } + } + } + + pub(super) fn into_set(self) -> FxHashSet> { + self.regions + } + + fn len(&self) -> usize { + self.regions.len() + } + + fn add_edge(&mut self, + source: ty::Region<'tcx>, + target: ty::Region<'tcx>) { + if self.directions.incoming { + if self.regions.contains(&target) { + self.regions.insert(source); + } + } + + if self.directions.outgoing { + if self.regions.contains(&source) { + self.regions.insert(target); + } + } + } +} + diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs deleted file mode 100644 index f5327fad3123..000000000000 --- a/src/librustc/infer/region_inference/mod.rs +++ /dev/null @@ -1,1648 +0,0 @@ -// Copyright 2012-2014 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. - -//! See README.md - -pub use self::Constraint::*; -pub use self::UndoLogEntry::*; -pub use self::CombineMapType::*; -pub use self::RegionResolutionError::*; -pub use self::VarValue::*; - -use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable}; -use super::unify_key; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; -use rustc_data_structures::unify::{self, UnificationTable}; -use middle::free_region::RegionRelations; -use ty::{self, Ty, TyCtxt}; -use ty::{Region, RegionVid}; -use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; -use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh}; - -use std::collections::BTreeMap; -use std::cell::{Cell, RefCell}; -use std::fmt; -use std::mem; -use std::u32; - -mod graphviz; - -/// A constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] -pub enum Constraint<'tcx> { - /// One region variable is subregion of another - ConstrainVarSubVar(RegionVid, RegionVid), - - /// Concrete region is subregion of region variable - ConstrainRegSubVar(Region<'tcx>, RegionVid), - - /// Region variable is subregion of concrete region. This does not - /// directly affect inference, but instead is checked after - /// inference is complete. - ConstrainVarSubReg(RegionVid, Region<'tcx>), - - /// A constraint where neither side is a variable. This does not - /// directly affect inference, but instead is checked after - /// inference is complete. - ConstrainRegSubReg(Region<'tcx>, Region<'tcx>), -} - -/// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or -/// associated type) must outlive the region `R`. `T` is known to -/// outlive `RS`. Therefore verify that `R <= RS[i]` for some -/// `i`. Inference variables may be involved (but this verification -/// step doesn't influence inference). -#[derive(Debug)] -pub struct Verify<'tcx> { - kind: GenericKind<'tcx>, - origin: SubregionOrigin<'tcx>, - region: Region<'tcx>, - bound: VerifyBound<'tcx>, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum GenericKind<'tcx> { - Param(ty::ParamTy), - Projection(ty::ProjectionTy<'tcx>), -} - -/// When we introduce a verification step, we wish to test that a -/// particular region (let's call it `'min`) meets some bound. -/// The bound is described the by the following grammar: -#[derive(Debug)] -pub enum VerifyBound<'tcx> { - /// B = exists {R} --> some 'r in {R} must outlive 'min - /// - /// Put another way, the subject value is known to outlive all - /// regions in {R}, so if any of those outlives 'min, then the - /// bound is met. - AnyRegion(Vec>), - - /// B = forall {R} --> all 'r in {R} must outlive 'min - /// - /// Put another way, the subject value is known to outlive some - /// region in {R}, so if all of those outlives 'min, then the bound - /// is met. - AllRegions(Vec>), - - /// B = exists {B} --> 'min must meet some bound b in {B} - AnyBound(Vec>), - - /// B = forall {B} --> 'min must meet all bounds b in {B} - AllBounds(Vec>), -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct TwoRegions<'tcx> { - a: Region<'tcx>, - b: Region<'tcx>, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum UndoLogEntry<'tcx> { - /// Pushed when we start a snapshot. - OpenSnapshot, - - /// Replaces an `OpenSnapshot` when a snapshot is committed, but - /// that snapshot is not the root. If the root snapshot is - /// unrolled, all nested snapshots must be committed. - CommitedSnapshot, - - /// We added `RegionVid` - AddVar(RegionVid), - - /// We added the given `constraint` - AddConstraint(Constraint<'tcx>), - - /// We added the given `verify` - AddVerify(usize), - - /// We added the given `given` - AddGiven(Region<'tcx>, ty::RegionVid), - - /// We added a GLB/LUB "combination variable" - AddCombination(CombineMapType, TwoRegions<'tcx>), - - /// During skolemization, we sometimes purge entries from the undo - /// log in a kind of minisnapshot (unlike other snapshots, this - /// purging actually takes place *on success*). In that case, we - /// replace the corresponding entry with `Noop` so as to avoid the - /// need to do a bunch of swapping. (We can't use `swap_remove` as - /// the order of the vector is important.) - Purged, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum CombineMapType { - Lub, - Glb, -} - -#[derive(Clone, Debug)] -pub enum RegionResolutionError<'tcx> { - /// `ConcreteFailure(o, a, b)`: - /// - /// `o` requires that `a <= b`, but this does not hold - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - - /// `GenericBoundFailure(p, s, a) - /// - /// The parameter/associated-type `p` must be known to outlive the lifetime - /// `a` (but none of the known bounds are sufficient). - GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), - - /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: - /// - /// Could not infer a value for `v` because `sub_r <= v` (due to - /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and - /// `sub_r <= sup_r` does not hold. - SubSupConflict(RegionVariableOrigin, - SubregionOrigin<'tcx>, - Region<'tcx>, - SubregionOrigin<'tcx>, - Region<'tcx>), -} - -#[derive(Clone, Debug)] -pub enum ProcessedErrorOrigin<'tcx> { - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - VariableFailure(RegionVariableOrigin), -} - -pub type CombineMap<'tcx> = FxHashMap, RegionVid>; - -pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - var_origins: RefCell>, - - /// Constraints of the form `A <= B` introduced by the region - /// checker. Here at least one of `A` and `B` must be a region - /// variable. - /// - /// Using `BTreeMap` because the order in which we iterate over - /// these constraints can affect the way we build the region graph, - /// which in turn affects the way that region errors are reported, - /// leading to small variations in error output across runs and - /// platforms. - constraints: RefCell, SubregionOrigin<'tcx>>>, - - /// A "verify" is something that we need to verify after inference is - /// done, but which does not directly affect inference in any way. - /// - /// An example is a `A <= B` where neither `A` nor `B` are - /// inference variables. - verifys: RefCell>>, - - /// A "given" is a relationship that is known to hold. In particular, - /// we often know from closure fn signatures that a particular free - /// region must be a subregion of a region variable: - /// - /// foo.iter().filter(<'a> |x: &'a &'b T| ...) - /// - /// In situations like this, `'b` is in fact a region variable - /// introduced by the call to `iter()`, and `'a` is a bound region - /// on the closure (as indicated by the `<'a>` prefix). If we are - /// naive, we wind up inferring that `'b` must be `'static`, - /// because we require that it be greater than `'a` and we do not - /// know what `'a` is precisely. - /// - /// This hashmap is used to avoid that naive scenario. Basically we - /// record the fact that `'a <= 'b` is implied by the fn signature, - /// and then ignore the constraint when solving equations. This is - /// a bit of a hack but seems to work. - givens: RefCell, ty::RegionVid)>>, - - lubs: RefCell>, - glbs: RefCell>, - skolemization_count: Cell, - bound_count: Cell, - - /// The undo log records actions that might later be undone. - /// - /// Note: when the undo_log is empty, we are not actively - /// snapshotting. When the `start_snapshot()` method is called, we - /// push an OpenSnapshot entry onto the list to indicate that we - /// are now actively snapshotting. The reason for this is that - /// otherwise we end up adding entries for things like the lower - /// bound on a variable and so forth, which can never be rolled - /// back. - undo_log: RefCell>>, - - unification_table: RefCell>, - - /// This contains the results of inference. It begins as an empty - /// option and only acquires a value after inference is complete. - values: RefCell>>>, -} - -pub struct RegionSnapshot { - length: usize, - region_snapshot: unify::Snapshot, - skolemization_count: u32, -} - -/// When working with skolemized regions, we often wish to find all of -/// the regions that are either reachable from a skolemized region, or -/// which can reach a skolemized region, or both. We call such regions -/// *tained* regions. This struct allows you to decide what set of -/// tainted regions you want. -#[derive(Debug)] -pub struct TaintDirections { - incoming: bool, - outgoing: bool, -} - -impl TaintDirections { - pub fn incoming() -> Self { - TaintDirections { incoming: true, outgoing: false } - } - - pub fn outgoing() -> Self { - TaintDirections { incoming: false, outgoing: true } - } - - pub fn both() -> Self { - TaintDirections { incoming: true, outgoing: true } - } -} - -struct TaintSet<'tcx> { - directions: TaintDirections, - regions: FxHashSet> -} - -impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { - fn new(directions: TaintDirections, - initial_region: ty::Region<'tcx>) - -> Self { - let mut regions = FxHashSet(); - regions.insert(initial_region); - TaintSet { directions: directions, regions: regions } - } - - fn fixed_point(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - undo_log: &[UndoLogEntry<'tcx>], - verifys: &[Verify<'tcx>]) { - let mut prev_len = 0; - while prev_len < self.len() { - debug!("tainted: prev_len = {:?} new_len = {:?}", - prev_len, self.len()); - - prev_len = self.len(); - - for undo_entry in undo_log { - match undo_entry { - &AddConstraint(ConstrainVarSubVar(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), - tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainRegSubVar(a, b)) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainVarSubReg(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), b); - } - &AddConstraint(ConstrainRegSubReg(a, b)) => { - self.add_edge(a, b); - } - &AddGiven(a, b) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddVerify(i) => { - verifys[i].bound.for_each_region(&mut |b| { - self.add_edge(verifys[i].region, b); - }); - } - &Purged | - &AddCombination(..) | - &AddVar(..) | - &OpenSnapshot | - &CommitedSnapshot => {} - } - } - } - } - - fn into_set(self) -> FxHashSet> { - self.regions - } - - fn len(&self) -> usize { - self.regions.len() - } - - fn add_edge(&mut self, - source: ty::Region<'tcx>, - target: ty::Region<'tcx>) { - if self.directions.incoming { - if self.regions.contains(&target) { - self.regions.insert(source); - } - } - - if self.directions.outgoing { - if self.regions.contains(&source) { - self.regions.insert(target); - } - } - } -} - -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> { - RegionVarBindings { - tcx, - var_origins: RefCell::new(Vec::new()), - values: RefCell::new(None), - constraints: RefCell::new(BTreeMap::new()), - verifys: RefCell::new(Vec::new()), - givens: RefCell::new(FxHashSet()), - lubs: RefCell::new(FxHashMap()), - glbs: RefCell::new(FxHashMap()), - skolemization_count: Cell::new(0), - bound_count: Cell::new(0), - undo_log: RefCell::new(Vec::new()), - unification_table: RefCell::new(UnificationTable::new()), - } - } - - fn in_snapshot(&self) -> bool { - !self.undo_log.borrow().is_empty() - } - - pub fn start_snapshot(&self) -> RegionSnapshot { - let length = self.undo_log.borrow().len(); - debug!("RegionVarBindings: start_snapshot({})", length); - self.undo_log.borrow_mut().push(OpenSnapshot); - RegionSnapshot { - length, - region_snapshot: self.unification_table.borrow_mut().snapshot(), - skolemization_count: self.skolemization_count.get(), - } - } - - pub fn commit(&self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: commit({})", snapshot.length); - assert!(self.undo_log.borrow().len() > snapshot.length); - assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() == snapshot.skolemization_count, - "failed to pop skolemized regions: {} now vs {} at start", - self.skolemization_count.get(), - snapshot.skolemization_count); - - let mut undo_log = self.undo_log.borrow_mut(); - if snapshot.length == 0 { - undo_log.truncate(0); - } else { - (*undo_log)[snapshot.length] = CommitedSnapshot; - } - self.unification_table.borrow_mut().commit(snapshot.region_snapshot); - } - - pub fn rollback_to(&self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: rollback_to({:?})", snapshot); - let mut undo_log = self.undo_log.borrow_mut(); - assert!(undo_log.len() > snapshot.length); - assert!((*undo_log)[snapshot.length] == OpenSnapshot); - while undo_log.len() > snapshot.length + 1 { - self.rollback_undo_entry(undo_log.pop().unwrap()); - } - let c = undo_log.pop().unwrap(); - assert!(c == OpenSnapshot); - self.skolemization_count.set(snapshot.skolemization_count); - self.unification_table.borrow_mut() - .rollback_to(snapshot.region_snapshot); - } - - pub fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) { - match undo_entry { - OpenSnapshot => { - panic!("Failure to observe stack discipline"); - } - Purged | CommitedSnapshot => { - // nothing to do here - } - AddVar(vid) => { - let mut var_origins = self.var_origins.borrow_mut(); - var_origins.pop().unwrap(); - assert_eq!(var_origins.len(), vid.index as usize); - } - AddConstraint(ref constraint) => { - self.constraints.borrow_mut().remove(constraint); - } - AddVerify(index) => { - self.verifys.borrow_mut().pop(); - assert_eq!(self.verifys.borrow().len(), index); - } - AddGiven(sub, sup) => { - self.givens.borrow_mut().remove(&(sub, sup)); - } - AddCombination(Glb, ref regions) => { - self.glbs.borrow_mut().remove(regions); - } - AddCombination(Lub, ref regions) => { - self.lubs.borrow_mut().remove(regions); - } - } - } - - pub fn num_vars(&self) -> u32 { - let len = self.var_origins.borrow().len(); - // enforce no overflow - assert!(len as u32 as usize == len); - len as u32 - } - - pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid { - let vid = RegionVid { index: self.num_vars() }; - self.var_origins.borrow_mut().push(origin.clone()); - - let u_vid = self.unification_table.borrow_mut().new_key( - unify_key::RegionVidKey { min_vid: vid } - ); - assert_eq!(vid, u_vid); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVar(vid)); - } - debug!("created new region variable {:?} with origin {:?}", - vid, - origin); - return vid; - } - - pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.var_origins.borrow()[vid.index as usize].clone() - } - - /// Creates a new skolemized region. Skolemized regions are fresh - /// regions used when performing higher-ranked computations. They - /// must be used in a very particular way and are never supposed - /// to "escape" out into error messages or the code at large. - /// - /// The idea is to always create a snapshot. Skolemized regions - /// can be created in the context of this snapshot, but before the - /// snapshot is committed or rolled back, they must be popped - /// (using `pop_skolemized_regions`), so that their numbers can be - /// recycled. Normally you don't have to think about this: you use - /// the APIs in `higher_ranked/mod.rs`, such as - /// `skolemize_late_bound_regions` and `plug_leaks`, which will - /// guide you on this path (ensure that the `SkolemizationMap` is - /// consumed and you are good). There are also somewhat extensive - /// comments in `higher_ranked/README.md`. - /// - /// The `snapshot` argument to this function is not really used; - /// it's just there to make it explicit which snapshot bounds the - /// skolemized region that results. It should always be the top-most snapshot. - pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) - -> Region<'tcx> { - assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); - - let sc = self.skolemization_count.get(); - self.skolemization_count.set(sc + 1); - self.tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) - } - - /// Removes all the edges to/from the skolemized regions that are - /// in `skols`. This is used after a higher-ranked operation - /// completes to remove all trace of the skolemized regions - /// created in that time. - pub fn pop_skolemized(&self, - skols: &FxHashSet>, - snapshot: &RegionSnapshot) { - debug!("pop_skolemized_regions(skols={:?})", skols); - - assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() as usize >= skols.len(), - "popping more skolemized variables than actually exist, \ - sc now = {}, skols.len = {}", - self.skolemization_count.get(), - skols.len()); - - let last_to_pop = self.skolemization_count.get(); - let first_to_pop = last_to_pop - (skols.len() as u32); - - assert!(first_to_pop >= snapshot.skolemization_count, - "popping more regions than snapshot contains, \ - sc now = {}, sc then = {}, skols.len = {}", - self.skolemization_count.get(), - snapshot.skolemization_count, - skols.len()); - debug_assert! { - skols.iter() - .all(|&k| match *k { - ty::ReSkolemized(index, _) => - index.index >= first_to_pop && - index.index < last_to_pop, - _ => - false - }), - "invalid skolemization keys or keys out of range ({}..{}): {:?}", - snapshot.skolemization_count, - self.skolemization_count.get(), - skols - } - - let mut undo_log = self.undo_log.borrow_mut(); - - let constraints_to_kill: Vec = - undo_log.iter() - .enumerate() - .rev() - .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) - .map(|(index, _)| index) - .collect(); - - for index in constraints_to_kill { - let undo_entry = mem::replace(&mut undo_log[index], Purged); - self.rollback_undo_entry(undo_entry); - } - - self.skolemization_count.set(snapshot.skolemization_count); - return; - - fn kill_constraint<'tcx>(skols: &FxHashSet>, - undo_entry: &UndoLogEntry<'tcx>) - -> bool { - match undo_entry { - &AddConstraint(ConstrainVarSubVar(..)) => - false, - &AddConstraint(ConstrainRegSubVar(a, _)) => - skols.contains(&a), - &AddConstraint(ConstrainVarSubReg(_, b)) => - skols.contains(&b), - &AddConstraint(ConstrainRegSubReg(a, b)) => - skols.contains(&a) || skols.contains(&b), - &AddGiven(..) => - false, - &AddVerify(_) => - false, - &AddCombination(_, ref two_regions) => - skols.contains(&two_regions.a) || - skols.contains(&two_regions.b), - &AddVar(..) | - &OpenSnapshot | - &Purged | - &CommitedSnapshot => - false, - } - } - - } - - pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region<'tcx> { - // Creates a fresh bound variable for use in GLB computations. - // See discussion of GLB computation in the large comment at - // the top of this file for more details. - // - // This computation is potentially wrong in the face of - // rollover. It's conceivable, if unlikely, that one might - // wind up with accidental capture for nested functions in - // that case, if the outer function had bound regions created - // a very long time before and the inner function somehow - // wound up rolling over such that supposedly fresh - // identifiers were in fact shadowed. For now, we just assert - // that there is no rollover -- eventually we should try to be - // robust against this possibility, either by checking the set - // of bound identifiers that appear in a given expression and - // ensure that we generate one that is distinct, or by - // changing the representation of bound regions in a fn - // declaration - - let sc = self.bound_count.get(); - self.bound_count.set(sc + 1); - - if sc >= self.bound_count.get() { - bug!("rollover in RegionInference new_bound()"); - } - - self.tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) - } - - fn values_are_none(&self) -> bool { - self.values.borrow().is_none() - } - - fn add_constraint(&self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: add_constraint({:?})", constraint); - - // never overwrite an existing (constraint, origin) - only insert one if it isn't - // present in the map yet. This prevents origins from outside the snapshot being - // replaced with "less informative" origins e.g. during calls to `can_eq` - self.constraints.borrow_mut().entry(constraint).or_insert_with(|| { - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddConstraint(constraint)); - } - origin - }); - } - - fn add_verify(&self, verify: Verify<'tcx>) { - // cannot add verifys once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: add_verify({:?})", verify); - - // skip no-op cases known to be satisfied - match verify.bound { - VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { return; } - _ => { } - } - - let mut verifys = self.verifys.borrow_mut(); - let index = verifys.len(); - verifys.push(verify); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVerify(index)); - } - } - - pub fn add_given(&self, sub: Region<'tcx>, sup: ty::RegionVid) { - // cannot add givens once regions are resolved - assert!(self.values_are_none()); - - let mut givens = self.givens.borrow_mut(); - if givens.insert((sub, sup)) { - debug!("add_given({:?} <= {:?})", sub, sup); - - self.undo_log.borrow_mut().push(AddGiven(sub, sup)); - } - } - - pub fn make_eqregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { - if sub != sup { - // Eventually, it would be nice to add direct support for - // equating regions. - self.make_subregion(origin.clone(), sub, sup); - self.make_subregion(origin, sup, sub); - - if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { - self.unification_table.borrow_mut().union(sub, sup); - } - } - } - - pub fn make_subregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", - sub, - sup, - origin); - - match (sub, sup) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) => { - span_bug!(origin.span(), - "cannot relate bound region: {:?} <= {:?}", - sub, - sup); - } - (_, &ReStatic) => { - // all regions are subregions of static, so we can ignore this - } - (&ReVar(sub_id), &ReVar(sup_id)) => { - self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin); - } - (_, &ReVar(sup_id)) => { - self.add_constraint(ConstrainRegSubVar(sub, sup_id), origin); - } - (&ReVar(sub_id), _) => { - self.add_constraint(ConstrainVarSubReg(sub_id, sup), origin); - } - _ => { - self.add_constraint(ConstrainRegSubReg(sub, sup), origin); - } - } - } - - /// See `Verify::VerifyGenericBound` - pub fn verify_generic_bound(&self, - origin: SubregionOrigin<'tcx>, - kind: GenericKind<'tcx>, - sub: Region<'tcx>, - bound: VerifyBound<'tcx>) { - self.add_verify(Verify { - kind, - origin, - region: sub, - bound, - }); - } - - pub fn lub_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); - match (a, b) { - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - _ if a == b => { - a // LUB(a,a) = a - } - - _ => { - self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), old_r, new_r) - }) - } - } - } - - pub fn glb_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - - debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); - match (a, b) { - (&ReStatic, r) | (r, &ReStatic) => { - r // static lives longer than everything else - } - - _ if a == b => { - a // GLB(a,a) = a - } - - _ => { - self.combine_vars(Glb, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), new_r, old_r) - }) - } - } - } - - pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - match *self.values.borrow() { - None => { - span_bug!((*self.var_origins.borrow())[rid.index as usize].span(), - "attempt to resolve region variable before values have \ - been computed!") - } - Some(ref values) => { - let r = lookup(self.tcx, values, rid); - debug!("resolve_var({:?}) = {:?}", rid, r); - r - } - } - } - - pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - let vid = self.unification_table.borrow_mut().find_value(rid).min_vid; - self.tcx.mk_region(ty::ReVar(vid)) - } - - fn combine_map(&self, t: CombineMapType) -> &RefCell> { - match t { - Glb => &self.glbs, - Lub => &self.lubs, - } - } - - pub fn combine_vars(&self, - t: CombineMapType, - a: Region<'tcx>, - b: Region<'tcx>, - origin: SubregionOrigin<'tcx>, - mut relate: F) - -> Region<'tcx> - where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>) - { - let vars = TwoRegions { a: a, b: b }; - if let Some(&c) = self.combine_map(t).borrow().get(&vars) { - return self.tcx.mk_region(ReVar(c)); - } - let c = self.new_region_var(MiscVariable(origin.span())); - self.combine_map(t).borrow_mut().insert(vars, c); - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddCombination(t, vars)); - } - relate(self, a, self.tcx.mk_region(ReVar(c))); - relate(self, b, self.tcx.mk_region(ReVar(c))); - debug!("combine_vars() c={:?}", c); - self.tcx.mk_region(ReVar(c)) - } - - pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { - self.undo_log.borrow()[mark.length..] - .iter() - .filter_map(|&elt| { - match elt { - AddVar(vid) => Some(vid), - _ => None, - } - }) - .collect() - } - - /// Computes all regions that have been related to `r0` since the - /// mark `mark` was made---`r0` itself will be the first - /// entry. The `directions` parameter controls what kind of - /// relations are considered. For example, one can say that only - /// "incoming" edges to `r0` are desired, in which case one will - /// get the set of regions `{r|r <= r0}`. This is used when - /// checking whether skolemized regions are being improperly - /// related to other regions. - pub fn tainted(&self, - mark: &RegionSnapshot, - r0: Region<'tcx>, - directions: TaintDirections) - -> FxHashSet> { - debug!("tainted(mark={:?}, r0={:?}, directions={:?})", - mark, r0, directions); - - // `result_set` acts as a worklist: we explore all outgoing - // edges and add any new regions we find to result_set. This - // is not a terribly efficient implementation. - let mut taint_set = TaintSet::new(directions, r0); - taint_set.fixed_point(self.tcx, - &self.undo_log.borrow()[mark.length..], - &self.verifys.borrow()); - debug!("tainted: result={:?}", taint_set.regions); - return taint_set.into_set(); - } - - /// This function performs the actual region resolution. It must be - /// called after all constraints have been added. It performs a - /// fixed-point iteration to find region values which satisfy all - /// constraints, assuming such values can be found; if they cannot, - /// errors are reported. - pub fn resolve_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>) - -> Vec> { - debug!("RegionVarBindings: resolve_regions()"); - let mut errors = vec![]; - let v = self.infer_variable_values(region_rels, &mut errors); - *self.values.borrow_mut() = Some(v); - errors - } - - fn lub_concrete_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - match (a, b) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) | - (&ReErased, _) | - (_, &ReErased) => { - bug!("cannot relate region: LUB({:?}, {:?})", a, b); - } - - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - (&ReEmpty, r) | (r, &ReEmpty) => { - r // everything lives longer than empty - } - - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { - span_bug!((*self.var_origins.borrow())[v_id.index as usize].span(), - "lub_concrete_regions invoked with non-concrete \ - regions: {:?}, {:?}", - a, - b); - } - - (&ReEarlyBound(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReEarlyBound(_)) | - (&ReFree(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReFree(_)) => { - // A "free" region can be interpreted as "some region - // at least as big as fr.scope". So, we can - // reasonably compare free regions and scopes: - let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - region_rels.region_scope_tree.early_free_scope(self.tcx, br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - region_rels.region_scope_tree.free_scope(self.tcx, fr) - } - _ => bug!() - }; - let r_id = region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); - if r_id == fr_scope { - // if the free region's scope `fr.scope` is bigger than - // the scope region `s_id`, then the LUB is the free - // region itself: - match (a, b) { - (_, &ReScope(_)) => return a, - (&ReScope(_), _) => return b, - _ => bug!() - } - } - - // otherwise, we don't know what the free region is, - // so we must conservatively say the LUB is static: - self.tcx.types.re_static - } - - (&ReScope(a_id), &ReScope(b_id)) => { - // The region corresponding to an outer block is a - // subtype of the region corresponding to an inner - // block. - let lub = region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); - self.tcx.mk_region(ReScope(lub)) - } - - (&ReEarlyBound(_), &ReEarlyBound(_)) | - (&ReFree(_), &ReEarlyBound(_)) | - (&ReEarlyBound(_), &ReFree(_)) | - (&ReFree(_), &ReFree(_)) => { - region_rels.lub_free_regions(a, b) - } - - // For these types, we cannot define any additional - // relationship: - (&ReSkolemized(..), _) | - (_, &ReSkolemized(..)) => { - if a == b { - a - } else { - self.tcx.types.re_static - } - } - } - } -} - -// ______________________________________________________________________ - -#[derive(Copy, Clone, Debug)] -pub enum VarValue<'tcx> { - Value(Region<'tcx>), - ErrorValue, -} - -struct RegionAndOrigin<'tcx> { - region: Region<'tcx>, - origin: SubregionOrigin<'tcx>, -} - -type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; - -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - fn infer_variable_values(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - errors: &mut Vec>) - -> Vec> { - let mut var_data = self.construct_var_data(); - - // Dorky hack to cause `dump_constraints` to only get called - // if debug mode is enabled: - debug!("----() End constraint listing (context={:?}) {:?}---", - region_rels.context, - self.dump_constraints(region_rels)); - graphviz::maybe_print_constraints_for(self, region_rels); - - let graph = self.construct_graph(); - self.expand_givens(&graph); - self.expansion(region_rels, &mut var_data); - self.collect_errors(region_rels, &mut var_data, errors); - self.collect_var_errors(region_rels, &var_data, &graph, errors); - var_data - } - - fn construct_var_data(&self) -> Vec> { - (0..self.num_vars() as usize) - .map(|_| Value(self.tcx.types.re_empty)) - .collect() - } - - fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { - debug!("----() Start constraint listing (context={:?}) ()----", - free_regions.context); - for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() { - debug!("Constraint {} => {:?}", idx, constraint); - } - } - - fn expand_givens(&self, graph: &RegionGraph) { - // Givens are a kind of horrible hack to account for - // constraints like 'c <= '0 that are known to hold due to - // closure signatures (see the comment above on the `givens` - // field). They should go away. But until they do, the role - // of this fn is to account for the transitive nature: - // - // Given 'c <= '0 - // and '0 <= '1 - // then 'c <= '1 - - let mut givens = self.givens.borrow_mut(); - let seeds: Vec<_> = givens.iter().cloned().collect(); - for (r, vid) in seeds { - let seed_index = NodeIndex(vid.index as usize); - for succ_index in graph.depth_traverse(seed_index, OUTGOING) { - let succ_index = succ_index.0 as u32; - if succ_index < self.num_vars() { - let succ_vid = RegionVid { index: succ_index }; - givens.insert((r, succ_vid)); - } - } - } - } - - fn expansion(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &mut [VarValue<'tcx>]) { - self.iterate_until_fixed_point("Expansion", |constraint, origin| { - debug!("expansion: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(a_region, b_vid) => { - let b_data = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_data) - } - ConstrainVarSubVar(a_vid, b_vid) => { - match var_values[a_vid.index as usize] { - ErrorValue => false, - Value(a_region) => { - let b_node = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_node) - } - } - } - ConstrainRegSubReg(..) | - ConstrainVarSubReg(..) => { - // These constraints are checked after expansion - // is done, in `collect_errors`. - false - } - } - }) - } - - fn expand_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a_region: Region<'tcx>, - b_vid: RegionVid, - b_data: &mut VarValue<'tcx>) - -> bool { - debug!("expand_node({:?}, {:?} == {:?})", - a_region, - b_vid, - b_data); - - // Check if this relationship is implied by a given. - match *a_region { - ty::ReEarlyBound(_) | - ty::ReFree(_) => { - if self.givens.borrow().contains(&(a_region, b_vid)) { - debug!("given"); - return false; - } - } - _ => {} - } - - match *b_data { - Value(cur_region) => { - let lub = self.lub_concrete_regions(region_rels, a_region, cur_region); - if lub == cur_region { - return false; - } - - debug!("Expanding value of {:?} from {:?} to {:?}", - b_vid, - cur_region, - lub); - - *b_data = Value(lub); - return true; - } - - ErrorValue => { - return false; - } - } - } - - /// After expansion is complete, go and check upper bounds (i.e., - /// cases where the region cannot grow larger than a fixed point) - /// and check that they are satisfied. - fn collect_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &mut Vec>, - errors: &mut Vec>) { - let constraints = self.constraints.borrow(); - for (constraint, origin) in constraints.iter() { - debug!("collect_errors: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(..) | - ConstrainVarSubVar(..) => { - // Expansion will ensure that these constraints hold. Ignore. - } - - ConstrainRegSubReg(sub, sup) => { - if region_rels.is_subregion_of(sub, sup) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - origin, - sub, - sup); - - errors.push(ConcreteFailure((*origin).clone(), sub, sup)); - } - - ConstrainVarSubReg(a_vid, b_region) => { - let a_data = &mut var_data[a_vid.index as usize]; - debug!("contraction: {:?} == {:?}, {:?}", - a_vid, - a_data, - b_region); - - let a_region = match *a_data { - ErrorValue => continue, - Value(a_region) => a_region, - }; - - // Do not report these errors immediately: - // instead, set the variable value to error and - // collect them later. - if !region_rels.is_subregion_of(a_region, b_region) { - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?}={:?} <= {:?}", - origin, - a_vid, - a_region, - b_region); - *a_data = ErrorValue; - } - } - } - } - - for verify in self.verifys.borrow().iter() { - debug!("collect_errors: verify={:?}", verify); - let sub = normalize(self.tcx, var_data, verify.region); - - // This was an inference variable which didn't get - // constrained, therefore it can be assume to hold. - if let ty::ReEmpty = *sub { - continue; - } - - if verify.bound.is_met(region_rels, var_data, sub) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - verify.origin, - verify.region, - verify.bound); - - errors.push(GenericBoundFailure(verify.origin.clone(), - verify.kind.clone(), - sub)); - } - } - - /// Go over the variables that were declared to be error variables - /// and create a `RegionResolutionError` for each of them. - fn collect_var_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &[VarValue<'tcx>], - graph: &RegionGraph<'tcx>, - errors: &mut Vec>) { - debug!("collect_var_errors"); - - // This is the best way that I have found to suppress - // duplicate and related errors. Basically we keep a set of - // flags for every node. Whenever an error occurs, we will - // walk some portion of the graph looking to find pairs of - // conflicting regions to report to the user. As we walk, we - // trip the flags from false to true, and if we find that - // we've already reported an error involving any particular - // node we just stop and don't report the current error. The - // idea is to report errors that derive from independent - // regions of the graph, but not those that derive from - // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; - - for idx in 0..self.num_vars() as usize { - match var_data[idx] { - Value(_) => { - /* Inference successful */ - } - ErrorValue => { - /* Inference impossible, this value contains - inconsistent constraints. - - I think that in this case we should report an - error now---unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ - - let node_vid = RegionVid { index: idx as u32 }; - self.collect_error_for_expanding_node(region_rels, - graph, - &mut dup_vec, - node_vid, - errors); - } - } - } - } - - fn construct_graph(&self) -> RegionGraph<'tcx> { - let num_vars = self.num_vars(); - - let constraints = self.constraints.borrow(); - - let mut graph = graph::Graph::new(); - - for _ in 0..num_vars { - graph.add_node(()); - } - - // Issue #30438: two distinct dummy nodes, one for incoming - // edges (dummy_source) and another for outgoing edges - // (dummy_sink). In `dummy -> a -> b -> dummy`, using one - // dummy node leads one to think (erroneously) there exists a - // path from `b` to `a`. Two dummy nodes sidesteps the issue. - let dummy_source = graph.add_node(()); - let dummy_sink = graph.add_node(()); - - for (constraint, _) in constraints.iter() { - match *constraint { - ConstrainVarSubVar(a_id, b_id) => { - graph.add_edge(NodeIndex(a_id.index as usize), - NodeIndex(b_id.index as usize), - *constraint); - } - ConstrainRegSubVar(_, b_id) => { - graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); - } - ConstrainVarSubReg(a_id, _) => { - graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); - } - ConstrainRegSubReg(..) => { - // this would be an edge from `dummy_source` to - // `dummy_sink`; just ignore it. - } - } - } - - return graph; - } - - fn collect_error_for_expanding_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - graph: &RegionGraph<'tcx>, - dup_vec: &mut [u32], - node_idx: RegionVid, - errors: &mut Vec>) { - // Errors in expanding nodes result from a lower-bound that is - // not contained by an upper-bound. - let (mut lower_bounds, lower_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::INCOMING, - dup_vec); - let (mut upper_bounds, upper_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::OUTGOING, - dup_vec); - - if lower_dup || upper_dup { - return; - } - - // We place free regions first because we are special casing - // SubSupConflict(ReFree, ReFree) when reporting error, and so - // the user will more likely get a specific suggestion. - fn region_order_key(x: &RegionAndOrigin) -> u8 { - match *x.region { - ReEarlyBound(_) => 0, - ReFree(_) => 1, - _ => 2 - } - } - lower_bounds.sort_by_key(region_order_key); - upper_bounds.sort_by_key(region_order_key); - - for lower_bound in &lower_bounds { - for upper_bound in &upper_bounds { - if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { - let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone(); - debug!("region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ - sup: {:?}", - origin, - node_idx, - lower_bound.region, - upper_bound.region); - errors.push(SubSupConflict(origin, - lower_bound.origin.clone(), - lower_bound.region, - upper_bound.origin.clone(), - upper_bound.region)); - return; - } - } - } - - span_bug!((*self.var_origins.borrow())[node_idx.index as usize].span(), - "collect_error_for_expanding_node() could not find \ - error for var {:?}, lower_bounds={:?}, \ - upper_bounds={:?}", - node_idx, - lower_bounds, - upper_bounds); - } - - fn collect_concrete_regions(&self, - graph: &RegionGraph<'tcx>, - orig_node_idx: RegionVid, - dir: Direction, - dup_vec: &mut [u32]) - -> (Vec>, bool) { - struct WalkState<'tcx> { - set: FxHashSet, - stack: Vec, - result: Vec>, - dup_found: bool, - } - let mut state = WalkState { - set: FxHashSet(), - stack: vec![orig_node_idx], - result: Vec::new(), - dup_found: false, - }; - state.set.insert(orig_node_idx); - - // to start off the process, walk the source node in the - // direction specified - process_edges(self, &mut state, graph, orig_node_idx, dir); - - while !state.stack.is_empty() { - let node_idx = state.stack.pop().unwrap(); - - // check whether we've visited this node on some previous walk - if dup_vec[node_idx.index as usize] == u32::MAX { - dup_vec[node_idx.index as usize] = orig_node_idx.index; - } else if dup_vec[node_idx.index as usize] != orig_node_idx.index { - state.dup_found = true; - } - - debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, - node_idx); - - process_edges(self, &mut state, graph, node_idx, dir); - } - - let WalkState {result, dup_found, ..} = state; - return (result, dup_found); - - fn process_edges<'a, 'gcx, 'tcx>(this: &RegionVarBindings<'a, 'gcx, 'tcx>, - state: &mut WalkState<'tcx>, - graph: &RegionGraph<'tcx>, - source_vid: RegionVid, - dir: Direction) { - debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); - - let source_node_index = NodeIndex(source_vid.index as usize); - for (_, edge) in graph.adjacent_edges(source_node_index, dir) { - match edge.data { - ConstrainVarSubVar(from_vid, to_vid) => { - let opp_vid = if from_vid == source_vid { - to_vid - } else { - from_vid - }; - if state.set.insert(opp_vid) { - state.stack.push(opp_vid); - } - } - - ConstrainRegSubVar(region, _) | - ConstrainVarSubReg(_, region) => { - state.result.push(RegionAndOrigin { - region, - origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), - }); - } - - ConstrainRegSubReg(..) => { - panic!("cannot reach reg-sub-reg edge in region inference \ - post-processing") - } - } - } - } - } - - fn iterate_until_fixed_point(&self, tag: &str, mut body: F) - where F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool - { - let mut iteration = 0; - let mut changed = true; - while changed { - changed = false; - iteration += 1; - debug!("---- {} Iteration {}{}", "#", tag, iteration); - for (constraint, origin) in self.constraints.borrow().iter() { - let edge_changed = body(constraint, origin); - if edge_changed { - debug!("Updated due to constraint {:?}", constraint); - changed = true; - } - } - } - debug!("---- {} Complete after {} iteration(s)", tag, iteration); - } - -} - -fn normalize<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - r: ty::Region<'tcx>) - -> ty::Region<'tcx> { - match *r { - ty::ReVar(rid) => lookup(tcx, values, rid), - _ => r, - } -} - -fn lookup<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - rid: ty::RegionVid) - -> ty::Region<'tcx> { - match values[rid.index as usize] { - Value(r) => r, - ErrorValue => tcx.types.re_static, // Previously reported error. - } -} - -impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) - } -} - -impl fmt::Debug for RegionSnapshot { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionSnapshot(length={},skolemization={})", - self.length, self.skolemization_count) - } -} - -impl<'tcx> fmt::Debug for GenericKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenericKind::Param(ref p) => write!(f, "{:?}", p), - GenericKind::Projection(ref p) => write!(f, "{:?}", p), - } - } -} - -impl<'tcx> fmt::Display for GenericKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenericKind::Param(ref p) => write!(f, "{}", p), - GenericKind::Projection(ref p) => write!(f, "{}", p), - } - } -} - -impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { - pub fn to_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { - match *self { - GenericKind::Param(ref p) => p.to_ty(tcx), - GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), - } - } -} - -impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { - fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) { - match self { - &VerifyBound::AnyRegion(ref rs) | - &VerifyBound::AllRegions(ref rs) => for &r in rs { - f(r); - }, - - &VerifyBound::AnyBound(ref bs) | - &VerifyBound::AllBounds(ref bs) => for b in bs { - b.for_each_region(f); - }, - } - } - - pub fn must_hold(&self) -> bool { - match self { - &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), - &VerifyBound::AllRegions(ref bs) => bs.is_empty(), - &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), - } - } - - pub fn cannot_hold(&self) -> bool { - match self { - &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), - &VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty), - &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), - } - } - - pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { - if self.must_hold() || vb.cannot_hold() { - self - } else if self.cannot_hold() || vb.must_hold() { - vb - } else { - VerifyBound::AnyBound(vec![self, vb]) - } - } - - pub fn and(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { - if self.must_hold() && vb.must_hold() { - self - } else if self.cannot_hold() && vb.cannot_hold() { - self - } else { - VerifyBound::AllBounds(vec![self, vb]) - } - } - - fn is_met(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &Vec>, - min: ty::Region<'tcx>) - -> bool { - let tcx = region_rels.tcx; - match self { - &VerifyBound::AnyRegion(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .any(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AllRegions(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .all(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AnyBound(ref bs) => - bs.iter() - .any(|b| b.is_met(region_rels, var_values, min)), - - &VerifyBound::AllBounds(ref bs) => - bs.iter() - .all(|b| b.is_met(region_rels, var_values, min)), - } - } -} diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 10899e42afb8..5e70c0ce368f 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -74,8 +74,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(rid), - _ => r, + ty::ReVar(rid) => + self.infcx.borrow_region_constraints() + .opportunistic_resolve_var(self.tcx(), rid), + _ => + r, } } } @@ -185,7 +188,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.resolve_var(rid), + ty::ReVar(rid) => self.infcx.lexical_region_resolutions + .borrow() + .as_ref() + .expect("region resolution not performed") + .resolve_var(rid), _ => r, } } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 405699968135..f891f692c7d8 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -137,7 +137,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_subregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_subregion(origin, a, b); Ok(a) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 498e1aa3520d..5e9019c92c5b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -45,17 +45,21 @@ #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(drain_filter)] #![feature(i128_type)] +#![feature(match_default_bindings)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] #![feature(macro_vis_matcher)] #![feature(never_type)] #![feature(nonzero)] #![feature(quote)] +#![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] #![feature(specialization)] #![feature(unboxed_closures)] +#![feature(underscore_lifetimes)] #![feature(trace_macros)] #![feature(test)] #![feature(const_atomic_bool_new)] diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 855cc069d117..75446586365d 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -161,12 +161,6 @@ declare_lint! { "patterns in functions without body were erroneously allowed" } -declare_lint! { - pub EXTRA_REQUIREMENT_IN_IMPL, - Deny, - "detects extra requirements in impls that were erroneously allowed" -} - declare_lint! { pub LEGACY_DIRECTORY_OWNERSHIP, Deny, @@ -254,7 +248,6 @@ impl LintPass for HardwiredLints { RESOLVE_TRAIT_ON_DEFAULTED_UNIT, SAFE_EXTERN_STATICS, PATTERNS_IN_FNS_WITHOUT_BODY, - EXTRA_REQUIREMENT_IN_IMPL, LEGACY_DIRECTORY_OWNERSHIP, LEGACY_IMPORTS, LEGACY_CONSTRUCTOR_VISIBILITY, diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 3bcdc4f7e2c6..da505f167241 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -192,7 +192,7 @@ impl<'tcx> FreeRegionMap<'tcx> { /// /// if `r_a` represents `'a`, this function would return `{'b, 'c}`. pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> { - assert!(is_free(r_a)); + assert!(is_free(r_a) || *r_a == ty::ReStatic); self.relation.greater_than(&r_a) } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index a7882992d61c..d3aa80e5585e 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -12,7 +12,7 @@ //! the parent links in the region hierarchy. //! //! Most of the documentation on regions can be found in -//! `middle/infer/region_inference/README.md` +//! `middle/infer/region_constraints/README.md` use ich::{StableHashingContext, NodeIdHashingMode}; use util::nodemap::{FxHashMap, FxHashSet}; @@ -320,7 +320,7 @@ pub struct ScopeTree { /// hierarchy based on their lexical mapping. This is used to /// handle the relationships between regions in a fn and in a /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_inference for + /// section of the README in infer::region_constraints for /// more details. closure_tree: FxHashMap, @@ -407,7 +407,7 @@ pub struct Context { /// of the innermost fn body. Each fn forms its own disjoint tree /// in the region hierarchy. These fn bodies are themselves /// arranged into a tree. See the "Modeling closures" section of - /// the README in infer::region_inference for more + /// the README in infer::region_constraints for more /// details. root_id: Option, @@ -646,7 +646,7 @@ impl<'tcx> ScopeTree { // different functions. Compare those fn for lexical // nesting. The reasoning behind this is subtle. See the // "Modeling closures" section of the README in - // infer::region_inference for more details. + // infer::region_constraints for more details. let a_root_scope = a_ancestors[a_index]; let b_root_scope = a_ancestors[a_index]; return match (a_root_scope.data(), b_root_scope.data()) { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 18c26500dbe1..bea273c84a92 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -555,6 +555,15 @@ pub struct UpvarDecl { newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" }); +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { + block: self, + statement_index: 0, + } + } +} + /////////////////////////////////////////////////////////////////////////// // BasicBlockData and Terminator @@ -638,7 +647,32 @@ pub enum TerminatorKind<'tcx> { unwind: Option }, - /// Drop the Lvalue and assign the new value over it + /// Drop the Lvalue and assign the new value over it. This ensures + /// that the assignment to LV occurs *even if* the destructor for + /// lvalue unwinds. Its semantics are best explained by by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(LV <- RV, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(LV, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // LV is now unitialized + /// LV <- RV + /// } + /// BB2 { + /// // LV is now unitialized -- its dtor panicked + /// LV <- RV + /// } + /// ``` DropAndReplace { location: Lvalue<'tcx>, value: Operand<'tcx>, diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 00863abc84de..b09ab8da7c12 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -292,11 +292,10 @@ macro_rules! make_mir_visitor { self.visit_visibility_scope_data(scope); } - let lookup = TyContext::SourceInfo(SourceInfo { + self.visit_ty(&$($mutability)* mir.return_ty, TyContext::ReturnTy(SourceInfo { span: mir.span, scope: ARGUMENT_VISIBILITY_SCOPE, - }); - self.visit_ty(&$($mutability)* mir.return_ty, lookup); + })); for local in mir.local_decls.indices() { self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]); @@ -811,7 +810,7 @@ make_mir_visitor!(MutVisitor,mut); /// Extra information passed to `visit_ty` and friends to give context /// about where the type etc appears. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum TyContext { LocalDecl { /// The index of the local variable we are visiting. @@ -821,9 +820,11 @@ pub enum TyContext { source_info: SourceInfo, }, - Location(Location), + /// The return type of the function. + ReturnTy(SourceInfo), - SourceInfo(SourceInfo), + /// A type found at some location. + Location(Location), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 106b1b08656f..7c38cf75b8d5 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -33,7 +33,6 @@ use hir::def_id::DefId; use infer::{self, InferCtxt}; use infer::type_variable::TypeVariableOrigin; use middle::const_val; -use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL; use std::fmt; use syntax::ast; use session::DiagnosticMessageId; @@ -481,30 +480,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { item_name: ast::Name, _impl_item_def_id: DefId, trait_item_def_id: DefId, - requirement: &fmt::Display, - lint_id: Option) // (*) + requirement: &fmt::Display) -> DiagnosticBuilder<'tcx> { - // (*) This parameter is temporary and used only for phasing - // in the bug fix to #18937. If it is `Some`, it has a kind of - // weird effect -- the diagnostic is reported as a lint, and - // the builder which is returned is marked as canceled. - let msg = "impl has stricter requirements than trait"; - let mut err = match lint_id { - Some(node_id) => { - self.tcx.struct_span_lint_node(EXTRA_REQUIREMENT_IN_IMPL, - node_id, - error_span, - msg) - } - None => { - struct_span_err!(self.tcx.sess, - error_span, - E0276, - "{}", msg) - } - }; + let mut err = struct_span_err!(self.tcx.sess, + error_span, + E0276, + "{}", msg); if let Some(trait_item_span) = self.tcx.hir.span_if_local(trait_item_def_id) { let span = self.tcx.sess.codemap().def_span(trait_item_span); @@ -543,15 +526,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let mut err = match *error { SelectionError::Unimplemented => { if let ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, lint_id + item_name, impl_item_def_id, trait_item_def_id, } = obligation.cause.code { self.report_extra_impl_obligation( span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}`", obligation.predicate), - lint_id) + &format!("`{}`", obligation.predicate)) .emit(); return; } diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index cc2506d1afc5..297feead6176 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk}; +use infer::{RegionObligation, InferCtxt, InferOk}; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{ObligationForest, Error}; use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; use std::marker::PhantomData; -use syntax::ast; -use util::nodemap::NodeMap; use hir::def_id::DefId; use super::CodeAmbiguity; @@ -48,39 +46,6 @@ pub struct FulfillmentContext<'tcx> { // A list of all obligations that have been registered with this // fulfillment context. predicates: ObligationForest>, - - // A set of constraints that regionck must validate. Each - // constraint has the form `T:'a`, meaning "some type `T` must - // outlive the lifetime 'a". These constraints derive from - // instantiated type parameters. So if you had a struct defined - // like - // - // struct Foo { ... } - // - // then in some expression `let x = Foo { ... }` it will - // instantiate the type parameter `T` with a fresh type `$0`. At - // the same time, it will record a region obligation of - // `$0:'static`. This will get checked later by regionck. (We - // can't generally check these things right away because we have - // to wait until types are resolved.) - // - // These are stored in a map keyed to the id of the innermost - // enclosing fn body / static initializer expression. This is - // because the location where the obligation was incurred can be - // relevant with respect to which sublifetime assumptions are in - // place. The reason that we store under the fn-id, and not - // something more fine-grained, is so that it is easier for - // regionck to be sure that it has found *all* the region - // obligations (otherwise, it's easy to fail to walk to a - // particular node-id). - region_obligations: NodeMap>>, -} - -#[derive(Clone)] -pub struct RegionObligation<'tcx> { - pub sub_region: ty::Region<'tcx>, - pub sup_type: Ty<'tcx>, - pub cause: ObligationCause<'tcx>, } #[derive(Clone, Debug)] @@ -94,7 +59,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), - region_obligations: NodeMap(), } } @@ -157,14 +121,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_region_obligation(&mut self, - t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>) - { - register_region_obligation(t_a, r_b, cause, &mut self.region_obligations); - } - pub fn register_predicate_obligation(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, obligation: PredicateObligation<'tcx>) @@ -183,26 +139,16 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_predicate_obligations(&mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - obligations: Vec>) + pub fn register_predicate_obligations(&mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + obligations: I) + where I: IntoIterator> { for obligation in obligations { self.register_predicate_obligation(infcx, obligation); } } - - pub fn region_obligations(&self, - body_id: ast::NodeId) - -> &[RegionObligation<'tcx>] - { - match self.region_obligations.get(&body_id) { - None => Default::default(), - Some(vec) => vec, - } - } - pub fn select_all_or_error(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Result<(),Vec>> @@ -245,10 +191,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { debug!("select: starting another iteration"); // Process pending obligations. - let outcome = self.predicates.process_obligations(&mut FulfillProcessor { - selcx, - region_obligations: &mut self.region_obligations, - }); + let outcome = self.predicates.process_obligations(&mut FulfillProcessor { selcx }); debug!("select: outcome={:?}", outcome); // FIXME: if we kept the original cache key, we could mark projection @@ -277,7 +220,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> { selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, - region_obligations: &'a mut NodeMap>>, } impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> { @@ -288,9 +230,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, obligation: &mut Self::Obligation) -> Result>, Self::Error> { - process_predicate(self.selcx, - obligation, - self.region_obligations) + process_predicate(self.selcx, obligation) .map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] @@ -329,8 +269,7 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't /// - `Err` if the predicate does not hold fn process_predicate<'a, 'gcx, 'tcx>( selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, - pending_obligation: &mut PendingPredicateObligation<'tcx>, - region_obligations: &mut NodeMap>>) + pending_obligation: &mut PendingPredicateObligation<'tcx>) -> Result>>, FulfillmentErrorCode<'tcx>> { @@ -452,18 +391,26 @@ fn process_predicate<'a, 'gcx, 'tcx>( // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`. Some(t_a) => { let r_static = selcx.tcx().types.re_static; - register_region_obligation(t_a, r_static, - obligation.cause.clone(), - region_obligations); + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_static, + cause: obligation.cause.clone(), + }); Ok(Some(vec![])) } } } // If there aren't, register the obligation. Some(ty::OutlivesPredicate(t_a, r_b)) => { - register_region_obligation(t_a, r_b, - obligation.cause.clone(), - region_obligations); + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_b, + cause: obligation.cause.clone() + }); Ok(Some(vec![])) } } @@ -566,25 +513,6 @@ fn process_predicate<'a, 'gcx, 'tcx>( } } - -fn register_region_obligation<'tcx>(t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>, - region_obligations: &mut NodeMap>>) -{ - let region_obligation = RegionObligation { sup_type: t_a, - sub_region: r_b, - cause: cause }; - - debug!("register_region_obligation({:?}, cause={:?})", - region_obligation, region_obligation.cause); - - region_obligations.entry(region_obligation.cause.body_id) - .or_insert(vec![]) - .push(region_obligation); - -} - fn to_fulfillment_error<'tcx>( error: Error, FulfillmentErrorCode<'tcx>>) -> FulfillmentError<'tcx> diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 62d2fe79b217..55b1a913f0d1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -30,7 +30,7 @@ use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult}; -pub use self::fulfill::{FulfillmentContext, RegionObligation}; +pub use self::fulfill::FulfillmentContext; pub use self::project::MismatchedProjectionTypes; pub use self::project::{normalize, normalize_projection_type, Normalized}; pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal}; @@ -152,7 +152,6 @@ pub enum ObligationCauseCode<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - lint_id: Option, }, /// Checking that this expression can be assigned where it needs to be @@ -537,6 +536,17 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let region_scope_tree = region::ScopeTree::default(); let free_regions = FreeRegionMap::new(); + + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + let _ = infcx.ignore_region_obligations(); + infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index fd93aa162a61..923199501806 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -26,13 +26,6 @@ impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> { } } -impl<'tcx> fmt::Debug for traits::RegionObligation<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, - self.sup_type) - } -} impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Obligation(predicate={:?},depth={})", @@ -221,13 +214,11 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { } super::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { Some(super::CompareImplMethodObligation { item_name, impl_item_def_id, trait_item_def_id, - lint_id, }) } super::ExprAssignable => Some(super::ExprAssignable), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index bf1cc682a8aa..a9efb042f3d8 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -144,6 +144,15 @@ pub enum AssociatedItemContainer { } impl AssociatedItemContainer { + /// Asserts that this is the def-id of an associated item declared + /// in a trait, and returns the trait def-id. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self) + } + } + pub fn id(&self) -> DefId { match *self { TraitContainer(id) => id, @@ -895,6 +904,12 @@ pub enum Predicate<'tcx> { ConstEvaluatable(DefId, &'tcx Substs<'tcx>), } +impl<'tcx> AsRef> for Predicate<'tcx> { + fn as_ref(&self) -> &Predicate<'tcx> { + self + } +} + impl<'a, 'gcx, 'tcx> Predicate<'tcx> { /// Performs a substitution suitable for going from a /// poly-trait-ref to supertraits that must hold if that @@ -1200,6 +1215,25 @@ impl<'tcx> Predicate<'tcx> { } } } + + pub fn to_opt_type_outlives(&self) -> Option> { + match *self { + Predicate::TypeOutlives(data) => { + Some(data) + } + Predicate::Trait(..) | + Predicate::Projection(..) | + Predicate::Equate(..) | + Predicate::Subtype(..) | + Predicate::RegionOutlives(..) | + Predicate::WellFormed(..) | + Predicate::ObjectSafe(..) | + Predicate::ClosureKind(..) | + Predicate::ConstEvaluatable(..) => { + None + } + } + } } /// Represents the bounds declared on a particular set of type diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a60cad0de9f7..65406c3d16cc 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -14,6 +14,7 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; use middle::region; +use rustc_data_structures::indexed_vec::Idx; use ty::subst::{Substs, Subst}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{Slice, TyS}; @@ -898,6 +899,18 @@ pub struct RegionVid { pub index: u32, } +// FIXME: We could convert this to use `newtype_index!` +impl Idx for RegionVid { + fn new(value: usize) -> Self { + assert!(value < ::std::u32::MAX as usize); + RegionVid { index: value as u32 } + } + + fn index(self) -> usize { + self.index as usize + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] pub struct SkolemizedRegionVid { pub index: u32, @@ -1037,6 +1050,35 @@ impl RegionKind { flags } + + /// Given an early-bound or free region, returns the def-id where it was bound. + /// For example, consider the regions in this snippet of code: + /// + /// ``` + /// impl<'a> Foo { + /// ^^ -- early bound, declared on an impl + /// + /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c + /// ^^ ^^ ^ anonymous, late-bound + /// | early-bound, appears in where-clauses + /// late-bound, appears only in fn args + /// {..} + /// } + /// ``` + /// + /// Here, `free_region_binding_scope('a)` would return the def-id + /// of the impl, and for all the other highlighted regions, it + /// would return the def-id of the function. In other cases (not shown), this + /// function might return the def-id of a closure. + pub fn free_region_binding_scope(&self, tcx: TyCtxt<'_, '_, '_>) -> DefId { + match self { + ty::ReEarlyBound(br) => { + tcx.parent_def_id(br.def_id).unwrap() + } + ty::ReFree(fr) => fr.scope, + _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), + } + } } /// Type utilities diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index a733e9de5a1a..e2f50c8c8891 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -384,6 +384,11 @@ impl IndexVec { idx } + #[inline] + pub fn pop(&mut self) -> Option { + self.raw.pop() + } + #[inline] pub fn len(&self) -> usize { self.raw.len() @@ -411,7 +416,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated(&self) -> Enumerated> + pub fn iter_enumerated(&self) -> Enumerated> { self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData }) } @@ -427,7 +432,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated_mut(&mut self) -> Enumerated> + pub fn iter_enumerated_mut(&mut self) -> Enumerated> { self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData }) } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 3a20343033c2..8862ba3545eb 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -31,6 +31,7 @@ #![feature(i128)] #![feature(conservative_impl_trait)] #![feature(specialization)] +#![feature(underscore_lifetimes)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 1a8ad9718cfa..97c34a1c3027 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -207,10 +207,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(INVALID_TYPE_PARAM_DEFAULT), reference: "issue #36887 ", }, - FutureIncompatibleInfo { - id: LintId::of(EXTRA_REQUIREMENT_IN_IMPL), - reference: "issue #37166 ", - }, FutureIncompatibleInfo { id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), reference: "issue #37872 ", @@ -276,4 +272,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { "converted into hard error, see https://github.com/rust-lang/rust/issues/36891"); store.register_removed("lifetime_underscore", "converted into hard error, see https://github.com/rust-lang/rust/issues/36892"); + store.register_removed("extra_requirement_in_impl", + "converted into hard error, see https://github.com/rust-lang/rust/issues/37166"); } diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 2a7a62cd64b5..f5f7b53a2357 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -112,7 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { None } else { - Some(nll::compute_regions(infcx, def_id, mir)) + Some(nll::compute_regions(infcx, def_id, param_env, mir)) }; let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; @@ -136,7 +136,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, node_id: id, move_data: &mdpe.move_data, param_env: param_env, - fake_infer_ctxt: &infcx, }; let mut state = InProgress::new(flow_borrows, @@ -148,13 +147,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } #[allow(dead_code)] -pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - mir: &'b Mir<'tcx>, +pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + mir: &'cx Mir<'tcx>, node_id: ast::NodeId, - move_data: &'b MoveData<'tcx>, - param_env: ParamEnv<'tcx>, - fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>, + move_data: &'cx MoveData<'tcx>, + param_env: ParamEnv<'gcx>, } // (forced to be `pub` due to its use as an associated type below.) @@ -177,12 +175,10 @@ struct FlowInProgress where BD: BitDenotation { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx> - for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> -{ - type FlowState = InProgress<'b, 'gcx, 'tcx>; +impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + type FlowState = InProgress<'cx, 'gcx, 'tcx>; - fn mir(&self) -> &'b Mir<'tcx> { self.mir } + fn mir(&self) -> &'cx Mir<'tcx> { self.mir } fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { flow_state.each_flow(|b| b.reset_to_entry_of(bb), @@ -437,12 +433,12 @@ enum WriteKind { Move, } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn access_lvalue(&mut self, context: Context, lvalue_span: (&Lvalue<'tcx>, Span), kind: (ShallowOrDeep, ReadOrWrite), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { let (sd, rw) = kind; @@ -501,7 +497,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> lvalue_span: (&Lvalue<'tcx>, Span), kind: ShallowOrDeep, mode: MutateMode, - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. match mode { MutateMode::WriteAndRead => { @@ -522,7 +518,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, (rvalue, span): (&Rvalue<'tcx>, Span), _location: Location, - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { match *rvalue { Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => { let access_kind = match bk { @@ -579,7 +575,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, consume_via_drop: ConsumeKind, (operand, span): (&Operand<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { match *operand { Operand::Consume(ref lvalue) => { self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state) @@ -592,11 +588,22 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, consume_via_drop: ConsumeKind, lvalue_span: (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { let lvalue = lvalue_span.0; + let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - let moves_by_default = - self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP); + + // Erase the regions in type before checking whether it moves by + // default. There are a few reasons to do this: + // + // - They should not affect the result. + // - It avoids adding new region constraints into the surrounding context, + // which would trigger an ICE, since the infcx will have been "frozen" by + // the NLL region context. + let gcx = self.tcx.global_tcx(); + let erased_ty = gcx.lift(&self.tcx.erase_regions(&ty)).unwrap(); + let moves_by_default = erased_ty.moves_by_default(gcx, self.param_env, DUMMY_SP); + if moves_by_default { // move of lvalue: check if this is move of already borrowed path self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state); @@ -619,11 +626,11 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn check_if_reassignment_to_immutable_state(&mut self, context: Context, (lvalue, span): (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { let move_data = self.move_data; // determine if this path has a non-mut owner (and thus needs checking). @@ -674,7 +681,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, desired_action: &str, lvalue_span: (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // FIXME: analogous code in check_loans first maps `lvalue` to // its base_path ... but is that what we want here? let lvalue = self.base_path(lvalue_span.0); @@ -802,7 +809,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn check_if_assigned_path_is_moved(&mut self, context: Context, (lvalue, span): (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // recur down lvalue; dispatch to check_if_path_is_moved when necessary let mut lvalue = lvalue; loop { @@ -1015,11 +1022,11 @@ enum NoMovePathFound { ReachedStatic, } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn each_borrow_involving_path(&mut self, _context: Context, access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>), - flow_state: &InProgress<'b, 'gcx, 'tcx>, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, mut op: F) where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue<'tcx>) -> Control { @@ -1119,11 +1126,11 @@ mod prefixes { } - pub(super) struct Prefixes<'c, 'gcx: 'tcx, 'tcx: 'c> { - mir: &'c Mir<'tcx>, - tcx: TyCtxt<'c, 'gcx, 'tcx>, + pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + mir: &'cx Mir<'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, kind: PrefixSet, - next: Option<&'c Lvalue<'tcx>>, + next: Option<&'cx Lvalue<'tcx>>, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -1137,21 +1144,21 @@ mod prefixes { Supporting, } - impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { + impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Returns an iterator over the prefixes of `lvalue` /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. - pub(super) fn prefixes<'d>(&self, - lvalue: &'d Lvalue<'tcx>, - kind: PrefixSet) - -> Prefixes<'d, 'gcx, 'tcx> where 'b: 'd + pub(super) fn prefixes(&self, + lvalue: &'cx Lvalue<'tcx>, + kind: PrefixSet) + -> Prefixes<'cx, 'gcx, 'tcx> { Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx } } } - impl<'c, 'gcx, 'tcx> Iterator for Prefixes<'c, 'gcx, 'tcx> { - type Item = &'c Lvalue<'tcx>; + impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { + type Item = &'cx Lvalue<'tcx>; fn next(&mut self) -> Option { let mut cursor = match self.next { None => return None, @@ -1244,7 +1251,7 @@ mod prefixes { } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn report_use_of_moved_or_uninitialized(&mut self, _context: Context, desired_action: &str, @@ -1483,7 +1490,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // End-user visible description of `lvalue` fn describe_lvalue(&self, lvalue: &Lvalue<'tcx>) -> String { let mut buf = String::new(); @@ -1641,7 +1648,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // FIXME (#16118): function intended to allow the borrow checker // to be less precise in its handling of Box while still allowing // moves out of a Box. They should be removed when/if we stop diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 928c07b7fbc0..2e4dddc212b3 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -22,7 +22,7 @@ use rustc_data_structures::indexed_vec::{IndexVec}; use dataflow::{BitDenotation, BlockSets, DataflowOperator}; pub use dataflow::indexes::BorrowIndex; use transform::nll::region_infer::RegionInferenceContext; -use transform::nll::ToRegionIndex; +use transform::nll::ToRegionVid; use syntax_pos::Span; @@ -145,7 +145,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { location: Location) { if let Some(regioncx) = self.nonlexical_regioncx { for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { - let borrow_region = borrow_data.region.to_region_index(); + let borrow_region = borrow_data.region.to_region_vid(); if !regioncx.region_contains_point(borrow_region, location) { // The region checker really considers the borrow // to start at the point **after** the location of diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5e65398e2b90..af309342dc52 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(core_intrinsics)] #![feature(decl_macro)] #![feature(i128_type)] +#![feature(match_default_bindings)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] #![feature(collection_placement)] diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index b095a198d8f0..1f905d32f84e 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -9,7 +9,7 @@ // except according to those terms. use rustc::hir; -use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind}; +use rustc::mir::{Location, Lvalue, Mir, Rvalue}; use rustc::mir::visit::Visitor; use rustc::mir::Lvalue::Projection; use rustc::mir::{LvalueProjection, ProjectionElem}; @@ -21,9 +21,8 @@ use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; -use super::subtype; use super::LivenessResults; -use super::ToRegionIndex; +use super::ToRegionVid; use super::region_infer::RegionInferenceContext; pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( @@ -102,7 +101,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { self.infcx .tcx .for_each_free_region(&live_ty, |live_region| { - let vid = live_region.to_region_index(); + let vid = live_region.to_region_vid(); self.regioncx.add_live_point(vid, location); }); } @@ -179,29 +178,6 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { self.visit_mir(self.mir); } - fn add_borrow_constraint( - &mut self, - location: Location, - destination_lv: &Lvalue<'tcx>, - borrow_region: ty::Region<'tcx>, - _borrow_kind: BorrowKind, - _borrowed_lv: &Lvalue<'tcx>, - ) { - let tcx = self.infcx.tcx; - let span = self.mir.source_info(location).span; - let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); - - let destination_region = match destination_ty.sty { - ty::TyRef(r, _) => r, - _ => bug!() - }; - - self.regioncx.add_outlives(span, - borrow_region.to_region_index(), - destination_region.to_region_index(), - location.successor_within_block()); - } - fn add_reborrow_constraint( &mut self, location: Location, @@ -227,8 +203,8 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { let span = self.mir.source_info(location).span; self.regioncx.add_outlives(span, - base_region.to_region_index(), - borrow_region.to_region_index(), + base_region.to_region_vid(), + borrow_region.to_region_vid(), location.successor_within_block()); } } @@ -237,35 +213,22 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { } impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { - fn visit_statement(&mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location) { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location); - debug!("visit_statement(statement={:?}, location={:?})", statement, location); - - // Look for a statement like: + // Look for an rvalue like: // - // D = & L + // & L // - // where D is the path to which we are assigning, and - // L is the path that is borrowed. - if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind { - if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv { - self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv); - self.add_reborrow_constraint(location, region, borrowed_lv); - } - - let tcx = self.infcx.tcx; - let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); - let rv_ty = rv.ty(self.mir, tcx); - - let span = self.mir.source_info(location).span; - for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) { - self.regioncx.add_outlives(span, a, b, location.successor_within_block()); - } + // where L is the path that is borrowed. In that case, we have + // to add the reborrow constraints (which don't fall out + // naturally from the type-checker). + if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue { + self.add_reborrow_constraint(location, region, borrowed_lv); } - self.super_statement(block, statement, location); + self.super_rvalue(rvalue, location); } } diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs index 554d212880ea..92a8a714d525 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -25,17 +25,18 @@ use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::middle::free_region::FreeRegionMap; -use rustc::ty; +use rustc::ty::{self, RegionVid}; use rustc::ty::subst::Substs; use rustc::util::nodemap::FxHashMap; +use rustc_data_structures::indexed_vec::Idx; #[derive(Debug)] pub struct FreeRegions<'tcx> { /// Given a free region defined on this function (either early- or - /// late-bound), this maps it to its internal region index. The - /// corresponding variable will be "capped" so that it cannot - /// grow. - pub indices: FxHashMap, usize>, + /// late-bound), this maps it to its internal region index. When + /// the region context is created, the first N variables will be + /// created based on these indices. + pub indices: FxHashMap, RegionVid>, /// The map from the typeck tables telling us how to relate free regions. pub free_region_map: &'tcx FreeRegionMap<'tcx>, @@ -49,6 +50,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>( let mut indices = FxHashMap(); + // `'static` is always free. + insert_free_region(&mut indices, infcx.tcx.types.re_static); + // Extract the early regions. let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id); for item_subst in item_substs { @@ -78,9 +82,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>( } fn insert_free_region<'tcx>( - free_regions: &mut FxHashMap, usize>, + free_regions: &mut FxHashMap, RegionVid>, region: ty::Region<'tcx>, ) { - let len = free_regions.len(); - free_regions.entry(region).or_insert(len); + let next = RegionVid::new(free_regions.len()); + free_regions.entry(region).or_insert(next); } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index f27d0a8da16c..147f061ad113 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -11,19 +11,19 @@ use rustc::hir::def_id::DefId; use rustc::mir::Mir; use rustc::infer::InferCtxt; -use rustc::ty::{self, RegionKind}; +use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; use std::collections::BTreeSet; use transform::MirSource; +use transform::type_check; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use util as mir_util; use self::mir_util::PassWhere; mod constraint_generation; +mod subtype_constraint_generation; mod free_regions; -mod subtype; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; @@ -36,13 +36,24 @@ mod renumber; pub fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, def_id: DefId, + param_env: ty::ParamEnv<'gcx>, mir: &mut Mir<'tcx>, ) -> RegionInferenceContext<'tcx> { // Compute named region information. let free_regions = &free_regions::free_regions(infcx, def_id); // Replace all regions with fresh inference variables. - let num_region_variables = renumber::renumber_mir(infcx, free_regions, mir); + renumber::renumber_mir(infcx, free_regions, mir); + + // Run the MIR type-checker. + let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); + let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir); + + // Create the region inference context, taking ownership of the region inference + // data that was contained in `infcx`. + let var_origins = infcx.take_region_var_origins(); + let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir); + subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets); // Compute what is live where. let liveness = &LivenessResults { @@ -63,11 +74,10 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( ), }; - // Create the region inference context, generate the constraints, - // and then solve them. - let mut regioncx = RegionInferenceContext::new(free_regions, num_region_variables, mir); - let param_env = infcx.tcx.param_env(def_id); + // Generate non-subtyping constraints. constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness); + + // Solve the region constraints. regioncx.solve(infcx, &mir); // Dump MIR results into a file, if that is enabled. This let us @@ -123,12 +133,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => for region in regioncx.regions() { - writeln!( - out, - "| {:?}: {:?}", - region, - regioncx.region_value(region) - )?; + writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; }, // Before each basic block, dump out the values @@ -152,23 +157,19 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( }); } -newtype_index!(RegionIndex { - DEBUG_FORMAT = "'_#{}r", -}); - /// Right now, we piggy back on the `ReVar` to store our NLL inference -/// regions. These are indexed with `RegionIndex`. This method will -/// assert that the region is a `ReVar` and convert the internal index -/// into a `RegionIndex`. This is reasonable because in our MIR we -/// replace all free regions with inference variables. -pub trait ToRegionIndex { - fn to_region_index(&self) -> RegionIndex; +/// regions. These are indexed with `RegionVid`. This method will +/// assert that the region is a `ReVar` and extract its interal index. +/// This is reasonable because in our MIR we replace all free regions +/// with inference variables. +pub trait ToRegionVid { + fn to_region_vid(&self) -> RegionVid; } -impl ToRegionIndex for RegionKind { - fn to_region_index(&self) -> RegionIndex { +impl ToRegionVid for RegionKind { + fn to_region_vid(&self) -> RegionVid { if let &ty::ReVar(vid) = self { - RegionIndex::new(vid.index as usize) + vid } else { bug!("region is not an ReVar: {:?}", self) } diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 553d5ad4a320..1609c1236b0c 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::RegionIndex; use super::free_regions::FreeRegions; use rustc::infer::InferCtxt; +use rustc::infer::RegionVariableOrigin; +use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::region_constraints::VarOrigins; use rustc::mir::{Location, Mir}; -use rustc::ty; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::ty::{self, RegionVid}; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; use std::collections::BTreeSet; use std::fmt; @@ -21,28 +23,22 @@ use syntax_pos::Span; pub struct RegionInferenceContext<'tcx> { /// Contains the definition for every region variable. Region - /// variables are identified by their index (`RegionIndex`). The + /// variables are identified by their index (`RegionVid`). The /// definition contains information about where the region came /// from as well as its final inferred value. - definitions: IndexVec>, - - /// The indices of all "free regions" in scope. These are the - /// lifetime parameters (anonymous and named) declared in the - /// function signature: - /// - /// fn foo<'a, 'b>(x: &Foo<'a, 'b>) - /// ^^ ^^ ^ - /// - /// These indices will be from 0..N, as it happens, but we collect - /// them into a vector for convenience. - free_regions: Vec, + definitions: IndexVec>, /// The constraints we have accumulated and used during solving. constraints: Vec, } -#[derive(Default)] struct RegionDefinition<'tcx> { + /// Why we created this variable. Mostly these will be + /// `RegionVariableOrigin::NLL`, but some variables get created + /// elsewhere in the code with other causes (e.g., instantiation + /// late-bound-regions). + origin: RegionVariableOrigin, + /// If this is a free-region, then this is `Some(X)` where `X` is /// the name of the region. name: Option>, @@ -66,7 +62,7 @@ struct RegionDefinition<'tcx> { #[derive(Clone, Default, PartialEq, Eq)] struct Region { points: BTreeSet, - free_regions: BTreeSet, + free_regions: BTreeSet, } impl fmt::Debug for Region { @@ -84,7 +80,7 @@ impl Region { self.points.insert(point) } - fn add_free_region(&mut self, region: RegionIndex) -> bool { + fn add_free_region(&mut self, region: RegionVid) -> bool { self.free_regions.insert(region) } @@ -93,19 +89,24 @@ impl Region { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { - /// Where did this constraint arise? - span: Span, + // NB. The ordering here is not significant for correctness, but + // it is for convenience. Before we dump the constraints in the + // debugging logs, we sort them, and we'd like the "super region" + // to be first, etc. (In particular, span should remain last.) /// The region SUP must outlive SUB... - sup: RegionIndex, + sup: RegionVid, /// Region that must be outlived. - sub: RegionIndex, + sub: RegionVid, /// At this location. point: Location, + + /// Where did this constraint arise? + span: Span, } impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { @@ -113,17 +114,16 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free /// regions defined in `free_regions`. - pub fn new( - free_regions: &FreeRegions<'tcx>, - num_region_variables: usize, - mir: &Mir<'tcx>, - ) -> Self { + pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self { + // Create a RegionDefinition for each inference variable. + let definitions = var_origins + .into_iter() + .map(|origin| RegionDefinition::new(origin)) + .collect(); + let mut result = Self { - definitions: (0..num_region_variables) - .map(|_| RegionDefinition::default()) - .collect(), + definitions: definitions, constraints: Vec::new(), - free_regions: Vec::new(), }; result.init_free_regions(free_regions, mir); @@ -151,16 +151,18 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// is just itself. R1 (`'b`) in contrast also outlives `'a` and /// hence contains R0 and R1. fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) { - let &FreeRegions { - ref indices, - ref free_region_map, + let FreeRegions { + indices, + free_region_map, } = free_regions; // For each free region X: - for (free_region, index) in indices { - let variable = RegionIndex::new(*index); - - self.free_regions.push(variable); + for (free_region, &variable) in indices { + // These should be free-region variables. + assert!(match self.definitions[variable].origin { + RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true, + _ => false, + }); // Initialize the name and a few other details. self.definitions[variable].name = Some(free_region); @@ -181,10 +183,19 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // Add `end(X)` into the set for X. self.definitions[variable].value.add_free_region(variable); + // `'static` outlives all other free regions as well. + if let ty::ReStatic = free_region { + for &other_variable in indices.values() { + self.definitions[variable] + .value + .add_free_region(other_variable); + } + } + // Go through each region Y that outlives X (i.e., where // Y: X is true). Add `end(X)` into the set for `Y`. for superregion in free_region_map.regions_that_outlive(&free_region) { - let superregion_index = RegionIndex::new(indices[superregion]); + let superregion_index = indices[superregion]; self.definitions[superregion_index] .value .add_free_region(variable); @@ -200,24 +211,24 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { } /// Returns an iterator over all the region indices. - pub fn regions(&self) -> impl Iterator { + pub fn regions(&self) -> impl Iterator { self.definitions.indices() } /// Returns true if the region `r` contains the point `p`. /// /// Until `solve()` executes, this value is not particularly meaningful. - pub fn region_contains_point(&self, r: RegionIndex, p: Location) -> bool { + pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { self.definitions[r].value.contains_point(p) } /// Returns access to the value of `r` for debugging purposes. - pub(super) fn region_value(&self, r: RegionIndex) -> &fmt::Debug { + pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug { &self.definitions[r].value } /// Indicates that the region variable `v` is live at the point `point`. - pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) { + pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) { debug!("add_live_point({:?}, {:?})", v, point); let definition = &mut self.definitions[v]; if !definition.constant { @@ -233,8 +244,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { pub(super) fn add_outlives( &mut self, span: Span, - sup: RegionIndex, - sub: RegionIndex, + sup: RegionVid, + sub: RegionVid, point: Location, ) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); @@ -267,23 +278,28 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - fn propagate_constraints( - &mut self, - mir: &Mir<'tcx>, - ) -> Vec<(RegionIndex, Span, RegionIndex)> { + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, RegionVid)> { let mut changed = true; let mut dfs = Dfs::new(mir); let mut error_regions = FxHashSet(); let mut errors = vec![]; + + debug!("propagate_constraints()"); + debug!("propagate_constraints: constraints={:#?}", { + let mut constraints: Vec<_> = self.constraints.iter().collect(); + constraints.sort(); + constraints + }); + while changed { changed = false; for constraint in &self.constraints { - debug!("constraint: {:?}", constraint); + debug!("propagate_constraints: constraint={:?}", constraint); let sub = &self.definitions[constraint.sub].value.clone(); let sup_def = &mut self.definitions[constraint.sup]; - debug!(" sub (before): {:?}", sub); - debug!(" sup (before): {:?}", sup_def.value); + debug!("propagate_constraints: sub (before): {:?}", sub); + debug!("propagate_constraints: sup (before): {:?}", sup_def.value); if !sup_def.constant { // If this is not a constant, then grow the value as needed to @@ -293,8 +309,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { changed = true; } - debug!(" sup (after) : {:?}", sup_def.value); - debug!(" changed : {:?}", changed); + debug!("propagate_constraints: sup (after) : {:?}", sup_def.value); + debug!("propagate_constraints: changed : {:?}", changed); } else { // If this is a constant, check whether it *would // have* to grow in order for the constraint to be @@ -310,7 +326,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { .difference(&sup_def.value.free_regions) .next() .unwrap(); - debug!(" new_region : {:?}", new_region); + debug!("propagate_constraints: new_region : {:?}", new_region); if error_regions.insert(constraint.sup) { errors.push((constraint.sup, constraint.span, new_region)); } @@ -398,3 +414,30 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { changed } } + +impl<'tcx> RegionDefinition<'tcx> { + fn new(origin: RegionVariableOrigin) -> Self { + // Create a new region definition. Note that, for free + // regions, these fields get updated later in + // `init_free_regions`. + Self { + origin, + name: None, + constant: false, + value: Region::default(), + } + } +} + +impl fmt::Debug for Constraint { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + formatter, + "({:?}: {:?} @ {:?}) due to {:?}", + self.sup, + self.sub, + self.point, + self.span + ) + } +} diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index a3ff7a041ca0..1076b774de65 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -8,15 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::indexed_vec::Idx; -use rustc::ty::subst::{Kind, Substs}; -use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::ty::subst::Substs; +use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable}; +use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; -use rustc::infer::{self as rustc_infer, InferCtxt}; -use syntax_pos::DUMMY_SP; -use std::collections::HashMap; +use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; +use super::ToRegionVid; use super::free_regions::FreeRegions; /// Replaces all free regions appearing in the MIR with fresh @@ -25,33 +24,35 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, free_regions: &FreeRegions<'tcx>, mir: &mut Mir<'tcx>, -) -> usize { +) { // Create inference variables for each of the free regions // declared on the function signature. let free_region_inference_vars = (0..free_regions.indices.len()) - .map(|_| { - infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + .map(RegionVid::new) + .map(|vid_expected| { + let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion); + assert_eq!(vid_expected, r.to_region_vid()); + r }) .collect(); + debug!("renumber_mir()"); + debug!("renumber_mir: free_regions={:#?}", free_regions); + debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); + let mut visitor = NLLVisitor { infcx, - lookup_map: HashMap::new(), - num_region_variables: free_regions.indices.len(), free_regions, free_region_inference_vars, arg_count: mir.arg_count, }; visitor.visit_mir(mir); - visitor.num_region_variables } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - lookup_map: HashMap, - num_region_variables: usize, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, free_regions: &'a FreeRegions<'tcx>, - free_region_inference_vars: Vec>, + free_region_inference_vars: IndexVec>, arg_count: usize, } @@ -59,16 +60,17 @@ 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, value: &T) -> T + 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| { - self.num_region_variables += 1; - self.infcx - .next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + let origin = NLLRegionVariableOrigin::Inferred(ty_context); + self.infcx.next_nll_region_var(origin) }) } @@ -78,6 +80,8 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!("renumber_free_regions(value={:?})", value); + self.infcx .tcx .fold_regions(value, &mut false, |region, _depth| { @@ -86,26 +90,6 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { }) } - fn store_region(&mut self, region: &RegionKind, lookup: TyContext) { - if let RegionKind::ReVar(rid) = *region { - self.lookup_map.entry(rid).or_insert(lookup); - } - } - - fn store_ty_regions(&mut self, ty: &Ty<'tcx>, ty_context: TyContext) { - for region in ty.regions() { - self.store_region(region, ty_context); - } - } - - fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) { - if let Some(ty) = kind.as_type() { - self.store_ty_regions(&ty, ty_context); - } else if let Some(region) = kind.as_region() { - self.store_region(region, ty_context); - } - } - fn is_argument_or_return_slot(&self, local: Local) -> bool { // The first argument is return slot, next N are arguments. local.index() <= self.arg_count @@ -116,56 +100,55 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { let is_arg = match ty_context { TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), - _ => false, + TyContext::ReturnTy(..) => true, + TyContext::Location(..) => false, }; + debug!( + "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", + ty, + is_arg, + ty_context + ); let old_ty = *ty; *ty = if is_arg { self.renumber_free_regions(&old_ty) } else { - self.renumber_regions(&old_ty) + self.renumber_regions(ty_context, &old_ty) }; - self.store_ty_regions(ty, ty_context); + debug!("visit_ty: ty={:?}", ty); } fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { - *substs = self.renumber_regions(&{ *substs }); + debug!("visit_substs(substs={:?}, location={:?})", substs, location); + let ty_context = TyContext::Location(location); - for kind in *substs { - self.store_kind_regions(kind, ty_context); - } + *substs = self.renumber_regions(ty_context, &{ *substs }); + + debug!("visit_substs: substs={:?}", substs); } - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - match *rvalue { - Rvalue::Ref(ref mut r, _, _) => { - let old_r = *r; - *r = self.renumber_regions(&old_r); - let ty_context = TyContext::Location(location); - self.store_region(r, ty_context); - } - Rvalue::Use(..) | - Rvalue::Repeat(..) | - Rvalue::Len(..) | - Rvalue::Cast(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) | - Rvalue::NullaryOp(..) | - Rvalue::Aggregate(..) => { - // These variants don't contain regions. - } - } - self.super_rvalue(rvalue, location); + fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) { + debug!("visit_region(region={:?}, location={:?})", region, location); + + let old_region = *region; + let ty_context = TyContext::Location(location); + *region = self.renumber_regions(ty_context, &old_region); + + debug!("visit_region: region={:?}", region); } fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) { - *substs = self.renumber_regions(substs); + debug!( + "visit_closure_substs(substs={:?}, location={:?})", + substs, + location + ); + let ty_context = TyContext::Location(location); - for kind in substs.substs { - self.store_kind_regions(kind, ty_context); - } + *substs = self.renumber_regions(ty_context, substs); + + debug!("visit_closure_substs: substs={:?}", substs); } fn visit_statement( diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs deleted file mode 100644 index 953fc0eb733c..000000000000 --- a/src/librustc_mir/transform/nll/subtype.rs +++ /dev/null @@ -1,99 +0,0 @@ -// 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 super::RegionIndex; -use transform::nll::ToRegionIndex; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; - -pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> Vec<(RegionIndex, RegionIndex)> -{ - let mut subtype = Subtype::new(tcx); - match subtype.relate(&a, &b) { - Ok(_) => subtype.outlives_pairs, - - Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b) - } -} - -struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - outlives_pairs: Vec<(RegionIndex, RegionIndex)>, - ambient_variance: ty::Variance, -} - -impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> { - Subtype { - tcx, - outlives_pairs: vec![], - ambient_variance: ty::Covariant, - } - } -} - -impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> { - fn tag(&self) -> &'static str { "Subtype" } - fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx } - fn a_is_expected(&self) -> bool { true } // irrelevant - - fn relate_with_variance>(&mut self, - variance: ty::Variance, - a: &T, - b: &T) - -> RelateResult<'tcx, T> - { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - - let result = self.relate(a, b); - self.ambient_variance = old_ambient_variance; - result - } - - fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - relate::super_relate_tys(self, t, t2) - } - - fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>) - -> RelateResult<'tcx, ty::Region<'tcx>> { - let a = r_a.to_region_index(); - let b = r_b.to_region_index(); - - match self.ambient_variance { - ty::Covariant => { - self.outlives_pairs.push((b, a)); - }, - - ty::Invariant => { - self.outlives_pairs.push((a, b)); - self.outlives_pairs.push((b, a)); - }, - - ty::Contravariant => { - self.outlives_pairs.push((a, b)); - }, - - ty::Bivariant => {}, - } - - Ok(r_a) - } - - fn binders(&mut self, _a: &ty::Binder, _b: &ty::Binder) - -> RelateResult<'tcx, ty::Binder> - where T: Relate<'tcx> - { - unimplemented!(); - } -} diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs new file mode 100644 index 000000000000..c1850c76541d --- /dev/null +++ b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs @@ -0,0 +1,112 @@ +// 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 rustc::mir::Mir; +use rustc::infer::region_constraints::Constraint; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::ty; +use transform::type_check::MirTypeckRegionConstraints; +use transform::type_check::OutlivesSet; + +use super::free_regions::FreeRegions; +use super::region_infer::RegionInferenceContext; + +/// When the MIR type-checker executes, it validates all the types in +/// the MIR, and in the process generates a set of constraints that +/// must hold regarding the regions in the MIR, along with locations +/// *where* they must hold. This code takes those constriants and adds +/// them into the NLL `RegionInferenceContext`. +pub(super) fn generate<'tcx>( + regioncx: &mut RegionInferenceContext<'tcx>, + free_regions: &FreeRegions<'tcx>, + mir: &Mir<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, +) { + SubtypeConstraintGenerator { + regioncx, + free_regions, + mir, + }.generate(constraints); +} + +struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { + regioncx: &'cx mut RegionInferenceContext<'tcx>, + free_regions: &'cx FreeRegions<'tcx>, + mir: &'cx Mir<'tcx>, +} + +impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { + fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) { + let MirTypeckRegionConstraints { + liveness_set, + outlives_sets, + } = constraints; + + debug!( + "generate(liveness_set={} items, outlives_sets={} items)", + liveness_set.len(), + outlives_sets.len() + ); + + for (region, location) 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); + } + + for OutlivesSet { locations, data } in outlives_sets { + debug!("generate: constraints at: {:#?}", locations); + let RegionConstraintData { + constraints, + verifys, + givens, + } = data; + + for constraint in constraints.keys() { + debug!("generate: constraint: {:?}", constraint); + let (a_vid, b_vid) = match constraint { + Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid), + Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid), + Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)), + Constraint::RegSubReg(a_r, b_r) => { + (self.to_region_vid(a_r), self.to_region_vid(b_r)) + } + }; + + // We have the constraint that `a_vid <= b_vid`. Add + // `b_vid: a_vid` to our region checker. Note that we + // reverse direction, because `regioncx` talks about + // "outlives" (`>=`) whereas the region constraints + // talk about `<=`. + let span = self.mir.source_info(locations.from_location).span; + self.regioncx + .add_outlives(span, b_vid, a_vid, locations.at_location); + } + + assert!(verifys.is_empty(), "verifys not yet implemented"); + assert!( + givens.is_empty(), + "MIR type-checker does not use givens (thank goodness)" + ); + } + } + + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { + // Every region that we see in the constraints came from the + // MIR or from the parameter environment. If the former, it + // will be a region variable. If the latter, it will be in + // the set of free regions *somewhere*. + if let ty::ReVar(vid) = r { + *vid + } else { + self.free_regions.indices[&r] + } + } +} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index dc462cd9c74f..837c3d42fe83 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,8 +11,10 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::infer::{self, InferCtxt, InferOk}; -use rustc::traits; +use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::traits::{self, FulfillmentContext}; +use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, Ty, TyCtxt, TypeVariants}; use rustc::middle::const_val::ConstVal; @@ -27,6 +29,34 @@ use transform::{MirPass, MirSource}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +/// Type checks the given `mir` in the context of the inference +/// context `infcx`. Returns any region constraints that have yet to +/// be proven. +/// +/// This phase of type-check ought to be infallible -- this is because +/// the original, HIR-based type-check succeeded. So if any errors +/// occur here, we will get a `bug!` reported. +pub fn type_check<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + let mut checker = TypeChecker::new(infcx, body_id, param_env); + let errors_reported = { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + verifier.errors_reported + }; + + if !errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + checker.typeck_mir(mir); + } + + checker.constraints +} + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { tcx.sess.diagnostic().span_bug(span, msg); } @@ -51,7 +81,7 @@ macro_rules! span_mirbug_and_err { } enum FieldAccessError { - OutOfRange { field_count: usize } + OutOfRange { field_count: usize }, } /// Verifies that MIR types are sane to not crash further checks. @@ -59,12 +89,12 @@ enum FieldAccessError { /// The sanitize_XYZ methods here take an MIR object and compute its /// type, calling `span_mirbug` and returning an error type if there /// is a problem. -struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> { +struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> { cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, last_span: Span, body_id: ast::NodeId, - errors_reported: bool + errors_reported: bool, } impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { @@ -74,10 +104,12 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - _context: visit::LvalueContext, - location: Location) { + fn visit_lvalue( + &mut self, + lvalue: &Lvalue<'tcx>, + _context: visit::LvalueContext, + location: Location, + ) { self.sanitize_lvalue(lvalue, location); } @@ -116,7 +148,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { body_id: cx.body_id, cx, last_span: mir.span, - errors_reported: false + errors_reported: false, } } @@ -125,7 +157,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() { + if ty.has_escaping_regions() || ty.references_error() { span_mirbug_and_err!(self, parent, "bad type {:?}", ty) } else { ty @@ -135,25 +167,35 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> { debug!("sanitize_lvalue: {:?}", lvalue); match *lvalue { - Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty }, + Lvalue::Local(index) => LvalueTy::Ty { + ty: self.mir.local_decls[index].ty, + }, Lvalue::Static(box Static { def_id, ty: sty }) => { let sty = self.sanitize_type(lvalue, sty); let ty = self.tcx().type_of(def_id); - let ty = self.cx.normalize(&ty); - if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) { + let ty = self.cx.normalize(&ty, location); + if let Err(terr) = self.cx + .eq_types(self.last_span, ty, sty, location.at_self()) + { span_mirbug!( - self, lvalue, "bad static type ({:?}: {:?}): {:?}", - ty, sty, terr); + self, + lvalue, + "bad static type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); } LvalueTy::Ty { ty: sty } - - }, + } Lvalue::Projection(ref proj) => { let base_ty = self.sanitize_lvalue(&proj.base, location); if let LvalueTy::Ty { ty } = base_ty { if ty.references_error() { assert!(self.errors_reported); - return LvalueTy::Ty { ty: self.tcx().types.err }; + return LvalueTy::Ty { + ty: self.tcx().types.err, + }; } } self.sanitize_projection(base_ty, &proj.elem, lvalue, location) @@ -161,12 +203,13 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn sanitize_projection(&mut self, - base: LvalueTy<'tcx>, - pi: &LvalueElem<'tcx>, - lvalue: &Lvalue<'tcx>, - _: Location) - -> LvalueTy<'tcx> { + fn sanitize_projection( + &mut self, + base: LvalueTy<'tcx>, + pi: &LvalueElem<'tcx>, + lvalue: &Lvalue<'tcx>, + location: Location, + ) -> LvalueTy<'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); @@ -176,23 +219,21 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); LvalueTy::Ty { ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "deref of non-pointer {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "deref of non-pointer {:?}", base_ty) + }), } } ProjectionElem::Index(i) => { let index_ty = Lvalue::Local(i).ty(self.mir, tcx).to_ty(tcx); if index_ty != tcx.types.usize { LvalueTy::Ty { - ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i) + ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), } } else { LvalueTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "index of non-array {:?}", base_ty) + }), } } } @@ -200,73 +241,82 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // consider verifying in-bounds LvalueTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "index of non-array {:?}", base_ty) + }), } } - ProjectionElem::Subslice { from, to } => { - LvalueTy::Ty { - ty: match base_ty.sty { - ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); - let min_size = (from as u64) + (to as u64); - if let Some(rest_size) = size.checked_sub(min_size) { - tcx.mk_array(inner, rest_size) - } else { - span_mirbug_and_err!( - self, lvalue, "taking too-small slice of {:?}", base_ty) - } - } - ty::TySlice(..) => base_ty, - _ => { + ProjectionElem::Subslice { from, to } => LvalueTy::Ty { + ty: match base_ty.sty { + ty::TyArray(inner, size) => { + let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let min_size = (from as u64) + (to as u64); + if let Some(rest_size) = size.checked_sub(min_size) { + tcx.mk_array(inner, rest_size) + } else { span_mirbug_and_err!( - self, lvalue, "slice of non-array {:?}", base_ty) + self, + lvalue, + "taking too-small slice of {:?}", + base_ty + ) } } - } - } - ProjectionElem::Downcast(adt_def1, index) => - match base_ty.sty { - ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { - if index >= adt_def.variants.len() { - LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, - lvalue, - "cast to variant #{:?} but enum only has {:?}", - index, - adt_def.variants.len()) - } - } else { - LvalueTy::Downcast { - adt_def, - substs, - variant_index: index - } + ty::TySlice(..) => base_ty, + _ => span_mirbug_and_err!(self, lvalue, "slice of non-array {:?}", base_ty), + }, + }, + ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { + ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { + if index >= adt_def.variants.len() { + LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, + lvalue, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len() + ), + } + } else { + LvalueTy::Downcast { + adt_def, + substs, + variant_index: index, } } - _ => LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, lvalue, "can't downcast {:?} as {:?}", - base_ty, adt_def1) - } + } + _ => LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, + lvalue, + "can't downcast {:?} as {:?}", + base_ty, + adt_def1 + ), }, + }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(lvalue, fty); - match self.field_ty(lvalue, base, field) { + match self.field_ty(lvalue, base, field, location) { Ok(ty) => { - if let Err(terr) = self.cx.eq_types(span, ty, fty) { + if let Err(terr) = self.cx.eq_types(span, ty, fty, location.at_self()) { span_mirbug!( - self, lvalue, "bad field access ({:?}: {:?}): {:?}", - ty, fty, terr); + self, + lvalue, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); } } - Err(FieldAccessError::OutOfRange { field_count }) => { - span_mirbug!( - self, lvalue, "accessed field #{} but variant only has {}", - field.index(), field_count) - } + Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( + self, + lvalue, + "accessed field #{} but variant only has {}", + field.index(), + field_count + ), } LvalueTy::Ty { ty: fty } } @@ -278,28 +328,31 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.tcx().types.err } - fn field_ty(&mut self, - parent: &fmt::Debug, - base_ty: LvalueTy<'tcx>, - field: Field) - -> Result, FieldAccessError> - { + fn field_ty( + &mut self, + parent: &fmt::Debug, + base_ty: LvalueTy<'tcx>, + field: Field, + location: Location, + ) -> Result, FieldAccessError> { let tcx = self.tcx(); let (variant, substs) = match base_ty { - LvalueTy::Downcast { adt_def, substs, variant_index } => { - (&adt_def.variants[variant_index], substs) - } + LvalueTy::Downcast { + adt_def, + substs, + variant_index, + } => (&adt_def.variants[variant_index], substs), LvalueTy::Ty { ty } => match ty.sty { ty::TyAdt(adt_def, substs) if adt_def.is_univariant() => { - (&adt_def.variants[0], substs) - } + (&adt_def.variants[0], substs) + } ty::TyClosure(def_id, substs) => { return match substs.upvar_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.upvar_tys(def_id, tcx).count() - }) + field_count: substs.upvar_tys(def_id, tcx).count(), + }), } } ty::TyGenerator(def_id, substs, _) => { @@ -311,52 +364,109 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { return match substs.field_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.field_tys(def_id, tcx).count() + 1 - }) - } + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + }; } ty::TyTuple(tys, _) => { return match tys.get(field.index()) { Some(&ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: tys.len() - }) + field_count: tys.len(), + }), } } - _ => return Ok(span_mirbug_and_err!( - self, parent, "can't project out of {:?}", base_ty)) - } + _ => { + return Ok(span_mirbug_and_err!( + self, + parent, + "can't project out of {:?}", + base_ty + )) + } + }, }; if let Some(field) = variant.fields.get(field.index()) { - Ok(self.cx.normalize(&field.ty(tcx, substs))) + Ok(self.cx.normalize(&field.ty(tcx, substs), location)) } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) } } } -pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. +pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, - fulfillment_cx: traits::FulfillmentContext<'tcx>, last_span: Span, body_id: ast::NodeId, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, + constraints: MirTypeckRegionConstraints<'tcx>, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +#[derive(Default)] +pub 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 + /// cases, the MIR type-checker creates temporary regions that do + /// 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)>, + + /// During the course of type-checking, we will accumulate region + /// constraints due to performing subtyping operations or solving + /// traits. These are accumulated into this vector for later use. + pub outlives_sets: Vec>, +} + +/// Outlives relationships between regions and types created at a +/// particular point within the control-flow graph. +pub struct OutlivesSet<'tcx> { + /// The locations associated with these constraints. + pub locations: Locations, + + /// Constraints generated. In terms of the NLL RFC, when you have + /// a constraint `R1: R2 @ P`, the data in there specifies things + /// like `R1: R2`. + pub data: RegionConstraintData<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +pub struct Locations { + /// The location in the MIR that generated these constraints. + /// This is intended for error reporting and diagnosis; the + /// constraints may *take effect* at a distinct spot. + pub from_location: Location, + + /// The constraints must be met at this location. In terms of the + /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field + /// is the `P` value. + pub at_location: Location, } impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { - fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'gcx>) - -> Self { + fn new( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + ) -> Self { TypeChecker { infcx, - fulfillment_cx: traits::FulfillmentContext::new(), last_span: DUMMY_SP, body_id, param_env, reported_errors: FxHashSet(), + constraints: MirTypeckRegionConstraints::default(), } } @@ -364,61 +474,105 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { traits::ObligationCause::misc(span, self.body_id) } - pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { - for obligation in infer_ok.obligations { - self.fulfillment_cx.register_predicate_obligation(self.infcx, obligation); + fn fully_perform_op( + &mut self, + locations: Locations, + op: OP, + ) -> Result> + where + OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, + { + let mut fulfill_cx = FulfillmentContext::new(); + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; + fulfill_cx.register_predicate_obligations(self.infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", e); + } + + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + self.constraints + .outlives_sets + .push(OutlivesSet { locations, data }); } - infer_ok.value + + Ok(value) } - fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(self.last_span), self.param_env) - .sup(sup, sub) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + ) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .sup(sup, sub) + }) } - fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(span), self.param_env) - .eq(b, a) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn eq_types( + &mut self, + _span: Span, + a: Ty<'tcx>, + b: Ty<'tcx>, + locations: Locations, + ) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) + .eq(b, a) + }) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } - fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) { + fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>, location: Location) { debug!("check_stmt: {:?}", stmt); let tcx = self.tcx(); match stmt.kind { StatementKind::Assign(ref lv, ref rv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { - span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + if let Err(terr) = + self.sub_types(rv_ty, lv_ty, location.at_successor_within_block()) + { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); } } - StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { + StatementKind::SetDiscriminant { + ref lvalue, + variant_index, + } => { let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx); let adt = match lvalue_type.sty { TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt, _ => { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): lhs is not an enum", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + lvalue, + variant_index + ); } }; if variant_index >= adt.variants.len() { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): value of of range", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + lvalue, + variant_index + ); }; } StatementKind::StorageLive(_) | @@ -430,9 +584,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_terminator(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>) { + fn check_terminator( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + term_location: Location, + ) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { @@ -446,33 +603,77 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // no checks needed for these } - TerminatorKind::DropAndReplace { ref location, ref value, - .. + target, + unwind, } => { let lv_ty = location.ty(mir, tcx).to_ty(tcx); let rv_ty = value.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { - span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + + let locations = Locations { + from_location: term_location, + at_location: target.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, lv_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); + } + + // Subtle: this assignment occurs at the start of + // *both* blocks, so we need to ensure that it holds + // at both locations. + if let Some(unwind) = unwind { + let locations = Locations { + from_location: term_location, + at_location: unwind.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, lv_ty, locations) { + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); + } } } - TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { + ref discr, + switch_ty, + .. + } => { let discr_ty = discr.ty(mir, tcx); - if let Err(terr) = self.sub_types(discr_ty, switch_ty) { - span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, discr_ty, terr); + if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad SwitchInt ({:?} on {:?}): {:?}", + switch_ty, + discr_ty, + terr + ); } - if !switch_ty.is_integral() && !switch_ty.is_char() && - !switch_ty.is_bool() - { - span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty); + if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); } // FIXME: check the values } - TerminatorKind::Call { ref func, ref args, ref destination, .. } => { + TerminatorKind::Call { + ref func, + ref args, + ref destination, + .. + } => { let func_ty = func.ty(mir, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); let sig = match func_ty.sty { @@ -482,17 +683,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { return; } }; - let sig = tcx.erase_late_bound_regions(&sig); - let sig = self.normalize(&sig); - self.check_call_dest(mir, term, &sig, destination); + let (sig, map) = self.infcx.replace_late_bound_regions_with_fresh_var( + term.source_info.span, + LateBoundRegionConversionTime::FnCall, + &sig, + ); + let sig = self.normalize(&sig, term_location); + self.check_call_dest(mir, term, &sig, destination, term_location); + + // The ordinary liveness rules will ensure that all + // regions in the type of the callee are live here. We + // then further constrain the late-bound regions that + // were instantiated at the call site to be live as + // well. The resulting is that all the input (and + // 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)); + } if self.is_box_free(func) { - self.check_box_free_inputs(mir, term, &sig, args); + self.check_box_free_inputs(mir, term, &sig, args, term_location); } else { - self.check_call_inputs(mir, term, &sig, args); + self.check_call_inputs(mir, term, &sig, args, term_location); } } - TerminatorKind::Assert { ref cond, ref msg, .. } => { + TerminatorKind::Assert { + ref cond, ref msg, .. + } => { let cond_ty = cond.ty(mir, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); @@ -512,13 +732,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { match mir.yield_ty { None => span_mirbug!(self, term, "yield in non-generator"), Some(ty) => { - if let Err(terr) = self.sub_types(value_ty, ty) { - span_mirbug!(self, + if let Err(terr) = self.sub_types(value_ty, ty, term_location.at_self()) { + span_mirbug!( + self, term, "type of yield value is {:?}, but the yield type is {:?}: {:?}", value_ty, ty, - terr); + terr + ); } } } @@ -526,46 +748,66 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_call_dest(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: &Option<(Lvalue<'tcx>, BasicBlock)>) { + fn check_call_dest( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: &Option<(Lvalue<'tcx>, BasicBlock)>, + term_location: Location, + ) { let tcx = self.tcx(); match *destination { - Some((ref dest, _)) => { + Some((ref dest, target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = self.sub_types(sig.output(), dest_ty) { - span_mirbug!(self, term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, sig.output(), terr); + let locations = Locations { + from_location: term_location, + at_location: target_block.start_location(), + }; + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); } - }, + } None => { // FIXME(canndrew): This is_never should probably be an is_uninhabited if !sig.output().is_never() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } - }, + } } } - fn check_call_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) - { + fn check_call_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { debug!("check_call_inputs({:?}, {:?})", sig, args); - if args.len() < sig.inputs().len() || - (args.len() > sig.inputs().len() && !sig.variadic) { + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); - if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) { - span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, fn_arg, op_arg_ty, terr); + if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); } } } @@ -573,22 +815,29 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { match operand { &Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. - }, .. - }) => { - Some(def_id) == self.tcx().lang_items().box_free_fn() - } + literal: + Literal::Value { + value: + &ty::Const { + val: ConstVal::Function(def_id, _), + .. + }, + .. + }, + .. + }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), _ => false, } } - fn check_box_free_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) - { + fn check_box_free_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + term_location: Location, + ) { debug!("check_box_free_inputs"); // box_free takes a Box as a pointer. Allow for that. @@ -621,93 +870,108 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; - if let Err(terr) = self.sub_types(arg_ty, pointee_ty) { - span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", - pointee_ty, arg_ty, terr); + if let Err(terr) = self.sub_types(arg_ty, pointee_ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "bad box_free arg ({:?} <- {:?}): {:?}", + pointee_ty, + arg_ty, + terr + ); } } - fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>) - { - let is_cleanup = block.is_cleanup; - self.last_span = block.terminator().source_info.span; - match block.terminator().kind { - TerminatorKind::Goto { target } => - self.assert_iscleanup(mir, block, target, is_cleanup), - TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.assert_iscleanup(mir, block, *target, is_cleanup); - } - } - TerminatorKind::Resume => { - if !is_cleanup { - span_mirbug!(self, block, "resume on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block, "return on cleanup block") - } - } - TerminatorKind::GeneratorDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block, "generator_drop in cleanup block") - } + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { + let is_cleanup = block_data.is_cleanup; + self.last_span = block_data.terminator().source_info.span; + match block_data.terminator().kind { + TerminatorKind::Goto { target } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup) } + TerminatorKind::SwitchInt { ref targets, .. } => for target in targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); + }, + TerminatorKind::Resume => if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + }, + TerminatorKind::Return => if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + }, + TerminatorKind::GeneratorDrop { .. } => if is_cleanup { + span_mirbug!(self, block_data, "generator_drop in cleanup block") + }, TerminatorKind::Yield { resume, drop, .. } => { if is_cleanup { - span_mirbug!(self, block, "yield in cleanup block") + span_mirbug!(self, block_data, "yield in cleanup block") } - self.assert_iscleanup(mir, block, resume, is_cleanup); + self.assert_iscleanup(mir, block_data, resume, is_cleanup); if let Some(drop) = drop { - self.assert_iscleanup(mir, block, drop, is_cleanup); + self.assert_iscleanup(mir, block_data, drop, is_cleanup); } } TerminatorKind::Unreachable => {} TerminatorKind::Drop { target, unwind, .. } | TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { target, cleanup: unwind, .. } => { - self.assert_iscleanup(mir, block, target, is_cleanup); + TerminatorKind::Assert { + target, + cleanup: unwind, + .. + } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup); if let Some(unwind) = unwind { if is_cleanup { - span_mirbug!(self, block, "unwind on cleanup block") + span_mirbug!(self, block_data, "unwind on cleanup block") } - self.assert_iscleanup(mir, block, unwind, true); + self.assert_iscleanup(mir, block_data, unwind, true); } } - TerminatorKind::Call { ref destination, cleanup, .. } => { + TerminatorKind::Call { + ref destination, + cleanup, + .. + } => { if let &Some((_, target)) = destination { - self.assert_iscleanup(mir, block, target, is_cleanup); + self.assert_iscleanup(mir, block_data, target, is_cleanup); } if let Some(cleanup) = cleanup { if is_cleanup { - span_mirbug!(self, block, "cleanup on cleanup block") + span_mirbug!(self, block_data, "cleanup on cleanup block") } - self.assert_iscleanup(mir, block, cleanup, true); + self.assert_iscleanup(mir, block_data, cleanup, true); } } - TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { - self.assert_iscleanup(mir, block, real_target, is_cleanup); + TerminatorKind::FalseEdges { + real_target, + ref imaginary_targets, + } => { + self.assert_iscleanup(mir, block_data, real_target, is_cleanup); for target in imaginary_targets { - self.assert_iscleanup(mir, block, *target, is_cleanup); + self.assert_iscleanup(mir, block_data, *target, is_cleanup); } } } } - fn assert_iscleanup(&mut self, - mir: &Mir<'tcx>, - ctxt: &fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool) - { + fn assert_iscleanup( + &mut self, + mir: &Mir<'tcx>, + ctxt: &fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool, + ) { if mir[bb].is_cleanup != iscleanuppad { - span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", - bb, iscleanuppad); + span_mirbug!( + self, + ctxt, + "cleanuppad mismatch: {:?} should be {:?}", + bb, + iscleanuppad + ); } } - fn check_local(&mut self, mir: &Mir<'gcx>, local: Local, local_decl: &LocalDecl<'gcx>) { + fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { match mir.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -716,27 +980,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // // Unbound parts of arguments were never required to be Sized // - maybe we should make that a warning. - return + return; } LocalKind::Var | LocalKind::Temp => {} } let span = local_decl.source_info.span; let ty = local_decl.ty; - if !ty.is_sized(self.tcx().global_tcx(), self.param_env, span) { + + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let gcx = self.tcx().global_tcx(); + let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap(); + if !erased_ty.is_sized(gcx, self.param_env, span) { // in current MIR construction, all non-control-flow rvalue // expressions evaluate through `as_temp` or `into` a return // slot or local, so to find all unsized rvalues it is enough // to check all temps, return slots and locals. if let None = self.reported_errors.replace((ty, span)) { - span_err!(self.tcx().sess, span, E0161, - "cannot move a value of type {0}: the size of {0} \ - cannot be statically determined", ty); + span_err!( + self.tcx().sess, + span, + E0161, + "cannot move a value of type {0}: the size of {0} \ + cannot be statically determined", + ty + ); } } } - fn typeck_mir(&mut self, mir: &Mir<'gcx>) { + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); @@ -744,56 +1019,42 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_local(mir, local, local_decl); } - for block in mir.basic_blocks() { - for stmt in &block.statements { + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + let mut location = Location { + block, + statement_index: 0, + }; + for stmt in &block_data.statements { if stmt.source_info.span != DUMMY_SP { self.last_span = stmt.source_info.span; } - self.check_stmt(mir, stmt); + self.check_stmt(mir, stmt, location); + location.statement_index += 1; } - self.check_terminator(mir, block.terminator()); - self.check_iscleanup(mir, block); + self.check_terminator(mir, block_data.terminator(), location); + self.check_iscleanup(mir, block_data); } } - - fn normalize(&mut self, value: &T) -> T - where T: fmt::Debug + TypeFoldable<'tcx> + fn normalize(&mut self, value: &T, location: Location) -> T + where + T: fmt::Debug + TypeFoldable<'tcx>, { - let mut selcx = traits::SelectionContext::new(self.infcx); - let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, self.param_env, cause, value); - - debug!("normalize: value={:?} obligations={:?}", - value, - obligations); - - let fulfill_cx = &mut self.fulfillment_cx; - for obligation in obligations { - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - } - - value - } - - fn verify_obligations(&mut self, mir: &Mir<'tcx>) { - self.last_span = mir.span; - if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", - e); - } + self.fully_perform_op(location.at_self(), |this| { + let mut selcx = traits::SelectionContext::new(this.infcx); + let cause = traits::ObligationCause::misc(this.last_span, ast::CRATE_NODE_ID); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, this.param_env, cause, value); + Ok(InferOk { value, obligations }) + }).unwrap() } } pub struct TypeckMir; impl MirPass for TypeckMir { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &mut Mir<'tcx>) { + fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { let def_id = src.def_id; let id = tcx.hir.as_local_node_id(def_id).unwrap(); debug!("run_pass: {:?}", def_id); @@ -805,17 +1066,44 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let mut checker = TypeChecker::new(&infcx, id, param_env); - { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - if verifier.errors_reported { - // don't do further checks to avoid ICEs - return; - } - } - checker.typeck_mir(mir); - checker.verify_obligations(mir); + let _region_constraint_sets = type_check(&infcx, id, param_env, mir); + + // For verification purposes, we just ignore the resulting + // region constraint sets. Not our problem. =) }); } } + +trait AtLocation { + /// Creates a `Locations` where `self` is both the from-location + /// and the at-location. This means that any required region + /// relationships must hold upon entering the statement/terminator + /// indicated by `self`. This is typically used when processing + /// "inputs" to the given location. + fn at_self(self) -> Locations; + + /// Creates a `Locations` where `self` is the from-location and + /// its successor within the block is the at-location. This means + /// that any required region relationships must hold only upon + /// **exiting** the statement/terminator indicated by `self`. This + /// is for example used when you have a `lv = rv` statement: it + /// indicates that the `typeof(rv) <: typeof(lv)` as of the + /// **next** statement. + fn at_successor_within_block(self) -> Locations; +} + +impl AtLocation for Location { + fn at_self(self) -> Locations { + Locations { + from_location: self, + at_location: self, + } + } + + fn at_successor_within_block(self) -> Locations { + Locations { + from_location: self, + at_location: self.successor_within_block(), + } + } +} diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 5dc7a324c2d4..e71f4fbef265 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -348,7 +348,7 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, let indented_retptr = format!("{}let mut {:?}: {};", INDENT, RETURN_POINTER, - mir.return_ty); + mir.local_decls[RETURN_POINTER].ty); writeln!(w, "{0:1$} // return pointer", indented_retptr, ALIGN)?; diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 272f13b28030..ea0fa945c378 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -471,7 +471,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // // 2. Things go horribly wrong if we use subtype. The reason for // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_inference`, as well as the test + // `givens` field in `region_constraints`, as well as the test // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, // for details. Short version is that we must sometimes detect // relationships between specific region variables and regions diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 139449e5ab0a..24efb7917042 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -10,8 +10,6 @@ use rustc::hir::{self, ImplItemKind, TraitItemKind}; use rustc::infer::{self, InferOk}; -use rustc::middle::free_region::FreeRegionMap; -use rustc::middle::region; use rustc::ty::{self, TyCtxt}; use rustc::ty::util::ExplicitSelf; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; @@ -38,8 +36,7 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m_span: Span, trait_m: &ty::AssociatedItem, impl_trait_ref: ty::TraitRef<'tcx>, - trait_item_span: Option, - old_broken_mode: bool) { + trait_item_span: Option) { debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); @@ -79,8 +76,7 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m, impl_m_span, trait_m, - impl_trait_ref, - old_broken_mode) { + impl_trait_ref) { return; } } @@ -89,8 +85,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m: &ty::AssociatedItem, impl_m_span: Span, trait_m: &ty::AssociatedItem, - impl_trait_ref: ty::TraitRef<'tcx>, - old_broken_mode: bool) + impl_trait_ref: ty::TraitRef<'tcx>) -> Result<(), ErrorReported> { let trait_to_impl_substs = impl_trait_ref.substs; @@ -106,7 +101,6 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_name: impl_m.name, impl_item_def_id: impl_m.def_id, trait_item_def_id: trait_m.def_id, - lint_id: if !old_broken_mode { Some(impl_m_node_id) } else { None }, }, }; @@ -342,22 +336,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - if old_broken_mode { - // FIXME(#18937) -- this is how the code used to - // work. This is buggy because the fulfillment cx creates - // region obligations that get overlooked. The right - // thing to do is the code below. But we keep this old - // pass around temporarily. - let region_scope_tree = region::ScopeTree::default(); - let mut free_regions = FreeRegionMap::new(); - free_regions.relate_free_regions_from_predicates(¶m_env.caller_bounds); - infcx.resolve_regions_and_report_errors(impl_m.def_id, - ®ion_scope_tree, - &free_regions); - } else { - let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); - fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); - } + let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); + fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); Ok(()) }) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d9f502cbdeeb..b3a07027fb03 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -137,7 +137,7 @@ mod autoderef; pub mod dropck; pub mod _match; pub mod writeback; -pub mod regionck; +mod regionck; pub mod coercion; pub mod demand; pub mod method; @@ -658,29 +658,10 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { value: &T) -> T where T : TypeFoldable<'tcx> { - let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, param_env, value); + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); self.register_infer_ok_obligations(ok) } - fn normalize_associated_types_in_as_infer_ok(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> - where T : TypeFoldable<'tcx> - { - debug!("normalize_associated_types_in(value={:?})", value); - let mut selcx = traits::SelectionContext::new(self); - let cause = ObligationCause::misc(span, body_id); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - debug!("normalize_associated_types_in: result={:?} predicates={:?}", - value, - obligations); - InferOk { value, obligations } - } - /// Replace any late-bound regions bound in `value` with /// free variants attached to `all_outlive_scope`. fn liberate_late_bound_regions(&self, @@ -1340,24 +1321,12 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir::ImplItemKind::Method(..) => { let trait_span = tcx.hir.span_if_local(ty_trait_item.def_id); if ty_trait_item.kind == ty::AssociatedKind::Method { - let err_count = tcx.sess.err_count(); compare_impl_method(tcx, &ty_impl_item, impl_item.span, &ty_trait_item, impl_trait_ref, - trait_span, - true); // start with old-broken-mode - if err_count == tcx.sess.err_count() { - // old broken mode did not report an error. Try with the new mode. - compare_impl_method(tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - trait_span, - false); // use the new mode - } + trait_span); } else { let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324, "item `{}` is an associated method, \ @@ -1986,10 +1955,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> InferOk<'tcx, T> where T : TypeFoldable<'tcx> { - self.inh.normalize_associated_types_in_as_infer_ok(span, - self.body_id, - self.param_env, - value) + self.inh.partially_normalize_associated_types_in(span, + self.body_id, + self.param_env, + value) } pub fn require_type_meets(&self, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index ad7978480a6b..a17133d412c7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -84,18 +84,14 @@ use check::dropck; use check::FnCtxt; -use middle::free_region::FreeRegionMap; use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; -use rustc::traits; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; +use rustc::ty::{self, Ty}; +use rustc::infer::{self, OutlivesEnvironment}; use rustc::ty::adjustment; -use rustc::ty::outlives::Component; -use rustc::ty::wf; use std::mem; use std::ops::Deref; @@ -117,7 +113,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn regionck_expr(&self, body: &'gcx hir::Body) { let subject = self.tcx.hir.body_owner_def_id(body.id()); let id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(id), id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(id), + id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_body(body); @@ -126,7 +126,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.resolve_regions_and_report_errors(); assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } /// Region checking during the WF phase for items. `wf_tys` are the @@ -137,37 +137,48 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wf_tys: &[Ty<'tcx>]) { debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys); let subject = self.tcx.hir.local_def_id(item_id); - let mut rcx = RegionCtxt::new(self, RepeatingScope(item_id), item_id, Subject(subject)); - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.relate_free_regions(wf_tys, item_id, span); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(item_id), + item_id, + Subject(subject), + self.param_env); + rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } + /// Region check a function body. Not invoked on closures, but + /// only on the "root" fn item (in which closures may be + /// embedded). Walks the function body and adds various add'l + /// constraints that are needed for region inference. This is + /// separated both to isolate "pure" region constraints from the + /// rest of type check and because sometimes we need type + /// inference to have completed before we can determine which + /// constraints to add. pub fn regionck_fn(&self, fn_id: ast::NodeId, body: &'gcx hir::Body) { debug!("regionck_fn(id={})", fn_id); let subject = self.tcx.hir.body_owner_def_id(body.id()); let node_id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(node_id), node_id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(node_id), + node_id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.resolve_regions_and_report_errors(); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes // (e.g., `regionck_item`), we don't have an enclosing tables. assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } } @@ -177,11 +188,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, - pub region_scope_tree: Rc, - free_region_map: FreeRegionMap<'tcx>, + outlives_environment: OutlivesEnvironment<'tcx>, // id of innermost fn body id body_id: ast::NodeId, @@ -197,24 +206,6 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. -#[derive(Debug)] -enum ImpliedBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> { type Target = FnCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -229,8 +220,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { pub fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, RepeatingScope(initial_repeating_scope): RepeatingScope, initial_body_id: ast::NodeId, - Subject(subject): Subject) -> RegionCtxt<'a, 'gcx, 'tcx> { + Subject(subject): Subject, + param_env: ty::ParamEnv<'tcx>) + -> RegionCtxt<'a, 'gcx, 'tcx> { let region_scope_tree = fcx.tcx.region_scope_tree(subject); + let outlives_environment = OutlivesEnvironment::new(param_env); RegionCtxt { fcx, region_scope_tree, @@ -238,20 +232,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_id: initial_body_id, call_site_scope: None, subject_def_id: subject, - region_bound_pairs: Vec::new(), - free_region_map: FreeRegionMap::new(), + outlives_environment, } } - fn set_call_site_scope(&mut self, call_site_scope: Option) - -> Option { - mem::replace(&mut self.call_site_scope, call_site_scope) - } - - fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId { - mem::replace(&mut self.body_id, body_id) - } - fn set_repeating_scope(&mut self, scope: ast::NodeId) -> ast::NodeId { mem::replace(&mut self.repeating_scope, scope) } @@ -295,6 +279,18 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.resolve_type(ty) } + /// This is the "main" function when region-checking a function item or a closure + /// within a function item. It begins by updating various fields (e.g., `call_site_scope` + /// and `outlives_environment`) to be appropriate to the function and then adds constraints + /// derived from the function body. + /// + /// Note that it does **not** restore the state of the fields that + /// it updates! This is intentional, since -- for the main + /// function -- we wish to be able to read the final + /// `outlives_environment` and other fields from the caller. For + /// closures, however, we save and restore any "scoped state" + /// before we invoke this function. (See `visit_fn` in the + /// `intravisit::Visitor` impl below.) fn visit_fn_body(&mut self, id: ast::NodeId, // the id of the fn itself body: &'gcx hir::Body, @@ -304,9 +300,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_fn_body(id={})", id); let body_id = body.id(); + self.body_id = body_id.node_id; let call_site = region::Scope::CallSite(body.value.hir_id.local_id); - let old_call_site_scope = self.set_call_site_scope(Some(call_site)); + self.call_site_scope = Some(call_site); let fn_sig = { let fn_hir_id = self.tcx.hir.node_to_hir_id(id); @@ -318,8 +315,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } }; - let old_region_bounds_pairs_len = self.region_bound_pairs.len(); - // Collect the types from which we create inferred bounds. // For the return type, if diverging, substitute `bool` just // because it will have no effect. @@ -328,8 +323,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let fn_sig_tys: Vec<_> = fn_sig.inputs().iter().cloned().chain(Some(fn_sig.output())).collect(); - let old_body_id = self.set_body_id(body_id.node_id); - self.relate_free_regions(&fn_sig_tys[..], body_id.node_id, span); + self.outlives_environment.add_implied_bounds( + self.fcx, + &fn_sig_tys[..], + body_id.node_id, + span); self.link_fn_args(region::Scope::Node(body.value.hir_id.local_id), &body.arguments); self.visit_body(body); self.visit_region_obligations(body_id.node_id); @@ -342,11 +340,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region); - - self.region_bound_pairs.truncate(old_region_bounds_pairs_len); - - self.set_body_id(old_body_id); - self.set_call_site_scope(old_call_site_scope); } fn visit_region_obligations(&mut self, node_id: ast::NodeId) @@ -358,231 +351,17 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // obligations. So make sure we process those. self.select_all_obligations_or_error(); - // Make a copy of the region obligations vec because we'll need - // to be able to borrow the fulfillment-cx below when projecting. - let region_obligations = - self.fulfillment_cx - .borrow() - .region_obligations(node_id) - .to_vec(); - - for r_o in ®ion_obligations { - debug!("visit_region_obligations: r_o={:?} cause={:?}", - r_o, r_o.cause); - let sup_type = self.resolve_type(r_o.sup_type); - let origin = self.code_to_origin(&r_o.cause, sup_type); - self.type_must_outlive(origin, sup_type, r_o.sub_region); - } - - // Processing the region obligations should not cause the list to grow further: - assert_eq!(region_obligations.len(), - self.fulfillment_cx.borrow().region_obligations(node_id).len()); - } - - fn code_to_origin(&self, - cause: &traits::ObligationCause<'tcx>, - sup_type: Ty<'tcx>) - -> SubregionOrigin<'tcx> { - SubregionOrigin::from_obligation_cause(cause, - || infer::RelateParamBound(cause.span, sup_type)) - } - - /// This method populates the region map's `free_region_map`. It walks over the transformed - /// argument and return types for each function just before we check the body of that function, - /// looking for types where you have a borrowed pointer to other borrowed data (e.g., `&'a &'b - /// [usize]`. We do not allow references to outlive the things they point at, so we can assume - /// that `'a <= 'b`. This holds for both the argument and return types, basically because, on - /// the caller side, the caller is responsible for checking that the type of every expression - /// (including the actual values for the arguments, as well as the return type of the fn call) - /// is well-formed. - /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` - fn relate_free_regions(&mut self, - fn_sig_tys: &[Ty<'tcx>], - body_id: ast::NodeId, - span: Span) { - debug!("relate_free_regions >>"); - - for &ty in fn_sig_tys { - let ty = self.resolve_type(ty); - debug!("relate_free_regions(t={:?})", ty); - let implied_bounds = self.implied_bounds(body_id, ty, span); - - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. - for implication in implied_bounds { - debug!("implication: {:?}", implication); - match implication { - ImpliedBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), - &ty::ReVar(vid_b)) | - ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), - &ty::ReVar(vid_b)) => { - self.add_given(r_a, vid_b); - } - ImpliedBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Param(param_b))); - } - ImpliedBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b))); - } - ImpliedBound::RegionSubRegion(r_a, r_b) => { - // In principle, we could record (and take - // advantage of) every relationship here, but - // we are also free not to -- it simply means - // strictly less that we can successfully type - // check. Right now we only look for things - // relationships between free regions. (It may - // also be that we should revise our inference - // system to be more general and to make use - // of *every* relationship that arises here, - // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); - } - } - } - } - - debug!("<< relate_free_regions"); - } - - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. - fn implied_bounds(&mut self, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span) - -> Vec> { - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Currently (at least) these resulting - // predicates are always guaranteed to be a subset of the original - // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; - - let mut implied_bounds = vec![]; - - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - let obligations = - wf::obligations(self, self.fcx.param_env, body_id, ty, span) - .unwrap_or(vec![]); - - // NB: All of these predicates *ought* to be easily proven - // true. In fact, their correctness is (mostly) implied by - // other parts of the program. However, in #42552, we had - // an annoying scenario where: - // - // - Some `T::Foo` gets normalized, resulting in a - // variable `_1` and a `T: Trait` constraint - // (not sure why it couldn't immediately get - // solved). This result of `_1` got cached. - // - These obligations were dropped on the floor here, - // rather than being registered. - // - Then later we would get a request to normalize - // `T::Foo` which would result in `_1` being used from - // the cache, but hence without the `T: Trait` - // constraint. As a result, `_1` never gets resolved, - // and we get an ICE (in dropck). - // - // Therefore, we register any predicates involving - // inference variables. We restrict ourselves to those - // involving inference variables both for efficiency and - // to avoids duplicate errors that otherwise show up. - self.fcx.register_predicates( - obligations.iter() - .filter(|o| o.predicate.has_infer_types()) - .cloned()); - - // From the full set of obligations, just filter down to the - // region relationships. - implied_bounds.extend( - obligations - .into_iter() - .flat_map(|obligation| { - assert!(!obligation.has_escaping_regions()); - match obligation.predicate { - ty::Predicate::Trait(..) | - ty::Predicate::Equate(..) | - ty::Predicate::Subtype(..) | - ty::Predicate::Projection(..) | - ty::Predicate::ClosureKind(..) | - ty::Predicate::ObjectSafe(..) | - ty::Predicate::ConstEvaluatable(..) => - vec![], - - ty::Predicate::WellFormed(subty) => { - wf_types.push(subty); - vec![] - } - - ty::Predicate::RegionOutlives(ref data) => - match self.tcx.no_late_bound_regions(data) { - None => - vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => - vec![ImpliedBound::RegionSubRegion(r_b, r_a)], - }, - - ty::Predicate::TypeOutlives(ref data) => - match self.tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = self.resolve_type_vars_if_possible(&ty_a); - let components = self.tcx.outlives_components(ty_a); - self.implied_bounds_from_components(r_b, components) - } - }, - }})); - } - - implied_bounds - } - - /// When we have an implied bound that `T: 'a`, we can further break - /// this down to determine what relationships would have to hold for - /// `T: 'a` to hold. We get to assume that the caller has validated - /// those relationships. - fn implied_bounds_from_components(&self, - sub_region: ty::Region<'tcx>, - sup_components: Vec>) - -> Vec> - { - sup_components - .into_iter() - .flat_map(|component| { - match component { - Component::Region(r) => - vec![ImpliedBound::RegionSubRegion(sub_region, r)], - Component::Param(p) => - vec![ImpliedBound::RegionSubParam(sub_region, p)], - Component::Projection(p) => - vec![ImpliedBound::RegionSubProjection(sub_region, p)], - Component::EscapingProjection(_) => - // If the projection has escaping regions, don't - // try to infer any implied bounds even for its - // free components. This is conservative, because - // the caller will still have to prove that those - // free components outlive `sub_region`. But the - // idea is that the WAY that the caller proves - // that may change in the future and we want to - // give ourselves room to get smarter here. - vec![], - Component::UnresolvedInferenceVariable(..) => - vec![], - } - }) - .collect() + self.infcx.process_registered_region_obligations( + self.outlives_environment.region_bound_pairs(), + self.implicit_region_bound, + self.param_env, + self.body_id); } fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, - &self.free_region_map); + self.outlives_environment.free_region_map()); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { @@ -638,10 +417,28 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { NestedVisitorMap::None } - fn visit_fn(&mut self, _fk: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl, - b: hir::BodyId, span: Span, id: ast::NodeId) { - let body = self.tcx.hir.body(b); - self.visit_fn_body(id, body, span) + fn visit_fn(&mut self, + fk: intravisit::FnKind<'gcx>, + _: &'gcx hir::FnDecl, + body_id: hir::BodyId, + span: Span, + id: ast::NodeId) { + assert!(match fk { intravisit::FnKind::Closure(..) => true, _ => false }, + "visit_fn invoked for something other than a closure"); + + // Save state of current function before invoking + // `visit_fn_body`. We will restore afterwards. + let old_body_id = self.body_id; + let old_call_site_scope = self.call_site_scope; + let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); + + let body = self.tcx.hir.body(body_id); + self.visit_fn_body(id, body, span); + + // Restore state from previous function. + self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.call_site_scope = old_call_site_scope; + self.body_id = old_body_id; } //visit_pat: visit_pat, // (..) see above @@ -1137,6 +934,27 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin, ty, minimum_lifetime); } + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive(&self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>) + { + self.infcx.type_must_outlive(self.outlives_environment.region_bound_pairs(), + self.implicit_region_bound, + self.param_env, + origin, + ty, + region); + } + /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the /// resulting pointer is linked to the lifetime of its guarantor (if any). fn link_addr_of(&mut self, expr: &hir::Expr, @@ -1492,345 +1310,4 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin.clone(), ty, expr_region); } } - - /// Ensures that type is well-formed in `region`, which implies (among - /// other things) that all borrowed data reachable via `ty` outlives - /// `region`. - pub fn type_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>) - { - let ty = self.resolve_type(ty); - - debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", - ty, - region, - origin); - - assert!(!ty.has_escaping_regions()); - - let components = self.tcx.outlives_components(ty); - self.components_must_outlive(origin, components, region); - } - - fn components_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - components: Vec>, - region: ty::Region<'tcx>) - { - for component in components { - let origin = origin.clone(); - match component { - Component::Region(region1) => { - self.sub_regions(origin, region, region1); - } - Component::Param(param_ty) => { - self.param_ty_must_outlive(origin, region, param_ty); - } - Component::Projection(projection_ty) => { - self.projection_must_outlive(origin, region, projection_ty); - } - Component::EscapingProjection(subcomponents) => { - self.components_must_outlive(origin, subcomponents, region); - } - Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be - self.tcx.sess.delay_span_bug( - origin.span(), - &format!("unresolved inference variable in outlives: {:?}", v)); - } - } - } - } - - fn param_ty_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - param_ty: ty::ParamTy) { - debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", - region, param_ty, origin); - - let verify_bound = self.param_bound(param_ty); - let generic = GenericKind::Param(param_ty); - self.verify_generic_bound(origin, generic, region, verify_bound); - } - - fn projection_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>) - { - debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, projection_ty, origin); - - // This case is thorny for inference. The fundamental problem is - // that there are many cases where we have choice, and inference - // doesn't like choice (the current region inference in - // particular). :) First off, we have to choose between using the - // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and - // OutlivesProjectionComponent rules, any one of which is - // sufficient. If there are no inference variables involved, it's - // not hard to pick the right rule, but if there are, we're in a - // bit of a catch 22: if we picked which rule we were going to - // use, we could add constraints to the region inference graph - // that make it apply, but if we don't add those constraints, the - // rule might not apply (but another rule might). For now, we err - // on the side of adding too few edges into the graph. - - // Compute the bounds we can derive from the environment or trait - // definition. We know that the projection outlives all the - // regions in this list. - let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); - - debug!("projection_must_outlive: env_bounds={:?}", - env_bounds); - - // If we know that the projection outlives 'static, then we're - // done here. - if env_bounds.contains(&&ty::ReStatic) { - debug!("projection_must_outlive: 'static as declared bound"); - return; - } - - // If declared bounds list is empty, the only applicable rule is - // OutlivesProjectionComponent. If there are inference variables, - // then, we can break down the outlives into more primitive - // components without adding unnecessary edges. - // - // If there are *no* inference variables, however, we COULD do - // this, but we choose not to, because the error messages are less - // good. For example, a requirement like `T::Item: 'r` would be - // translated to a requirement that `T: 'r`; when this is reported - // to the user, it will thus say "T: 'r must hold so that T::Item: - // 'r holds". But that makes it sound like the only way to fix - // the problem is to add `T: 'r`, which isn't true. So, if there are no - // inference variables, we use a verify constraint instead of adding - // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); - if env_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - for component_ty in projection_ty.substs.types() { - self.type_must_outlive(origin.clone(), component_ty, region); - } - - for r in projection_ty.substs.regions() { - self.sub_regions(origin.clone(), region, r); - } - - return; - } - - // If we find that there is a unique declared bound `'b`, and this bound - // appears in the trait reference, then the best action is to require that `'b:'r`, - // so do that. This is best no matter what rule we use: - // - // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to - // the requirement that `'b:'r` - // - OutlivesProjectionComponent: this would require `'b:'r` in addition to - // other conditions - if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { - let unique_bound = env_bounds[0]; - debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound); - if projection_ty.substs.regions().any(|r| env_bounds.contains(&r)) { - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.sub_regions(origin.clone(), region, unique_bound); - return; - } - } - - // Fallback to verifying after the fact that there exists a - // declared bound, or that all the components appearing in the - // projection outlive; in some cases, this may add insufficient - // edges into the inference graph, leading to inference failures - // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); - let generic = GenericKind::Projection(projection_ty); - self.verify_generic_bound(origin, generic.clone(), region, verify_bound); - } - - fn type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { - ty::TyParam(p) => { - self.param_bound(p) - } - ty::TyProjection(data) => { - let declared_bounds = self.projection_declared_bounds(span, data); - self.projection_bound(span, declared_bounds, data) - } - _ => { - self.recursive_type_bound(span, ty) - } - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", - param_ty); - - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); - - // Add in the default bound of fn body that applies to all in - // scope type parameters: - param_bounds.extend(self.implicit_region_bound); - - VerifyBound::AnyRegion(param_bounds) - } - - fn projection_declared_bounds(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds.extend_from_slice( - &self.declared_projection_bounds_from_trait(span, projection_ty)); - - declared_bounds - } - - fn projection_bound(&self, - span: Span, - declared_bounds: Vec>, - projection_ty: ty::ProjectionTy<'tcx>) - -> VerifyBound<'tcx> { - debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, projection_ty); - - // see the extensive comment in projection_must_outlive - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(span, ty); - - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) - } - - fn recursive_type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = vec![]; - - for subty in ty.walk_shallow() { - bounds.push(self.type_bound(span, subty)); - } - - let mut regions = ty.regions(); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); - - if bounds.len() == 1 { - bounds.pop().unwrap() - } else { - VerifyBound::AllBounds(bounds) - } - } - - fn declared_generic_bounds_from_env(&self, generic: GenericKind<'tcx>) - -> Vec> - { - let param_env = &self.param_env; - - // To start, collect bounds from user: - let mut param_bounds = self.tcx.required_region_bounds(generic.to_ty(self.tcx), - param_env.caller_bounds.to_vec()); - - // Next, collect regions we scraped from the well-formedness - // constraints in the fn signature. To do that, we walk the list - // of known relations from the fn ctxt. - // - // This is crucial because otherwise code like this fails: - // - // fn foo<'a, A>(x: &'a A) { x.bar() } - // - // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. - for &(r, p) in &self.region_bound_pairs { - debug!("generic={:?} p={:?}", - generic, - p); - if generic == p { - param_bounds.push(r); - } - } - - param_bounds - } - - fn declared_projection_bounds_from_trait(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - debug!("projection_bounds(projection_ty={:?})", - projection_ty); - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - - // Say we have a projection `>::SomeType`. We are interested - // in looking for a trait definition like: - // - // ``` - // trait SomeTrait<'a> { - // type SomeType : 'a; - // } - // ``` - // - // we can thus deduce that `>::SomeType : 'a`. - let trait_predicates = self.tcx.predicates_of(projection_ty.trait_ref(self.tcx).def_id); - assert_eq!(trait_predicates.parent, None); - let predicates = trait_predicates.predicates.as_slice().to_vec(); - traits::elaborate_predicates(self.tcx, predicates) - .filter_map(|predicate| { - // we're only interesting in `T : 'a` style predicates: - let outlives = match predicate { - ty::Predicate::TypeOutlives(data) => data, - _ => { return None; } - }; - - debug!("projection_bounds: outlives={:?} (1)", - outlives); - - // apply the substitutions (and normalize any projected types) - let outlives = self.instantiate_type_scheme(span, - projection_ty.substs, - &outlives); - - debug!("projection_bounds: outlives={:?} (2)", - outlives); - - let region_result = self.commit_if_ok(|_| { - let (outlives, _) = - self.replace_late_bound_regions_with_fresh_var( - span, - infer::AssocTypeProjection(projection_ty.item_def_id), - &outlives); - - debug!("projection_bounds: outlives={:?} (3)", - outlives); - - // check whether this predicate applies to our current projection - let cause = self.fcx.misc(span); - match self.at(&cause, self.fcx.param_env).eq(outlives.0, ty) { - Ok(ok) => Ok((ok, outlives.1)), - Err(_) => Err(()) - } - }).map(|(ok, result)| { - self.register_infer_ok_obligations(ok); - result - }); - - debug!("projection_bounds: region_result={:?}", - region_result); - - region_result.ok() - }) - .collect() - } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 5227955d7b90..014b8b14edb8 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -75,6 +75,7 @@ This API is completely unstable and subject to change. #![feature(advanced_slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(crate_visibility_modifier)] #![feature(conservative_impl_trait)] #![feature(match_default_bindings)] #![feature(never_type)] diff --git a/src/test/compile-fail/issue-18937.rs b/src/test/compile-fail/issue-18937.rs index 5996c8e54387..f7f84e6452dd 100644 --- a/src/test/compile-fail/issue-18937.rs +++ b/src/test/compile-fail/issue-18937.rs @@ -27,7 +27,6 @@ trait A<'a> { impl<'a> A<'a> for B { fn foo(&mut self, f: F) //~ ERROR impl has stricter - //~^ WARNING future release where F: fmt::Debug + 'static, { self.list.push(Box::new(f)); diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index e3f67d817f3c..34d0482a623a 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -26,9 +26,9 @@ fn main() { // END RUST SOURCE // START rustc.use_x.nll.0.mir -// | '_#0r: {bb0[0], bb0[1], '_#0r} -// | '_#1r: {bb0[0], bb0[1], '_#0r, '_#1r} -// | '_#2r: {bb0[0], bb0[1], '_#2r} -// ... -// fn use_x(_1: &'_#0r mut i32, _2: &'_#1r u32, _3: &'_#0r u32, _4: &'_#2r u32) -> bool { +// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r} +// | '_#1r: {bb0[0], bb0[1], '_#1r} +// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#2r} +// | '_#3r: {bb0[0], bb0[1], '_#3r} +// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { // END rustc.use_x.nll.0.mir diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index c3df0c840ded..f51e839e4fc3 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,12 +28,12 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#5r mut i32; +// let _2: &'_#6r mut i32; // ... -// let _4: &'_#7r mut i32; +// let _4: &'_#8r mut i32; // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index f7276cb29790..ae059febc710 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,15 +31,15 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb2[0], bb2[1]} // | '_#1r: {bb1[1], bb2[0], bb2[1]} +// | '_#2r: {bb1[1], bb2[0], bb2[1]} // ... -// let _2: &'_#1r usize; +// let _2: &'_#2r usize; // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // bb1: { // | Live variables at bb1[0]: [_1, _3] -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // | Live variables at bb1[1]: [_2] // switchInt(const true) -> [0u8: bb3, otherwise: bb2]; // } diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index 6527df26eae7..6d7aa0a26c8a 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} +// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} // END rustc.main.nll.0.mir 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 index aedb3f562a66..2968ae7d485b 100644 --- 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 @@ -46,5 +46,5 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]} +// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]} // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index 23809d176f69..736d2902ec63 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -36,14 +36,14 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb2[0], bb2[1]} +// | '_#1r: {bb1[1], bb2[0], bb2[1]} // ... -// | '_#2r: {bb7[2], bb7[3], bb7[4]} -// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} +// | '_#3r: {bb7[2], bb7[3], bb7[4]} +// | '_#4r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} // ... -// let mut _2: &'_#3r usize; +// let mut _2: &'_#4r usize; // ... -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // ... -// _2 = &'_#2r (*_11); +// _2 = &'_#3r (*_11); // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index cada9c7b2548..fb178b46b470 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,16 +32,16 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} // | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} -// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#2r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#3r: {bb1[5], bb1[6], bb2[0], bb2[1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#1r usize; +// let _2: &'_#2r usize; // ... -// let _6: &'_#2r usize; +// let _6: &'_#3r usize; // ... -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // ... // _7 = _2; // ... diff --git a/src/test/run-pass/implied-bounds-closure-arg-outlives.rs b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs new file mode 100644 index 000000000000..0e5cc574f002 --- /dev/null +++ b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs @@ -0,0 +1,44 @@ +// 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 are able to handle the relationships between free +// regions bound in a closure callback. + +#[derive(Copy, Clone)] +struct MyCx<'short, 'long: 'short> { + short: &'short u32, + long: &'long u32, +} + +impl<'short, 'long> MyCx<'short, 'long> { + fn short(self) -> &'short u32 { self.short } + fn long(self) -> &'long u32 { self.long } + fn set_short(&mut self, v: &'short u32) { self.short = v; } +} + +fn with(op: F) -> R +where + F: for<'short, 'long> FnOnce(MyCx<'short, 'long>) -> R, +{ + op(MyCx { + short: &22, + long: &22, + }) +} + +fn main() { + with(|mut cx| { + // For this to type-check, we need to be able to deduce that + // the lifetime of `l` can be `'short`, even though it has + // input from `'long`. + let l = if true { cx.long() } else { cx.short() }; + cx.set_short(l); + }); +} diff --git a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs index ae4adbfb1f49..3162ef54f39b 100644 --- a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs +++ b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs @@ -42,7 +42,7 @@ impl<'a,'tcx> Foo<'a,'tcx> { // inferring `'_2` to be `'static` in this case, because // it is created outside the closure but then related to // regions bound by the closure itself. See the - // `region_inference.rs` file (and the `givens` field, in + // `region_constraints.rs` file (and the `givens` field, in // particular) for more details. this.foo() })) diff --git a/src/test/ui/compare-method/proj-outlives-region.stderr b/src/test/ui/compare-method/proj-outlives-region.stderr index e58251c846f8..f871f034a924 100644 --- a/src/test/ui/compare-method/proj-outlives-region.stderr +++ b/src/test/ui/compare-method/proj-outlives-region.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/proj-outlives-region.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where U: 'a { } //~ ERROR E0276 | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error diff --git a/src/test/ui/compare-method/region-unrelated.stderr b/src/test/ui/compare-method/region-unrelated.stderr index 95db68fea5cf..1df83c7fb0c3 100644 --- a/src/test/ui/compare-method/region-unrelated.stderr +++ b/src/test/ui/compare-method/region-unrelated.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/region-unrelated.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where V: 'a { } | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs new file mode 100644 index 000000000000..5605206221a1 --- /dev/null +++ b/src/test/ui/nll/get_default.rs @@ -0,0 +1,53 @@ +// 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. + +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// 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-mir + +struct Map { +} + +impl Map { + fn get(&self) -> Option<&String> { None } + fn set(&mut self, v: String) { } +} + +fn ok(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + return v; + } + None => { + map.set(String::new()); // Just AST errors here + } + } + } +} + +fn err(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + map.set(String::new()); // Both AST and MIR error here + return v; + } + None => { + map.set(String::new()); // Just AST errors here + } + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr new file mode 100644 index 000000000000..9586f4267203 --- /dev/null +++ b/src/test/ui/nll/get_default.stderr @@ -0,0 +1,47 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:33:17 + | +28 | match map.get() { + | --- immutable borrow occurs here +... +33 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +37 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:43:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +42 | Some(v) => { +43 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here +... +51 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:47:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +... +47 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +51 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:43:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +42 | Some(v) => { +43 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here + +error: aborting due to 4 previous errors +