[NLL] extend MIR type checker to check aggregate rvalues #45889
Labels
E-mentor
Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
Once #45825 lands, we will be using the MIR type checker to drive the NLL region checker. This means we'll have to fix a few omissions. One of them is that MIR type checker does not check MIR rvalues (expressions that construct a value) for internal validity.
Here is an example NLL program affected by this:
If you run this with
-Znll -Zborrowck-mir
, you'll see that it emits just one error, tagged with(Ast)
:This error indicates that the old borrow checker is reporting an error, but we would expect both the old and new borrow check to report an error.
What's going wrong here is as follows:
Wrap { w: &mut x }
expression introduces a region variable'?0
representing the region parameter ofWrap
. You can think of it likeWrap::<'?0> { w: &mut x }
.E
of each field (as taken from the struct declaration) is a supertype of the typeV
of the value assigned to it (in other words, thatV <: E
).&'?0 mut u32
, and the typeV
would be the type&'?1 mut u32
, where'?1
that is the lifetime of the borrow. Hence you would have&'?1 u32 <: &'?0 u32
, which in turn means'?1: '?0
.'?0
and'?1
.The reason why is not hard to see. The MIR type checker's "main loop" is right here:
rust/src/librustc_mir/transform/type_check.rs
Lines 747 to 757 in fd9ecfd
It basically invokes
check_stmt
on every MIR statement. In this case, the statement in question will be an assignment statement. You can see that yourself if you run with-Zverbose -Zmir-dump='foo&nll'
(in addition to the other options) and open the resulting NLL file (it will generate a lot of files; the you want isrustc.node12.-------.nll.0.mir
). The interesting statement is this one, the 7th statement inbb0
(writtenbb0[7]
):Here we have an assignment statement, with the lvalue (left-hand side) being the variable
_2
and the rvalue (right-hand side) beingWrap<'_#2r> { w: _3 }
. This is a struct aggregate rvalue. If we look then at the types of the variables_2
and_3
, we see:Right now, what
check_stmt
does for an assignment statement is to compute the type of the rvalue and check that it is a subtype of the lvalue:rust/src/librustc_mir/transform/type_check.rs
Lines 398 to 405 in fd9ecfd
The type of the rvalue here will be
Wrap<'_#2r>
, so we will get a relationship'_#2r: '_#5r
(this'_#
is the compiler's way of printing a region variable; equivalent to the'?
I was using). But we get no relation to the type of_3
.So we need to add some kind of function
check_rvalue
, and call it here. It would visit the inside of an rvalue and make sure its types are internally consistent. I think that for the purposes of this bug, it would only handle aggregate rvalues, we can add other cases later.The first step we need to do for this case is to compute the expected types of all the fields, based on the field declaration. There is actually code to do this already. The
sanitize_projection
method has the job of checking that a path likea.b
has a valid type. To do that, it must lookup the type ofb
and check it. This is done by invoking thefield_ty
method, defined onTypeVerifier
. (The type verifier is kind of like a little pre-pass; it checks that some basic assumptions are met before the type-checker proper runs.)There is no reason for this method to be confined to
TypeVerified
; it should be easy enough to promote to theTypeChecker
(the verifier can then invoke it viaself.cx.field_ty
instead).Once that method has been moved, our
check_rvalue
method can invoke it to find the types of each field in the aggregate, and then can invokesub_types
on the actual type of the operand.So, the complete set of steps:
field_ty
onto theTypeChecker
check_rvalue
method, leaving cases largely unhandledfield_ty
to check the relationship between the operand provided and the field's expected typeOnce we've finished this, there is more to do. For example, we need to check that the various where clauses declared on the type are met, and handle other kinds of rvalues. But I'm going to defer those to other issues. (Predicates, for example, are #45827)
The text was updated successfully, but these errors were encountered: