22
33use rustc_data_structures:: fx:: FxHashSet ;
44use rustc_index:: bit_set:: BitSet ;
5- use rustc_infer:: infer:: TyCtxtInferExt ;
5+ use rustc_infer:: infer:: { DefiningAnchor , TyCtxtInferExt } ;
6+ use rustc_infer:: traits:: ObligationCause ;
67use rustc_middle:: mir:: interpret:: Scalar ;
78use rustc_middle:: mir:: visit:: NonUseContext :: VarDebugInfo ;
89use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
@@ -12,12 +13,12 @@ use rustc_middle::mir::{
1213 ProjectionElem , RuntimePhase , Rvalue , SourceScope , Statement , StatementKind , Terminator ,
1314 TerminatorKind , UnOp , START_BLOCK ,
1415} ;
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 } ;
1717use rustc_mir_dataflow:: impls:: MaybeStorageLive ;
1818use rustc_mir_dataflow:: storage:: always_storage_live_locals;
1919use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
2020use rustc_target:: abi:: { Size , VariantIdx } ;
21+ use rustc_trait_selection:: traits:: ObligationCtxt ;
2122
2223#[ derive( Copy , Clone , Debug ) ]
2324enum EdgeKind {
@@ -70,13 +71,11 @@ impl<'tcx> MirPass<'tcx> for Validator {
7071 }
7172}
7273
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.
7675///
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 > (
8079 tcx : TyCtxt < ' tcx > ,
8180 param_env : ParamEnv < ' tcx > ,
8281 src : Ty < ' tcx > ,
@@ -87,27 +86,40 @@ pub fn equal_up_to_regions<'tcx>(
8786 return true ;
8887 }
8988
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)
10991}
11092
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+ }
111123struct TypeChecker < ' a , ' tcx > {
112124 when : & ' a str ,
113125 body : & ' a Body < ' tcx > ,
@@ -183,22 +195,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
183195 return true ;
184196 }
185197
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)
202199 }
203200}
204201
0 commit comments