2
2
3
3
use rustc_data_structures:: fx:: FxHashSet ;
4
4
use rustc_index:: bit_set:: BitSet ;
5
- use rustc_infer:: infer:: TyCtxtInferExt ;
5
+ use rustc_infer:: infer:: { DefiningAnchor , TyCtxtInferExt } ;
6
+ use rustc_infer:: traits:: ObligationCause ;
6
7
use rustc_middle:: mir:: interpret:: Scalar ;
7
8
use rustc_middle:: mir:: visit:: NonUseContext :: VarDebugInfo ;
8
9
use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
@@ -12,12 +13,12 @@ use rustc_middle::mir::{
12
13
ProjectionElem , RuntimePhase , Rvalue , SourceScope , Statement , StatementKind , Terminator ,
13
14
TerminatorKind , UnOp , START_BLOCK ,
14
15
} ;
15
- use rustc_middle:: ty:: fold:: BottomUpFolder ;
16
- use rustc_middle:: ty:: { self , InstanceDef , ParamEnv , Ty , TyCtxt , TypeFoldable , TypeVisitable } ;
16
+ use rustc_middle:: ty:: { self , InstanceDef , ParamEnv , Ty , TyCtxt , TypeVisitable } ;
17
17
use rustc_mir_dataflow:: impls:: MaybeStorageLive ;
18
18
use rustc_mir_dataflow:: storage:: always_storage_live_locals;
19
19
use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
20
20
use rustc_target:: abi:: { Size , VariantIdx } ;
21
+ use rustc_trait_selection:: traits:: ObligationCtxt ;
21
22
22
23
#[ derive( Copy , Clone , Debug ) ]
23
24
enum EdgeKind {
@@ -70,13 +71,11 @@ impl<'tcx> MirPass<'tcx> for Validator {
70
71
}
71
72
}
72
73
73
- /// Returns whether the two types are equal up to lifetimes.
74
- /// All lifetimes, including higher-ranked ones, get ignored for this comparison.
75
- /// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
74
+ /// Returns whether the two types are equal up to subtyping.
76
75
///
77
- /// The point of this function is to approximate "equal up to subtyping". However,
78
- /// the approximation is incorrect as variance is ignored .
79
- pub fn equal_up_to_regions < ' tcx > (
76
+ /// This is used in case we don't know the expected subtyping direction
77
+ /// and still want to check whether anything is broken .
78
+ pub fn is_equal_up_to_subtyping < ' tcx > (
80
79
tcx : TyCtxt < ' tcx > ,
81
80
param_env : ParamEnv < ' tcx > ,
82
81
src : Ty < ' tcx > ,
@@ -87,27 +86,40 @@ pub fn equal_up_to_regions<'tcx>(
87
86
return true ;
88
87
}
89
88
90
- // Normalize lifetimes away on both sides, then compare.
91
- let normalize = |ty : Ty < ' tcx > | {
92
- tcx. try_normalize_erasing_regions ( param_env, ty) . unwrap_or ( ty) . fold_with (
93
- & mut BottomUpFolder {
94
- tcx,
95
- // FIXME: We erase all late-bound lifetimes, but this is not fully correct.
96
- // If you have a type like `<for<'a> fn(&'a u32) as SomeTrait>::Assoc`,
97
- // this is not necessarily equivalent to `<fn(&'static u32) as SomeTrait>::Assoc`,
98
- // since one may have an `impl SomeTrait for fn(&32)` and
99
- // `impl SomeTrait for fn(&'static u32)` at the same time which
100
- // specify distinct values for Assoc. (See also #56105)
101
- lt_op : |_| tcx. lifetimes . re_erased ,
102
- // Leave consts and types unchanged.
103
- ct_op : |ct| ct,
104
- ty_op : |ty| ty,
105
- } ,
106
- )
107
- } ;
108
- tcx. infer_ctxt ( ) . build ( ) . can_eq ( param_env, normalize ( src) , normalize ( dest) ) . is_ok ( )
89
+ // Check for subtyping in either direction.
90
+ is_subtype ( tcx, param_env, src, dest) || is_subtype ( tcx, param_env, dest, src)
109
91
}
110
92
93
+ pub fn is_subtype < ' tcx > (
94
+ tcx : TyCtxt < ' tcx > ,
95
+ param_env : ParamEnv < ' tcx > ,
96
+ src : Ty < ' tcx > ,
97
+ dest : Ty < ' tcx > ,
98
+ ) -> bool {
99
+ if src == dest {
100
+ return true ;
101
+ }
102
+
103
+ let mut builder =
104
+ tcx. infer_ctxt ( ) . ignoring_regions ( ) . with_opaque_type_inference ( DefiningAnchor :: Bubble ) ;
105
+ let infcx = builder. build ( ) ;
106
+ let ocx = ObligationCtxt :: new ( & infcx) ;
107
+ let cause = ObligationCause :: dummy ( ) ;
108
+ let src = ocx. normalize ( cause. clone ( ) , param_env, src) ;
109
+ let dest = ocx. normalize ( cause. clone ( ) , param_env, dest) ;
110
+ let Ok ( infer_ok) = infcx. at ( & cause, param_env) . sub ( src, dest) else {
111
+ return false ;
112
+ } ;
113
+ let ( ) = ocx. register_infer_ok_obligations ( infer_ok) ;
114
+ let errors = ocx. select_all_or_error ( ) ;
115
+ // With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
116
+ // we would get unification errors because we're unable to look into opaque types,
117
+ // even if they're constrained in our current function.
118
+ //
119
+ // It seems very unlikely that this hides any bugs.
120
+ let _ = infcx. inner . borrow_mut ( ) . opaque_type_storage . take_opaque_types ( ) ;
121
+ errors. is_empty ( )
122
+ }
111
123
struct TypeChecker < ' a , ' tcx > {
112
124
when : & ' a str ,
113
125
body : & ' a Body < ' tcx > ,
@@ -183,22 +195,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
183
195
return true ;
184
196
}
185
197
186
- // Normalize projections and things like that.
187
- // Type-changing assignments can happen when subtyping is used. While
188
- // all normal lifetimes are erased, higher-ranked types with their
189
- // late-bound lifetimes are still around and can lead to type
190
- // differences. So we compare ignoring lifetimes.
191
-
192
- // First, try with reveal_all. This might not work in some cases, as the predicates
193
- // can be cleared in reveal_all mode. We try the reveal first anyways as it is used
194
- // by some other passes like inlining as well.
195
- let param_env = self . param_env . with_reveal_all_normalized ( self . tcx ) ;
196
- if equal_up_to_regions ( self . tcx , param_env, src, dest) {
197
- return true ;
198
- }
199
-
200
- // If this fails, we can try it without the reveal.
201
- equal_up_to_regions ( self . tcx , self . param_env , src, dest)
198
+ is_subtype ( self . tcx , self . param_env , src, dest)
202
199
}
203
200
}
204
201
0 commit comments