Menu ▾ ▴

#39 6.4.2 `gensym` counted past the largest integer

CLIPS
open
nobody
None
5
2 days ago
2 days ago
Batch Job 1
No

gensym counted past the largest integer

What happened. Once the gensym index reached the largest CLIPS integer, the next call counted past it and returned a name with a negative number in it.

(printout t "setgen: " (setgen 9223372036854775807) crlf)
(printout t "gensym1: " (gensym) crlf)
(printout t "gensym2: " (gensym) crlf)
(printout t "gensym3: " (gensym) crlf)

printed

setgen: 9223372036854775807
gensym1: gen9223372036854775807
gensym2: gen-9223372036854775808
gensym3: gen-9223372036854775807

gensym* did the same.

Why it is a fix and not a preference. Section 12.7.1 of the Basic Programming Guide says gensym returns "identifiers of the form genX where X is a positive integer". −9223372036854775808 is not a positive integer.

Section 12.7.3 says setgen's argument "must be a positive integer value" and that "all subsequent calls to gensym will return a sequenced symbol with the numeric portion of the symbol starting at <integer-expression>", so gen9223372036854775807 is the name (setgen 9223372036854775807) asks for and the sequence has nowhere to go after it.

div and mod already treat an integer result they cannot represent as an evaluation error, reporting through ArgumentOverUnderflowErrorMessage.

Where it came from. GensymFunction in miscfun.c formats the index and then increments it:

   gensnprintf(genstring,sizeof(genstring),"gen%lld",MiscFunctionData(theEnv)->GensymNumber);
   MiscFunctionData(theEnv)->GensymNumber++;

GensymNumber is a long long, and incrementing the largest one has no result in long long; on this host it produced the most negative. GensymStar runs the same two lines inside its search loop, so it increments on every pass.

The change. miscFunctionData gains a GensymIndexExhausted flag. GensymFunction and GensymStar set it instead of incrementing when the index is already LLONG_MAX, and report through ArgumentOverUnderflowErrorMessage when they find it set. SetgenFunction clears it.

The index alone cannot record this. GensymNumber holds the index of the symbol just handed out, so the state after gen9223372036854775807 and the state before it are the same number, and the two have to be told apart for 12.7.1 and 12.7.3 to both hold: the symbol for the largest index is handed out once, and the call after it reports rather than repeating a name. Nothing else can reach the flag's condition, since the field starts at 1, setgen refuses anything below 1, and counting only goes up.

GensymStar has to ask inside the loop. After gen9223372036854775807 has been interned, a gensym* at that index finds the name taken, goes round, and has no further index to try — so it reports, which is the right answer for a function documented to "produce a unique symbol that does not currently exist".

What was checked. (setgen 9223372036854775807) then three gensym calls: the first returns gen9223372036854775807, the next two report, neither returns a symbol, and gen- appears nowhere in the output. (setgen 5) then starts a new sequence at gen5 and gen6, so the flag clears. The same sequence through gensym*, in its own file so that gen9223372036854775807 is not already interned when it runs.

Then the examples in 12.7.1 to 12.7.3: (setgen 32) and the two gensym calls after it, (setgen 1) with (assert (gen1 gen2 gen3)) and then gensym returning gen1 and gensym* returning gen4, and the default-dynamic template whose two facts get gen1 and gen2. None of them reports.

The reason to care about this one: a gensym name is used as an identifier, and two calls that return the same name or a name with a minus sign in it break whatever is keying on them, a long way from the counter.

Present in 7.0? Not checked.

3 Attachments

Discussion

Anonymous
Anonymous

Add attachments
Cancel