@@ -184,6 +184,7 @@ impl<'tcx> Inliner<'tcx> {
184184
185185 self . check_mir_is_available ( caller_body, & callsite. callee ) ?;
186186 let callee_body = try_instance_mir ( self . tcx , callsite. callee . def ) ?;
187+ self . check_mir_is_exportable ( & callsite. callee , callee_attrs, callee_body) ?;
187188 self . check_mir_body ( callsite, callee_body, callee_attrs) ?;
188189
189190 if !self . tcx . consider_optimizing ( || {
@@ -353,6 +354,44 @@ impl<'tcx> Inliner<'tcx> {
353354 None
354355 }
355356
357+ fn check_mir_is_exportable (
358+ & self ,
359+ callee : & Instance < ' tcx > ,
360+ callee_attrs : & CodegenFnAttrs ,
361+ callee_body : & Body < ' tcx > ,
362+ ) -> Result < ( ) , & ' static str > {
363+ if !callee. def_id ( ) . is_local ( ) {
364+ return Ok ( ( ) ) ;
365+ }
366+ if self . tcx . is_constructor ( callee. def_id ( ) ) {
367+ return Ok ( ( ) ) ;
368+ }
369+ if callee_attrs. requests_inline ( ) {
370+ return Ok ( ( ) ) ;
371+ }
372+ let is_generic = callee. substs . non_erasable_generics ( ) . next ( ) . is_some ( ) ;
373+ if is_generic {
374+ return Ok ( ( ) ) ;
375+ }
376+
377+ // So now we are trying to inline a function from another crate which is not inline and is
378+ // not a generic. This might work. But if this pulls in a symbol from the other crater, we
379+ // will fail to link.
380+ // So this is our heuritic for handling this:
381+ // If this function has any calls in it, that might reference an internal symbol.
382+ // If this function contains a pointer constant, that might be a reference to an internal
383+ // static.
384+ // So if either of those conditions are met, we cannot inline this.
385+ if self . tcx . mir_inliner_callees ( callee. def ) . is_empty ( )
386+ && !contains_pointer_constant ( callee_body)
387+ {
388+ debug ! ( "Has no calls and no pointer constants, must be exportable" ) ;
389+ return Ok ( ( ) ) ;
390+ }
391+
392+ Err ( "not exported" )
393+ }
394+
356395 /// Returns an error if inlining is not possible based on codegen attributes alone. A success
357396 /// indicates that inlining decision should be based on other criteria.
358397 fn check_codegen_attributes (
@@ -364,16 +403,6 @@ impl<'tcx> Inliner<'tcx> {
364403 return Err ( "never inline hint" ) ;
365404 }
366405
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-
377406 if callsite. fn_sig . c_variadic ( ) {
378407 return Err ( "C variadic" ) ;
379408 }
@@ -1147,3 +1176,25 @@ fn try_instance_mir<'tcx>(
11471176 _ => Ok ( tcx. instance_mir ( instance) ) ,
11481177 }
11491178}
1179+
1180+ fn contains_pointer_constant ( body : & Body < ' _ > ) -> bool {
1181+ let mut finder = ConstFinder { found : false } ;
1182+ finder. visit_body ( body) ;
1183+ finder. found
1184+ }
1185+
1186+ struct ConstFinder {
1187+ found : bool ,
1188+ }
1189+
1190+ impl Visitor < ' _ > for ConstFinder {
1191+ fn visit_constant ( & mut self , constant : & Constant < ' _ > , location : Location ) {
1192+ debug ! ( "Visiting constant: {:?}" , constant. literal) ;
1193+ if let ConstantKind :: Val ( _val, ty) = constant. literal {
1194+ if ty. is_any_ptr ( ) || ty. is_fn ( ) {
1195+ self . found = true ;
1196+ }
1197+ }
1198+ self . super_constant ( constant, location) ;
1199+ }
1200+ }
0 commit comments