11use rustc_ast:: { NestedMetaItem , CRATE_NODE_ID } ;
22use rustc_attr as attr;
3- use rustc_data_structures:: fx:: FxHashSet ;
3+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
44use rustc_errors:: struct_span_err;
55use rustc_hir as hir;
66use rustc_hir:: def:: DefKind ;
@@ -12,12 +12,17 @@ use rustc_session::Session;
1212use rustc_span:: symbol:: { sym, Symbol } ;
1313use rustc_target:: spec:: abi:: Abi ;
1414
15+ use std:: { iter, mem} ;
16+
1517crate fn collect ( tcx : TyCtxt < ' _ > ) -> Vec < NativeLib > {
16- let mut collector = Collector { tcx, libs : Vec :: new ( ) } ;
18+ let mut collector = Collector { tcx, libs : Vec :: new ( ) , attr_libs : 0 } ;
1719 for id in tcx. hir ( ) . items ( ) {
1820 collector. process_item ( id) ;
1921 }
22+ collector. attr_libs = collector. libs . len ( ) ;
2023 collector. process_command_line ( ) ;
24+ collector. unify_kinds_and_modifiers ( ) ;
25+ collector. compat_reorder ( ) ;
2126 collector. libs
2227}
2328
@@ -31,6 +36,7 @@ crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
3136struct Collector < ' tcx > {
3237 tcx : TyCtxt < ' tcx > ,
3338 libs : Vec < NativeLib > ,
39+ attr_libs : usize ,
3440}
3541
3642impl < ' tcx > Collector < ' tcx > {
@@ -363,99 +369,132 @@ impl<'tcx> Collector<'tcx> {
363369
364370 // Process libs passed on the command line
365371 fn process_command_line ( & mut self ) {
366- // First, check for errors
367- let mut renames = FxHashSet :: default ( ) ;
368- for lib in & self . tcx . sess . opts . libs {
369- if let NativeLibKind :: Framework { .. } = lib . kind && !self . tcx . sess . target . is_like_osx {
372+ // Collect overrides and check them for errors
373+ let mut overrides = FxHashMap :: default ( ) ;
374+ for cmd_lib in & self . tcx . sess . opts . libs {
375+ if let NativeLibKind :: Framework { .. } = cmd_lib . kind && !self . tcx . sess . target . is_like_osx {
370376 // Cannot check this when parsing options because the target is not yet available.
371377 self . tcx . sess . err ( "library kind `framework` is only supported on Apple targets" ) ;
372378 }
373- if let Some ( ref new_name) = lib. new_name {
374- let any_duplicate = self
379+ if let Some ( override_name) = & cmd_lib. new_name {
380+ if override_name. is_empty ( ) {
381+ self . tcx . sess . err ( & format ! (
382+ "empty override name was specified for library `{}`" ,
383+ cmd_lib. name
384+ ) ) ;
385+ } else if self
375386 . libs
376387 . iter ( )
377- . filter_map ( |lib| lib. name . as_ref ( ) )
378- . any ( |n| n. as_str ( ) == lib. name ) ;
379- if new_name. is_empty ( ) {
380- self . tcx . sess . err ( format ! (
381- "an empty renaming target was specified for library `{}`" ,
382- lib. name
388+ . filter_map ( |attr_lib| attr_lib. name )
389+ . all ( |attr_lib_name| attr_lib_name. as_str ( ) != cmd_lib. name )
390+ {
391+ self . tcx . sess . err ( & format ! (
392+ "override of the library `{}` was specified, however this crate \
393+ contains no `#[link]` attributes referencing this library",
394+ cmd_lib. name
383395 ) ) ;
384- } else if !any_duplicate {
385- self . tcx . sess . err ( format ! (
386- "renaming of the library `{}` was specified, \
387- however this crate contains no `#[link(...)]` \
388- attributes referencing this library",
389- lib. name
390- ) ) ;
391- } else if !renames. insert ( & lib. name ) {
392- self . tcx . sess . err ( format ! (
393- "multiple renamings were \
394- specified for library `{}`",
395- lib. name
396+ } else if overrides. insert ( & cmd_lib. name , cmd_lib) . is_some ( ) {
397+ self . tcx . sess . err ( & format ! (
398+ "multiple overrides were specified for library `{}`" ,
399+ cmd_lib. name
396400 ) ) ;
397401 }
398402 }
399403 }
400404
401- // Update kind and, optionally, the name of all native libraries
402- // (there may be more than one) with the specified name. If any
403- // library is mentioned more than once, keep the latest mention
404- // of it, so that any possible dependent libraries appear before
405- // it. (This ensures that the linker is able to see symbols from
406- // all possible dependent libraries before linking in the library
407- // in question.)
408- for passed_lib in & self . tcx . sess . opts . libs {
409- // If we've already added any native libraries with the same
410- // name, they will be pulled out into `existing`, so that we
411- // can move them to the end of the list below.
412- let mut existing = self
413- . libs
414- . drain_filter ( |lib| {
415- if let Some ( lib_name) = lib. name {
416- if lib_name. as_str ( ) == passed_lib. name {
417- // FIXME: This whole logic is questionable, whether modifiers are
418- // involved or not, library reordering and kind overriding without
419- // explicit `:rename` in particular.
420- if lib. has_modifiers ( ) || passed_lib. has_modifiers ( ) {
421- self . tcx . sess . span_err (
422- self . tcx . def_span ( lib. foreign_module . unwrap ( ) ) ,
423- "overriding linking modifiers from command line is not supported"
424- ) ;
405+ // Apply overrides
406+ if !overrides. is_empty ( ) {
407+ let orig_attr_lib_names = Vec :: from_iter ( self . libs . iter ( ) . map ( |lib| lib. name ) ) ;
408+ for ( name, override_lib) in overrides {
409+ for ( orig_attr_lib_name, attr_lib) in
410+ iter:: zip ( & orig_attr_lib_names, & mut self . libs )
411+ {
412+ if let Some ( orig_attr_lib_name) = orig_attr_lib_name
413+ && orig_attr_lib_name. as_str ( ) == name {
414+ // The name is overridden unconditionally
415+ attr_lib. name =
416+ Some ( Symbol :: intern ( & override_lib. new_name . as_ref ( ) . unwrap ( ) ) ) ;
417+ // The kind and modifiers are overridden only if the override specifies
418+ // them explicitly
419+ if override_lib. kind != NativeLibKind :: Unspecified {
420+ if attr_lib. has_modifiers ( ) && !override_lib. has_modifiers ( ) {
421+ // Not clear what behavior is desirable here
422+ self . tcx . sess . err ( & format ! (
423+ "override for library `{name}` must specify modifiers because \
424+ the overridden `#[link]` attribute specified modifiers",
425+ ) ) ;
425426 }
426- if passed_lib. kind != NativeLibKind :: Unspecified {
427- lib. kind = passed_lib. kind ;
428- }
429- if let Some ( new_name) = & passed_lib. new_name {
430- lib. name = Some ( Symbol :: intern ( new_name) ) ;
431- }
432- lib. verbatim = passed_lib. verbatim ;
433- return true ;
427+ attr_lib. kind = override_lib. kind ;
428+ attr_lib. verbatim = override_lib. verbatim ;
434429 }
435430 }
436- false
437- } )
438- . collect :: < Vec < _ > > ( ) ;
439- if existing. is_empty ( ) {
440- // Add if not found
441- let new_name: Option < & str > = passed_lib. new_name . as_deref ( ) ;
431+ }
432+ }
433+ }
434+
435+ // Add regular (non-override) libraries from the command line
436+ for cmd_lib in & self . tcx . sess . opts . libs {
437+ if cmd_lib. new_name . is_none ( ) {
442438 self . libs . push ( NativeLib {
443- name : Some ( Symbol :: intern ( new_name . unwrap_or ( & passed_lib . name ) ) ) ,
444- kind : passed_lib . kind ,
439+ name : Some ( Symbol :: intern ( & cmd_lib . name ) ) ,
440+ kind : cmd_lib . kind ,
445441 cfg : None ,
446442 foreign_module : None ,
447443 wasm_import_module : None ,
448- verbatim : passed_lib . verbatim ,
444+ verbatim : cmd_lib . verbatim ,
449445 dll_imports : Vec :: new ( ) ,
450446 } ) ;
451- } else {
452- // Move all existing libraries with the same name to the
453- // end of the command line.
454- self . libs . append ( & mut existing) ;
455447 }
456448 }
457449 }
458450
451+ fn unify_kinds_and_modifiers ( & mut self ) {
452+ let mut kinds_and_modifiers =
453+ FxHashMap :: < Symbol , FxHashSet < ( NativeLibKind , Option < bool > ) > > :: default ( ) ;
454+ for NativeLib { name, kind, verbatim, cfg, .. } in & self . libs {
455+ if let Some ( name) = * name && * kind != NativeLibKind :: Unspecified && cfg. is_none ( ) {
456+ kinds_and_modifiers. entry ( name) . or_default ( ) . insert ( ( * kind, * verbatim) ) ;
457+ }
458+ }
459+
460+ for NativeLib { name, kind, verbatim, .. } in & mut self . libs {
461+ if let Some ( name) = name
462+ && * kind == NativeLibKind :: Unspecified
463+ && let Some ( kinds_and_modifiers) = kinds_and_modifiers. get ( name) {
464+ if kinds_and_modifiers. len ( ) == 1 {
465+ ( * kind, * verbatim) = * kinds_and_modifiers. iter ( ) . next ( ) . unwrap ( ) ;
466+ } else {
467+ self . tcx . sess . err ( & format ! (
468+ "cannot infer kind for library `{name}`, it is linked more than once \
469+ with different kinds or modifiers",
470+ ) ) ;
471+ }
472+ }
473+ }
474+ }
475+
476+ fn compat_reorder ( & mut self ) {
477+ let mut tmp = Vec :: with_capacity ( self . libs . len ( ) ) ;
478+
479+ let mut cmd_libs = Vec :: from_iter ( self . libs . drain ( self . attr_libs ..) ) ;
480+ cmd_libs. reverse ( ) ;
481+ let mut attr_libs = mem:: take ( & mut self . libs ) ;
482+ attr_libs. reverse ( ) ;
483+
484+ while !cmd_libs. is_empty ( ) {
485+ let cmd_lib = cmd_libs. remove ( 0 ) ;
486+ let name = cmd_lib. name ;
487+ tmp. push ( cmd_lib) ;
488+ tmp. extend ( cmd_libs. drain_filter ( |cmd_lib| cmd_lib. name == name) ) ;
489+ tmp. extend ( attr_libs. drain_filter ( |attr_lib| attr_lib. name == name) ) ;
490+ }
491+
492+ tmp. append ( & mut attr_libs) ;
493+ tmp. reverse ( ) ;
494+
495+ self . libs = tmp;
496+ }
497+
459498 fn i686_arg_list_size ( & self , item : & hir:: ForeignItemRef ) -> usize {
460499 let argument_types: & List < Ty < ' _ > > = self . tcx . erase_late_bound_regions (
461500 self . tcx
0 commit comments