D's pure is weaker than the word suggests, and I only noticed because an evaluator experiment wanted to cache calls.
int[] log;
pure void bump(int[] a)
{
a[0]++; // fine, writes through the argument
// log ~= a[0]; // rejected, touches a mutable global
}
pure in D means no access to mutable global or static state. It says nothing against modifying memory reachable from the parameters. The spec calls this weak purity. A call only becomes something you could cache when the parameters can't carry mutable indirections, immutable data or plain values, and that's strong purity. Same keyword for both, and the parameter types pick which one you get.
So if the evaluator caches results of pure host functions, keying on the attribute alone would cache bump too, and the second call would skip the write. Key on strongly pure signatures instead? Or does that list of conditions get long enough that an explicit opt-in attribute on the host side is simpler?