There has always been a fuzzy aspect to answering that question.
Any language requires a standard runtime. For example, you can't run any C program without some kind of runtime. The platform doesn't know how to call main
- the runtime library has to provide an entry-point function that is called according to the platforms conventions, parses the command-line parameters, and calls main
, then does whatever the platform expects to provide the exit value.
Since there is no universal convention for how all platforms call the entry-point of a program, the runtime must bridge that gap in any language that can run on multiple platforms.
The runtime isn't defined according to the language standard (where an official standard exists) either, so much as to the compiler. For example, few if any languages define the details of call conventions for functions (standard library or otherwise) because these details depend heavily on the platform. So although the set of library types and functions and their behaviour can be defined by a standard, a lot of implementation details aren't.
There can also be surprising interactions between the compiler and the library. Sometimes, when you import a library, the compiler doesn't refer to any kind of header/lib or precompiled module or whatever at all - the compiler replaces the calls with built-in implementations, usually for efficiency reasons.
However, it is normally possible (and sometimes quite practical) to replace most or all of the standard library, right down to the entry-point function. For some parts it requires some assembler coding. Some bits may require some options to be set, preventing the use of those built-in implementations. But replacement is often possible.
Some languages inherently have such a close link between the compiler and the supplied libraries that the only sane way to use an alternative library is to use an alternative compiler too. This is probably more and more the case these days, with the close ties between frameworks like .NET and the Java platform and the languages they support such as C# and, of course, Java.
- Oops - of course it's very easy to replace the whole .NET or Java platform with an alternative implementation while still using the same compilers, because the platforms themselves are standardized - right down to the virtual machine code. Sorry.
One context where you're probably still relatively likely to see a complete replacement runtime is in embedded systems - but I don't mean mobile phones. More likely, washing machines.
One of Bjarne Stroustrups principles for C++ was that there should be nothing the language can do that the programmer can't. For example, operator overloading is there so that library developers can support operators with types - in C, only the built-in types for the compiler can support operators. There are cases where this requires some assembler programming though - obviously, it's impossible to write a library to provide low-level O/S access and I/O capabilities directly in C++ unless there's already some way to provide low-level O/S access and I/O capabilities.
So that's basically it - you can't completely opt out of having a runtime, but you can opt out of using large parts of it, and you may be able to replace the parts you can't do without, or even replace the whole thing.