@@ -6,6 +6,7 @@ use rustc_attr_parsing::{EIIDecl, EIIImpl};
6
6
use rustc_data_structures:: fx:: FxIndexMap ;
7
7
use rustc_hir:: def:: DefKind ;
8
8
use rustc_hir:: def_id:: { CRATE_DEF_ID , CrateNum , DefId , LOCAL_CRATE , LocalDefId } ;
9
+ use rustc_middle:: bug;
9
10
use rustc_middle:: middle:: eii:: EiiMapping ;
10
11
use rustc_middle:: ty:: TyCtxt ;
11
12
use rustc_session:: config:: CrateType ;
@@ -21,39 +22,59 @@ pub(crate) fn get_externally_implementable_item_impls<'tcx>(
21
22
tcx : TyCtxt < ' tcx > ,
22
23
( ) : ( ) ,
23
24
) -> & ' tcx FxIndexMap < LocalDefId , EiiMapping > {
24
- // We only need to check whether there are duplicate or missing EIIs if we're
25
- // emitting something that's not an rlib.
26
- let needs_check = tcx. crate_types ( ) . iter ( ) . any ( |kind| match * kind {
27
- // Executables are leafs of the crate graph and need all EIIs to be satisfied,
28
- // either with defaults or explicit implementations. So they check their crate
29
- // graph to make sure this is the case.
30
- CrateType :: Executable => true ,
31
- // Proc macros are leafs of their crate graph and will be run,
32
- // and so need to check the EIIs of their dependencies.
33
- CrateType :: ProcMacro => true ,
34
-
35
- // These are a litte difficult. We don't know whether things depending on these
36
- // will perform checks to see if EIIs are implemented, or duplicated, or any other
37
- // of the checks performed in this function. So we must do the checks. However,
38
- // this can later lead to duplicate symbols when linking them together.
39
- // For this reason, we later mark EII symbols as "globally shared" and "may conflict".
40
- // In other words, if two shared libraries both provide an implementation for an EII,
41
- // that's fine! Just choose one... And because their mangled symbol names are the same
42
- // (that's exactly the conflict we're having) we hopefully have the same exact implementation.
43
- CrateType :: Dylib | CrateType :: Cdylib | CrateType :: Staticlib => true ,
44
-
45
- // Rlibs are just a step in the crate graph.
46
- // Later on we'll link it together into an executable and over there we can check for EIIs
47
- CrateType :: Rlib => false ,
48
- } ) ;
49
- if !needs_check {
50
- // In this case we could only have called it when checking,
51
- // and not when we were actually codegenning functions so we don't need to return any real data
52
- return & * tcx. arena . alloc ( FxIndexMap :: default ( ) ) ;
25
+ #[ derive( Copy , Clone ) ]
26
+ enum Case {
27
+ /// We need to generate all EII shims because we are generating some final target like an
28
+ /// executable or library (not rlib)
29
+ AlwaysEmit ,
30
+ /// We need to generate all EII shims because one of our crate types is a final target like
31
+ /// an executable. However, we're also generating an rlib. So. If we see explicit
32
+ /// definitions of EIIs we can generate them with external linkage. However, if we find
33
+ /// defaults, they must also be emitted because some of our crate types are final targets.
34
+ /// And unfortunately the rlib will also contain these definitions. However, because rlibs
35
+ /// will later be used in final targets, which will use `AlwaysEmit`, these symbols that were
36
+ /// spuriously generated in rlibs will be redefined and then flagged by the linker as
37
+ /// duplicate definitions. So, we have to emit EII shims which are default impls (not
38
+ /// explicit ones) as weak symbols.
39
+ EmitMaybeWeak ,
40
+ /// We don't always need to emit EIIs because we're generating an Rlib. However, if we see
41
+ /// an explicit implementation, we can! Because it cannot be overwritten anymore.
42
+ EmitExternalIfExplicit ,
43
+ }
44
+
45
+ let has_rlib = tcx. crate_types ( ) . iter ( ) . any ( |i| matches ! ( i, CrateType :: Rlib ) ) ;
46
+ let has_target = tcx. crate_types ( ) . iter ( ) . any ( |i| !matches ! ( i, CrateType :: Rlib ) ) ;
47
+
48
+ let case = match ( has_rlib, has_target) {
49
+ ( true , true ) => {
50
+ Case :: EmitMaybeWeak
51
+ } ,
52
+ ( true , false ) => {
53
+ Case :: EmitExternalIfExplicit
54
+ } ,
55
+ ( false , true ) => {
56
+ Case :: AlwaysEmit
57
+ } ,
58
+ ( false , false ) => {
59
+ bug ! ( "no targets but somehow we are running the compiler" )
60
+ } ,
61
+ } ;
62
+
63
+ #[ derive( Debug ) ]
64
+ struct FoundImpl {
65
+ imp : EIIImpl ,
66
+ impl_crate : CrateNum
67
+ }
68
+
69
+ #[ derive( Debug ) ]
70
+ struct FoundEii {
71
+ decl : EIIDecl ,
72
+ decl_crate : CrateNum ,
73
+ impls : FxIndexMap < DefId , FoundImpl >
53
74
}
54
75
55
76
let mut eiis =
56
- FxIndexMap :: < DefId , ( EIIDecl , CrateNum , FxIndexMap < DefId , ( EIIImpl , CrateNum ) > ) > :: default ( ) ;
77
+ FxIndexMap :: < DefId , FoundEii > :: default ( ) ;
57
78
58
79
// println!("current crate: {}", tcx.crate_name(LOCAL_CRATE));
59
80
@@ -66,28 +87,28 @@ pub(crate) fn get_externally_implementable_item_impls<'tcx>(
66
87
// update or insert the corresponding entries
67
88
for ( did, ( decl, impls) ) in crate_eiis {
68
89
eiis. entry ( * did)
69
- . or_insert_with ( || ( * decl, cnum, Default :: default ( ) ) )
70
- . 2
71
- . extend ( impls. into_iter ( ) . map ( |( did, i) | ( * did, ( * i, cnum) ) ) ) ;
90
+ . or_insert_with ( || FoundEii { decl : * decl, decl_crate : cnum, impls : Default :: default ( ) } )
91
+ . impls
92
+ . extend ( impls. into_iter ( ) . map ( |( did, i) | ( * did, FoundImpl { imp : * i, impl_crate : cnum} ) ) ) ;
72
93
}
73
94
}
74
95
75
96
let mut final_impls = FxIndexMap :: default ( ) ;
76
97
77
98
// now we have all eiis! For each of them, choose one we want to actually generate.
78
99
79
- for ( decl_did, ( decl, decl_crate, impls) ) in eiis {
100
+ for ( decl_did, FoundEii { decl, decl_crate, impls } ) in eiis {
80
101
// println!("for decl: {decl_did:?}: {decl:?}");
81
102
let mut default_impls = Vec :: new ( ) ;
82
103
let mut explicit_impls = Vec :: new ( ) ;
83
104
84
- for ( impl_did, ( impl_metadata , cnum ) ) in impls {
85
- if impl_metadata . is_default {
105
+ for ( impl_did, FoundImpl { imp , impl_crate } ) in impls {
106
+ if imp . is_default {
86
107
// println!("found default impl in {}", tcx.crate_name(cnum));
87
- default_impls. push ( ( impl_did, cnum ) ) ;
108
+ default_impls. push ( ( impl_did, impl_crate ) ) ;
88
109
} else {
89
110
// println!("found impl in {}", tcx.crate_name(cnum));
90
- explicit_impls. push ( ( impl_did, cnum ) ) ;
111
+ explicit_impls. push ( ( impl_did, impl_crate ) ) ;
91
112
}
92
113
}
93
114
@@ -115,43 +136,78 @@ pub(crate) fn get_externally_implementable_item_impls<'tcx>(
115
136
panic ! ( "multiple not supported right now, but this is easily possible" ) ;
116
137
}
117
138
118
- // println!("impls: {explicit_impls:?}");
119
- // println!("default impls: {default_impls:?}");
120
139
121
- if let Some ( ( chosen_impl, _) ) = explicit_impls. first ( ) . or ( default_impls. first ( ) ) {
122
- let feed = tcx. create_def (
123
- CRATE_DEF_ID ,
124
- Some ( Symbol :: intern ( & format ! ( "EII shim for {decl_did:?}" ) ) ) ,
125
- DefKind :: Fn ,
126
- ) ;
140
+ let ( chosen_impl, weak_linkage) = match ( case, explicit_impls. first ( ) , default_impls. first ( ) ) {
141
+ ( Case :: EmitExternalIfExplicit , Some ( ( explicit, impl_crate) ) , _) => {
142
+ if impl_crate != & LOCAL_CRATE {
143
+ continue ;
144
+ }
145
+ ( explicit, false )
146
+ }
147
+ // we don't care in this case if we find no implementation yet. Another can come
148
+ // downstream.
149
+ ( Case :: EmitExternalIfExplicit , None , _) => {
150
+ continue ;
151
+ }
152
+
153
+ ( Case :: AlwaysEmit , Some ( ( explicit, impl_crate) ) , _) => {
154
+ if impl_crate != & LOCAL_CRATE {
155
+ continue ;
156
+ }
157
+
158
+ ( explicit, false )
159
+ }
160
+ ( Case :: AlwaysEmit , _, Some ( ( deflt, _) ) ) => ( deflt, false ) ,
161
+
162
+ ( Case :: EmitMaybeWeak , Some ( ( explicit, impl_crate) ) , _) => {
163
+ if impl_crate != & LOCAL_CRATE {
164
+ continue ;
165
+ }
127
166
128
- let extern_item_did = decl. eii_extern_item ;
167
+ ( explicit, false )
168
+ }
169
+ // IMPORTANT! weak linkage because the symbol will also end up in the rlib and may need
170
+ // to be overwritten :(
171
+ ( Case :: EmitMaybeWeak , _, Some ( ( deflt, _) ) ) => ( deflt, true ) ,
172
+
173
+ // We have a target to generate, but no impl to put in it. error!
174
+ ( Case :: EmitMaybeWeak | Case :: AlwaysEmit , None , None ) => {
175
+ tcx. dcx ( ) . emit_err ( EiiWithoutImpl {
176
+ current_crate_name : tcx. crate_name ( LOCAL_CRATE ) ,
177
+ decl_crate_name : tcx. crate_name ( decl_crate) ,
178
+ name : tcx. item_name ( decl_did) ,
179
+ span : decl. span ,
180
+ help : ( ) ,
181
+ } ) ;
182
+
183
+ continue ;
184
+ }
185
+ } ;
129
186
130
- feed . generics_of ( tcx. generics_of ( extern_item_did ) . clone ( ) ) ;
131
- feed . type_of ( tcx . type_of ( extern_item_did ) . clone ( ) ) ;
132
- feed . def_span ( tcx . def_span ( chosen_impl ) ) ;
133
- feed . visibility ( tcx . visibility ( chosen_impl ) ) ;
134
- feed . feed_hir ( ) ;
187
+ let feed = tcx. create_def (
188
+ CRATE_DEF_ID ,
189
+ Some ( Symbol :: intern ( & format ! ( "EII shim for {decl_did:?}" ) ) ) ,
190
+ DefKind :: Fn ,
191
+ ) ;
135
192
136
- // println!("generating { extern_item_did:?} for impl {chosen_impl:?} in crate {} with did {decl_did:?}", tcx.crate_name(LOCAL_CRATE)) ;
193
+ let extern_item_did = decl . eii_extern_item ;
137
194
138
- let shim_did = feed. def_id ( ) ;
195
+ feed. generics_of ( tcx. generics_of ( extern_item_did) . clone ( ) ) ;
196
+ feed. type_of ( tcx. type_of ( extern_item_did) . clone ( ) ) ;
197
+ feed. def_span ( tcx. def_span ( chosen_impl) ) ;
198
+ feed. visibility ( tcx. visibility ( chosen_impl) ) ;
199
+ feed. feed_hir ( ) ;
139
200
140
- // println!("shim: {shim_did :?}" );
201
+ // println!("generating {extern_item_did:?} for impl {chosen_impl :?} in crate {} with did {decl_did:?}", tcx.crate_name(LOCAL_CRATE) );
141
202
142
- final_impls. insert (
143
- shim_did,
144
- EiiMapping { extern_item : extern_item_did, chosen_impl : * chosen_impl } ,
145
- ) ;
146
- } else {
147
- tcx. dcx ( ) . emit_err ( EiiWithoutImpl {
148
- current_crate_name : tcx. crate_name ( LOCAL_CRATE ) ,
149
- decl_crate_name : tcx. crate_name ( decl_crate) ,
150
- name : tcx. item_name ( decl_did) ,
151
- span : decl. span ,
152
- help : ( ) ,
153
- } ) ;
154
- }
203
+ let shim_did = feed. def_id ( ) ;
204
+
205
+ // println!("shim: {shim_did:?}");
206
+
207
+ final_impls. insert (
208
+ shim_did,
209
+ EiiMapping { extern_item : extern_item_did, chosen_impl : * chosen_impl, weak_linkage } ,
210
+ ) ;
155
211
}
156
212
157
213
tcx. arena . alloc ( final_impls)
0 commit comments