Struct rustc_typeck::check::regionck::RegionCtxt [−][src]
pub struct RegionCtxt<'a, 'tcx> {
pub fcx: &'a FnCtxt<'a, 'tcx>,
pub region_scope_tree: &'tcx ScopeTree,
outlives_environment: OutlivesEnvironment<'tcx>,
body_id: HirId,
body_owner: LocalDefId,
subject_def_id: LocalDefId,
}
Fields
fcx: &'a FnCtxt<'a, 'tcx>
region_scope_tree: &'tcx ScopeTree
outlives_environment: OutlivesEnvironment<'tcx>
body_id: HirId
body_owner: LocalDefId
subject_def_id: LocalDefId
Implementations
pub fn new(
fcx: &'a FnCtxt<'a, 'tcx>,
initial_body_id: HirId,
Subject: Subject,
param_env: ParamEnv<'tcx>
) -> RegionCtxt<'a, 'tcx>
Try to resolve the type for the given node, returning t_err
if an error results. Note that
we never care about the details of the error, the same error will be detected and reported
in the writeback phase.
Note one important point: we do not attempt to resolve region variables here. This is because regionck is essentially adding constraints to those region variables and so may yet influence how they are resolved.
Consider this silly example:
fn borrow(x: &i32) -> &i32 {x}
fn foo(x: @i32) -> i32 { // block: B
let b = borrow(x); // region: <R0>
*b
}
Here, the region of b
will be <R0>
. <R0>
is constrained to be some subregion of the
block B and some superregion of the call. If we forced it now, we’d choose the smaller
region (the call). But that would make the *b illegal. Since we don’t resolve, the type
of b will be &<R0>.i32
and then *b
will require that <R0>
be bigger than the let and
the *b
expression, so we will effectively resolve <R0>
to be the block B.
Try to resolve the type for the given node.
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., 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 with_mc<F, R>(&self, f: F) -> R where
F: for<'b> FnOnce(MemCategorizationContext<'b, 'tcx>) -> R,
fn with_mc<F, R>(&self, f: F) -> R where
F: for<'b> FnOnce(MemCategorizationContext<'b, 'tcx>) -> R,
Creates a temporary MemCategorizationContext
and pass it to the closure.
Invoked on any adjustments that occur. Checks that if this is a region pointer being dereferenced, the lifetime of the pointer includes the deref expr.
fn check_safety_of_rvalue_destructor_if_necessary(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
span: Span
)
pub fn type_must_outlive(
&self,
origin: SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
region: Region<'tcx>
)
pub fn type_must_outlive(
&self,
origin: SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
region: Region<'tcx>
)
Adds constraints to inference such that T: 'a
holds (or
reports an error if it cannot).
Parameters
origin
, the reason we need this constraintty
, the typeT
region
, the region'a
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).
Computes the guarantors for any ref bindings in a let
and
then ensures that the lifetime of the resulting pointer is
linked to the lifetime of the initialization expression.
Computes the guarantors for any ref bindings in a match and then ensures that the lifetime of the resulting pointer is linked to the lifetime of its guarantor (if any).
Computes the guarantors for any ref bindings in a match and then ensures that the lifetime of the resulting pointer is linked to the lifetime of its guarantor (if any).
Link lifetimes of any ref bindings in root_pat
to the pointers found
in the discriminant, if needed.
fn link_autoref(
&self,
expr: &Expr<'_>,
expr_cmt: &PlaceWithHirId<'tcx>,
autoref: &AutoBorrow<'tcx>
)
fn link_autoref(
&self,
expr: &Expr<'_>,
expr_cmt: &PlaceWithHirId<'tcx>,
autoref: &AutoBorrow<'tcx>
)
Link lifetime of borrowed pointer resulting from autoref to lifetimes in the value being autoref’d.
fn link_region_from_node_type(
&self,
span: Span,
id: HirId,
mutbl: Mutability,
cmt_borrowed: &PlaceWithHirId<'tcx>
)
fn link_region_from_node_type(
&self,
span: Span,
id: HirId,
mutbl: Mutability,
cmt_borrowed: &PlaceWithHirId<'tcx>
)
Like link_region()
, except that the region is extracted from the type of id
,
which must be some reference (&T
, &str
, etc).
fn link_region(
&self,
span: Span,
borrow_region: Region<'tcx>,
borrow_kind: BorrowKind,
borrow_place: &PlaceWithHirId<'tcx>
)
fn link_region(
&self,
span: Span,
borrow_region: Region<'tcx>,
borrow_kind: BorrowKind,
borrow_place: &PlaceWithHirId<'tcx>
)
Informs the inference engine that borrow_cmt
is being borrowed with
kind borrow_kind
and lifetime borrow_region
.
In order to ensure borrowck is satisfied, this may create constraints
between regions, as explained in link_reborrowed_region()
.
fn link_reborrowed_region(
&self,
span: Span,
borrow_region: Region<'tcx>,
ref_region: Region<'tcx>,
ref_mutability: Mutability
) -> bool
fn link_reborrowed_region(
&self,
span: Span,
borrow_region: Region<'tcx>,
ref_region: Region<'tcx>,
ref_mutability: Mutability
) -> bool
This is the most complicated case: the path being borrowed is itself the referent of a borrowed pointer. Let me give an example fragment of code to make clear(er) the situation:
let r: &'a mut T = ...; // the original reference "r" has lifetime 'a
...
&'z *r // the reborrow has lifetime 'z
Now, in this case, our primary job is to add the inference
constraint that 'z <= 'a
. Given this setup, let’s clarify the
parameters in (roughly) terms of the example:
A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T`
borrow_region ^~ ref_region ^~
borrow_kind ^~ ref_kind ^~
ref_cmt ^
Here bk
stands for some borrow-kind (e.g., mut
, uniq
, etc).
There is a complication beyond the simple scenario I just painted: there
may in fact be more levels of reborrowing. In the example, I said the
borrow was like &'z *r
, but it might in fact be a borrow like
&'z **q
where q
has type &'a &'b mut T
. In that case, we want to
ensure that 'z <= 'a
and 'z <= 'b
.
The return value of this function indicates whether we don’t need to the recurse to the next reference up.
This is explained more below.
An upvar may be behind up to 2 references:
- One can come from the reference to a “by-reference” upvar.
- Another one can come from the reference to the closure itself if it’s
a
FnMut
orFn
closure.
This function links the lifetimes of those references to the lifetime of the borrow that’s provided. See RegionCtxt::link_reborrowed_region for some more explanation of this in the general case.
We also supply a cause, and in this case we set the cause to indicate that the reference being “reborrowed” is itself an upvar. This provides a nicer error message should something go wrong.
Methods from Deref<Target = FnCtxt<'a, 'tcx>>
pub fn check_match(
&self,
expr: &'tcx Expr<'tcx>,
scrut: &'tcx Expr<'tcx>,
arms: &'tcx [Arm<'tcx>],
orig_expected: Expectation<'tcx>,
match_src: MatchSource
) -> Ty<'tcx>
fn get_appropriate_arm_semicolon_removal_span(
&self,
arms: &'tcx [Arm<'tcx>],
i: usize,
prior_arm_ty: Option<Ty<'tcx>>,
arm_ty: Ty<'tcx>
) -> (Span, Option<(Span, StatementAsExpression)>)
When the previously checked expression (the scrutinee) diverges, warn the user about the match arms being unreachable.
pub(super) fn if_fallback_coercion<T>(
&self,
span: Span,
then_expr: &'tcx Expr<'tcx>,
coercion: &mut CoerceMany<'tcx, '_, T>
) -> bool where
T: AsCoercionSite,
pub(super) fn if_fallback_coercion<T>(
&self,
span: Span,
then_expr: &'tcx Expr<'tcx>,
coercion: &mut CoerceMany<'tcx, '_, T>
) -> bool where
T: AsCoercionSite,
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.
pub(crate) fn if_cause(
&self,
span: Span,
then_expr: &'tcx Expr<'tcx>,
else_expr: &'tcx Expr<'tcx>,
then_ty: Ty<'tcx>,
else_ty: Ty<'tcx>,
opt_suggest_box_span: Option<Span>
) -> ObligationCause<'tcx>
pub(super) fn demand_scrutinee_type(
&self,
scrut: &'tcx Expr<'tcx>,
contains_ref_bindings: Option<Mutability>,
no_arms: bool
) -> Ty<'tcx>
fn find_block_span(
&self,
block: &'tcx Block<'tcx>,
expected_ty: Option<Ty<'tcx>>
) -> (Span, Option<(Span, StatementAsExpression)>)
pub(crate) fn opt_suggest_box_span(
&self,
span: Span,
outer_ty: &'tcx TyS<'tcx>,
orig_expected: Expectation<'tcx>
) -> Option<Span>
Like autoderef
, but provides a custom Span
to use for calls to
an overloaded Deref
operator
pub fn try_overloaded_deref(
&self,
span: Span,
base_ty: Ty<'tcx>
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>>
Returns the adjustment steps.
pub fn adjust_steps_as_infer_ok(
&self,
autoderef: &Autoderef<'a, 'tcx>
) -> InferOk<'tcx, Vec<Adjustment<'tcx>>>
pub fn check_call(
&self,
call_expr: &'tcx Expr<'tcx>,
callee_expr: &'tcx Expr<'tcx>,
arg_exprs: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>
) -> Ty<'tcx>
fn try_overloaded_call_step(
&self,
call_expr: &'tcx Expr<'tcx>,
callee_expr: &'tcx Expr<'tcx>,
arg_exprs: &'tcx [Expr<'tcx>],
autoderef: &Autoderef<'a, 'tcx>
) -> Option<CallStep<'tcx>>
fn try_overloaded_call_traits(
&self,
call_expr: &Expr<'_>,
adjusted_ty: Ty<'tcx>,
opt_arg_exprs: Option<&'tcx [Expr<'tcx>]>
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)>
fn identify_bad_closure_def_and_call(
&self,
err: &mut DiagnosticBuilder<'a>,
hir_id: HirId,
callee_node: &ExprKind<'_>,
callee_span: Span
)
fn identify_bad_closure_def_and_call(
&self,
err: &mut DiagnosticBuilder<'a>,
hir_id: HirId,
callee_node: &ExprKind<'_>,
callee_span: Span
)
Give appropriate suggestion when encountering ||{/* not callable */}()
, where the
likely intention is to call the closure, suggest (||{})()
. (#55851)
fn confirm_builtin_call(
&self,
call_expr: &'tcx Expr<'tcx>,
callee_expr: &'tcx Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>
) -> Ty<'tcx>
fn confirm_deferred_closure_call(
&self,
call_expr: &'tcx Expr<'tcx>,
arg_exprs: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>,
fn_sig: FnSig<'tcx>
) -> Ty<'tcx>
fn confirm_overloaded_call(
&self,
call_expr: &'tcx Expr<'tcx>,
arg_exprs: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>,
method_callee: MethodCallee<'tcx>
) -> Ty<'tcx>
fn pointer_kind(
&self,
t: Ty<'tcx>,
span: Span
) -> Result<Option<PointerKind<'tcx>>, ErrorReported>
fn pointer_kind(
&self,
t: Ty<'tcx>,
span: Span
) -> Result<Option<PointerKind<'tcx>>, ErrorReported>
Returns the kind of unsize information of t, or None if t is unknown.
pub fn check_expr_closure(
&self,
expr: &Expr<'_>,
_capture: CaptureBy,
decl: &'tcx FnDecl<'tcx>,
body_id: BodyId,
gen: Option<Movability>,
expected: Expectation<'tcx>
) -> Ty<'tcx>
fn check_closure(
&self,
expr: &Expr<'_>,
opt_kind: Option<ClosureKind>,
decl: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
gen: Option<Movability>,
expected_sig: Option<ExpectedSig<'tcx>>
) -> Ty<'tcx>
fn deduce_expectations_from_expected_type(
&self,
expected_ty: Ty<'tcx>
) -> (Option<ExpectedSig<'tcx>>, Option<ClosureKind>)
fn deduce_expectations_from_expected_type(
&self,
expected_ty: Ty<'tcx>
) -> (Option<ExpectedSig<'tcx>>, Option<ClosureKind>)
Given the expected type, figures out what it can about this closure we are about to type check:
fn deduce_expectations_from_obligations(
&self,
expected_vid: TyVid
) -> (Option<ExpectedSig<'tcx>>, Option<ClosureKind>)
fn deduce_sig_from_projection(
&self,
cause_span: Option<Span>,
projection: PolyProjectionPredicate<'tcx>
) -> Option<ExpectedSig<'tcx>>
fn deduce_sig_from_projection(
&self,
cause_span: Option<Span>,
projection: PolyProjectionPredicate<'tcx>
) -> Option<ExpectedSig<'tcx>>
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.
fn sig_of_closure(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sig: Option<ExpectedSig<'tcx>>
) -> ClosureSignatures<'tcx>
fn sig_of_closure_no_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>
) -> ClosureSignatures<'tcx>
fn sig_of_closure_no_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>
) -> ClosureSignatures<'tcx>
If there is no expected signature, then we will convert the types that the user gave into a signature.
fn sig_of_closure_with_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sig: ExpectedSig<'tcx>
) -> ClosureSignatures<'tcx>
fn sig_of_closure_with_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sig: ExpectedSig<'tcx>
) -> ClosureSignatures<'tcx>
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 isS
. - Otherwise, the signature of the closure is E. Moreover:
- Skolemize the late-bound regions in
E
, yieldingE'
. - Instantiate all the late-bound regions bound in the closure within
S
with fresh (existential) variables, yieldingS'
- 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.
- Skolemize the late-bound regions in
- If we have no expectation
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
: theDefId
of the closure expressiondecl
: the HIR declaration of the closurebody
: the body of the closureexpected_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.
fn sig_of_closure_with_mismatched_number_of_arguments(
&self,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sig: ExpectedSig<'tcx>
) -> ClosureSignatures<'tcx>
fn check_supplied_sig_against_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sigs: &ClosureSignatures<'tcx>
) -> InferResult<'tcx, ()>
fn check_supplied_sig_against_expectation(
&self,
hir_id: HirId,
expr_def_id: DefId,
decl: &FnDecl<'_>,
body: &Body<'_>,
expected_sigs: &ClosureSignatures<'tcx>
) -> InferResult<'tcx, ()>
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
.
fn deduce_future_output_from_projection(
&self,
cause_span: Span,
predicate: PolyProjectionPredicate<'tcx>
) -> Option<Ty<'tcx>>
fn deduce_future_output_from_projection(
&self,
cause_span: Span,
predicate: PolyProjectionPredicate<'tcx>
) -> Option<Ty<'tcx>>
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
.
fn closure_sigs(
&self,
expr_def_id: DefId,
body: &Body<'_>,
bound_sig: PolyFnSig<'tcx>
) -> ClosureSignatures<'tcx>
pub fn try_coerce(
&self,
expr: &Expr<'_>,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>
) -> RelateResult<'tcx, Ty<'tcx>>
pub fn try_coerce(
&self,
expr: &Expr<'_>,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>
) -> RelateResult<'tcx, Ty<'tcx>>
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
.
fn try_find_coercion_lub<E>(
&self,
cause: &ObligationCause<'tcx>,
exprs: &[E],
prev_ty: Ty<'tcx>,
new: &Expr<'_>,
new_ty: Ty<'tcx>
) -> RelateResult<'tcx, Ty<'tcx>> where
E: AsCoercionSite,
fn try_find_coercion_lub<E>(
&self,
cause: &ObligationCause<'tcx>,
exprs: &[E],
prev_ty: Ty<'tcx>,
new: &Expr<'_>,
new_ty: Ty<'tcx>
) -> RelateResult<'tcx, Ty<'tcx>> where
E: AsCoercionSite,
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.
pub fn emit_coerce_suggestions(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expr_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>
)
pub fn demand_suptype_diag(
&self,
sp: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>
) -> Option<DiagnosticBuilder<'tcx>>
pub fn demand_suptype_with_origin(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>
) -> Option<DiagnosticBuilder<'tcx>>
pub fn demand_eqtype_diag(
&self,
sp: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>
) -> Option<DiagnosticBuilder<'tcx>>
pub fn demand_eqtype_with_origin(
&self,
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>
) -> Option<DiagnosticBuilder<'tcx>>
pub fn demand_coerce(
&self,
expr: &Expr<'_>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>,
allow_two_phase: AllowTwoPhase
) -> Ty<'tcx>
pub fn demand_coerce_diag(
&self,
expr: &Expr<'_>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>,
allow_two_phase: AllowTwoPhase
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>)
pub fn demand_coerce_diag(
&self,
expr: &Expr<'_>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>,
allow_two_phase: AllowTwoPhase
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>)
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”.
fn suggest_compatible_variants(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
expr_ty: Ty<'tcx>
)
fn suggest_compatible_variants(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
expr_ty: Ty<'tcx>
)
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)
pub fn get_conversion_methods(
&self,
span: Span,
expected: Ty<'tcx>,
checked_ty: Ty<'tcx>,
hir_id: HirId
) -> Vec<AssocItem>
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) });
pub(crate) fn is_hir_id_from_struct_pattern_shorthand_field(
&self,
hir_id: HirId,
sp: Span
) -> bool
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
!”.
pub fn check_for_cast(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
checked_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>
) -> bool
fn report_closure_inferred_return_type(
&self,
err: &mut DiagnosticBuilder<'_>,
expected: Ty<'tcx>
)
pub fn check_expr_has_type_or_error(
&self,
expr: &'tcx Expr<'tcx>,
expected: Ty<'tcx>,
extend_err: impl Fn(&mut DiagnosticBuilder<'_>)
) -> Ty<'tcx>
fn check_expr_meets_expectation_or_error(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
extend_err: impl Fn(&mut DiagnosticBuilder<'_>)
) -> Ty<'tcx>
pub(super) fn check_expr_coercable_to_type(
&self,
expr: &'tcx Expr<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>
) -> Ty<'tcx>
pub(super) fn check_expr_with_hint(
&self,
expr: &'tcx Expr<'tcx>,
expected: Ty<'tcx>
) -> Ty<'tcx>
fn check_expr_with_expectation_and_needs(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
needs: Needs
) -> Ty<'tcx>
pub(super) fn check_expr_with_expectation(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>
) -> Ty<'tcx>
pub(super) fn check_expr_with_expectation(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>
) -> Ty<'tcx>
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.
pub(super) fn check_expr_with_expectation_and_args(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
args: &'tcx [Expr<'tcx>]
) -> Ty<'tcx>
pub(super) fn check_expr_with_expectation_and_args(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
args: &'tcx [Expr<'tcx>]
) -> Ty<'tcx>
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
.
fn check_expr_unary(
&self,
unop: UnOp,
oprnd: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_addr_of(
&self,
kind: BorrowKind,
mutbl: Mutability,
oprnd: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
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.
pub(crate) fn check_expr_path(
&self,
qpath: &'tcx QPath<'tcx>,
expr: &'tcx Expr<'tcx>,
args: &'tcx [Expr<'tcx>]
) -> Ty<'tcx>
fn check_expr_break(
&self,
destination: Destination,
expr_opt: Option<&'tcx Expr<'tcx>>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_return(
&self,
expr_opt: Option<&'tcx Expr<'tcx>>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
explicit_return
is true
if we’re checkng an explicit return expr
,
and false
if we’re checking a trailing expression.
pub(crate) fn check_lhs_assignable(
&self,
lhs: &'tcx Expr<'tcx>,
err_code: &'static str,
expr_span: &Span
)
fn check_then_else(
&self,
cond_expr: &'tcx Expr<'tcx>,
then_expr: &'tcx Expr<'tcx>,
opt_else_expr: Option<&'tcx Expr<'tcx>>,
sp: Span,
orig_expected: Expectation<'tcx>
) -> Ty<'tcx>
fn check_expr_assign(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
lhs: &'tcx Expr<'tcx>,
rhs: &'tcx Expr<'tcx>,
span: &Span
) -> Ty<'tcx>
fn check_expr_assign(
&self,
expr: &'tcx Expr<'tcx>,
expected: Expectation<'tcx>,
lhs: &'tcx Expr<'tcx>,
rhs: &'tcx Expr<'tcx>,
span: &Span
) -> Ty<'tcx>
Type check assignment expression expr
of form lhs = rhs
.
The expected type is ()
and is passed to the function for the purposes of diagnostics.
fn check_expr_loop(
&self,
body: &'tcx Block<'tcx>,
source: LoopSource,
expected: Expectation<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_method_call(
&self,
expr: &'tcx Expr<'tcx>,
segment: &PathSegment<'_>,
span: Span,
args: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>
) -> Ty<'tcx>
fn check_method_call(
&self,
expr: &'tcx Expr<'tcx>,
segment: &PathSegment<'_>,
span: Span,
args: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>
) -> Ty<'tcx>
Checks a method call.
fn check_expr_cast(
&self,
e: &'tcx Expr<'tcx>,
t: &'tcx Ty<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_array(
&self,
args: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_repeat(
&self,
element: &'tcx Expr<'tcx>,
count: &'tcx AnonConst,
expected: Expectation<'tcx>,
_expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_tuple(
&self,
elts: &'tcx [Expr<'tcx>],
expected: Expectation<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_struct(
&self,
expr: &Expr<'_>,
expected: Expectation<'tcx>,
qpath: &QPath<'_>,
fields: &'tcx [ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx Expr<'tcx>>
) -> Ty<'tcx>
fn check_expr_struct_fields(
&self,
adt_ty: Ty<'tcx>,
expected: Expectation<'tcx>,
expr_id: HirId,
span: Span,
variant: &'tcx VariantDef,
ast_fields: &'tcx [ExprField<'tcx>],
check_completeness: bool,
expr_span: Span
) -> bool
fn check_struct_fields_on_error(
&self,
fields: &'tcx [ExprField<'tcx>],
base_expr: &'tcx Option<&'tcx Expr<'tcx>>
)
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
Report an error for a struct field expression when there are invisible fields.
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
--> src/main.rs:8:5
|
8 | foo::Foo {};
| ^^^^^^^^
error: aborting due to previous error
fn report_unknown_field(
&self,
ty: Ty<'tcx>,
variant: &'tcx VariantDef,
field: &ExprField<'_>,
skip_fields: &[ExprField<'_>],
kind_name: &str,
expr_span: Span
)
fn check_field(
&self,
expr: &'tcx Expr<'tcx>,
base: &'tcx Expr<'tcx>,
field: Ident
) -> Ty<'tcx>
fn suggest_await_on_field_access(
&self,
err: &mut DiagnosticBuilder<'_>,
field_ident: Ident,
base: &'tcx Expr<'tcx>,
ty: Ty<'tcx>
)
fn ban_nonexisting_field(
&self,
field: Ident,
base: &'tcx Expr<'tcx>,
expr: &'tcx Expr<'tcx>,
expr_t: Ty<'tcx>
)
fn ban_private_field_access(
&self,
expr: &Expr<'_>,
expr_t: Ty<'tcx>,
field: Ident,
base_did: DefId
)
fn suggest_fields_on_recordish(
&self,
err: &mut DiagnosticBuilder<'_>,
def: &'tcx AdtDef,
field: Ident
)
fn maybe_suggest_array_indexing(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
base: &Expr<'_>,
field: Ident,
len: &Const<'tcx>
)
fn suggest_first_deref_field(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
base: &Expr<'_>,
field: Ident
)
fn get_field_candidates(
&self,
span: Span,
base_t: Ty<'tcx>
) -> Option<(&Vec<FieldDef>, SubstsRef<'tcx>)>
This method is called after we have encountered a missing field error to recursively search for the field
fn check_expr_index(
&self,
base: &'tcx Expr<'tcx>,
idx: &'tcx Expr<'tcx>,
expr: &'tcx Expr<'tcx>
) -> Ty<'tcx>
fn check_expr_yield(
&self,
value: &'tcx Expr<'tcx>,
expr: &'tcx Expr<'tcx>,
src: &'tcx YieldSource
) -> Ty<'tcx>
Performs type inference fallback, returning true if any fallback occurs.
fn fallback_if_possible(
&self,
ty: Ty<'tcx>,
diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>
) -> bool
Second round of fallback: Unconstrained type variables created
from the instantiation of an opaque type fall back to the
opaque type itself. This is a somewhat incomplete attempt to
manage “identity passthrough” for impl Trait
types.
For example, in this code:
type MyType = impl Copy;
fn defining_use() -> MyType { true }
fn other_use() -> MyType { defining_use() }
defining_use
will constrain the instantiated inference
variable to bool
, while other_use
will constrain
the instantiated inference variable to MyType
.
When we process opaque types during writeback, we
will handle cases like other_use
, and not count
them as defining usages
However, we also need to handle cases like this:
pub type Foo = impl Copy;
fn produce() -> Option<Foo> {
None
}
In the above snippet, the inference variable created by
instantiating Option<Foo>
will be completely unconstrained.
We treat this as a non-defining use by making the inference
variable fall back to the opaque type itself.
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
.
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.
pub(in check) fn resolve_vars_with_obligations_and_mutate_fulfillment(
&self,
ty: Ty<'tcx>,
mutate_fulfillment_errors: impl Fn(&mut Vec<FulfillmentError<'tcx>>)
) -> Ty<'tcx>
pub(in check) fn record_deferred_call_resolution(
&self,
closure_def_id: DefId,
r: DeferredCallResolution<'tcx>
)
pub(in check) fn remove_deferred_call_resolutions(
&self,
closure_def_id: DefId
) -> Vec<DeferredCallResolution<'tcx>>
pub(in check) fn write_resolution(
&self,
hir_id: HirId,
r: Result<(DefKind, DefId), ErrorReported>
)
pub fn write_user_type_annotation_from_substs(
&self,
hir_id: HirId,
def_id: DefId,
substs: SubstsRef<'tcx>,
user_self_ty: Option<UserSelfTy<'tcx>>
)
pub fn write_user_type_annotation_from_substs(
&self,
hir_id: HirId,
def_id: DefId,
substs: SubstsRef<'tcx>,
user_self_ty: Option<UserSelfTy<'tcx>>
)
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.
pub fn write_user_type_annotation(
&self,
hir_id: HirId,
canonical_user_type_annotation: CanonicalUserType<'tcx>
)
fn instantiate_type_scheme<T>(
&self,
span: Span,
substs: SubstsRef<'tcx>,
value: T
) -> T where
T: TypeFoldable<'tcx>,
fn instantiate_type_scheme<T>(
&self,
span: Span,
substs: SubstsRef<'tcx>,
value: T
) -> T where
T: TypeFoldable<'tcx>,
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.
pub(in check) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
&self,
value: T,
value_span: Span
) -> T
pub(in check) fn instantiate_opaque_types_from_value<T: TypeFoldable<'tcx>>(
&self,
value: T,
value_span: Span
) -> T
Replaces the opaque types from the given value with type variables,
and records the OpaqueTypeMap
for later use during writeback. See
InferCtxt::instantiate_opaque_types
for more details.
pub(in check) fn normalize_associated_types_in_wf<T>(
&self,
span: Span,
value: T,
loc: WellFormedLoc
) -> T where
T: TypeFoldable<'tcx>,
pub(in check) fn normalize_associated_types_in_wf<T>(
&self,
span: Span,
value: T,
loc: WellFormedLoc
) -> T where
T: TypeFoldable<'tcx>,
Convenience method which tracks extra diagnostic information for normalization
that occurs as a result of WF checking. The hir_id
is the HirId
of the hir item
whose type is being wf-checked - this is used to construct a more precise span if
an error occurs.
It is never necessary to call this method - calling normalize_associated_types_in
will
just result in a slightly worse diagnostic span, and will still be sound.
pub(in check) fn normalize_associated_types_in<T>(
&self,
span: Span,
value: T
) -> T where
T: TypeFoldable<'tcx>,
pub(in check) fn normalize_associated_types_in_as_infer_ok<T>(
&self,
span: Span,
value: T
) -> InferOk<'tcx, T> where
T: TypeFoldable<'tcx>,
pub fn require_type_meets(
&self,
ty: Ty<'tcx>,
span: Span,
code: ObligationCauseCode<'tcx>,
def_id: DefId
)
pub fn require_type_is_sized(
&self,
ty: Ty<'tcx>,
span: Span,
code: ObligationCauseCode<'tcx>
)
pub fn require_type_is_sized_deferred(
&self,
ty: Ty<'tcx>,
span: Span,
code: ObligationCauseCode<'tcx>
)
pub fn register_wf_obligation(
&self,
arg: GenericArg<'tcx>,
span: Span,
code: ObligationCauseCode<'tcx>
)
pub fn register_wf_obligation(
&self,
arg: GenericArg<'tcx>,
span: Span,
code: ObligationCauseCode<'tcx>
)
Registers an obligation for checking later, during regionck, that arg
is well-formed.
Registers obligations that all substs
are well-formed.
pub fn add_obligations_for_parameters(
&self,
cause: ObligationCause<'tcx>,
predicates: InstantiatedPredicates<'tcx>
)
pub fn add_obligations_for_parameters(
&self,
cause: ObligationCause<'tcx>,
predicates: InstantiatedPredicates<'tcx>
)
Given a fully substituted set of bounds (generic_bounds
), and the values with which each
type/region parameter was instantiated (substs
), creates and registers suitable
trait/region obligations.
For example, if there is a function:
fn foo<'a,T:'a>(...)
and a reference:
let f = foo;
Then we will create a fresh region variable '$0
and a fresh type variable $1
for 'a
and T
. This routine will add a region obligation $1:'$0
and register it locally.
pub(in check) fn select_obligations_where_possible(
&self,
fallback_has_occurred: bool,
mutate_fulfillment_errors: impl Fn(&mut Vec<FulfillmentError<'tcx>>)
)
pub(in check) fn select_obligations_where_possible(
&self,
fallback_has_occurred: bool,
mutate_fulfillment_errors: impl Fn(&mut Vec<FulfillmentError<'tcx>>)
)
Select as many obligations as we can at present.
pub(in check) fn make_overloaded_place_return_type(
&self,
method: MethodCallee<'tcx>
) -> TypeAndMut<'tcx>
pub(in check) fn make_overloaded_place_return_type(
&self,
method: MethodCallee<'tcx>
) -> TypeAndMut<'tcx>
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
.
fn self_type_matches_expected_vid(
&self,
trait_ref: PolyTraitRef<'tcx>,
expected_vid: TyVid
) -> bool
pub(in check) fn obligations_for_self_ty<'b>(
&'b self,
self_ty: TyVid
) -> impl Iterator<Item = (PolyTraitRef<'tcx>, PredicateObligation<'tcx>)> + Captures<'tcx> + 'b
Unifies the output type with the expected type early, for more coercions and forward type information on the input expressions.
pub(in check) fn resolve_lang_item_path(
&self,
lang_item: LangItem,
span: Span,
hir_id: HirId
) -> (Res, Ty<'tcx>)
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.
pub(in check) fn note_internal_mutation_in_method(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
pub(in check) fn note_need_for_fn_pointer(
&self,
err: &mut DiagnosticBuilder<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
pub(in check) fn could_remove_semicolon(
&self,
blk: &'tcx Block<'tcx>,
expected_ty: Ty<'tcx>
) -> Option<(Span, StatementAsExpression)>
pub fn instantiate_value_path(
&self,
segments: &[PathSegment<'_>],
self_ty: Option<Ty<'tcx>>,
res: Res,
span: Span,
hir_id: HirId
) -> (Ty<'tcx>, Res)
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.
pub(in check) fn with_breakable_ctxt<F: FnOnce() -> R, R>(
&self,
id: HirId,
ctxt: BreakableCtxt<'tcx>,
f: F
) -> (BreakableCtxt<'tcx>, R)
pub(in check) fn probe_instantiate_query_response(
&self,
span: Span,
original_values: &OriginalQueryValues<'tcx>,
query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>
) -> InferResult<'tcx, Ty<'tcx>>
pub(in check) fn probe_instantiate_query_response(
&self,
span: Span,
original_values: &OriginalQueryValues<'tcx>,
query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>
) -> InferResult<'tcx, Ty<'tcx>>
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.
pub(in check) fn check_method_argument_types(
&self,
sp: Span,
expr: &'tcx Expr<'tcx>,
method: Result<MethodCallee<'tcx>, ()>,
args_no_rcvr: &'tcx [Expr<'tcx>],
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>
) -> Ty<'tcx>
Generic function that factors out common logic from function calls, method calls and overloaded operators.
pub fn check_struct_path(
&self,
qpath: &QPath<'_>,
hir_id: HirId
) -> Option<(&'tcx VariantDef, Ty<'tcx>)>
pub fn check_decl_initializer(
&self,
local: &'tcx Local<'tcx>,
init: &'tcx Expr<'tcx>
) -> Ty<'tcx>
Type check a let
statement.
pub(in check) fn check_block_with_expected(
&self,
blk: &'tcx Block<'tcx>,
expected: Expectation<'tcx>
) -> Ty<'tcx>
fn consider_hint_about_removing_semicolon(
&self,
blk: &'tcx Block<'tcx>,
expected_ty: Ty<'tcx>,
err: &mut DiagnosticBuilder<'_>
)
fn consider_hint_about_removing_semicolon(
&self,
blk: &'tcx Block<'tcx>,
expected_ty: Ty<'tcx>,
err: &mut DiagnosticBuilder<'_>
)
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.
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
fn overwrite_local_ty_if_err(
&self,
local: &'tcx Local<'tcx>,
decl_ty: Ty<'tcx>,
ty: Ty<'tcx>
)
fn finish_resolving_struct_path(
&self,
qpath: &QPath<'_>,
path_span: Span,
hir_id: HirId
) -> (Res, Ty<'tcx>)
Given a vec of evaluated FulfillmentError
s and an fn
call argument expressions, we walk
the checked and coerced types for each argument to see if any of the FulfillmentError
s
reference a type argument. The reason to walk also the checked type is that the coerced type
can be not easily comparable with predicate type (because of coercion). If the types match
for either checked or coerced type, and there’s only one argument that does, we point at
the corresponding argument’s expression span instead of the fn
call path span.
fn point_at_type_arg_instead_of_call_if_possible(
&self,
errors: &mut Vec<FulfillmentError<'tcx>>,
call_expr: &'tcx Expr<'tcx>
)
fn point_at_type_arg_instead_of_call_if_possible(
&self,
errors: &mut Vec<FulfillmentError<'tcx>>,
call_expr: &'tcx Expr<'tcx>
)
Given a vec of evaluated FulfillmentError
s and an fn
call expression, we walk the
PathSegment
s and resolve their type parameters to see if any of the FulfillmentError
s
were caused by them. If they were, we point at the corresponding type argument’s span
instead of the fn
call path span.
pub fn suggest_mismatched_types_on_tail(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &'tcx Expr<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
blk_id: HirId
) -> bool
pub fn suggest_mismatched_types_on_tail(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &'tcx Expr<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
blk_id: HirId
) -> bool
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()
.
fn suggest_fn_call(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
) -> bool
fn suggest_fn_call(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
) -> bool
When encountering an fn-like ctor that needs to unify with a value, check whether calling the ctor would successfully solve the type mismatch and if so, suggest it:
fn foo(x: usize) -> usize { x }
let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
pub fn suggest_deref_ref_or_into(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
expected_ty_expr: Option<&'tcx Expr<'tcx>>
)
pub(in check) fn suggest_boxing_when_appropriate(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
pub(in check) fn suggest_boxing_when_appropriate(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
When encountering the expected boxed value allocated in the stack, suggest allocating it
in the heap by calling Box::new()
.
pub(in check) fn suggest_no_capture_closure(
&self,
err: &mut DiagnosticBuilder<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
pub(in check) fn suggest_no_capture_closure(
&self,
err: &mut DiagnosticBuilder<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
)
When encountering a closure that captures variables, where a FnPtr is expected, suggest a non-capturing closure
pub(in check) fn suggest_calling_boxed_future_when_appropriate(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
) -> bool
pub(in check) fn suggest_calling_boxed_future_when_appropriate(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>
) -> bool
When encountering an impl Future
where BoxFuture
is expected, suggest Box::pin
.
fn suggest_missing_semicolon(
&self,
err: &mut DiagnosticBuilder<'_>,
expression: &'tcx Expr<'tcx>,
expected: Ty<'tcx>
)
fn suggest_missing_semicolon(
&self,
err: &mut DiagnosticBuilder<'_>,
expression: &'tcx Expr<'tcx>,
expected: Ty<'tcx>
)
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.
pub(in check) fn suggest_missing_return_type(
&self,
err: &mut DiagnosticBuilder<'_>,
fn_decl: &FnDecl<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
can_suggest: bool,
fn_id: HirId
) -> bool
pub(in check) fn suggest_missing_return_type(
&self,
err: &mut DiagnosticBuilder<'_>,
fn_decl: &FnDecl<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
can_suggest: bool,
fn_id: HirId
) -> bool
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.
pub(in check) fn suggest_missing_break_or_return_expr(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &'tcx Expr<'tcx>,
fn_decl: &FnDecl<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
id: HirId,
fn_id: HirId
)
pub(in check) fn suggest_missing_parentheses(
&self,
err: &mut DiagnosticBuilder<'_>,
expr: &Expr<'_>
)
pub fn confirm_method(
&self,
span: Span,
self_expr: &'tcx Expr<'tcx>,
call_expr: &'tcx Expr<'tcx>,
unadjusted_self_ty: Ty<'tcx>,
pick: Pick<'tcx>,
segment: &PathSegment<'_>
) -> ConfirmResult<'tcx>
pub(super) fn lint_dot_call_from_2018(
&self,
self_ty: Ty<'tcx>,
segment: &PathSegment<'_>,
span: Span,
call_expr: &'tcx Expr<'tcx>,
self_expr: &'tcx Expr<'tcx>,
pick: &Pick<'tcx>,
args: &'tcx [Expr<'tcx>]
)
pub(super) fn lint_fully_qualified_call_from_2018(
&self,
span: Span,
method_name: Ident,
self_ty: Ty<'tcx>,
self_ty_span: Span,
expr_id: HirId,
pick: &Pick<'tcx>
)
fn trait_path_or_bare_name(
&self,
span: Span,
expr_hir_id: HirId,
trait_def_id: DefId
) -> String
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).
pub fn probe_for_name(
&self,
span: Span,
mode: Mode,
item_name: Ident,
is_suggestion: IsSuggestion,
self_ty: Ty<'tcx>,
scope_expr_id: HirId,
scope: ProbeScope
) -> PickResult<'tcx>
fn probe_op<OP, R>(
&'a self,
span: Span,
mode: Mode,
method_name: Option<Ident>,
return_type: Option<Ty<'tcx>>,
is_suggestion: IsSuggestion,
self_ty: Ty<'tcx>,
scope_expr_id: HirId,
scope: ProbeScope,
op: OP
) -> Result<R, MethodError<'tcx>> where
OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>,
pub fn report_method_error(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
args: Option<&'tcx [Expr<'tcx>]>
) -> Option<DiagnosticBuilder<'_>>
pub(crate) fn note_unmet_impls_on_type(
&self,
err: &mut DiagnosticBuilder<'_>,
errors: Vec<FulfillmentError<'tcx>>
)
fn suggest_derive(
&self,
err: &mut DiagnosticBuilder<'_>,
unsatisfied_predicates: &Vec<(Predicate<'tcx>, Option<Predicate<'tcx>>)>
)
Print out the type for use in value namespace.
fn suggest_await_before_method(
&self,
err: &mut DiagnosticBuilder<'_>,
item_name: Ident,
ty: Ty<'tcx>,
call: &Expr<'_>,
span: Span
)
fn suggest_use_candidates(
&self,
err: &mut DiagnosticBuilder<'_>,
msg: String,
candidates: Vec<DefId>
)
fn suggest_valid_traits(
&self,
err: &mut DiagnosticBuilder<'_>,
valid_out_of_scope_traits: Vec<DefId>
) -> bool
fn suggest_traits_to_import(
&self,
err: &mut DiagnosticBuilder<'_>,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
source: SelfSource<'tcx>,
valid_out_of_scope_traits: Vec<DefId>,
unsatisfied_predicates: &[(Predicate<'tcx>, Option<Predicate<'tcx>>)],
unsatisfied_bounds: bool
)
fn type_derefs_to_local(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
source: SelfSource<'tcx>
) -> bool
fn type_derefs_to_local(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
source: SelfSource<'tcx>
) -> bool
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.
pub(crate) fn suggest_method_call(
&self,
err: &mut DiagnosticBuilder<'a>,
msg: &str,
method_name: Ident,
self_ty: Ty<'tcx>,
call_expr: &Expr<'_>,
span: Option<Span>
)
pub(crate) fn suggest_method_call(
&self,
err: &mut DiagnosticBuilder<'a>,
msg: &str,
method_name: Ident,
self_ty: Ty<'tcx>,
call_expr: &Expr<'_>,
span: Option<Span>
)
Adds a suggestion to call the given method to the provided diagnostic.
pub fn lookup_method(
&self,
self_ty: Ty<'tcx>,
segment: &PathSegment<'_>,
span: Span,
call_expr: &'tcx Expr<'tcx>,
self_expr: &'tcx Expr<'tcx>,
args: &'tcx [Expr<'tcx>]
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>>
pub fn lookup_method(
&self,
self_ty: Ty<'tcx>,
segment: &PathSegment<'_>,
span: Span,
call_expr: &'tcx Expr<'tcx>,
self_expr: &'tcx Expr<'tcx>,
args: &'tcx [Expr<'tcx>]
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>>
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 surroundingFnCtxt
(!)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 callcall_expr
: the complete method call: (foo.bar::<T1,...Tn>(...)
)self_expr
: the self expression (foo
)args
: the expressions of the arguments (a, b + 1, ...
)
pub fn lookup_probe(
&self,
span: Span,
method_name: Ident,
self_ty: Ty<'tcx>,
call_expr: &'tcx Expr<'tcx>,
scope: ProbeScope
) -> PickResult<'tcx>
pub(super) fn obligation_for_method(
&self,
span: Span,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
opt_input_types: Option<&[Ty<'tcx>]>
) -> (Obligation<'tcx, Predicate<'tcx>>, &'tcx List<GenericArg<'tcx>>)
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 surroundingFnCtxt
(!)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 ofFoo
)expr_id
: thehir::HirId
of the expression composing the entire call
Checks a a <op>= b
Checks a potentially overloaded binary operator.
fn enforce_builtin_binop_types(
&self,
lhs_span: &Span,
lhs_ty: Ty<'tcx>,
rhs_span: &Span,
rhs_ty: Ty<'tcx>,
op: BinOp
) -> Ty<'tcx>
fn check_overloaded_binop(
&self,
expr: &'tcx Expr<'tcx>,
lhs_expr: &'tcx Expr<'tcx>,
rhs_expr: &'tcx Expr<'tcx>,
op: BinOp,
is_assign: IsAssign
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)
fn add_type_neq_err_label(
&self,
err: &mut DiagnosticBuilder<'_>,
span: Span,
ty: Ty<'tcx>,
other_ty: Ty<'tcx>,
op: BinOp,
is_assign: IsAssign
) -> bool
fn add_type_neq_err_label(
&self,
err: &mut DiagnosticBuilder<'_>,
span: Span,
ty: Ty<'tcx>,
other_ty: Ty<'tcx>,
op: BinOp,
is_assign: IsAssign
) -> bool
If one of the types is an uncalled function and calling it would yield the other type,
suggest calling the function. Returns true
if suggestion would apply (even if not given).
fn check_str_addition(
&self,
lhs_expr: &'tcx Expr<'tcx>,
rhs_expr: &'tcx Expr<'tcx>,
lhs_ty: Ty<'tcx>,
rhs_ty: Ty<'tcx>,
err: &mut DiagnosticBuilder<'_>,
is_assign: IsAssign,
op: BinOp
) -> bool
fn check_str_addition(
&self,
lhs_expr: &'tcx Expr<'tcx>,
rhs_expr: &'tcx Expr<'tcx>,
lhs_ty: Ty<'tcx>,
rhs_ty: Ty<'tcx>,
err: &mut DiagnosticBuilder<'_>,
is_assign: IsAssign,
op: BinOp
) -> bool
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
pub fn check_user_unop(
&self,
ex: &'tcx Expr<'tcx>,
operand_ty: Ty<'tcx>,
op: UnOp
) -> Ty<'tcx>
fn lookup_op_method(
&self,
lhs_ty: Ty<'tcx>,
other_tys: &[Ty<'tcx>],
op: Op
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>>
fn demand_eqtype_pat_diag(
&self,
cause_span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
ti: TopInfo<'tcx>
) -> Option<DiagnosticBuilder<'tcx>>
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.
fn calc_default_binding_mode(
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode,
adjust_mode: AdjustMode
) -> (Ty<'tcx>, BindingMode)
fn calc_default_binding_mode(
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode,
adjust_mode: AdjustMode
) -> (Ty<'tcx>, BindingMode)
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)
.
fn peel_off_references(
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode
) -> (Ty<'tcx>, BindingMode)
fn peel_off_references(
&self,
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode
) -> (Ty<'tcx>, BindingMode)
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.
fn check_pat_lit(
&self,
span: Span,
lt: &Expr<'tcx>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn check_pat_range(
&self,
span: Span,
lhs: Option<&'tcx Expr<'tcx>>,
rhs: Option<&'tcx Expr<'tcx>>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn emit_err_pat_range(
&self,
span: Span,
lhs: Option<(bool, Ty<'tcx>, Span)>,
rhs: Option<(bool, Ty<'tcx>, Span)>
)
fn check_pat_ident(
&self,
pat: &'tcx Pat<'tcx>,
ba: BindingAnnotation,
var_id: HirId,
sub: Option<&'tcx Pat<'tcx>>,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn borrow_pat_suggestion(
&self,
err: &mut DiagnosticBuilder<'_>,
pat: &Pat<'_>,
inner: &Pat<'_>,
expected: Ty<'tcx>
)
fn check_pat_struct(
&self,
pat: &'tcx Pat<'tcx>,
qpath: &QPath<'_>,
fields: &'tcx [PatField<'tcx>],
has_rest_pat: bool,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn check_pat_path<'b>(
&self,
pat: &Pat<'_>,
path_resolution: (Res, Option<Ty<'tcx>>, &'b [PathSegment<'b>]),
expected: Ty<'tcx>,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn maybe_suggest_range_literal(
&self,
e: &mut DiagnosticBuilder<'_>,
opt_def_id: Option<DefId>,
ident: Ident
) -> bool
fn emit_bad_pat_path<'b>(
&self,
e: DiagnosticBuilder<'_>,
pat_span: Span,
res: Res,
pat_res: Res,
pat_ty: Ty<'tcx>,
segments: &'b [PathSegment<'b>],
parent_pat: Option<&Pat<'_>>
)
fn check_pat_tuple_struct(
&self,
pat: &'tcx Pat<'tcx>,
qpath: &'tcx QPath<'tcx>,
subpats: &'tcx [Pat<'tcx>],
ddpos: Option<usize>,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn e0023(
&self,
pat_span: Span,
res: Res,
qpath: &QPath<'_>,
subpats: &'tcx [Pat<'tcx>],
fields: &'tcx [FieldDef],
expected: Ty<'tcx>,
had_err: bool
)
fn check_pat_tuple(
&self,
span: Span,
elements: &'tcx [Pat<'tcx>],
ddpos: Option<usize>,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn check_struct_pat_fields(
&self,
adt_ty: Ty<'tcx>,
pat: &'tcx Pat<'tcx>,
variant: &'tcx VariantDef,
fields: &'tcx [PatField<'tcx>],
has_rest_pat: bool,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> bool
fn error_tuple_variant_index_shorthand(
&self,
variant: &VariantDef,
pat: &Pat<'_>,
fields: &[PatField<'_>]
) -> Option<DiagnosticBuilder<'_>>
fn error_inexistent_fields(
&self,
kind_name: &str,
inexistent_fields: &[Ident],
unmentioned_fields: &mut Vec<(&FieldDef, Ident)>,
variant: &VariantDef
) -> DiagnosticBuilder<'tcx>
fn error_tuple_variant_as_struct_pat(
&self,
pat: &Pat<'_>,
fields: &'tcx [PatField<'tcx>],
variant: &VariantDef
) -> Option<DiagnosticBuilder<'tcx>>
fn get_suggested_tuple_struct_pattern(
&self,
fields: &[PatField<'_>],
variant: &VariantDef
) -> String
fn error_no_accessible_fields(
&self,
pat: &Pat<'_>,
fields: &'tcx [PatField<'tcx>]
) -> DiagnosticBuilder<'tcx>
fn error_no_accessible_fields(
&self,
pat: &Pat<'_>,
fields: &'tcx [PatField<'tcx>]
) -> DiagnosticBuilder<'tcx>
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`
fn check_pat_box(
&self,
span: Span,
inner: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
fn check_pat_ref(
&self,
pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>,
mutbl: Mutability,
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>
) -> Ty<'tcx>
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.
fn negative_index(
&self,
ty: Ty<'tcx>,
span: Span,
base_expr: &Expr<'_>
) -> Option<(Ty<'tcx>, Ty<'tcx>)>
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
.
fn try_mutable_overloaded_place_op(
&self,
span: Span,
base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>],
op: PlaceOp
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>>
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.
Region checking during the WF phase for items. wf_tys
are the
types from which we should derive implied bounds, if any.
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.
Analysis starting point.
fn process_collected_capture_information(
&self,
capture_clause: CaptureBy,
capture_information: FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>
) -> (FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>, ClosureKind, Option<(Span, Place<'tcx>)>)
fn process_collected_capture_information(
&self,
capture_clause: CaptureBy,
capture_information: FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>
) -> (FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>, ClosureKind, Option<(Span, Place<'tcx>)>)
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 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.
fn compute_min_captures(
&self,
closure_def_id: DefId,
capture_information: FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>
)
fn compute_min_captures(
&self,
closure_def_id: DefId,
capture_information: FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>
)
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:
struct Point { x: i32, y: i32 }
let s: String; // hir_id_s
let mut p: Point; // 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.
fn compute_2229_migrations_reasons(
&self,
auto_trait_reasons: FxHashSet<&str>,
drop_reason: bool
) -> String
fn compute_2229_migrations_reasons(
&self,
auto_trait_reasons: FxHashSet<&str>,
drop_reason: bool
) -> String
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
fn has_significant_drop_outside_of_captures(
&self,
closure_def_id: DefId,
closure_span: Span,
base_path_ty: Ty<'tcx>,
captured_by_move_projs: Vec<&[Projection<'tcx>]>
) -> bool
fn has_significant_drop_outside_of_captures(
&self,
closure_def_id: DefId,
closure_span: Span,
base_path_ty: Ty<'tcx>,
captured_by_move_projs: Vec<&[Projection<'tcx>]>
) -> bool
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 parametersbase_path_ty
andcaptured_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.
fn init_capture_kind_for_place(
&self,
place: &Place<'tcx>,
capture_clause: CaptureBy,
upvar_id: UpvarId,
closure_span: Span
) -> UpvarCapture<'tcx>
fn place_for_root_variable(
&self,
closure_def_id: LocalDefId,
var_hir_id: HirId
) -> Place<'tcx>
fn log_capture_analysis_first_pass(
&self,
closure_def_id: DefId,
capture_information: &FxIndexMap<Place<'tcx>, CaptureInfo<'tcx>>,
closure_span: Span
)
fn determine_capture_mutability(
&self,
typeck_results: &'a TypeckResults<'tcx>,
place: &Place<'tcx>
) -> Mutability
fn determine_capture_mutability(
&self,
typeck_results: &'a TypeckResults<'tcx>,
place: &Place<'tcx>
) -> Mutability
A captured place is mutable if
- Projections don’t include a Deref of an immut-borrow, and
- PlaceBase is mut or projections include a Deref of a mut-borrow.
pub(super) fn impl_implied_bounds(
&self,
impl_def_id: DefId,
span: Span
) -> FxHashSet<Ty<'tcx>>
Trait Implementations
The default versions of the visit_nested_XXX
routines invoke
this method to get a map to use. By selecting an enum variant,
you control which kinds of nested HIR are visited; see
NestedVisitorMap
for details. By “nested HIR”, we are
referring to bits of HIR that are not directly embedded within
one another but rather indirectly, through a table in the
crate. This is done to control dependencies during incremental
compilation: the non-inline bits of HIR can be tracked and
hashed separately. Read more
fn visit_fn(
&mut self,
fk: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
body_id: BodyId,
span: Span,
hir_id: HirId
)
Invoked when a nested item is encountered. By default does
nothing unless you override nested_visit_map
to return other than
None
, in which case it will walk the item. You probably
don’t want to override this method – instead, override
nested_visit_map
or use the “shallow” or “deep” visit
patterns described on itemlikevisit::ItemLikeVisitor
. The only
reason to override this method is if you want a nested pattern
but cannot supply a Map
; see nested_visit_map
for advice. Read more
Like visit_nested_item()
, but for trait items. See
visit_nested_item()
for advice on when to override this
method. Read more
Like visit_nested_item()
, but for impl items. See
visit_nested_item()
for advice on when to override this
method. Read more
Like visit_nested_item()
, but for foreign items. See
visit_nested_item()
for advice on when to override this
method. Read more
Invoked to visit the body of a function, method or closure. Like
visit_nested_item, does nothing by default unless you override
nested_visit_map
to return other than None
, in which case it will walk
the body. Read more
Visits the top-level item and (optionally) nested items / impl items. See
visit_nested_item
for details. Read more
When invoking visit_all_item_likes()
, you need to supply an
item-like visitor. This method converts an “intra-visit”
visitor into an item-like visitor that walks the entire tree.
If you use this, you probably don’t want to process the
contents of nested item-like things, since the outer loop will
visit them as well. Read more
fn visit_variant_data(
&mut self,
s: &'v VariantData<'v>,
Symbol,
&'v Generics<'v>,
_parent_id: HirId,
Span
)
fn visit_enum_def(
&mut self,
enum_definition: &'v EnumDef<'v>,
generics: &'v Generics<'v>,
item_id: HirId,
Span
)
Auto Trait Implementations
impl<'a, 'tcx> !RefUnwindSafe for RegionCtxt<'a, 'tcx>
impl<'a, 'tcx> !Send for RegionCtxt<'a, 'tcx>
impl<'a, 'tcx> !Sync for RegionCtxt<'a, 'tcx>
impl<'a, 'tcx> Unpin for RegionCtxt<'a, 'tcx> where
'tcx: 'a,
impl<'a, 'tcx> !UnwindSafe for RegionCtxt<'a, 'tcx>
Blanket Implementations
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: 224 bytes