pub struct InferCtxt<'tcx> {
Show 15 fields pub tcx: TyCtxt<'tcx>, pub defining_use_anchor: DefiningAnchor, pub considering_regions: bool, pub inner: RefCell<InferCtxtInner<'tcx>>, skip_leak_check: Cell<bool>, lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>, pub selection_cache: SelectionCache<'tcx>, pub evaluation_cache: EvaluationCache<'tcx>, pub reported_trait_errors: RefCell<FxIndexMap<Span, Vec<Predicate<'tcx>>>>, pub reported_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>, tainted_by_errors: Cell<Option<ErrorGuaranteed>>, err_count_on_creation: usize, in_snapshot: Cell<bool>, universe: Cell<UniverseIndex>, pub intercrate: bool,
}

Fields§

§tcx: TyCtxt<'tcx>§defining_use_anchor: DefiningAnchor

The DefId of the item in whose context we are performing inference or typeck. It is used to check whether an opaque type use is a defining use.

If it is DefiningAnchor::Bubble, we can’t resolve opaque types here and need to bubble up the obligation. This frequently happens for short lived InferCtxt within queries. The opaque type obligations are forwarded to the outside until the end up in an InferCtxt for typeck or borrowck.

It is default value is DefiningAnchor::Error, this way it is easier to catch errors that might come up during inference or typeck.

§considering_regions: bool

Whether this inference context should care about region obligations in the root universe. Most notably, this is used during hir typeck as region solving is left to borrowck instead.

§inner: RefCell<InferCtxtInner<'tcx>>§skip_leak_check: Cell<bool>

If set, this flag causes us to skip the ‘leak check’ during higher-ranked subtyping operations. This flag is a temporary one used to manage the removal of the leak-check: for the time being, we still run the leak-check, but we issue warnings. This flag can only be set to true when entering a snapshot.

§lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>

Once region inference is done, the values for each variable.

§selection_cache: SelectionCache<'tcx>

Caches the results of trait selection. This cache is used for things that have to do with the parameters in scope.

§evaluation_cache: EvaluationCache<'tcx>

Caches the results of trait evaluation.

§reported_trait_errors: RefCell<FxIndexMap<Span, Vec<Predicate<'tcx>>>>

the set of predicates on which errors have been reported, to avoid reporting the same error twice.

§reported_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>§tainted_by_errors: Cell<Option<ErrorGuaranteed>>

When an error occurs, we want to avoid reporting “derived” errors that are due to this original failure. Normally, we handle this with the err_count_on_creation count, which basically just tracks how many errors were reported when we started type-checking a fn and checks to see if any new errors have been reported since then. Not great, but it works.

However, when errors originated in other passes – notably resolve – this heuristic breaks down. Therefore, we have this auxiliary flag that one can set whenever one creates a type-error that is due to an error in a prior pass.

Don’t read this flag directly, call is_tainted_by_errors() and set_tainted_by_errors().

§err_count_on_creation: usize

Track how many errors were reported when this infcx is created. If the number of errors increases, that’s also a sign (line tainted_by_errors) to avoid reporting certain kinds of errors.

§in_snapshot: Cell<bool>

This flag is true while there is an active snapshot.

§universe: Cell<UniverseIndex>

What is the innermost universe we have created? Starts out as UniverseIndex::root() but grows from there as we enter universal quantifiers.

N.B., at present, we exclude the universal quantifiers on the item we are type-checking, and just consider those names as part of the root universe. So this would only get incremented when we enter into a higher-ranked (for<..>) type or trait bound.

§intercrate: bool

During coherence we have to assume that other crates may add additional impls which we currently don’t know about.

To deal with this evaluation should be conservative and consider the possibility of impls from outside this crate. This comes up primarily when resolving ambiguity. Imagine there is some trait reference $0: Bar where $0 is an inference variable. If intercrate is true, then we can never say for sure that this reference is not implemented, even if there are no impls at all for Bar, because $0 could be bound to some type that in a downstream crate that implements Bar.

Outside of coherence we set this to false because we are only interested in types that the user could actually have written. In other words, we consider $0: Bar to be unimplemented if there is no type that the user could actually name that would satisfy it. This avoids crippling inference, basically.

Implementations§

Forks the inference context, creating a new inference context with the same inference variables in the same state. This can be used to “branch off” many tests from the same common state. Used in coherence.

Canonicalizes a query value V. When we canonicalize a query, we not only canonicalize unbound inference variables, but we also replace all free regions whatsoever. So for example a query like T: Trait<'static> would be canonicalized to

T: Trait<'?0>

with a mapping M that maps '?0 to 'static.

To get a good understanding of what is happening here, check out the chapter in the rustc dev guide.

Like Self::canonicalize_query, but preserves distinct universes. For example, canonicalizing &'?0: Trait<'?1>, where '?0 is in U1 and '?1 is in U3 would be canonicalized to have ?0inU1and’?1inU2`.

This is used for Chalk integration.

Canonicalizes a query response V. When we canonicalize a query response, we only canonicalize unbound inference variables, and we leave other free regions alone. So, continuing with the example from canonicalize_query, if there was an input query T: Trait<'static>, it would have been canonicalized to

T: Trait<'?0>

with a mapping M that maps '?0 to 'static. But if we found that there exists only one possible impl of Trait, and it looks like

impl<T> Trait<'static> for T { .. }

then we would prepare a query result R that (among other things) includes a mapping to '?0 := 'static. When canonicalizing this query result R, we would leave this reference to 'static alone.

To get a good understanding of what is happening here, check out the chapter in the rustc dev guide.

A variant of canonicalize_query that does not canonicalize 'static. This is useful when the query implementation can perform more efficient handling of 'static regions (e.g. trait evaluation).

This method is meant to be invoked as the final step of a canonical query implementation. It is given:

  • the instantiated variables inference_vars created from the query key
  • the result answer of the query
  • a fulfillment context fulfill_cx that may contain various obligations which have yet to be proven.

Given this, the function will process the obligations pending in fulfill_cx:

  • If all the obligations can be proven successfully, it will package up any resulting region obligations (extracted from infcx) along with the fully resolved value answer into a query result (which is then itself canonicalized).
  • If some obligations can be neither proven nor disproven, then the same thing happens, but the resulting query is marked as ambiguous.
  • Finally, if any of the obligations result in a hard error, then Err(NoSolution) is returned.

A version of make_canonicalized_query_response that does not pack in obligations, for contexts that want to drop pending obligations instead of treating them as an ambiguity (e.g. typeck “probing” contexts).

If you DO want to keep track of pending obligations (which include all region obligations, so this includes all cases that care about regions) with this function, you have to do it yourself, by e.g., having them be a part of the answer.

Helper for make_canonicalized_query_response that does everything up until the final canonicalization.

Given the (canonicalized) result to a canonical query, instantiates the result so it can be used, plugging in the values from the canonical query. (Note that the result may have been ambiguous; you should check the certainty level of the query before applying this function.)

To get a good understanding of what is happening here, check out the chapter in the rustc dev guide.

An alternative to instantiate_query_response_and_region_obligations that is more efficient for NLL. NLL is a bit more advanced in the “transition to chalk” than the rest of the compiler. During the NLL type check, all of the “processing” of types and things happens in queries – the NLL checker itself is only interested in the region obligations ('a: 'b or T: 'b) that come out of these queries, which it wants to convert into MIR-based constraints and solve. Therefore, it is most convenient for the NLL Type Checker to directly consume the QueryOutlivesConstraint values that arise from doing a query. This is contrast to other parts of the compiler, which would prefer for those QueryOutlivesConstraint to be converted into the older infcx-style constraints (e.g., calls to sub_regions or register_region_obligation).

Therefore, instantiate_nll_query_response_and_region_obligations performs the same basic operations as instantiate_query_response_and_region_obligations but it returns its result differently:

  • It creates a substitution S that maps from the original query variables to the values computed in the query result. If any errors arise, they are propagated back as an Err result.
  • In the case of a successful substitution, we will append QueryOutlivesConstraint values onto the output_query_region_constraints vector for the solver to use (if an error arises, some values may also be pushed, but they should be ignored).
  • It can happen (though it rarely does currently) that equating types and things will give rise to subobligations that must be processed. In this case, those subobligations are propagated back in the return value.
  • Finally, the query result (of type R) is propagated back, after applying the substitution S.

Given the original values and the (canonicalized) result from computing a query, returns a substitution that can be applied to the query result to convert the result back into the original namespace.

The substitution also comes accompanied with subobligations that arose from unification; these might occur if (for example) we are doing lazy normalization and the value assigned to a type variable is unified with an unnormalized projection.

Given the original values and the (canonicalized) result from computing a query, returns a guess at a substitution that can be applied to the query result to convert the result back into the original namespace. This is called a guess because it uses a quick heuristic to find the values for each canonical variable; if that quick heuristic fails, then we will instantiate fresh inference variables for each canonical variable instead. Therefore, the result of this method must be properly unified

Given a “guess” at the values for the canonical variables in the input, try to unify with the actual values found in the query result. Often, but not always, this is a no-op, because we already found the mapping in the “guessing” step.

See also: query_response_substitution_guess

Converts the region constraints resulting from a query into an iterator of obligations.

Given two sets of values for the same set of canonical variables, unify them. The second set is produced lazily by supplying indices from the first set.

Creates a substitution S for the canonical value with fresh inference variables and applies it to the canonical value. Returns both the instantiated result and the substitution S.

This can be invoked as part of constructing an inference context at the start of a query (see InferCtxtBuilder::build_with_canonical). It basically brings the canonical value “into scope” within your new infcx.

At the end of processing, the substitution S (once canonicalized) then represents the values that you computed for each of the canonical inputs to your query.

Given the “infos” about the canonical variables from some canonical, creates fresh variables with the same characteristics (see instantiate_canonical_var for details). You can then use substitute to instantiate the canonical variable with these inference variables.

Given the “info” about a canonical variable, creates a fresh variable for it. If this is an existentially quantified variable, then you’ll get a new inference variable; if it is a universally quantified variable, you get a placeholder.

Unifies the const variable target_vid with the given constant.

This also tests if the given const ct contains an inference variable which was previously unioned with target_vid. If this is the case, inferring target_vid to ct would result in an infinite type as we continuously replace an inference variable in ct with ct itself.

This is especially important as unevaluated consts use their parents generics. They therefore often contain unused substs, making these errors far more likely.

A good example of this is the following:

#![feature(generic_const_exprs)]

fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
    todo!()
}

fn main() {
    let mut arr = Default::default();
    arr = bind(arr);
}

Here 3 + 4 ends up as ConstKind::Unevaluated which uses the generics of fn bind (meaning that its substs contain N).

bind(arr) now infers that the type of arr must be [u8; N]. The assignment arr = bind(arr) now tries to equate N with 3 + 4.

As 3 + 4 contains N in its substs, this must not succeed.

See src/test/ui/const-generics/occurs-check/ for more examples where this is relevant.

Extracts data used by diagnostic for either types or constants which were stuck during inference.

Used as a fallback in TypeErrCtxt::emit_inference_failure_err in case we weren’t able to get a better error.

Given a [hir::Block], get the span of its last expression or statement, peeling off any inner blocks.

Given a [hir::HirId] for a block, get the span of its last expression or statement, peeling off any inner blocks.

This rather funky routine is used while processing expected types. What happens here is that we want to propagate a coercion through the return type of a fn to its argument. Consider the type of Option::Some, which is basically for<T> fn(T) -> Option<T>. So if we have an expression Some(&[1, 2, 3]), and that has the expected type Option<&[u32]>, we would like to type check &[1, 2, 3] with the expectation of &[u32]. This will cause us to coerce from &[u32; 3] to &[u32] and make the users life more pleasant.

The way we do this is using fudge_inference_if_ok. What the routine actually does is to start a snapshot and execute the closure f. In our example above, what this closure will do is to unify the expectation (Option<&[u32]>) with the actual return type (Option<?T>, where ?T represents the variable instantiated for T). This will cause ?T to be unified with &?a [u32], where ?a is a fresh lifetime variable. The input type (?T) is then returned by f().

At this point, fudge_inference_if_ok will normalize all type variables, converting ?T to &?a [u32] and end the snapshot. The problem is that we can’t just return this type out, because it references the region variable ?a, and that region variable was popped when we popped the snapshot.

So what we do is to keep a list (region_vars, in the code below) of region variables created during the snapshot (here, ?a). We fold the return value and replace any such regions with a new region variable (e.g., ?b) and return the result (&?b [u32]). This can then be used as the expectation for the fn argument.

The important point here is that, for soundness purposes, the regions in question are not particularly important. We will use the expected types to guide coercions, but we will still type-check the resulting types from those coercions against the actual types (?T, Option<?T>) – and remember that after the snapshot is popped, the variable ?T is no longer unified.

Replaces all bound variables (lifetimes, types, and constants) bound by binder with placeholder variables in a new universe. This means that the new placeholders can only be named by inference variables created after this method has been called.

This is the first step of checking subtyping when higher-ranked things are involved. For more details visit the relevant sections of the rustc dev guide.

This is a backwards compatibility hack to prevent breaking changes from lazy TAIT around RPIT handling.

Given the map opaque_types containing the opaque impl Trait types whose underlying, hidden types are being inferred, this method adds constraints to the regions appearing in those underlying hidden types to ensure that they at least do not refer to random scopes within the current function. These constraints are not (quite) sufficient to guarantee that the regions are actually legal values; that final condition is imposed after region inference is done.

The Problem

Let’s work through an example to explain how it works. Assume the current function is as follows:

fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)

Here, we have two impl Trait types whose values are being inferred (the impl Bar<'a> and the impl Bar<'b>). Conceptually, this is sugar for a setup where we define underlying opaque types (Foo1, Foo2) and then, in the return type of foo, we reference those definitions:

type Foo1<'x> = impl Bar<'x>;
type Foo2<'x> = impl Bar<'x>;
fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
                   //  ^^^^ ^^
                   //  |    |
                   //  |    substs
                   //  def_id

As indicating in the comments above, each of those references is (in the compiler) basically a substitution (substs) applied to the type of a suitable def_id (which identifies Foo1 or Foo2).

Now, at this point in compilation, what we have done is to replace each of the references (Foo1<'a>, Foo2<'b>) with fresh inference variables C1 and C2. We wish to use the values of these variables to infer the underlying types of Foo1 and Foo2. That is, this gives rise to higher-order (pattern) unification constraints like:

for<'a> (Foo1<'a> = C1)
for<'b> (Foo1<'b> = C2)

For these equation to be satisfiable, the types C1 and C2 can only refer to a limited set of regions. For example, C1 can only refer to 'static and 'a, and C2 can only refer to 'static and 'b. The job of this function is to impose that constraint.

Up to this point, C1 and C2 are basically just random type inference variables, and hence they may contain arbitrary regions. In fact, it is fairly likely that they do! Consider this possible definition of foo:

fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
        (&*x, &*y)
    }

Here, the values for the concrete types of the two impl traits will include inference variables:

&'0 i32
&'1 i32

Ordinarily, the subtyping rules would ensure that these are sufficiently large. But since impl Bar<'a> isn’t a specific type per se, we don’t get such constraints by default. This is where this function comes into play. It adds extra constraints to ensure that all the regions which appear in the inferred type are regions that could validly appear.

This is actually a bit of a tricky constraint in general. We want to say that each variable (e.g., '0) can only take on values that were supplied as arguments to the opaque type (e.g., 'a for Foo1<'a>) or 'static, which is always in scope. We don’t have a constraint quite of this kind in the current region checker.

The Solution

We generally prefer to make <= constraints, since they integrate best into the region solver. To do that, we find the “minimum” of all the arguments that appear in the substs: that is, some region which is less than all the others. In the case of Foo1<'a>, that would be 'a (it’s the only choice, after all). Then we apply that as a least bound to the variables (e.g., 'a <= '0).

In some cases, there is no minimum. Consider this example:

fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }

Here we would report a more complex “in constraint”, like 'r in ['a, 'b, 'static] (where 'r is some region appearing in the hidden type).

Constrain regions, not the hidden concrete type

Note that generating constraints on each region Rc is not the same as generating an outlives constraint on Tc itself. For example, if we had a function like this:

fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> {
  (x, y)
}

// Equivalent to:
type FooReturn<'a, T> = impl Foo<'a>;
fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> {
  (x, y)
}

then the hidden type Tc would be (&'0 u32, T) (where '0 is an inference variable). If we generated a constraint that Tc: 'a, then this would incorrectly require that T: 'a – but this is not necessary, because the opaque type we create will be allowed to reference T. So we only generate a constraint that '0: 'a.

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).

Trait queries just want to pass back type obligations “as is”

NOTE: Prefer using InferCtxt::check_region_obligations_and_report_errors instead of calling this directly.

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 function fits into the overall expected flow of 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_map: 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.
  • param_env is the parameter environment for the enclosing function.
  • body_id is the body-id whose region obligations are being processed.

Processes registered region obliations and resolves regions, reporting any errors if any were raised. Prefer using this function over manually calling resolve_regions_and_report_errors.

Instead of normalizing an associated type projection, this function generates an inference variable and registers an obligation that this inference variable must be the result of the given projection. This allows us to proceed with projections while they cannot be resolved yet due to missing information or simply due to the lack of access to the trait resolution machinery.

Creates a TypeErrCtxt for emitting various inference errors. During typeck, use FnCtxt::err_ctxt instead.

Returns the origin of the type variable identified by vid, or None if this is not a type variable.

No attempt is made to resolve ty.

Like freshener, but does not replace 'static regions.

Execute f and commit the bindings if closure f returns Ok(_).

Execute f then unroll any bindings it creates.

If should_skip is true, then execute f then unroll any bindings it creates.

Scan the constraints produced since snapshot began and returns:

  • None – if none of them involve “region outlives” constraints
  • Some(true) – if there are 'a: 'b constraints where 'a or 'b is a placeholder
  • Some(false) – if there are 'a: 'b constraints but none involve placeholders

Require that the region r be equal to one of the regions in the set regions.

Processes a Coerce predicate from the fulfillment context. This is NOT the preferred way to handle coercion, which is to invoke FnCtxt::coerce or a similar method (see coercion.rs).

This method here is actually a fallback that winds up being invoked when FnCtxt::coerce encounters unresolved type variables and records a coercion predicate. Presently, this method is equivalent to subtype_predicate – that is, “coercing” a to b winds up actually requiring a <: b. This is of course a valid coercion, but it’s not as flexible as FnCtxt::coerce would be.

(We may refactor this in the future, but there are a number of practical obstacles. Among other things, FnCtxt::coerce presently records adjustments that are required on the HIR in order to perform the coercion, and we don’t currently have a way to manage that.)

Number of type variables created so far.

Creates a fresh region variable with the next available index. The variable will be created in the maximum universe created thus far, allowing it to name any region created thus far.

Creates a fresh region variable with the next available index in the given universe; typically, you can use next_region_var and just use the maximal universe.

Return the universe that the region r was created in. For most regions (e.g., 'static, named regions from the user, etc) this is the root universe U0. For inference variables or placeholders, however, it will return the universe which they are associated.

Number of region variables created so far.

Just a convenient wrapper of next_region_var for using during NLL.

Just a convenient wrapper of next_region_var for using during NLL.

Given a set of generics defined on a type or impl, returns a substitution mapping each type/region parameter to a fresh inference variable.

Returns 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 errors, but where it’s hard to be 100% sure (e.g., unresolved inference variables, regionck errors).

Set the “tainted by errors” flag to true. We call this when we observe an error from a prior pass.

Process the region constraints and return 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_vars_if_possible as well as fully_resolve.

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.

Gives temporary access to the region constraint data.

Takes ownership of the list of variable regions. This implies that all the region constraints 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 variables into the NLL region context.

If TyVar(vid) resolves to a type, return that type. Else, return the universe index of TyVar(vid).

Resolve any type variables found in value – but only one level. So, if the variable ?X is bound to some type Foo<?Y>, then this would return Foo<?Y> (but ?Y may itself be bound to a type).

Useful when you only need to inspect the outermost level of the type and don’t care about nested types (or perhaps you will be resolving them as well, e.g. in a loop).

Where possible, replaces type/const variables in value with their final value. Note that region variables are unaffected. If a type/const variable has not been unified, it is left as is. This is an idempotent operation that does not affect inference state in any way and so you can do it at will.

Returns the first unresolved type or const variable contained in T.

Attempts to resolve all type/region/const variables in value. Region inference must have been run already (e.g., by calling resolve_regions_and_report_errors). If some variable was never unified, an Err results.

This method is idempotent, but it not typically not invoked except during the writeback phase.

Obtains the latest type of the given closure; this may be a closure in the current function, in which case its ClosureKind may not yet be known.

Clears the selection, evaluation, and projection caches. This is useful when repeatedly attempting to select an Obligation while changing only its ParamEnv, since FulfillmentContext doesn’t use probing.

Creates and return a fresh universe that extends all previous universes. Updates self.universe to that new universe.

Resolves and evaluates a constant.

The constant can be located on a trait like <A as B>::C, in which case the given substitutions and environment are used to resolve the constant. Alternatively if the constant has generic parameters in scope the substitutions are used to evaluate the value of the constant. For example in fn foo<T>() { let _ = [0; bar::<T>()]; } the repeat count constant bar::<T>() requires a substitution for T, if the substitution for T is still too generic for the constant to be evaluated then Err(ErrorHandled::TooGeneric) is returned.

This handles inferences variables within both param_env and substs by performing the operation on their respective canonical forms.

ty_or_const_infer_var_changed is equivalent to one of these two:

  • shallow_resolve(ty) != ty (where ty.kind = ty::Infer(_))
  • shallow_resolve(ct) != ct (where ct.kind = ty::ConstKind::Infer(_))

However, ty_or_const_infer_var_changed is more efficient. It’s always inlined, despite being large, because it has only two call sites that are extremely hot (both in traits::fulfill’s checking of stalled_on inference variables), and it handles both Ty and ty::Const without having to resort to storing full GenericArgs in stalled_on.

Trait Implementations§

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.

Layout§

Note: Most layout information is completely unstable and may even differ between compilations. The only exception is types with certain repr(...) attributes. Please see the Rust Reference’s “Type Layout” chapter for details on type layout guarantees.

Size: 760 bytes