parsing C++ is mostly equivalent to becoming a C++ compiler.
It reallt isn't. Parsing a languagr just means validating its correctness wrt a grammar and in the process extract some information. Parsing something is just the first stage and a one of many stages required to map C++ source code to valid binaries.
template <int>
struct foo {
template <int>
static int bar(int);
};
template <>
struct foo<8> {
static const int bar = 99;
};
constexpr int some_function() { return (int) sizeof(void*); };
Now given the snippet foo<some_function()>::bar<0>(1);
then if some_function() returns something other than 8, we use the primary template and foo<N>::bar<0>(1) is a call to a static template member function.But if some_function() does return 8, we use the specialisation and the foo<8>::bar is an int with value 99; so we ask is 99 less than the expression 0>(1) (aka "false", promoted to the int 0).
That is, there are two entirely different but valid parses depending on whether we are compiling on a 32- or 64-bit system.
Parsing C++ is hard.
EDIT: Godbolt example: https://godbolt.org/z/yR3YHW
Here's an example of a program which compiles only if the constant N is prime, and otherwise emits a syntax error: https://stackoverflow.com/questions/14589346/is-c-context-fr....
In practice, compilers work around this by limiting template instantiation depth.
#if SOME_PREPROCESSOR_JUNK
import foo;
#else
import bar;
#endif
This has legitimate use cases, say #ifdef WIN32
import mymodule.windows
#else
import mymodule.posix
#endif
So in reality build systems will be required to invoke at least the preprocessor to extract dependency information.AFAIK the modules support in the Build2 build system does exactly this, and in fact caches the entire preprocessed file to pass to the compiler proper later.
Having the compiler produce header dependency information is possible, since the dependencies are just an optimization. If there's no dependency information available, you can just compile all of the files in an arbitrary order, and you get both the object file and a dep file. And then on further runs you use the old dep files to skip unnecessary recompilations.
With modules, you can't compile the files in an arbitrary order: if A uses a module defined in B, B must be compiled first. So you need to have the dependency information available up front even for the first build. And since it needs to be available up front, it can't be generated by the compiler. It must either be produced by the build tool which becomes vastly more complicated, or manually by humans.