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