1- use crate :: FnCtxt ;
1+ use std:: cell:: OnceCell ;
2+
3+ use crate :: { errors, FnCtxt } ;
24use rustc_data_structures:: {
35 graph:: { self , iterate:: DepthFirstSearch , vec_graph:: VecGraph } ,
46 unord:: { UnordBag , UnordMap , UnordSet } ,
57} ;
8+ use rustc_hir:: HirId ;
69use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk } ;
7- use rustc_middle:: ty:: { self , Ty } ;
10+ use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitable } ;
11+ use rustc_session:: lint;
12+ use rustc_span:: Span ;
813use rustc_span:: DUMMY_SP ;
914
1015#[ derive( Copy , Clone ) ]
@@ -248,7 +253,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
248253
249254 // Construct a coercion graph where an edge `A -> B` indicates
250255 // a type variable is that is coerced
251- let coercion_graph = self . create_coercion_graph ( ) ;
256+ let ( coercion_graph, coercion_graph2 ) = self . create_coercion_graph ( ) ;
252257
253258 // Extract the unsolved type inference variable vids; note that some
254259 // unsolved variables are integer/float variables and are excluded.
@@ -335,6 +340,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
335340 // reach a member of N. If so, it falls back to `()`. Else
336341 // `!`.
337342 let mut diverging_fallback = UnordMap :: with_capacity ( diverging_vids. len ( ) ) ;
343+ let unsafe_infer_vars = OnceCell :: new ( ) ;
338344 for & diverging_vid in & diverging_vids {
339345 let diverging_ty = Ty :: new_var ( self . tcx , diverging_vid) ;
340346 let root_vid = self . root_var ( diverging_vid) ;
@@ -354,11 +360,35 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
354360 output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
355361 } ;
356362
363+ let mut fallback_to = |ty| {
364+ let unsafe_infer_vars = unsafe_infer_vars. get_or_init ( || {
365+ let unsafe_infer_vars = compute_unsafe_infer_vars ( self . root_ctxt , self . body_id ) ;
366+ debug ! ( ?unsafe_infer_vars) ;
367+ unsafe_infer_vars
368+ } ) ;
369+
370+ let affected_unsafe_infer_vars =
371+ graph:: depth_first_search ( & coercion_graph2, root_vid)
372+ . filter_map ( |x| unsafe_infer_vars. get ( & x) . copied ( ) )
373+ . collect :: < Vec < _ > > ( ) ;
374+
375+ for ( hir_id, span) in affected_unsafe_infer_vars {
376+ self . tcx . emit_node_span_lint (
377+ lint:: builtin:: NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE ,
378+ hir_id,
379+ span,
380+ errors:: NeverTypeFallbackFlowingIntoUnsafe { } ,
381+ ) ;
382+ }
383+
384+ diverging_fallback. insert ( diverging_ty, ty) ;
385+ } ;
386+
357387 use DivergingFallbackBehavior :: * ;
358388 match behavior {
359389 FallbackToUnit => {
360390 debug ! ( "fallback to () - legacy: {:?}" , diverging_vid) ;
361- diverging_fallback . insert ( diverging_ty , self . tcx . types . unit ) ;
391+ fallback_to ( self . tcx . types . unit ) ;
362392 }
363393 FallbackToNiko => {
364394 if found_infer_var_info. self_in_trait && found_infer_var_info. output {
@@ -387,21 +417,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
387417 // set, see the relationship finding module in
388418 // compiler/rustc_trait_selection/src/traits/relationships.rs.
389419 debug ! ( "fallback to () - found trait and projection: {:?}" , diverging_vid) ;
390- diverging_fallback . insert ( diverging_ty , self . tcx . types . unit ) ;
420+ fallback_to ( self . tcx . types . unit ) ;
391421 } else if can_reach_non_diverging {
392422 debug ! ( "fallback to () - reached non-diverging: {:?}" , diverging_vid) ;
393- diverging_fallback . insert ( diverging_ty , self . tcx . types . unit ) ;
423+ fallback_to ( self . tcx . types . unit ) ;
394424 } else {
395425 debug ! ( "fallback to ! - all diverging: {:?}" , diverging_vid) ;
396- diverging_fallback . insert ( diverging_ty , self . tcx . types . never ) ;
426+ fallback_to ( self . tcx . types . never ) ;
397427 }
398428 }
399429 FallbackToNever => {
400430 debug ! (
401431 "fallback to ! - `rustc_never_type_mode = \" fallback_to_never\" )`: {:?}" ,
402432 diverging_vid
403433 ) ;
404- diverging_fallback . insert ( diverging_ty , self . tcx . types . never ) ;
434+ fallback_to ( self . tcx . types . never ) ;
405435 }
406436 NoFallback => {
407437 debug ! (
@@ -417,7 +447,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
417447
418448 /// Returns a graph whose nodes are (unresolved) inference variables and where
419449 /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
420- fn create_coercion_graph ( & self ) -> VecGraph < ty:: TyVid > {
450+ ///
451+ /// The second element of the return tuple is a graph with edges in both directions.
452+ fn create_coercion_graph ( & self ) -> ( VecGraph < ty:: TyVid > , VecGraph < ty:: TyVid > ) {
421453 let pending_obligations = self . fulfillment_cx . borrow_mut ( ) . pending_obligations ( ) ;
422454 debug ! ( "create_coercion_graph: pending_obligations={:?}" , pending_obligations) ;
423455 let coercion_edges: Vec < ( ty:: TyVid , ty:: TyVid ) > = pending_obligations
@@ -451,11 +483,113 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
451483 . collect ( ) ;
452484 debug ! ( "create_coercion_graph: coercion_edges={:?}" , coercion_edges) ;
453485 let num_ty_vars = self . num_ty_vars ( ) ;
454- VecGraph :: new ( num_ty_vars, coercion_edges)
486+
487+ // This essentially creates a non-directed graph.
488+ // Ideally we wouldn't do it like this, but it works ig :\
489+ let doubly_connected = VecGraph :: new (
490+ num_ty_vars,
491+ coercion_edges
492+ . iter ( )
493+ . copied ( )
494+ . chain ( coercion_edges. iter ( ) . copied ( ) . map ( |( a, b) | ( b, a) ) )
495+ . collect ( ) ,
496+ ) ;
497+
498+ let normal = VecGraph :: new ( num_ty_vars, coercion_edges. clone ( ) ) ;
499+
500+ ( normal, doubly_connected)
455501 }
456502
457503 /// If `ty` is an unresolved type variable, returns its root vid.
458504 fn root_vid ( & self , ty : Ty < ' tcx > ) -> Option < ty:: TyVid > {
459505 Some ( self . root_var ( self . shallow_resolve ( ty) . ty_vid ( ) ?) )
460506 }
461507}
508+
509+ /// Finds all type variables which are passed to an `unsafe` function.
510+ ///
511+ /// For example, for this function `f`:
512+ /// ```ignore (demonstrative)
513+ /// fn f() {
514+ /// unsafe {
515+ /// let x /* ?X */ = core::mem::zeroed();
516+ /// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span
517+ ///
518+ /// let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
519+ /// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span
520+ /// }
521+ /// }
522+ /// ```
523+ ///
524+ /// Will return `{ id(?X) -> (hir_id, span) }`
525+ fn compute_unsafe_infer_vars < ' a , ' tcx > (
526+ root_ctxt : & ' a crate :: TypeckRootCtxt < ' tcx > ,
527+ body_id : rustc_span:: def_id:: LocalDefId ,
528+ ) -> UnordMap < ty:: TyVid , ( HirId , Span ) > {
529+ use rustc_hir as hir;
530+
531+ let tcx = root_ctxt. infcx . tcx ;
532+ let body_id = tcx. hir ( ) . maybe_body_owned_by ( body_id) . unwrap ( ) ;
533+ let body = tcx. hir ( ) . body ( body_id) ;
534+ let mut res = <_ >:: default ( ) ;
535+
536+ struct UnsafeInferVarsVisitor < ' a , ' tcx , ' r > {
537+ root_ctxt : & ' a crate :: TypeckRootCtxt < ' tcx > ,
538+ res : & ' r mut UnordMap < ty:: TyVid , ( HirId , Span ) > ,
539+ }
540+
541+ use hir:: intravisit:: Visitor ;
542+ impl hir:: intravisit:: Visitor < ' _ > for UnsafeInferVarsVisitor < ' _ , ' _ , ' _ > {
543+ fn visit_expr ( & mut self , ex : & ' _ hir:: Expr < ' _ > ) {
544+ // FIXME: method calls
545+ if let hir:: ExprKind :: Call ( func, ..) = ex. kind {
546+ let typeck_results = self . root_ctxt . typeck_results . borrow ( ) ;
547+
548+ let func_ty = typeck_results. expr_ty ( func) ;
549+
550+ // `is_fn` is required to ignore closures (which can't be unsafe)
551+ if func_ty. is_fn ( )
552+ && let sig = func_ty. fn_sig ( self . root_ctxt . infcx . tcx )
553+ && let hir:: Unsafety :: Unsafe = sig. unsafety ( )
554+ {
555+ let mut collector =
556+ InferVarCollector { hir_id : ex. hir_id , call_span : ex. span , res : self . res } ;
557+
558+ // Collect generic arguments of the function which are inference variables
559+ typeck_results
560+ . node_args ( ex. hir_id )
561+ . types ( )
562+ . for_each ( |t| t. visit_with ( & mut collector) ) ;
563+
564+ // Also check the return type, for cases like `(unsafe_fn::<_> as unsafe fn() -> _)()`
565+ sig. output ( ) . visit_with ( & mut collector) ;
566+ }
567+ }
568+
569+ hir:: intravisit:: walk_expr ( self , ex) ;
570+ }
571+ }
572+
573+ struct InferVarCollector < ' r > {
574+ hir_id : HirId ,
575+ call_span : Span ,
576+ res : & ' r mut UnordMap < ty:: TyVid , ( HirId , Span ) > ,
577+ }
578+
579+ impl < ' tcx > ty:: TypeVisitor < TyCtxt < ' tcx > > for InferVarCollector < ' _ > {
580+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) {
581+ if let Some ( vid) = t. ty_vid ( ) {
582+ self . res . insert ( vid, ( self . hir_id , self . call_span ) ) ;
583+ } else {
584+ use ty:: TypeSuperVisitable as _;
585+ t. super_visit_with ( self )
586+ }
587+ }
588+ }
589+
590+ UnsafeInferVarsVisitor { root_ctxt, res : & mut res } . visit_expr ( & body. value ) ;
591+
592+ debug ! ( ?res, "collected the following unsafe vars for {body_id:?}" ) ;
593+
594+ res
595+ }
0 commit comments