1
1
use rustc_ast:: { NestedMetaItem , CRATE_NODE_ID } ;
2
2
use rustc_attr as attr;
3
- use rustc_data_structures:: fx:: FxHashSet ;
3
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
4
4
use rustc_errors:: struct_span_err;
5
5
use rustc_hir as hir;
6
6
use rustc_hir:: def:: DefKind ;
@@ -12,12 +12,17 @@ use rustc_session::Session;
12
12
use rustc_span:: symbol:: { sym, Symbol } ;
13
13
use rustc_target:: spec:: abi:: Abi ;
14
14
15
+ use std:: { iter, mem} ;
16
+
15
17
crate 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 } ;
17
19
for id in tcx. hir ( ) . items ( ) {
18
20
collector. process_item ( id) ;
19
21
}
22
+ collector. attr_libs = collector. libs . len ( ) ;
20
23
collector. process_command_line ( ) ;
24
+ collector. unify_kinds_and_modifiers ( ) ;
25
+ collector. compat_reorder ( ) ;
21
26
collector. libs
22
27
}
23
28
@@ -31,6 +36,7 @@ crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
31
36
struct Collector < ' tcx > {
32
37
tcx : TyCtxt < ' tcx > ,
33
38
libs : Vec < NativeLib > ,
39
+ attr_libs : usize ,
34
40
}
35
41
36
42
impl < ' tcx > Collector < ' tcx > {
@@ -363,99 +369,132 @@ impl<'tcx> Collector<'tcx> {
363
369
364
370
// Process libs passed on the command line
365
371
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 {
370
376
// Cannot check this when parsing options because the target is not yet available.
371
377
self . tcx . sess . err ( "library kind `framework` is only supported on Apple targets" ) ;
372
378
}
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
375
386
. libs
376
387
. 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
383
395
) ) ;
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
396
400
) ) ;
397
401
}
398
402
}
399
403
}
400
404
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
+ ) ) ;
425
426
}
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 ;
434
429
}
435
430
}
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 ( ) {
442
438
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 ,
445
441
cfg : None ,
446
442
foreign_module : None ,
447
443
wasm_import_module : None ,
448
- verbatim : passed_lib . verbatim ,
444
+ verbatim : cmd_lib . verbatim ,
449
445
dll_imports : Vec :: new ( ) ,
450
446
} ) ;
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) ;
455
447
}
456
448
}
457
449
}
458
450
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
+
459
498
fn i686_arg_list_size ( & self , item : & hir:: ForeignItemRef ) -> usize {
460
499
let argument_types: & List < Ty < ' _ > > = self . tcx . erase_late_bound_regions (
461
500
self . tcx
0 commit comments