It's interesting that you bring purity into the picture. Purity has the same problem that @compilerEvaluatable has: all the functions it calls must have the same attribute, and it quickly becomes viral.
(And you can always invent more useful attributes that are like this. For instance a @noAllocation attribute, guarantying some code will not silently allocate memory for some real-time operations. Or a @concurrent attribute that would make sure some code is free of low-level data races using the type system.)
Most functions focussed on one encapsulated task can and probably should be annotated if they can to make them usable in more contexts, yet most high-level code cannot and should not use these annotations to preserve flexibility, so there is no good default to choose.
If you decide that @pure or @compilerEvaluatable can be inferred for functions within the same module to lessen the annotation burden, then you basically get whole call stacks in errors (similar to C++ templates) and changing the implementation of one function somewhere can cause another function elsewhere to not compile. Perhaps it's tolerable when confined within the same module, but it's certainly not ideal.
Things becomes even muddier for annotations when @compilerEvaluatable becomes conditional. For instance, you might be able to easily evaluate at compile time max([1,2,3]), but perhaps not max([1,2,3] as [SomeBigIntType]). Yet, it's the same max function in both cases. I made a tentative for declaring conditions like this for purity in an earlier exploratory proposal and I think it'd work for @compilerEvaluatable too, but there's probably some loose ends remaining.