You have a few factual errors there. First for reference:
- dont use function calls in the ISR
- you cant use anything that uses interrupts inside the ISR, including importantly, timing functons like millis()
You can call functions. Why not? You need to be cautious that those functions work properly with interrupts disabled. A lot of the core functions do exactly that.
You can use millis(). It just doesn't increment. You can use micros(). Since that just interrogates the hardware timer it will increment unless you spend so long in the ISR (like, over a millisecond) that the overflow isn't caught.
occasional lost interrupts (lost accuracy?)
No. Each interrupt source sets a flag in the processor (eg. an external interrupt). That flag is tested next time interrupts are enabled, in interrupt priority order. If your statement was correct the processor would be extremely flaky, which it isn't.
Some interrupts (eg. an external LOW level interrupt) do not set such a flag. But these are primarily designed to wake the processor from sleep.
If your statement was true then timer interrupts, or serial interrupts would be constantly being lost if a timer happened to fire when serial data arrived. This simply doesn't happen.
occasional loss of characters during serial communications
No. For the reason given above for one thing, the interrupt sets a flag which is tested. Also the serial UART has an input buffer of a couple of bytes. You can afford to take a (reasonable) amount of time before grabbing the data.
inability to use standard timing mechanisms inside ISRs
No. You can use micros() which continues to be accurate because it just grabs a register from the hardware timer.
inability to use standard comms inside ISRs (makes debugging ISRs much more difficult, may have to resort to flashing LEDs etc)
You shouldn't be using comms inside an ISR anyway. Doing serial prints inside an ISR is going to throw the timing out so much that you are not even debugging what would happen without the debug prints. Flashing LEDs are perfectly acceptable. You can also send debugging out via SPI which is fast, and doesn't use interrupts to send, as described here: Gammon Forum : Electronics : Microprocessors : Debugging using SPI/I2C and a second processor
inaccuracy of timing mechanisms outside ISRs (since the millis() will not be counting while the ISR is running, and will therefore always be running slow, by an undetermined amount)
No. Where do you get this stuff from? The millis() result is based on a hardware timer that is running whether or not you are in an interrupt. The only thing that isn't handled is the overflow (every 1.024 mS) which is why you should keep ISRs short. This interrupt will be "remembered" as described above and handled correctly when any existing ISR finishes.
loss of structure (avoiding function calls), and/or redundant coding (replacing function calls) for fear of using function calls inside the ISR
You can call functions from an ISR, as I said before.
You can't stop the AVR chip from switching off interrupts before calling an ISR, but you CAN switch them back on again as soon as you're in the ISR (simply make sei(); the first instruction of your ISR.)
I strongly recommend against doing that. You can get into re-entrant ISR calls which will trash your registers, variables, etc. or simply send you into a loop.
In fact you can see that it is the very act of inhibiting further interrupts that is making the writing of ISRs seem difficult - by keeping interrupts enabled, most of the problems evaporate.
The processor disables interrupts for a very good reason, when entering an ISR. Your fears about what happens are unfounded, and I recommend that you not re-enable interrupts as you suggest.