@@ -146,6 +146,12 @@ impl<'tcx> Inliner<'tcx> {
146146 debug ! ( "inlined {}" , callsite. callee) ;
147147 self . changed = true ;
148148
149+ if !matches ! ( callsite. callee. def, InstanceDef :: CloneShim ( ..) ) {
150+ if let Some ( def_id) = callsite. callee . def_id ( ) . as_local ( ) {
151+ self . tcx . inlined_internal_defs . lock ( ) . insert ( def_id) ;
152+ }
153+ }
154+
149155 self . history . push ( callsite. callee . def_id ( ) ) ;
150156 self . process_blocks ( caller_body, new_blocks) ;
151157 self . history . pop ( ) ;
@@ -184,6 +190,7 @@ impl<'tcx> Inliner<'tcx> {
184190
185191 self . check_mir_is_available ( caller_body, & callsite. callee ) ?;
186192 let callee_body = try_instance_mir ( self . tcx , callsite. callee . def ) ?;
193+ self . check_mir_is_exportable ( & callsite. callee , callee_attrs, callee_body) ?;
187194 self . check_mir_body ( callsite, callee_body, callee_attrs) ?;
188195
189196 if !self . tcx . consider_optimizing ( || {
@@ -353,6 +360,44 @@ impl<'tcx> Inliner<'tcx> {
353360 None
354361 }
355362
363+ fn check_mir_is_exportable (
364+ & self ,
365+ callee : & Instance < ' tcx > ,
366+ callee_attrs : & CodegenFnAttrs ,
367+ callee_body : & Body < ' tcx > ,
368+ ) -> Result < ( ) , & ' static str > {
369+ // If the callee is not local, then it must be exportable because we passed the check for
370+ // if MIR is available.
371+ if !callee. def_id ( ) . is_local ( ) {
372+ return Ok ( ( ) ) ;
373+ }
374+ if self . tcx . is_constructor ( callee. def_id ( ) ) {
375+ return Ok ( ( ) ) ;
376+ }
377+ if callee_attrs. requests_inline ( ) {
378+ return Ok ( ( ) ) ;
379+ }
380+ let is_generic = callee. substs . non_erasable_generics ( ) . next ( ) . is_some ( ) ;
381+ if is_generic {
382+ return Ok ( ( ) ) ;
383+ }
384+
385+ // So now we are trying to inline a function from another crate which is not inline and is
386+ // not a generic. This might work. But if this pulls in a symbol from the other crater, we
387+ // will fail to link.
388+ // So this is our heuritic for handling this:
389+ // If this function has any calls in it, that might reference an internal symbol.
390+ // If this function contains a pointer constant, that might be a reference to an internal
391+ // static.
392+ // So if either of those conditions are met, we cannot inline this.
393+ if !contains_pointer_constant ( callee_body) {
394+ debug ! ( "Has no pointer constants, must be exportable" ) ;
395+ return Ok ( ( ) ) ;
396+ }
397+
398+ Err ( "not exported" )
399+ }
400+
356401 /// Returns an error if inlining is not possible based on codegen attributes alone. A success
357402 /// indicates that inlining decision should be based on other criteria.
358403 fn check_codegen_attributes (
@@ -364,16 +409,6 @@ impl<'tcx> Inliner<'tcx> {
364409 return Err ( "never inline hint" ) ;
365410 }
366411
367- // Only inline local functions if they would be eligible for cross-crate
368- // inlining. This is to ensure that the final crate doesn't have MIR that
369- // reference unexported symbols
370- if callsite. callee . def_id ( ) . is_local ( ) {
371- let is_generic = callsite. callee . substs . non_erasable_generics ( ) . next ( ) . is_some ( ) ;
372- if !is_generic && !callee_attrs. requests_inline ( ) {
373- return Err ( "not exported" ) ;
374- }
375- }
376-
377412 if callsite. fn_sig . c_variadic ( ) {
378413 return Err ( "C variadic" ) ;
379414 }
@@ -1148,3 +1183,41 @@ fn try_instance_mir<'tcx>(
11481183 _ => Ok ( tcx. instance_mir ( instance) ) ,
11491184 }
11501185}
1186+
1187+ fn contains_pointer_constant ( body : & Body < ' _ > ) -> bool {
1188+ let mut finder = ConstFinder { found : false } ;
1189+ finder. visit_body ( body) ;
1190+ finder. found
1191+ }
1192+
1193+ struct ConstFinder {
1194+ found : bool ,
1195+ }
1196+
1197+ impl Visitor < ' _ > for ConstFinder {
1198+ fn visit_constant ( & mut self , constant : & Constant < ' _ > , location : Location ) {
1199+ debug ! ( "Visiting constant: {:?}" , constant. literal) ;
1200+ if let ConstantKind :: Unevaluated ( ..) = constant. literal {
1201+ self . found = true ;
1202+ return ;
1203+ }
1204+
1205+ if let ConstantKind :: Val ( val, ty) = constant. literal {
1206+ ty:: tls:: with ( |tcx| {
1207+ let val = tcx. lift ( val) . unwrap ( ) ;
1208+ if let ConstValue :: Scalar ( Scalar :: Ptr ( ptr, _size) ) = val {
1209+ if let Some ( GlobalAlloc :: Static ( _) ) =
1210+ tcx. try_get_global_alloc ( ptr. into_parts ( ) . 0 )
1211+ {
1212+ self . found = true ;
1213+ }
1214+ }
1215+ } ) ;
1216+
1217+ if ty. is_fn ( ) {
1218+ self . found = true ;
1219+ }
1220+ }
1221+ self . super_constant ( constant, location) ;
1222+ }
1223+ }
0 commit comments