I'm designing a Python-like language: bytecode-compiled, brace-free syntax, reference semantics for variables. Among many differences from Python, I want to support a limited form of static typing. I think my planned scheme is a simplified form of sound gradual typing, combined with type inference.
Without getting into too much detail, the plan is that any given variable will have one of a few set types, including one which represents a type that needs to be checked at runtime. The compiler will perform basic type checks, and generate opcodes specialized to the type of the operands involved in each step of the computation - e.g. a separate opcode to add integers (using hard-coded logic) vs. dynamically-typed "objects" (dynamically looking up a function based on some form of runtime type information, possibly involving multiple dispatch).
Because I have this typing information, I had the thought of taking a cue from Java and representing types that fit within a machine word - booleans, floating-point numbers, restricted-range integers, perhaps enumerations - by directly writing the value into the stack frame when these types are used for ordinary variables and auto-boxing them if necessary for storage purposes.
This seems like it would allow avoiding a lot of overhead for simple cases: I wouldn't have to allocate or initialize an object for a simple integer value, maintain reference counts for garbage collection purposes, follow a pointer to access the underlying data, create and rebind objects for each mathematical operation, worry about maintaining a cache of small integer values, etc.... Since I would choose a different opcode statically, and I have to pay the code of switching on the opcode anyway, it also wouldn't cost a branch misprediction (I think?) when accessing the value (and conditionally not dereferencing like usual).
However, I'm worried about the complexity this could introduce. It seems like Java is able to handle this sort of dispatch, and presumably it does see a net benefit; but Java doesn't have to worry about boxing primitives to store them as object attributes, since fields are statically typed. I'm also worried about leaking the "reference semantics" abstraction, since such types would naturally have value semantics. I think it should be fine as long as there are no routes to implicitly aliasing the "values", since there shouldn't be a way, from inside the program, of observing the difference between a value type and an unaliased, immutable reference type. But maybe I'm forgetting something?
Is there extant literature on implementing this sort of system? Is the optimization effective? Does Java see significant performance benefits from having "primitive" types that don't behave like objects? (Can/do modern JVMs represent e.g.
java.lang.Integer and paper over the API differences?)