@@ -7,7 +7,7 @@ use rustc_middle::{
7
7
BasicBlock , Body , Location , Operand , Rvalue , Statement , StatementKind , Terminator ,
8
8
TerminatorKind ,
9
9
} ,
10
- ty:: { self , ParamEnv , TyCtxt } ,
10
+ ty:: { self , fold :: BottomUpFolder , ParamEnv , Ty , TyCtxt , TypeFoldable } ,
11
11
} ;
12
12
13
13
#[ derive( Copy , Clone , Debug ) ]
@@ -83,6 +83,40 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
83
83
}
84
84
}
85
85
86
+ /// Check if src can be assigned into dest.
87
+ /// This is not precise, it will accept some incorrect assignments.
88
+ fn mir_assign_valid_types < ' tcx > ( tcx : TyCtxt < ' tcx > , src : Ty < ' tcx > , dest : Ty < ' tcx > ) -> bool {
89
+ if src == dest {
90
+ // Equal types, all is good.
91
+ return true ;
92
+ }
93
+
94
+ // Type-changing assignments can happen for (at least) two reasons:
95
+ // 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
96
+ // 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
97
+ // with their late-bound lifetimes are still around and can lead to type differences.
98
+ // Normalize both of them away.
99
+ // FIXME: Share this code with `interpret/eval_context.rs`.
100
+ let normalize = |ty : Ty < ' tcx > | {
101
+ ty. fold_with ( & mut BottomUpFolder {
102
+ tcx,
103
+ // Normalize all references to immutable.
104
+ ty_op : |ty| match ty. kind {
105
+ ty:: Ref ( _, pointee, _) => tcx. mk_imm_ref ( tcx. lifetimes . re_erased , pointee) ,
106
+ _ => ty,
107
+ } ,
108
+ // We just erase all late-bound lifetimes, but this is not fully correct (FIXME):
109
+ // lifetimes in invariant positions could matter (e.g. through associated types).
110
+ // But that just means we miss some potential incompatible types, it will not
111
+ // lead to wrong errors.
112
+ lt_op : |_| tcx. lifetimes . re_erased ,
113
+ // Leave consts unchanged.
114
+ ct_op : |ct| ct,
115
+ } )
116
+ } ;
117
+ normalize ( src) == normalize ( dest)
118
+ }
119
+
86
120
impl < ' a , ' tcx > Visitor < ' tcx > for TypeChecker < ' a , ' tcx > {
87
121
fn visit_operand ( & mut self , operand : & Operand < ' tcx > , location : Location ) {
88
122
// `Operand::Copy` is only supposed to be used with `Copy` types.
@@ -99,9 +133,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
99
133
}
100
134
101
135
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
102
- // The sides of an assignment must not alias. Currently this just checks whether the places
103
- // are identical.
104
136
if let StatementKind :: Assign ( box ( dest, rvalue) ) = & statement. kind {
137
+ // LHS and RHS of the assignment must have the same type.
138
+ let left_ty = dest. ty ( & self . body . local_decls , self . tcx ) . ty ;
139
+ let right_ty = rvalue. ty ( & self . body . local_decls , self . tcx ) ;
140
+ if !mir_assign_valid_types ( self . tcx , right_ty, left_ty) {
141
+ self . fail (
142
+ location,
143
+ format ! (
144
+ "encountered `Assign` statement with incompatible types:\n \
145
+ left-hand side has type: {}\n \
146
+ right-hand side has type: {}",
147
+ left_ty, right_ty,
148
+ ) ,
149
+ ) ;
150
+ }
151
+ // The sides of an assignment must not alias. Currently this just checks whether the places
152
+ // are identical.
105
153
match rvalue {
106
154
Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) => {
107
155
if dest == src {
0 commit comments