1
- use crate :: FnCtxt ;
1
+ use std:: cell:: OnceCell ;
2
+
3
+ use crate :: { errors, FnCtxt } ;
2
4
use rustc_data_structures:: {
3
5
graph:: { self , iterate:: DepthFirstSearch , vec_graph:: VecGraph } ,
4
6
unord:: { UnordBag , UnordMap , UnordSet } ,
5
7
} ;
8
+ use rustc_hir:: HirId ;
6
9
use 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 ;
8
13
use rustc_span:: DUMMY_SP ;
9
14
10
15
#[ derive( Copy , Clone ) ]
@@ -335,6 +340,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
335
340
// reach a member of N. If so, it falls back to `()`. Else
336
341
// `!`.
337
342
let mut diverging_fallback = UnordMap :: with_capacity ( diverging_vids. len ( ) ) ;
343
+ let unsafe_infer_vars = OnceCell :: new ( ) ;
338
344
for & diverging_vid in & diverging_vids {
339
345
let diverging_ty = Ty :: new_var ( self . tcx , diverging_vid) ;
340
346
let root_vid = self . root_var ( diverging_vid) ;
@@ -354,11 +360,35 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
354
360
output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
355
361
} ;
356
362
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_as_undirected ( & coercion_graph, 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
+
357
387
use DivergingFallbackBehavior :: * ;
358
388
match behavior {
359
389
FallbackToUnit => {
360
390
debug ! ( "fallback to () - legacy: {:?}" , diverging_vid) ;
361
- diverging_fallback . insert ( diverging_ty , self . tcx . types . unit ) ;
391
+ fallback_to ( self . tcx . types . unit ) ;
362
392
}
363
393
FallbackToNiko => {
364
394
if found_infer_var_info. self_in_trait && found_infer_var_info. output {
@@ -387,21 +417,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
387
417
// set, see the relationship finding module in
388
418
// compiler/rustc_trait_selection/src/traits/relationships.rs.
389
419
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 ) ;
391
421
} else if can_reach_non_diverging {
392
422
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 ) ;
394
424
} else {
395
425
debug ! ( "fallback to ! - all diverging: {:?}" , diverging_vid) ;
396
- diverging_fallback . insert ( diverging_ty , self . tcx . types . never ) ;
426
+ fallback_to ( self . tcx . types . never ) ;
397
427
}
398
428
}
399
429
FallbackToNever => {
400
430
debug ! (
401
431
"fallback to ! - `rustc_never_type_mode = \" fallback_to_never\" )`: {:?}" ,
402
432
diverging_vid
403
433
) ;
404
- diverging_fallback . insert ( diverging_ty , self . tcx . types . never ) ;
434
+ fallback_to ( self . tcx . types . never ) ;
405
435
}
406
436
NoFallback => {
407
437
debug ! (
@@ -417,7 +447,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
417
447
418
448
/// Returns a graph whose nodes are (unresolved) inference variables and where
419
449
/// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
420
- fn create_coercion_graph ( & self ) -> VecGraph < ty:: TyVid > {
450
+ fn create_coercion_graph ( & self ) -> VecGraph < ty:: TyVid , true > {
421
451
let pending_obligations = self . fulfillment_cx . borrow_mut ( ) . pending_obligations ( ) ;
422
452
debug ! ( "create_coercion_graph: pending_obligations={:?}" , pending_obligations) ;
423
453
let coercion_edges: Vec < ( ty:: TyVid , ty:: TyVid ) > = pending_obligations
@@ -451,6 +481,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
451
481
. collect ( ) ;
452
482
debug ! ( "create_coercion_graph: coercion_edges={:?}" , coercion_edges) ;
453
483
let num_ty_vars = self . num_ty_vars ( ) ;
484
+
454
485
VecGraph :: new ( num_ty_vars, coercion_edges)
455
486
}
456
487
@@ -459,3 +490,91 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
459
490
Some ( self . root_var ( self . shallow_resolve ( ty) . ty_vid ( ) ?) )
460
491
}
461
492
}
493
+
494
+ /// Finds all type variables which are passed to an `unsafe` function.
495
+ ///
496
+ /// For example, for this function `f`:
497
+ /// ```ignore (demonstrative)
498
+ /// fn f() {
499
+ /// unsafe {
500
+ /// let x /* ?X */ = core::mem::zeroed();
501
+ /// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span
502
+ ///
503
+ /// let y = core::mem::zeroed::<Option<_ /* ?Y */>>();
504
+ /// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span
505
+ /// }
506
+ /// }
507
+ /// ```
508
+ ///
509
+ /// Will return `{ id(?X) -> (hir_id, span) }`
510
+ fn compute_unsafe_infer_vars < ' a , ' tcx > (
511
+ root_ctxt : & ' a crate :: TypeckRootCtxt < ' tcx > ,
512
+ body_id : rustc_span:: def_id:: LocalDefId ,
513
+ ) -> UnordMap < ty:: TyVid , ( HirId , Span ) > {
514
+ use rustc_hir as hir;
515
+
516
+ let tcx = root_ctxt. infcx . tcx ;
517
+ let body_id = tcx. hir ( ) . maybe_body_owned_by ( body_id) . unwrap ( ) ;
518
+ let body = tcx. hir ( ) . body ( body_id) ;
519
+ let mut res = <_ >:: default ( ) ;
520
+
521
+ struct UnsafeInferVarsVisitor < ' a , ' tcx , ' r > {
522
+ root_ctxt : & ' a crate :: TypeckRootCtxt < ' tcx > ,
523
+ res : & ' r mut UnordMap < ty:: TyVid , ( HirId , Span ) > ,
524
+ }
525
+
526
+ use hir:: intravisit:: Visitor ;
527
+ impl hir:: intravisit:: Visitor < ' _ > for UnsafeInferVarsVisitor < ' _ , ' _ , ' _ > {
528
+ fn visit_expr ( & mut self , ex : & ' _ hir:: Expr < ' _ > ) {
529
+ // FIXME: method calls
530
+ if let hir:: ExprKind :: Call ( func, ..) = ex. kind {
531
+ let typeck_results = self . root_ctxt . typeck_results . borrow ( ) ;
532
+
533
+ let func_ty = typeck_results. expr_ty ( func) ;
534
+
535
+ // `is_fn` is required to ignore closures (which can't be unsafe)
536
+ if func_ty. is_fn ( )
537
+ && let sig = func_ty. fn_sig ( self . root_ctxt . infcx . tcx )
538
+ && let hir:: Unsafety :: Unsafe = sig. unsafety ( )
539
+ {
540
+ let mut collector =
541
+ InferVarCollector { hir_id : ex. hir_id , call_span : ex. span , res : self . res } ;
542
+
543
+ // Collect generic arguments of the function which are inference variables
544
+ typeck_results
545
+ . node_args ( ex. hir_id )
546
+ . types ( )
547
+ . for_each ( |t| t. visit_with ( & mut collector) ) ;
548
+
549
+ // Also check the return type, for cases like `(unsafe_fn::<_> as unsafe fn() -> _)()`
550
+ sig. output ( ) . visit_with ( & mut collector) ;
551
+ }
552
+ }
553
+
554
+ hir:: intravisit:: walk_expr ( self , ex) ;
555
+ }
556
+ }
557
+
558
+ struct InferVarCollector < ' r > {
559
+ hir_id : HirId ,
560
+ call_span : Span ,
561
+ res : & ' r mut UnordMap < ty:: TyVid , ( HirId , Span ) > ,
562
+ }
563
+
564
+ impl < ' tcx > ty:: TypeVisitor < TyCtxt < ' tcx > > for InferVarCollector < ' _ > {
565
+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) {
566
+ if let Some ( vid) = t. ty_vid ( ) {
567
+ self . res . insert ( vid, ( self . hir_id , self . call_span ) ) ;
568
+ } else {
569
+ use ty:: TypeSuperVisitable as _;
570
+ t. super_visit_with ( self )
571
+ }
572
+ }
573
+ }
574
+
575
+ UnsafeInferVarsVisitor { root_ctxt, res : & mut res } . visit_expr ( & body. value ) ;
576
+
577
+ debug ! ( ?res, "collected the following unsafe vars for {body_id:?}" ) ;
578
+
579
+ res
580
+ }
0 commit comments