4 ms·
Yes, they don't; it would be possible for a compiler to gather info about such call sites into a list and then, when it's finished compiling a compilation unit,
by Joker_vD 3y ago
Yes, they don't; it would be possible for a compiler to gather info about such call sites into a list and then, when it's finished compiling a compilation unit, to check this list against the now-complete symbol table and fail if some called functions have mismatched definitions or are still undefined... that's apparently was too much work back when C was designed so we have "hope for the best" design instead.
- mfichman 3y agoI think that would by definition be a two-pass compiler.
- Joker_vD 3y agoNo, you emit all the code in one go, then after you've done that you have some residual pieces of data left one the side: the symbol table, and the list of all the calls of (hopefully)-forward-declared functions. At this point you could run the check on that list against the symbol table, no additional codegen or re-reading the source text needed. Granted, you can call that a second pass although that's not that different from emitting a function's epilogue IMHO.
- sltkr 3y agoThere is no need for the second phase. You can record the implicit declaration when you encounter it, and if there is a subsequent declaration, you can check that it's consistent or error out immediately. This is what C compilers already do, in fact, to produce warnings when an implicit declaration doesn't match a later explicit declaration. But this is a best-effort warning only; it doesn't work if there is no declaration because the function is defined in a different translation unit, as I pointed out above.
- mfichman 3y agoUsually, the second pass in a compiler does not re-parse source files. Rather, it operates on another data structure, like an AST, intermediate representation, or the list mentioned in the original comment. At least, that’s my understanding of multi-pass compilation.
- Joker_vD 3y agoWell, this "list mentioned in the original comment" is not an AST or an immediate representation of the program in any reasonable sense just as a symbol table is not. Otherwise, setting the exact values of the text/data/bss size fields in the executable file header's at the end of the compilation would count as the second pass as well which IMO it should not.
- mfichman 3y agoThe difference is that you need to make a complete second iteration (or pass) over the entire list to correctly check all of the callsites after all function type information is collected. The same is not true for symbol table usage in a single-pass C compiler.
- kryptiskt 3y agoThere is no information about the function's prototype in the object file, so the linker can't determine that. The compiler can't either since it's just compiling the compilation unit and has no knowledge of the outside world except what we tell it by prototypes (injected via header files or otherwise), but we aren't doing that here.
- Someone 3y ago> There is no information about the function's prototype in the object file, so the linker can't determine that. That that information isn’t there is an implementation choice. For example, they could have hacked it in like C++ did by mangling names (https://en.wikipedia.org/wiki/Name_mangling https://en.wikipedia.org/wiki/Name_mangling). That probably would have required supporting longer identifiers (IIRC archives limited them to 14 characters), but that’s doable.
- sltkr 3y agoThis doesn't work in C because the function is not necessarily defined in the same translation unit. You'd need to do this at link time instead, which would require completely overhauling the format of object files, dynamic libraries, static libraries so they carry information about the types of functions instead of just the symbol names. It's not an easy fix.
- zozbot234 3y agoThat's what DWARF is for, and similar formats on other platforms. Also Pascal-lineage languages commonly use this kind of embedded type information to provide module interfaces, it works quite well.
- Joker_vD 3y ago> This doesn't work in C because the function is not necessarily defined in the same translation unit. It does work in C ― that's what the include files are for (among other things), after all. So it's possible to be able to forward-declare functions outside the translation unit only (extern-declare?), those inside the translation units don't need to even if the compiler works in a single pass. And those external declaration could still be introduced at the very end of the translation unit and still would count. I dunno, seems like a pretty reasonable idea.