Bizarre Inform V10 Issue With I6 Code In Extensions!

So, I’ve come across another issue that’s completely stumped me. This is the extension code I’m using.

Test by Climbingstars begins here.

"Test Extension."

Section 1 - Z-machine Announce Story File Version Rule (for Z-machine only)

Include (-

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	if (actor ~= player) rfalse;
	Banner();
	print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
	if (UUID_ARRAY->0 >= 6) {
		print "Identification number: ";
		for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
		print "^";
	}
	ix = 0; ! shut up compiler warning
	if (standard_interpreter > 0) {
		print "Standard interpreter ",
			standard_interpreter/256, ".", standard_interpreter%256,
			" (", HDR_TERPNUMBER->0;
		print (char) HDR_TERPVERSION->0;
		print ")^";
	} else {
		print "Interpreter ", HDR_TERPNUMBER->0, " Version ";
		print (char) HDR_TERPVERSION->0;
		print "^";
	}
	ShowExtensionVersions();
	say__p = 1;
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

Section 2 - Glulx Announce Story File Version Rule (for Glulx only)

Include (-

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	if (actor ~= player) rfalse;
	Banner();
	print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
	if (UUID_ARRAY->0 >= 6) {
		print "Identification number: ";
		for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
		print "^";
	}
	@gestalt 1 0 ix;
	print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100,
	".", ix & $FF, " / ";
	@gestalt 0 0 ix;
	print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^";
	ShowExtensionVersions();
	say__p = 1;
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

Test ends here.

Basically, the I6 code for the announce the story file version rule copied verbatim into I6 includes, and this is the main code running it.

"Test"

Include Test by Climbingstars.

[Section 1 - Main Code]

Before reading a command when the initial text display state is false (this is the initial text rule):
follow the announce the story file version rule;
follow the initial room description rule;
now the initial text display state is true.

The initial text display state is a truth state that varies. The initial text display state is false.

The initial room description rule is not listed in the startup rulebook. The display banner rule is not listed in the startup rulebook.

The Testing Room is a Room. The description of the testing room is "This is a Test!".

When run as is, it runs completely fine, giving this as its output.

Test
An Interactive Fiction
Release 1 / Serial number 240613 / Inform 7 v10.1.2 / D
Inform 7 v10.1.2
Identification number: //AA456F5B-5EA8-4292-A767-47CE5B726D50//
Interpreter version 1.3.7 / VM 3.1.3
Test by Climbingstars

Testing Room
This is a Test!

>

However, if I uncomment the section header in the main code, it gives this.

Testing Room
This is a Test!

Testing Room
This is a Test!

>

Huh? How on earth is a version command somehow turning into essentially a look command and why is this being caused by uncommenting something that’s essentially supposed to be non code?

This only seems to happen with Glulx. ZCode doesn’t’ve this issue.

Other than the fact that certain functions appears in different places, the generated I6 code seems pretty much the same.

The only thing left at this point that I think could be causing this issue is the I6 compiler.

Any ideas? Thanks! :smile:

1 Like

What do you mean by “uncomment the section header”? There are two section headers in your extension. Could you just provide the version with which you get the misbehavior, please?

Not the extension headers. When I uncomment “[Section 1 - Main Code]” from the main code. This is Inform 7 v10.1.2.

I can confirm this in 10.1.2.

Things that the effect does not seem sensitive to:

  • name of extension
  • presence or absence of section headers in extension
  • placement of Include in main source
  • the name of the section in the main source
  • the position of the section header in the main source
  • the level (e.g. section, chapter) of the header in the main source

Other things I’ve observed:

  • both outputs of the room description are happening while executing the initial text rule
  • one of the room description outputs results from the line follow the announce the story file version rule; and one from line follow the initial room description rule;
  • changing the rule modification to say that each does nothing instead of is not listed... seems to work as intended
  • the generated I6 does seem to vary significantly between the versions with and without a section header in the main source, but it’s not clear why or how
1 Like

Thanks @otistdog! Looks like you’ve checked over some extra bits that didn’t occur to me.

It looks like the for printing the announcement of light rulebook is firing instead of the announce the story file version rule causing the extra room description.

I’ve also looked deeper into this issue and it turns out that you can get the issue without the extension, like so (glulx only).

"Test"

Include (-

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	if (actor ~= player) rfalse;
	Banner();
	print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
	if (UUID_ARRAY->0 >= 6) {
		print "Identification number: ";
		for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
		print "^";
	}
	@gestalt 1 0 ix;
	print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100,
	".", ix & $FF, " / ";
	@gestalt 0 0 ix;
	print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^";
	ShowExtensionVersions();
	say__p = 1;
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

Section 1 - Main Code

Before reading a command when the initial text display state is false (this is the initial text rule):
follow the announce the story file version rule;
follow the initial room description rule;
now the initial text display state is true.

The initial text display state is a truth state that varies. The initial text display state is false.

The initial room description rule is not listed in the startup rulebook. The display banner rule is not listed in the startup rulebook.

The Testing Room is a Room. The description of the testing room is "This is a Test!".

I thought this because I got the version text as it should be when moving that bit of text out of the extension. Turns out this was because moving it out put it below another include. With this in mind, I’ve found out that doing this seems to have an effect with different functions giving different results.

"Test"

Include (- {VerbatimFunctionCode} -) replacing "{FunctionName}".

Include (-

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	if (actor ~= player) rfalse;
	Banner();
	print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
	if (UUID_ARRAY->0 >= 6) {
		print "Identification number: ";
		for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
		print "^";
	}
	@gestalt 1 0 ix;
	print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100,
	".", ix & $FF, " / ";
	@gestalt 0 0 ix;
	print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^";
	ShowExtensionVersions();
	say__p = 1;
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

Section 1 - Main Code

Before reading a command when the initial text display state is false (this is the initial text rule):
follow the announce the story file version rule;
follow the initial room description rule;
now the initial text display state is true.

The initial text display state is a truth state that varies. The initial text display state is false.

The initial room description rule is not listed in the startup rulebook. The display banner rule is not listed in the startup rulebook.

The Testing Room is a Room. The description of the testing room is "This is a Test!".

The following results occur when substituting for {FunctionName} and {VerbatimFunctionCode} accordingly.

Using “Parser__parse”, “Keyboard”, “NounDomain”, “Adjudicate”, “Identical”, “TryNumber”, “Descriptors”, “PrepositionChain”, “ReviseMulti”, “ScoreMatchL”, “BestGuess” or “PrintInferredCommand” causes the version text to show up normally.

Using “KeyboardPrimitive”, “SafeSkipDescriptors”, “MakeMatch”, “ConsultNounFilterToken”, “SingleBestGuess”, “PrintCommand”, “CantSee” or “YesOrNo” gives no text at all. Sometime with an extra line break.

Using “LIST_OF_TY_Mol”, “LIST_OF_TY_Set_Mol” or “ArticleDescriptors” does nothing and the for printing the announcement of light rulebook fires.

Using “ResetDescriptors” causes the standard implicit taking rule to fire, then causing a programming error.

Using “SetPlayersCommand” causes the prefer sometimes abbreviated room descriptions rule to fire.

Using “CreatureTest” causes the standard closing rule to fire.

All in all, it looks like something is causing a stray rule or rulebook to fire.

I can shed some light on that. I went through the generated I6 code to see what was mismatching and it seems that the only differences I could find are I6 functions in different locations of the code and differing function names, which in essence makes the 2 equivalent (except for an extra variable in one of them).

You can think of it like a group of people ordering food and getting a ticket number. If the group queue up in a different order, they’ll get different ticket numbers but they’ll get their correct orders.

That said, there is the possibility that I7 compiler has mismatched functions here (ie. getting an incorrect order). I wasn’t able to confirm this, or more precisely, it’d probably take way too long to check through every single generate I6 function to make sure it’s not mismatched.

Putting all this together suggests that either the I7 compiler has somehow mismatched the auto generated I6 functions it creates or this is an I6 compiler issue.

I try to check in when someone says “I6 compiler issue”, but I’m running on an old laptop this weekend and may not be able to take a serious look.

My first guess was that the $OMIT_UNUSED_ROUTINES=1 option was going wrong and losing a routine into the void. But this was wrong: I get the same result when turning that option off.

My next guess is that I7’s Inter stage, which converts I6 kit code into data structures and back, is at fault. Unfortunately I know very little about it.

For the record, this is my test case: (because it generates an easy-to-spot “Programming error”):

"Test"

Include (-

[ ResetDescriptors;
    (indef_mode = 0);
    (indef_type = 0);
    (indef_wanted = 0);
    (indef_guess_p = 0);
    (indef_possambig = 0);
    (indef_owner = 0);
    (indef_cases = 4095);
    (indef_nspec_at = 0);
];

-) replacing "ResetDescriptors".

Include (-

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	if (actor ~= player) rfalse;
	Banner();
	print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
	if (UUID_ARRAY->0 >= 6) {
		print "Identification number: ";
		for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
		print "^";
	}
	@gestalt 1 0 ix;
	print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100,
	".", ix & $FF, " / ";
	@gestalt 0 0 ix;
	print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^";
	ShowExtensionVersions();
	say__p = 1;
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

Section 1 - Main Code

Before reading a command when the initial text display state is false (this is the initial text rule):
follow the announce the story file version rule;
follow the initial room description rule;
now the initial text display state is true.

The initial text display state is a truth state that varies. The initial text display state is false.

The initial room description rule is not listed in the startup rulebook. The display banner rule is not listed in the startup rulebook.

The Testing Room is a Room. The description of the testing room is "This is a Test!".
1 Like

No, now it looks like the regular I6 compiler is at fault.

In that test case auto.inf file, swapping the function ANNOUNCE_STORY_FILE_VERSION_R() with the function after it (call_U1()) changes the compiled behavior. That’s definitely hinky. However, to dig further I’d want to try a whole set of older I6 compilers, plus a memory overflow test. I may not be able to do that until after NarraScope.

Okay, turns out I didn’t need to do any of that.

This is not a I6 bug at all. It’s a bug in I7’s kit code, in the FollowRulebook() routine. This happens because of the test case line

follow the announce the story file version rule;

…which translates as…

FollowRulebook(ANNOUNCE_STORY_FILE_VERSION_R);

The first argument passed to FollowRulebook() can be either a rulebook value (a small integer, 0 to NUMBER_RULEBOOKS_CREATED) or a rule value (a function address). It distingishes these cases by saying

if ((((rulebook >= 0)) && ((rulebook < NUMBER_RULEBOOKS_CREATED)))) ...

But that’s making an assumption that function addresses will never be small. For a minimal I7 game, NUMBER_RULEBOOKS_CREATED is 362. Guess what just happened.

The “Include (- … -) replacing …” directive puts the new definition at the top of the generated I6 file. That means that a new function could have an address as low as 72. In this case, ANNOUNCE_STORY_FILE_VERSION_R has address 111.

So FollowRulebook() gets confused and executes rulebook 111 instead of the desired function.

5 Likes

That’s interesting. I had traced it to the same thing but still thought that something weird was happening because I didn’t think it was possible for a routine address to be so low.

I guess that the I6 compiler doesn’t put routines together when compiling for Glulx the same way that it does for Z-Machine?

EDIT: It’s not super-easy to create a workaround, either, since the I7 compiler doesn’t seem to want to lay out any included arrays or constant text strings before the replacement routine.

I did manage to get a workaround in place with

Include (-

[ EAT_ADDRESS_SPACE x;
	if (x == "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	... ! 32 repetitions
	if (x == "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	rfalse;
];

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
	EAT_ADDRESS_SPACE();	! ensure routine is used, or will be omitted
	... ! desired replacement code here
];

-) replacing "ANNOUNCE_STORY_FILE_VERSION_R".

It does, but routines are the first thing grouped after the header.