My understanding is that one of the valid usages of #[inline] is to mark public non-generic library functions, so that rustc saves their intermediate code in the compiled library, so that such functions could be inlined in downstream crates at all.
I'm interested to know if anyone knows this at the generated-assembly level, but my understanding of the inlining process is that no, tiny is a function-call indirection, and so is smol, and giving the compiler the option to inline tiny will allow it to take the instructions that would be located at an address for tiny (including the jmp or call to smol), but doesn't also 'unfold' smol.
I checked this assumption in the playground, and it seems to play out as expected - though do bear in mind that #[inline] doesn't force, just allows the inlining. I had to include the always qualifier in the playground here for the compiler to inline in such a trivial program.
Rust (llvm) will usually sort out the best optimisation for inlining. Adding the attribute has a chance of making code slower. So you only every should do so after profiling.
It's non-transitive because not marking smol as #[inline] can be very useful if tiny only calls it sometimes. For example, tiny might be a cache lookup, where the code to generate the value on a cache miss is intentionally an out-of-line call since duplicating it all over would be a waste.
Interesting! I've tested locally and it indeed seems to be the case, which is somewhat counter-intuitive.
Naively I would expect that, when compiling the library crate, smol is inlined (or not) into tiny, and that all that bunch is then inlinable into downstream create.
From my understanding of how current rlibs work, I sort of see why this doesn't work.