Just because something is #[inline(always)], though, doesn’t make it “trivial”. It can still do something arbitrarily expensive.
And if there’s a particularly large implementation and I care about performance, I don’t want it inlined, since that’ll actually reduce performance, typically.
So, again, why is some “constraint” better than a comment “implementations of this are expected to be inexpensive, and thus are called often”?
Even a marker attribute is better than just documentation comments in a way that tools can check it confidently, to enforce any restriction rules it feels should apply. For example, Clippy can warn if it sees potential complicated implementations (loops, for example) in a method marked as trivial.
Ahh. I think making it const fn will do almost all I need? Got to check.
#[trivial] requires presumptive knowledge of all architectures for which code might be generated. Integer multiply is trivial, unless the architecture does not have a multiply instruction (e.g., basic RISC-V), in which case a software loop is likely. Reverse bits is trivial, when the architecture has instructions to support it (e.g., ARM, AArch64).
I’m not really sure that this is a particularly good idea, but for sake of discussion I’ll propose a strawman subset of expressions we can consider “trivial”, which is not closed under composition. These are things I would expect you’d want to inline.
as casts
Primitive arithmetic, where both operands are identifiers or literals.
Single function calls, where all arguments are identifiers or literals.
Copying out of a variable or a pointer.
Constructing a trivially-destructible type.
Branching, maybe?
Unsafe intrinsics like ptr::read and mem::transmute (hahaha please no).
I could probably spend the rest of my morning listing these, but I think that’s a pointless exercise. I think what you really want is something like this:
pub unsafe trait Cast<T>: Into<T> {}
Implementing this trait is the assertion that your Into implementation is a cast (or a similar O(1) operation), in a similar way to how implementing Copy asserts that your type’s Clone is just a memcpy. I’m not really sure how this is useful, though…