I've run into a weirdness involving a recursive typedef and adding a constant. Example:
typedef string|int|array(foo) foo;
If compiled on its own, this creates a recursive typedef that can handle arbitrarily-nested arrays of strings and ints. Contrast:
typedef multiset(string|int) strint; add_constant("strint", strint);
typedef string|int|array(strint) foo;
This will reference the added constant, and will allow strings, integers, and multisets of strings/ints, but not recursively.
What if both names are the same?
constant code = #" typedef string|int|array(foo) foo; foo x = 1.0; "; typedef multiset(string|int) strint; void compile_error(string filename, int line, string msg) {write("%s\n", msg);}
int main() { write("Without constant:\n"); catch {compile_string(code, "-", this);}; add_constant("foo", strint); write("\nWith constant:\n"); catch {compile_string(code, "-", this);}; }
Is the array of foo supposed to be a recursive reference, or a reference to the constant?
And is there a way to force one interpretation or the other?
A related problem (or a consequence of this) is that repeatedly recompiling a provider of constants can cause strangenesses, up to and including quadratically-increasing compilation time. Consider this cut-down version of part of one of my apps:
constant code = #" #ifdef FAST typedef string|mapping(string:string|_echoable_message)|array(_echoable_message) _echoable_message; typedef _echoable_message echoable_message; #else typedef string|mapping(string:string|echoable_message)|array(echoable_message) echoable_message; #endif
typedef echoable_message|function(object,object,string:echoable_message) command_handler; bool assess_command(command_handler f) { object|mapping flags = functionp(f) ? function_object(f) : mappingp(f) ? f : ([]); return flags->ok; } protected void create(string n) { #ifndef FAST2 add_constant("echoable_message", echoable_message); #endif add_constant("assess_command", assess_command); } ";
int main() { add_constant("commands", ([])); System.Timer tm = System.Timer(); while (1) { compile_string(code)(); write("Compiled in %.2fs\n", tm->get()); } }
Running this with -DFAST uses a different name temporarily, dodging the problem. Using -DFAST2 doesn't add the typedef as a constant, also dodging the problem. But otherwise, it quickly starts taking notable time to run each compilation - each iteration about 3-4 times as long as the previous.
The question is: Should echoable_message be using the added constant, or should it be defined recursively? I actually don't know what is correct behaviour here.
ChrisA