@@ -4,17 +4,19 @@ use rustc_attr as attr;
44use  rustc_data_structures:: fx:: FxHashSet ; 
55use  rustc_errors:: Applicability ; 
66use  rustc_hir as  hir; 
7- use  rustc_hir:: { is_range_literal,  ExprKind ,  Node } ; 
7+ use  rustc_hir:: def_id:: DefId ; 
8+ use  rustc_hir:: { is_range_literal,  Expr ,  ExprKind ,  Node } ; 
89use  rustc_middle:: ty:: layout:: { IntegerExt ,  SizeSkeleton } ; 
910use  rustc_middle:: ty:: subst:: SubstsRef ; 
10- use  rustc_middle:: ty:: { self ,  AdtKind ,  Ty ,  TyCtxt ,  TypeFoldable } ; 
11+ use  rustc_middle:: ty:: { self ,  AdtKind ,  DefIdTree ,   Ty ,  TyCtxt ,  TypeFoldable } ; 
1112use  rustc_span:: source_map; 
1213use  rustc_span:: symbol:: sym; 
13- use  rustc_span:: { Span ,  DUMMY_SP } ; 
14+ use  rustc_span:: { Span ,  Symbol ,   DUMMY_SP } ; 
1415use  rustc_target:: abi:: Abi ; 
1516use  rustc_target:: abi:: { Integer ,  LayoutOf ,  TagEncoding ,  Variants } ; 
1617use  rustc_target:: spec:: abi:: Abi  as  SpecAbi ; 
1718
19+ use  if_chain:: if_chain; 
1820use  std:: cmp; 
1921use  std:: iter; 
2022use  std:: ops:: ControlFlow ; 
@@ -1379,3 +1381,234 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
13791381        } 
13801382    } 
13811383} 
1384+ 
1385+ declare_lint !  { 
1386+     /// The `invalid_atomic_ordering` lint detects passing an `Ordering` 
1387+      /// to an atomic operation that does not support that ordering. 
1388+      /// 
1389+      /// ### Example 
1390+      /// 
1391+      /// ```rust,compile_fail 
1392+      /// # use core::sync::atomic::{AtomicU8, Ordering}; 
1393+      /// let atom = AtomicU8::new(0); 
1394+      /// let value = atom.load(Ordering::Release); 
1395+      /// # let _ = value; 
1396+      /// ``` 
1397+      /// 
1398+      /// {{produces}} 
1399+      /// 
1400+      /// ### Explanation 
1401+      /// 
1402+      /// Some atomic operations are only supported for a subset of the 
1403+      /// `atomic::Ordering` variants. Passing an unsupported variant will cause 
1404+      /// an unconditional panic at runtime, which is detected by this lint. 
1405+      /// 
1406+      /// This lint will trigger in the following cases: (where `AtomicType` is an 
1407+      /// atomic type from `core::sync::atomic`, such as `AtomicBool`, 
1408+      /// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics). 
1409+      /// 
1410+      /// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to 
1411+      ///   `AtomicType::store`. 
1412+      /// 
1413+      /// - Passing `Ordering::Release` or `Ordering::AcqRel` to 
1414+      ///   `AtomicType::load`. 
1415+      /// 
1416+      /// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or 
1417+      ///   `core::sync::atomic::compiler_fence`. 
1418+      /// 
1419+      /// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure 
1420+      ///   ordering for any of `AtomicType::compare_exchange`, 
1421+      ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`. 
1422+      /// 
1423+      /// - Passing in a pair of orderings to `AtomicType::compare_exchange`, 
1424+      ///   `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update` 
1425+      ///   where the failure ordering is stronger than the success ordering. 
1426+      INVALID_ATOMIC_ORDERING , 
1427+     Deny , 
1428+     "usage of invalid atomic ordering in atomic operations and memory fences" 
1429+ } 
1430+ 
1431+ declare_lint_pass ! ( InvalidAtomicOrdering  => [ INVALID_ATOMIC_ORDERING ] ) ; 
1432+ 
1433+ impl  InvalidAtomicOrdering  { 
1434+     fn  inherent_atomic_method_call < ' hir > ( 
1435+         cx :  & LateContext < ' _ > , 
1436+         expr :  & Expr < ' hir > , 
1437+     )  -> Option < ( Symbol ,  & ' hir  [ Expr < ' hir > ] ) >  { 
1438+         const  ATOMIC_TYPES :  & [ Symbol ]  = & [ 
1439+             sym:: AtomicBool , 
1440+             sym:: AtomicPtr , 
1441+             sym:: AtomicUsize , 
1442+             sym:: AtomicU8 , 
1443+             sym:: AtomicU16 , 
1444+             sym:: AtomicU32 , 
1445+             sym:: AtomicU64 , 
1446+             sym:: AtomicU128 , 
1447+             sym:: AtomicIsize , 
1448+             sym:: AtomicI8 , 
1449+             sym:: AtomicI16 , 
1450+             sym:: AtomicI32 , 
1451+             sym:: AtomicI64 , 
1452+             sym:: AtomicI128 , 
1453+         ] ; 
1454+         if_chain !  { 
1455+             if  let  ExprKind :: MethodCall ( ref method_path,  _,  args,  _)  = & expr. kind; 
1456+             if  let  Some ( m_def_id)  = cx. typeck_results( ) . type_dependent_def_id( expr. hir_id) ; 
1457+             if  let  Some ( impl_did)  = cx. tcx. impl_of_method( m_def_id) ; 
1458+             if  let  Some ( adt)  = cx. tcx. type_of( impl_did) . ty_adt_def( ) ; 
1459+             // skip extension traits, only lint functions from the standard library 
1460+             if  cx. tcx. trait_id_of_impl( impl_did) . is_none( ) ; 
1461+ 
1462+             if  let  Some ( parent)  = cx. tcx. parent( adt. did) ; 
1463+             if  cx. tcx. is_diagnostic_item( sym:: atomic_mod,  parent) ; 
1464+             if  ATOMIC_TYPES . contains( & cx. tcx. item_name( adt. did) ) ; 
1465+             then { 
1466+                 return  Some ( ( method_path. ident. name,  args) ) ; 
1467+             } 
1468+         } 
1469+         None 
1470+     } 
1471+ 
1472+     fn  matches_ordering ( cx :  & LateContext < ' _ > ,  did :  DefId ,  orderings :  & [ Symbol ] )  -> bool  { 
1473+         let  tcx = cx. tcx ; 
1474+         let  atomic_ordering = tcx. get_diagnostic_item ( sym:: Ordering ) ; 
1475+         orderings. iter ( ) . any ( |ordering| { 
1476+             tcx. item_name ( did)  == * ordering && { 
1477+                 let  parent = tcx. parent ( did) ; 
1478+                 parent == atomic_ordering
1479+                     // needed in case this is a ctor, not a variant 
1480+                     || parent. map_or ( false ,  |parent| tcx. parent ( parent)  == atomic_ordering) 
1481+             } 
1482+         } ) 
1483+     } 
1484+ 
1485+     fn  opt_ordering_defid ( cx :  & LateContext < ' _ > ,  ord_arg :  & Expr < ' _ > )  -> Option < DefId >  { 
1486+         if  let  ExprKind :: Path ( ref  ord_qpath)  = ord_arg. kind  { 
1487+             cx. qpath_res ( ord_qpath,  ord_arg. hir_id ) . opt_def_id ( ) 
1488+         }  else  { 
1489+             None 
1490+         } 
1491+     } 
1492+ 
1493+     fn  check_atomic_load_store ( cx :  & LateContext < ' _ > ,  expr :  & Expr < ' _ > )  { 
1494+         use  rustc_hir:: def:: { DefKind ,  Res } ; 
1495+         use  rustc_hir:: QPath ; 
1496+         if_chain !  { 
1497+             if  let  Some ( ( method,  args) )  = Self :: inherent_atomic_method_call( cx,  expr) ; 
1498+             if  let  Some ( ( ordering_arg,  invalid_ordering) )  = match  method { 
1499+                 sym:: load => Some ( ( & args[ 1 ] ,  sym:: Release ) ) , 
1500+                 sym:: store => Some ( ( & args[ 2 ] ,  sym:: Acquire ) ) , 
1501+                 _ => None , 
1502+             } ; 
1503+ 
1504+             if  let  ExprKind :: Path ( QPath :: Resolved ( _,  path) )  = ordering_arg. kind; 
1505+             if  let  Res :: Def ( DefKind :: Ctor ( ..) ,  ctor_id)  = path. res; 
1506+             if  Self :: matches_ordering( cx,  ctor_id,  & [ invalid_ordering,  sym:: AcqRel ] ) ; 
1507+             then { 
1508+                 cx. struct_span_lint( INVALID_ATOMIC_ORDERING ,  ordering_arg. span,  |diag| { 
1509+                     if  method == sym:: load { 
1510+                         diag. build( "atomic loads cannot have `Release` or `AcqRel` ordering" ) 
1511+                             . help( "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`" ) 
1512+                             . emit( ) 
1513+                     }  else { 
1514+                         debug_assert_eq!( method,  sym:: store) ; 
1515+                         diag. build( "atomic stores cannot have `Acquire` or `AcqRel` ordering" ) 
1516+                             . help( "consider using ordering modes `Release`, `SeqCst` or `Relaxed`" ) 
1517+                             . emit( ) ; 
1518+                     } 
1519+                 } ) ; 
1520+             } 
1521+         } 
1522+     } 
1523+ 
1524+     fn  check_memory_fence ( cx :  & LateContext < ' _ > ,  expr :  & Expr < ' _ > )  { 
1525+         if_chain !  { 
1526+             if  let  ExprKind :: Call ( ref func,  ref args)  = expr. kind; 
1527+             if  let  ExprKind :: Path ( ref func_qpath)  = func. kind; 
1528+             if  let  Some ( def_id)  = cx. qpath_res( func_qpath,  func. hir_id) . opt_def_id( ) ; 
1529+             if  cx. tcx. is_diagnostic_item( sym:: fence,  def_id)  ||
1530+                 cx. tcx. is_diagnostic_item( sym:: compiler_fence,  def_id) ; 
1531+             if  let  ExprKind :: Path ( ref ordering_qpath)  = & args[ 0 ] . kind; 
1532+             if  let  Some ( ordering_def_id)  = cx. qpath_res( ordering_qpath,  args[ 0 ] . hir_id) . opt_def_id( ) ; 
1533+             if  Self :: matches_ordering( cx,  ordering_def_id,  & [ sym:: Relaxed ] ) ; 
1534+             then { 
1535+                 cx. struct_span_lint( INVALID_ATOMIC_ORDERING ,  args[ 0 ] . span,  |diag| { 
1536+                     diag. build( "memory fences cannot have `Relaxed` ordering" ) 
1537+                         . help( "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`" ) 
1538+                         . emit( ) ; 
1539+                 } ) ; 
1540+             } 
1541+         } 
1542+     } 
1543+ 
1544+     fn  check_atomic_compare_exchange ( cx :  & LateContext < ' _ > ,  expr :  & Expr < ' _ > )  { 
1545+         if_chain !  { 
1546+             if  let  Some ( ( method,  args) )  = Self :: inherent_atomic_method_call( cx,  expr) ; 
1547+             if  let  Some ( ( success_order_arg,  failure_order_arg) )  = match  method { 
1548+                 sym:: fetch_update => Some ( ( & args[ 1 ] ,  & args[ 2 ] ) ) , 
1549+                 sym:: compare_exchange | sym:: compare_exchange_weak => Some ( ( & args[ 3 ] ,  & args[ 4 ] ) ) , 
1550+                 _ => None , 
1551+             } ; 
1552+ 
1553+             if  let  Some ( fail_ordering_def_id)  = Self :: opt_ordering_defid( cx,  failure_order_arg) ; 
1554+             then { 
1555+                 // Helper type holding on to some checking and error reporting data. Has 
1556+                 // - (success ordering, 
1557+                 // - list of failure orderings forbidden by the success order, 
1558+                 // - suggestion message) 
1559+                 type  OrdLintInfo  = ( Symbol ,  & ' static  [ Symbol ] ,  & ' static  str ) ; 
1560+                 const  RELAXED :  OrdLintInfo  = ( sym:: Relaxed ,  & [ sym:: SeqCst ,  sym:: Acquire ] ,  "ordering mode `Relaxed`" ) ; 
1561+                 const  ACQUIRE :  OrdLintInfo  = ( sym:: Acquire ,  & [ sym:: SeqCst ] ,  "ordering modes `Acquire` or `Relaxed`" ) ; 
1562+                 const  SEQ_CST :  OrdLintInfo  = ( sym:: SeqCst ,  & [ ] ,  "ordering modes `Acquire`, `SeqCst` or `Relaxed`" ) ; 
1563+                 const  RELEASE :  OrdLintInfo  = ( sym:: Release ,  RELAXED . 1 ,  RELAXED . 2 ) ; 
1564+                 const  ACQREL :  OrdLintInfo  = ( sym:: AcqRel ,  ACQUIRE . 1 ,  ACQUIRE . 2 ) ; 
1565+                 const  SEARCH :  [ OrdLintInfo ;  5 ]  = [ RELAXED ,  ACQUIRE ,  SEQ_CST ,  RELEASE ,  ACQREL ] ; 
1566+ 
1567+                 let  success_lint_info = Self :: opt_ordering_defid( cx,  success_order_arg) 
1568+                     . and_then( |success_ord_def_id| -> Option <OrdLintInfo > { 
1569+                         SEARCH 
1570+                             . iter( ) 
1571+                             . copied( ) 
1572+                             . find( |( ordering,  ..) | { 
1573+                                 Self :: matches_ordering( cx,  success_ord_def_id,  & [ * ordering] ) 
1574+                             } ) 
1575+                     } ) ; 
1576+                 if  Self :: matches_ordering( cx,  fail_ordering_def_id,  & [ sym:: Release ,  sym:: AcqRel ] )  { 
1577+                     // If we don't know the success order is, use what we'd suggest 
1578+                     // if it were maximally permissive. 
1579+                     let  suggested = success_lint_info. unwrap_or( SEQ_CST ) . 2 ; 
1580+                     cx. struct_span_lint( INVALID_ATOMIC_ORDERING ,  failure_order_arg. span,  |diag| { 
1581+                         let  msg = format!( 
1582+                             "{}'s failure ordering may not be `Release` or `AcqRel`" , 
1583+                             method, 
1584+                         ) ; 
1585+                         diag. build( & msg) 
1586+                             . help( & format!( "consider using {} instead" ,  suggested) ) 
1587+                             . emit( ) ; 
1588+                     } ) ; 
1589+                 }  else if  let  Some ( ( success_ord,  bad_ords_given_success,  suggested) )  = success_lint_info { 
1590+                     if  Self :: matches_ordering( cx,  fail_ordering_def_id,  bad_ords_given_success)  { 
1591+                         cx. struct_span_lint( INVALID_ATOMIC_ORDERING ,  failure_order_arg. span,  |diag| { 
1592+                             let  msg = format!( 
1593+                                 "{}'s failure ordering may not be stronger than the success ordering of `{}`" , 
1594+                                 method, 
1595+                                 success_ord, 
1596+                             ) ; 
1597+                             diag. build( & msg) 
1598+                                 . help( & format!( "consider using {} instead" ,  suggested) ) 
1599+                                 . emit( ) ; 
1600+                         } ) ; 
1601+                     } 
1602+                 } 
1603+             } 
1604+         } 
1605+     } 
1606+ } 
1607+ 
1608+ impl < ' tcx >  LateLintPass < ' tcx >  for  InvalidAtomicOrdering  { 
1609+     fn  check_expr ( & mut  self ,  cx :  & LateContext < ' tcx > ,  expr :  & ' tcx  Expr < ' _ > )  { 
1610+         Self :: check_atomic_load_store ( cx,  expr) ; 
1611+         Self :: check_memory_fence ( cx,  expr) ; 
1612+         Self :: check_atomic_compare_exchange ( cx,  expr) ; 
1613+     } 
1614+ } 
0 commit comments