struct ProbeContext<'a, 'tcx> {
Show 15 fields fcx: &'a FnCtxt<'a, 'tcx>, span: Span, mode: Mode, method_name: Option<Ident>, return_type: Option<Ty<'tcx>>, orig_steps_var_values: &'a OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], inherent_candidates: Vec<Candidate<'tcx>>, extension_candidates: Vec<Candidate<'tcx>>, impl_dups: FxHashSet<DefId>, allow_similar_names: bool, private_candidate: Option<(DefKind, DefId)>, static_candidates: RefCell<Vec<CandidateSource>>, unsatisfied_predicates: RefCell<Vec<(Predicate<'tcx>, Option<Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>>, scope_expr_id: HirId,
}

Fields§

§fcx: &'a FnCtxt<'a, 'tcx>§span: Span§mode: Mode§method_name: Option<Ident>§return_type: Option<Ty<'tcx>>§orig_steps_var_values: &'a OriginalQueryValues<'tcx>

This is the OriginalQueryValues for the steps queries that are answered in steps.

§steps: &'tcx [CandidateStep<'tcx>]§inherent_candidates: Vec<Candidate<'tcx>>§extension_candidates: Vec<Candidate<'tcx>>§impl_dups: FxHashSet<DefId>§allow_similar_names: bool

When probing for names, include names that are close to the requested name (by Levenshtein distance)

§private_candidate: Option<(DefKind, DefId)>

Some(candidate) if there is a private candidate

§static_candidates: RefCell<Vec<CandidateSource>>

Collects near misses when the candidate functions are missing a self keyword and is only used for error reporting

§unsatisfied_predicates: RefCell<Vec<(Predicate<'tcx>, Option<Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>>

Collects near misses when trait bounds for type parameters are unsatisfied and is only used for error reporting

§scope_expr_id: HirId

Implementations§

For each type T in the step list, this attempts to find a method where the (transformed) self type is exactly T. We do however do one transformation on the adjustment: if we are passing a region pointer in, we will potentially reborrow it to a shorter lifetime. This allows us to transparently pass &mut pointers, in particular, without consuming them for their entire lifetime.

If self_ty is *mut T then this picks *const T methods. The reason why we have a special case for this is because going from *mut T to *const T with autoderefs and autorefs would require dereferencing the pointer, which is not safe.

Sometimes we get in a situation where we have multiple probes that are all impls of the same trait, but we don’t know which impl to use. In this case, since in all cases the external interface of the method can be determined from the trait, it’s ok not to decide. We can basically just collapse all of the probes for various impls into one where-clause probe. This will result in a pending obligation so when more type-info is available we can make the final decision.

Example (src/test/ui/method-two-trait-defer-resolution-1.rs):

trait Foo { ... }
impl Foo for Vec<i32> { ... }
impl Foo for Vec<usize> { ... }

Now imagine the receiver is Vec<_>. It doesn’t really matter at this time which impl we use, so it’s ok to just commit to “using the method from the trait Foo”.

Similarly to probe_for_return_type, this method attempts to find the best matching candidate method where the method name may have been misspelled. Similarly to other Levenshtein based suggestions, we provide at most one such suggestion.

Gets the type of an impl and generate substitutions with inference vars.

Replaces late-bound-regions bound by value with 'static using ty::erase_late_bound_regions.

This is only a reasonable thing to do during the probe phase, not the confirm phase, of method matching. It is reasonable during the probe phase because we don’t consider region relationships at all. Therefore, we can just replace all the region variables with ’static rather than creating fresh region variables. This is nice for two reasons:

  1. Because the numbers of the region variables would otherwise be fairly unique to this particular method call, it winds up creating fewer types overall, which helps for memory usage. (Admittedly, this is a rather small effect, though measurable.)

  2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any late-bound regions with ’static. Otherwise, if we were going to replace late-bound regions with actual region variables as is proper, we’d have to ensure that the same region got replaced with the same variable, which requires a bit more coordination and/or tracking the substitution and so forth.

Determine if the given associated item type is relevant in the current context.

Finds the method with the appropriate name (or return type, as the case may be). If allow_similar_names is set, find methods with close-matching names.

Methods from Deref<Target = FnCtxt<'a, 'tcx>>§

When the previously checked expression (the scrutinee) diverges, warn the user about the match arms being unreachable.

Handle the fallback arm of a desugared if(-let) like a missing else.

Returns true if there was an error forcing the coercion to the () type.

When we have a match as a tail expression in a fn with a returned impl Trait we check if the different arms would work with boxed trait objects instead and provide a structured suggestion in that case.

Returns the adjustment steps.

Give appropriate suggestion when encountering ||{/* not callable */}(), where the likely intention is to call the closure, suggest (||{})(). (#55851)

Give appropriate suggestion when encountering [("a", 0) ("b", 1)], where the likely intention is to create an array containing tuples.

Attempts to reinterpret method(rcvr, args...) as rcvr.method(args...) and suggesting the fix if the method probe is successful.

Returns the kind of unsize information of t, or None if t is unknown.

Given the expected type, figures out what it can about this closure we are about to type check:

Given a projection like “<F as Fn(X)>::Result == Y”, we can deduce everything we need to know about a closure or generator.

The cause_span should be the span that caused us to have this expected signature, or None if we can’t readily know that.

If there is no expected signature, then we will convert the types that the user gave into a signature.

Invoked to compute the signature of a closure expression. This combines any user-provided type annotations (e.g., |x: u32| -> u32 { .. }) with the expected signature.

The approach is as follows:

  • Let S be the (higher-ranked) signature that we derive from the user’s annotations.
  • Let E be the (higher-ranked) signature that we derive from the expectations, if any.
    • If we have no expectation E, then the signature of the closure is S.
    • Otherwise, the signature of the closure is E. Moreover:
      • Skolemize the late-bound regions in E, yielding E'.
      • Instantiate all the late-bound regions bound in the closure within S with fresh (existential) variables, yielding S'
      • Require that E' = S'
        • We could use some kind of subtyping relationship here, I imagine, but equality is easier and works fine for our purposes.

The key intuition here is that the user’s types must be valid from “the inside” of the closure, but the expectation ultimately drives the overall signature.

Examples
fn with_closure<F>(_: F)
  where F: Fn(&u32) -> &u32 { .. }

with_closure(|x: &u32| { ... })

Here:

  • E would be fn(&u32) -> &u32.
  • S would be `fn(&u32) ->
  • E’ is &'!0 u32 -> &'!0 u32
  • S’ is &'?0 u32 -> ?T

S’ can be unified with E’ with ['?0 = '!0, ?T = &'!10 u32].

Arguments
  • expr_def_id: the LocalDefId of the closure expression
  • decl: the HIR declaration of the closure
  • body: the body of the closure
  • expected_sig: the expected signature (if any). Note that this is missing a binder: that is, there may be late-bound regions with depth 1, which are bound then by the closure.

Enforce the user’s types against the expectation. See sig_of_closure_with_expectation for details on the overall strategy.

If there is no expected signature, then we will convert the types that the user gave into a signature.

Also, record this closure signature for later.

Invoked when we are translating the generator that results from desugaring an async fn. Returns the “sugared” return type of the async fn – that is, the return type that the user specified. The “desugared” return type is an impl Future<Output = T>, so we do this by searching through the obligations to extract the T.

Given a projection like

<X as Future>::Output = T

where X is some type that has no late-bound regions, returns Some(T). If the projection is for some other trait, returns None.

Converts the types that the user supplied, in case that doing so should yield an error, but returns back a signature where all parameters are of type TyErr.

Attempt to coerce an expression to a type, and return the adjusted type of the expression, if successful. Adjustments are only recorded if the coercion succeeded. The expressions must not have any pre-existing adjustments.

Same as try_coerce(), but without side-effects.

Returns false if the coercion creates any obligations that result in errors.

Given a type and a target type, this function will calculate and return how many dereference steps needed to achieve expr_ty <: target. If it’s not possible, return None.

Given a type, this function will calculate and return the type given for <Ty as Deref>::Target only if Ty also implements DerefMut.

This function is for diagnostics only, since it does not register trait or region sub-obligations. (presumably we could, but it’s not particularly important for diagnostics…)

Given some expressions, their known unified type and another expression, tries to unify the types, potentially inserting coercions on any of the provided expressions and returns their LUB (aka “common supertype”).

This is really an internal helper. From outside the coercion module, you should instantiate a CoerceMany instance.

Requires that the two types unify, and prints an error message if they don’t.

Checks that the type of expr can be coerced to expected.

N.B., this code relies on self.diverges to be accurate. In particular, assignments to ! will be permitted if the diverges flag is currently “always”.

If the expected type is an enum (Issue #55250) with any variants whose sole field is of the found type, suggest such variants. (Issue #42764)

This function checks whether the method is not static and does not accept other parameters than self.

Identify some cases where as_ref() would be appropriate and suggest it.

Given the following code:

struct Foo;
fn takes_ref(_: &Foo) {}
let ref opt = Some(Foo);

opt.map(|param| takes_ref(param));

Suggest using opt.as_ref().map(|param| takes_ref(param)); instead.

It only checks for Option and Result and won’t work with

opt.map(|param| { takes_ref(param) });

If the given HirId corresponds to a block with a trailing expression, return that expression

Returns whether the given expression is an else if.

This function is used to determine potential “simple” improvements or users’ errors and provide them useful help. For example:

fn some_fn(s: &str) {}

let x = "hey!".to_owned();
some_fn(x); // error

No need to find every potential function which could make a coercion to transform a String into a &str since a & would do the trick!

In addition of this check, it also checks between references mutability state. If the expected is mutable but the provided isn’t, maybe we could just say “Hey, try with &mut!”.

Invariant: If an expression has any sub-expressions that result in a type error, inspecting that expression’s type with ty.references_error() will return true. Likewise, if an expression is known to diverge, inspecting its type with ty::type_is_bot will return true (n.b.: since Rust is strict, | can appear in the type of an expression that does not, itself, diverge: for example, fn() -> |.) Note that inspecting a type’s structure directly may expose the fact that there are actually multiple representations for Error, so avoid that when err needs to be handled differently.

Same as check_expr_with_expectation, but allows us to pass in the arguments of a ExprKind::Call when evaluating its callee when it is an ExprKind::Path.

Does this expression refer to a place that either:

  • Is based on a local or static.
  • Contains a dereference Note that the adjustments for the children of expr should already have been resolved.

explicit_return is true if we’re checking an explicit return expr, and false if we’re checking a trailing expression.

as opposed from the body of a while loop, which we can naively check by iterating parents until we find a loop…

Type check assignment expression expr of form lhs = rhs. The expected type is () and is passed to the function for the purposes of diagnostics.

Checks a method call.

Report an error for a struct field expression when there are fields which aren’t provided.

error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
 --> src/main.rs:8:5
  |
8 |     foo::Foo {};
  |     ^^^^^^^^ missing `you_can_use_this_field`

error: aborting due to previous error

If the last field is a range literal, but it isn’t supposed to be, then they probably meant to use functional update syntax.

Report an error for a struct field expression when there are invisible fields.

error: cannot construct `Foo` with struct literal syntax due to private fields
 --> src/main.rs:8:5
  |
8 |     foo::Foo {};
  |     ^^^^^^^^

error: aborting due to previous error

This method is called after we have encountered a missing field error to recursively search for the field

Performs type inference fallback, setting FnCtxt::fallback_has_occurred if fallback has occurred.

The “diverging fallback” system is rather complicated. This is a result of our need to balance ‘do the right thing’ with backwards compatibility.

“Diverging” type variables are variables created when we coerce a ! type into an unbound type variable ?X. If they never wind up being constrained, the “right and natural” thing is that ?X should “fallback” to !. This means that e.g. an expression like Some(return) will ultimately wind up with a type like Option<!> (presuming it is not assigned or constrained to have some other type).

However, the fallback used to be () (before the ! type was added). Moreover, there are cases where the ! type ‘leaks out’ from dead code into type variables that affect live code. The most common case is something like this:

match foo() {
    22 => Default::default(), // call this type `?D`
    _ => return, // return has type `!`
} // call the type of this match `?M`

Here, coercing the type ! into ?M will create a diverging type variable ?X where ?X <: ?M. We also have that ?D <: ?M. If ?M winds up unconstrained, then ?X will fallback. If it falls back to !, then all the type variables will wind up equal to ! – this includes the type ?D (since ! doesn’t implement Default, we wind up a “trait not implemented” error in code like this). But since the original fallback was (), this code used to compile with ?D = (). This is somewhat surprising, since Default::default() on its own would give an error because the types are insufficiently constrained.

Our solution to this dilemma is to modify diverging variables so that they can either fallback to ! (the default) or to () (the backwards compatibility case). We decide which fallback to use based on whether there is a coercion pattern like this:

?Diverging -> ?V
?NonDiverging -> ?V
?V != ?NonDiverging

Here ?Diverging represents some diverging type variable and ?NonDiverging represents some non-diverging type variable. ?V can be any type variable (diverging or not), so long as it is not equal to ?NonDiverging.

Intuitively, what we are looking for is a case where a “non-diverging” type variable (like ?M in our example above) is coerced into some variable ?V that would otherwise fallback to !. In that case, we make ?V fallback to !, along with anything that would flow into ?V.

The algorithm we use:

  • Identify all variables that are coerced into by a diverging variable. Do this by iterating over each diverging, unsolved variable and finding all variables reachable from there. Call that set D.
  • Walk over all unsolved, non-diverging variables, and find any variable that has an edge into D.

Returns a graph whose nodes are (unresolved) inference variables and where an edge ?A -> ?B indicates that the variable ?A is coerced to ?B.

If ty is an unresolved type variable, returns its root vid.

Produces warning on the given node, if the current point in the function is unreachable, and there hasn’t been another warning.

Resolves type and const variables in ty if possible. Unlike the infcx version (resolve_vars_if_possible), this version will also select obligations if it seems useful, in an effort to get more type information.

Given the substs that we just converted from the HIR, try to canonicalize them and store them as user-given substitutions (i.e., substitutions that must be respected by the NLL check).

This should be invoked before any unifications have occurred, so that annotations like Vec<_> are preserved properly.

Basically whenever we are converting from a type scheme into the fn body space, we always want to normalize associated types as well. This function combines the two.

As instantiate_type_scheme, but for the bounds found in a generic type scheme.

Registers an obligation for checking later, during regionck, that arg is well-formed.

Registers obligations that all substs are well-formed.

Select as many obligations as we can at present.

For the overloaded place expressions (*x, x[3]), the trait returns a type of &T, but the actual type we assign to the expression is T. So this function just peels off the return type by one layer to yield T.

Unifies the output type with the expected type early, for more coercions and forward type information on the input expressions.

Resolves an associated value path into a base type and associated constant, or method resolution. The newly resolved definition is written into type_dependent_defs.

Given a function Node, return its FnDecl if it exists, or None otherwise.

Given a HirId, return the FnDecl of the method it is enclosed by and whether a suggestion can be made, None otherwise.

Add all the obligations that are required, substituting and normalized appropriately.

Resolves typ by a single level if typ is a type variable. If no resolution is possible, then an error is reported. Numeric inference variables may be left unresolved.

Instantiate a QueryResponse in a probe context, without a good ObligationCause.

Returns true if an expression is contained inside the LHS of an assignment expression.

Generic function that factors out common logic from function calls, method calls and overloaded operators.

Type check a let statement.

Given a function block’s HirId, returns its FnDecl if it exists, or None otherwise.

If expr is a match expression that has only one non-! arm, use that arm’s tail expression’s Span, otherwise return expr.span. This is done to give better errors when given code like the following:

if false { return 0i32; } else { 1u32 }
//                               ^^^^ point at this instead of the whole `if` expression

Given a vector of fulfillment errors, try to adjust the spans of the errors to more accurately point at the cause of the failure.

This applies to calls, methods, and struct expressions. This will also try to deduplicate errors that are due to the same cause but might have been created with different ObligationCauses.

On implicit return expressions with mismatched types, provides the following suggestions:

  • Points out the method’s return type as the reason for the expected type.
  • Possible missing semicolon.
  • Possible missing return type if the return type is the default, and not fn main().

When encountering an fn-like type, try accessing the output of the type and suggesting calling it if it satisfies a predicate (i.e. if the output has a method or a field):

fn foo(x: usize) -> usize { x }
let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`

Extracts information about a callable type for diagnostics. This is a heuristic – it doesn’t necessarily mean that a type is always callable, because the callable type must also be well-formed to be called.

When encountering the expected boxed value allocated in the stack, suggest allocating it in the heap by calling Box::new().

When encountering a closure that captures variables, where a FnPtr is expected, suggest a non-capturing closure

When encountering an impl Future where BoxFuture is expected, suggest Box::pin.

A common error is to forget to add a semicolon at the end of a block, e.g.,

fn foo() {
    bar_that_returns_u32()
}

This routine checks if the return expression in a block would make sense on its own as a statement and the return type has been left as default or has been specified as (). If so, it suggests adding a semicolon.

If the expression is the expression of a closure without block (|| expr), a block is needed to be added too (|| { expr; }). This is denoted by needs_block.

A possible error is to forget to add a return type that is needed:

fn foo() {
    bar_that_returns_u32()
}

This routine checks if the return type is left as default, the method is not part of an impl block and that it isn’t the main method. If so, it suggests setting the return type.

check whether the return type is a generic type with a trait bound only suggest this if the generic param is not present in the arguments if this is true, hint them towards changing the return type to impl Trait

fn cant_name_it<T: Fn() -> u32>() -> T {
    || 3
}

Given an expression type mismatch, peel any & expressions until we get to a block expression, and then suggest replacing the braces with square braces if it was possibly mistaken array syntax.

When expecting a bool and finding an Option, suggests using let Some(..) or .is_some()

Suggest wrapping the block in square brackets instead of curly braces in case the block was mistaken array syntax, e.g. { 1 } -> [ 1 ].

Suggest that &T was cloned instead of T because T does not implement Clone, which is a side-effect of autoref.

A common error is to add an extra semicolon:

fn foo() -> usize {
    22;
}

This routine checks if the final statement in a block is an expression with an explicit semicolon whose type is compatible with expected_ty. If so, it suggests removing the semicolon.

Creates an TypeErrCtxt with a reference to the in-progress TypeckResults which is used for diagnostics. Use InferCtxt::err_ctxt to start one without a TypeckResults.

Creates a string version of the expr that includes explicit adjustments. Returns the string and also a bool indicating whether this is a precise suggestion.

This is used to offer suggestions to users. It returns methods that could have been called which have the desired return type. Some effort is made to rule out methods that, if called, would result in an error (basically, the same criteria we would use to decide if a method is a plausible fit for ambiguity purposes).

Suggest calling Ty::method if .method() isn’t found because the method doesn’t take a self receiver.

Suggest calling a field with a type that implements the Fn* traits instead of a method with the same name as the field i.e. (a.my_fn_ptr)(10) instead of a.my_fn_ptr(10).

Suggest possible range with adding parentheses, for example: when encountering 0..1.map(|i| i + 1) suggest (0..1).map(|i| i + 1).

For code rect::area(...), if rect is a local variable and area is a valid assoc method for it, we try to suggest rect.area()

Suggest calling a method on a field i.e. a.field.bar() instead of a.bar()

Print out the type for use in value namespace.

issue #102320, for unwrap_or with closure as argument, suggest unwrap_or_else FIXME: currently not working for suggesting map_or_else, see #102408

Checks whether there is a local type somewhere in the chain of autoderefs of rcvr_ty.

Determines whether the type self_ty supports a method name method_name or not.

Adds a suggestion to call the given method to the provided diagnostic.

Performs method lookup. If lookup is successful, it will return the callee and store an appropriate adjustment for the self-expr. In some cases it may report an error (e.g., invoking the drop method).

Arguments

Given a method call like foo.bar::<T1,...Tn>(a, b + 1, ...):

  • self: the surrounding FnCtxt (!)
  • self_ty: the (unadjusted) type of the self expression (foo)
  • segment: the name and generic arguments of the method (bar::<T1, ...Tn>)
  • span: the span for the method call
  • call_expr: the complete method call: (foo.bar::<T1,...Tn>(...))
  • self_expr: the self expression (foo)
  • args: the expressions of the arguments (a, b + 1, ...)

lookup_method_in_trait is used for overloaded operators. It does a very narrow slice of what the normal probe/confirm path does. In particular, it doesn’t really do any probing: it simply constructs an obligation for a particular trait with the given self type and checks whether that trait is implemented.

Performs a full-qualified function call (formerly “universal function call”) lookup. If lookup is successful, it will return the type of definition and the DefId of the found function definition.

Arguments

Given a function call like Foo::bar::<T1,...Tn>(...):

  • self: the surrounding FnCtxt (!)
  • span: the span of the call, excluding arguments (Foo::bar::<T1, ...Tn>)
  • method_name: the identifier of the function within the container type (bar)
  • self_ty: the type to search within (Foo)
  • self_ty_span the span for the type being searched within (span of Foo)
  • expr_id: the hir::HirId of the expression composing the entire call

Finds item with name item_name defined in impl/trait def_id and return it, or None, if no such item was defined there.

Checks a a <op>= b

Checks a potentially overloaded binary operator.

Provide actionable suggestions when trying to add two strings with incorrect types, like &str + &str, String + String and &str + &String.

If this function returns true it means a note was printed, so we don’t need to print the normal “implementation of std::ops::Add might be missing” note

Type check the given top level pattern against the expected type.

If a Some(span) is provided and origin_expr holds, then the span represents the scrutinee’s span. The scrutinee is found in e.g. match scrutinee { ... } and let pat = scrutinee;.

Otherwise, Some(span) represents the span of a type expression which originated the expected type.

Type check the given pat against the expected type with the provided def_bm (default binding mode).

Outside of this module, check_pat_top should always be used. Conversely, inside this module, check_pat_top should never be used.

Compute the new expected type and default binding mode from the old ones as well as the pattern form we are currently checking.

How should the binding mode and expected type be adjusted?

When the pattern is a path pattern, opt_path_res must be Some(res).

Peel off as many immediately nested & mut? from the expected type as possible and return the new expected type and binding default binding mode. The adjustments vector, if non-empty is stored in a table.

Returns a diagnostic reporting a struct pattern which is missing an .. due to inaccessible fields.

error: pattern requires `..` due to inaccessible fields
  --> src/main.rs:10:9
   |
LL |     let foo::Foo {} = foo::Foo::default();
   |         ^^^^^^^^^^^
   |
help: add a `..`
   |
LL |     let foo::Foo { .. } = foo::Foo::default();
   |                  ^^^^^^

Report that a pattern for a #[non_exhaustive] struct marked with non_exhaustive_omitted_patterns is not exhaustive enough.

Nb: the partner lint for enums lives in compiler/rustc_mir_build/src/thir/pattern/usefulness.rs.

Returns a diagnostic reporting a struct pattern which does not mention some fields.

error[E0027]: pattern does not mention field `bar`
  --> src/main.rs:15:9
   |
LL |     let foo::Foo {} = foo::Foo::new();
   |         ^^^^^^^^^^^ missing field `bar`

Create a reference type with a fresh region variable.

Type check a slice pattern.

Syntactically, these look like [pat_0, ..., pat_n]. Semantically, we are type checking a pattern with structure:

[before_0, ..., before_n, (slice, after_0, ... after_n)?]

The type of slice, if it is present, depends on the expected type. If slice is missing, then so is after_i. If slice is present, it can still represent 0 elements.

Type check the length of an array pattern.

Returns both the type of the variable length pattern (or None), and the potentially inferred array type. We only return None for the slice type if slice.is_none().

Type-check *oprnd_expr with oprnd_expr type-checked already.

Type-check *base_expr[index_expr] with base_expr and index_expr type-checked already.

To type-check base_expr[index_expr], we progressively autoderef (and otherwise adjust) base_expr, looking for a type which either supports builtin indexing or overloaded indexing. This loop implements one step in that search; the autoderef loop is implemented by lookup_indexing.

Try to resolve an overloaded place op. We only deal with the immutable variant here (Deref/Index). In some contexts we would need the mutable variant (DerefMut/IndexMut); those would be later converted by convert_place_derefs_to_mutable.

Convert auto-derefs, indices, etc of an expression from Deref and Index into DerefMut and IndexMut respectively.

This is a second pass of typechecking derefs/indices. We need this because we do not always know whether a place needs to be mutable or not in the first pass. This happens whether there is an implicit mutable reborrow, e.g. when the type is used as the receiver of a method call.

Analysis starting point.

Adjusts the closure capture information to ensure that the operations aren’t unsafe, and that the path can be captured with required capture kind (depending on use in closure, move closure etc.)

Returns the set of adjusted information along with the inferred closure kind and span associated with the closure kind inference.

Note that we always infer a minimal kind, even if we don’t always use that in the final result (i.e., sometimes we’ve taken the closure kind from the expectations instead, and for generators we don’t even implement the closure traits really).

If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple contains a Some() with the Place that caused us to do so.

Analyzes the information collected by InferBorrowKind to compute the min number of Places (and corresponding capture kind) that we need to keep track of to support all the required captured paths.

Note: If this function is called multiple times for the same closure, it will update the existing min_capture map that is stored in TypeckResults.

Eg:

#[derive(Debug)]
struct Point { x: i32, y: i32 }

let s = String::from("s");  // hir_id_s
let mut p = Point { x: 2, y: -2 }; // his_id_p
let c = || {
       println!("{s:?}");  // L1
       p.x += 10;  // L2
       println!("{}" , p.y); // L3
       println!("{p:?}"); // L4
       drop(s);   // L5
};

and let hir_id_L1..5 be the expressions pointing to use of a captured variable on the lines L1..5 respectively.

InferBorrowKind results in a structure like this:

{
      Place(base: hir_id_s, projections: [], ....) -> {
                                                           capture_kind_expr: hir_id_L5,
                                                           path_expr_id: hir_id_L5,
                                                           capture_kind: ByValue
                                                      },
      Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> {
                                                                    capture_kind_expr: hir_id_L2,
                                                                    path_expr_id: hir_id_L2,
                                                                    capture_kind: ByValue
                                                                },
      Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> {
                                                                    capture_kind_expr: hir_id_L3,
                                                                    path_expr_id: hir_id_L3,
                                                                    capture_kind: ByValue
                                                                },
      Place(base: hir_id_p, projections: [], ...) -> {
                                                         capture_kind_expr: hir_id_L4,
                                                         path_expr_id: hir_id_L4,
                                                         capture_kind: ByValue
                                                     },
}

After the min capture analysis, we get:

{
      hir_id_s -> [
           Place(base: hir_id_s, projections: [], ....) -> {
                                                               capture_kind_expr: hir_id_L5,
                                                               path_expr_id: hir_id_L5,
                                                               capture_kind: ByValue
                                                           },
      ],
      hir_id_p -> [
           Place(base: hir_id_p, projections: [], ...) -> {
                                                              capture_kind_expr: hir_id_L2,
                                                              path_expr_id: hir_id_L4,
                                                              capture_kind: ByValue
                                                          },
      ],
}

Perform the migration analysis for RFC 2229, and emit lint disjoint_capture_drop_reorder if needed.

Combines all the reasons for 2229 migrations

Figures out the list of root variables (and their types) that aren’t completely captured by the closure when capture_disjoint_fields is enabled and auto-traits differ between the root variable and the captured paths.

Returns a tuple containing a HashMap of CapturesInfo that maps to a HashSet of trait names if migration is needed for traits for the provided var_hir_id, otherwise returns None

Figures out the list of root variables (and their types) that aren’t completely captured by the closure when capture_disjoint_fields is enabled and drop order of some path starting at that root variable might be affected.

The output list would include a root variable if:

  • It would have been moved into the closure when capture_disjoint_fields wasn’t enabled, and
  • It wasn’t completely captured by the closure, and
  • One of the paths starting at this root variable, that is not captured needs Drop.

This function only returns a HashSet of CapturesInfo for significant drops. If there are no significant drops than None is returned

Figures out the list of root variables (and their types) that aren’t completely captured by the closure when capture_disjoint_fields is enabled and either drop order of some path starting at that root variable might be affected or auto-traits differ between the root variable and the captured paths.

The output list would include a root variable if:

  • It would have been moved into the closure when capture_disjoint_fields wasn’t enabled, and
  • It wasn’t completely captured by the closure, and
  • One of the paths starting at this root variable, that is not captured needs Drop or
  • One of the paths captured does not implement all the auto-traits its root variable implements.

Returns a tuple containing a vector of MigrationDiagnosticInfo, as well as a String containing the reason why root variables whose HirId is contained in the vector should be captured

This is a helper function to compute_2229_migrations_precise_pass. Provided the type of a root variable and a list of captured paths starting at this root variable (expressed using list of Projection slices), it returns true if there is a path that is not captured starting at this root variable that implements Drop.

The way this function works is at a given call it looks at type base_path_ty of some base path say P and then list of projection slices which represent the different captures moved into the closure starting off of P.

This will make more sense with an example:

#![feature(capture_disjoint_fields)]

struct FancyInteger(i32); // This implements Drop

struct Point { x: FancyInteger, y: FancyInteger }
struct Color;

struct Wrapper { p: Point, c: Color }

fn f(w: Wrapper) {
  let c = || {
      // Closure captures w.p.x and w.c by move.
  };

  c();
}

If capture_disjoint_fields wasn’t enabled the closure would’ve moved w instead of the precise paths. If we look closely w.p.y isn’t captured which implements Drop and therefore Drop ordering would change and we want this function to return true.

Call stack to figure out if we need to migrate for w would look as follows:

Our initial base path is just w, and the paths captured from it are w[p, x] and w[c]. Notation:

  • Ty(place): Type of place
  • (a, b): Represents the function parameters base_path_ty and captured_by_move_projs respectively.
                 (Ty(w), [ &[p, x], &[c] ])
//                              |
//                 ----------------------------
//                 |                          |
//                 v                          v
       (Ty(w.p), [ &[x] ])          (Ty(w.c), [ &[] ]) // I(1)
//                 |                          |
//                 v                          v
       (Ty(w.p), [ &[x] ])                 false
//                 |
//                 |
//       -------------------------------
//       |                             |
//       v                             v
    (Ty((w.p).x), [ &[] ])     (Ty((w.p).y), []) // IMP 2
//       |                             |
//       v                             v
       false              NeedsSignificantDrop(Ty(w.p.y))
//                                     |
//                                     v
                                     true

IMP 1 (Ty(w.c), [ &[] ]): Notice the single empty slice inside captured_projs. This implies that the w.c is completely captured by the closure. Since drop for this path will be called when the closure is dropped we don’t need to migrate for it.

IMP 2 (Ty((w.p).y), []): Notice that captured_projs is empty. This implies that this path wasn’t captured by the closure. Also note that even though we didn’t capture this path, the function visits it, which is kind of the point of this function. We then return if the type of w.p.y implements Drop, which in this case is true.

Consider another example:

struct X;
impl Drop for X {}

struct Y(X);
impl Drop for Y {}

fn foo() {
    let y = Y(X);
    let c = || move(y.0);
}

Note that y.0 is captured by the closure. When this function is called for y, it will return true, because even though all paths starting at y are captured, y itself implements Drop which will be affected since y isn’t completely captured.

A captured place is mutable if

  1. Projections don’t include a Deref of an immut-borrow, and
  2. PlaceBase is mut or projections include a Deref of a mut-borrow.

Methods from Deref<Target = Inherited<'tcx>>§

Trait Implementations§

The resulting type after dereferencing.
Dereferences the value.

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: 232 bytes