Compiler bug? Condition "in <thing>" produces non-functional I6 code

EDIT: I’ve retitled this thread because although Draconis has pinpointed the change to the expected syntax in 6L02 it is not clear why the compiler is not rejecting conditions like in <enterable container>. (Note that such a condition is handled differently from one like while <thing> is in <enterable container>.) It looks like the pattern of code produced by such a condition can never evaluate true – see below for example details.

WorldModel.i6t contains a section with routine WhetherIn(), the description of which states:

A curiosity, this: the I6 definition of “To decide whether in (obj - object)”…

Note that “in X” is not equivalent to “the player is in X”: the latter uses direct containment, whereas we use indirect containment. Thus “in the Hall” and “in the laundry-basket” are both true if the player is in a laundry-basket in the Hall.

However, no such phrase To decide whether in... is defined by the Standard Rules in 6M62, and using the I7 condition as described does not invoke the WhetherIn() routine. For example, rule preamble:

After jumping in the huge basket:

will produce the non-functional I6 condition:

if ((((action ==##Jump) &&  (actor==player) && ((real_location == I130_huge_basket) && (true))))) { ! Runs only when pattern matches

I say “non-functional” because the global real_location can never be a thing. (… right?)

Is this something vestigial in the template, or is it an issue with the compiler’s handling of the in <X> phrase?

“To decide whether in” was removed years ago, for the sake of reducing confusion from “…when in…” and “…when the player is in…” and “…in…” all being different relations (location, containment, and enclosure respectively). It broke a lot of projects at the time, so you’ll find a lot of code from that era that defines “to decide whether in” in I7 as a patch.

I’m not sure why it still exists in WorldModel.i6t, but it’s there even in Inform 10. I don’t see it called anywhere in the Standard Rules so it might just be vestigial.

So, the compiler was changed to construct preamble conditions this way instead of making use of WhetherIn(), and now the phrase only produces sensible code if it’s in <room>? Is the fact that the compiler will accept a condition like in the tiny booth (as opposed to something like the actor is in the tiny booth) a bug, then?

The preamble:

After jumping in Lab:

produces the meaningful I6 code:

if ((((action ==##Jump) &&  (actor==player) && ((real_location == I126_lab) && (true))))) { ! Runs only when pattern matches

The example in the original post above produces the same basic pattern, but I’m pretty sure that the I6 condition can never evaluate true.

It seems as though WhetherIn() can still be useful in some cases. It does the equivalent of an enclosure relation test for enterable containers and enterable supporters (as compared to ContainerOf(), which allows only “directly in” i.e. direct child status). It also tests regional containment at any level. If it’s just a matter of conflicting/confusing phrasing, it could be redeployed with a specialized phrase like:

To decide whether somewhere inside (O - object):
	(- (WhetherIn({O})) -).

“…in…” was always specifically the location, unlike “…when in…”, which was enclosure (i.e. the thing WhetherIn tests) before it got axed. It’s implemented as part of the “action pattern grammar”, a weird, obscure little corner of the I7 compiler, rather than the Standard Rules or the I6T templates or anything an author can actually get at.

I’m trying to find where in the compiler this actually is, to check, but I believe the only conditions that are part of an action pattern are:

  • Specifying the actor, action, noun, or second noun
  • “In” (a room), “involving” (an object), “in the presence of” (a thing)
  • Any action variables from I7 code (like “from” a room)

WhetherIn() is not purely an enclosure relation test. Its meaning seems to vary by the type of object fed as a parameter:

[ WhetherIn obj;
	    if (obj has enterable) { 							! enterable container/supporter/custom kind
	            if (IndirectlyContains(obj, player)) rtrue;	! enclosure test for player
	    if (obj ofclass K9_region)								! region
			return TestRegionalContainment(real_location, obj);	! regional-containment test for player
	    if (obj ofclass K1_room) {						! room
	            if (obj == real_location) rtrue;		! short-cut equivalent to enclosure test for player
	    RunTimeProblem(RTP_NOTINAROOM, obj);

The latent run-time problem message suggests that at one point the routine was only intended to accept rooms and regions:

		print "Attempt to test if the current location is '",
			(the) par1, "', which is not a room or region.^";

… but it seems that the first block (handling enterable things) was subsequently installed.

I appreciate the backstory about the historical role of the routine. It does seem vestigial, though not necessarily useless. (Do you happen to know which version dropped the phrase for it in the Standard Rules?)

What I don’t understand is why the compiler is not complaining about a condition like in the bathtub where the bathtub is an enterable container. If the I6 code produced is definitely going to use the pattern comparing real_location to the X of in <X>, then shouldn’t the compiler be rejecting any X that’s not a room?

1 Like

It was one of the updates when I was active on the forums, which narrows it down a little bit…my bet is either 6L02 or 6L38. That seems like the right time period.

EDIT: Yep, found it in the 6L02 release notes.

1 Like

One last try to get a second opinion about whether or not I’ve found a bug.

Per the above:

  • Pre-6L02 there was a phrase to decide whether in (obj - object) that made use of the underlying WhetherIn() routine.
  • The WhetherIn() routine handled its single argument as follows:
    – for an enterable object it made an enclosure test
    – for a region it made a regional-containment test
    – for a room it made a comparison to real_location
    – for any other kind of object it threw a run-time problem
  • As of 6L02 to the phrase to decide whether in... was dropped from the Standard Rules and instead handling was moved to the “action pattern” processing in the compiler. The release notes for 6L02 point out that player is in... conditions are not an exact replacement for WhetherIn() because that condition will no longer perform enclosure tests.
  • Since that time, the “action pattern” processing in the compiler has been handling the object used in an in <object> condition as follows:
    – for an enterable object an incorrect comparison to real_location is made
    – for a region a correct regional-containment test is made
    – for a room a correct comparison to real_location is made
    – for any other kind of object an incorrect comparison to real_location is made

This certainly quacks like a bug to me. Either the compiler should be rejecting anything that’s not a correctly-handled room or region or the action pattern translation should be using an enclosure test (and probably restricting the allowable range to things so that conditions such as in west – which is currently accepted – are rejected).

1 Like

No, the action pattern processing was already there. “Whether in” was removed because it was considered unnecessarily confusing that it looked similar to the action pattern thing, but didn’t behave the same.

This depends what you consider “incorrect”. An “in” clause in an action pattern tests the location (or the region); that’s its documented behavior. So saying “instead of taking inventory in the crate” means “instead of taking inventory when the location is the crate”—which will, of course, never be true.

But I would call “when the location is the crate” an error on the author’s part, since of course the location will never be the crate (the location is a room and the crate is a thing). And similarly I would call “instead of taking inventory in the crate” an error on the author’s part, since it’s likewise testing a condition that can never be true.

You can argue that it should function differently, that an “in” clause should test enclosure rather than location. But this is a place where I’m actually going to side with Graham and say this is a suggestion rather than a bug (an area where I’ve very often disagreed with him); the behavior matches the documentation, it’s just that the documented behavior isn’t what you’d like it to be.


I’m arguing that in <thing> should either test enclosure or be rejected as invalid, because its current translation as a comparison to real_location has zero possible functional utility, and allowing it as valid code only serves to create an unexpected frustration for those trying to learn the language (i.e. creates undesirable confusion). In other words, it’s not that it doesn’t work how I would like but that it doesn’t work at all. Under the old Mantis bug reporting criteria, it would be “compiler accepts but produces invalid code”.

Would it be fine if in <region> made an invalid comparison to real_location if there was some other way to construct the condition correctly? There is: the location is regionally in <region> (or just the location is in <region>). Nonetheless, the compiler takes notice of the kind and generates code accordingly when translating in <region> as an action pattern.

Are you basing your reply above on the fact that player is in <room> does a direct child test via ContainerOf() so it’s functionally different than an enclosure test? That is true, but in <room> currently translates to the logical equivalent of an enclosure test, and so does TestRegionalContainment(), so wouldn’t it be logically consistent to do the same for in <thing>?

I don’t disagree that it can be considered an error on the author’s part, but in that case shouldn’t it be handled as an error by the compiler (i.e. issuing a problem message)?

I’m saying it generates valid but useless code. It’s perfectly legal, from a language perspective, to test “if the location is the crate”—even though we humans know it will never be.

In other words, imo, it’s not the language’s job to prevent you from writing useless code. If you want to test “if the player is the doorknob”, the compiler’s job is to translate that as if(player == doorknob), not to analyze all execution paths and conclude that it’s impossible to ever set the player variable to the doorknob object. (Halting problem and all that—what if I set player = doorknob only if the Collatz conjecture holds?)

Putting “in the crate” in an action pattern is useless, but so are a lot of other valid action patterns, like “instead of eating north”. It’s up to the author to make sure the conditions they’re testing are semantically meaningful.

I’d say that one of the biggest redeeming features of a statically typed language is that the compiler can and does give warnings and errors for detectable semantic problems.

player is a person variable. If doorknob is an object variable, I agree that it’d be unreasonable to expect the compiler to know whether now player is doorknob is problematic. But if doorknob identifies a specific non-person object, I don’t think it’s at all unreasonable to expect the compiler to either warn or give an error.

Now, to be clear, that doesn’t mean the compiler shouldn’t help authors write sensible code. Or that things should stay this way. It’s a reasonable feature request.

I’m just saying that it is a feature request, not a bug, because the behavior matches what’s documented and produces valid code. It’s just that the documented behavior isn’t what you want it to be.

To be fair, it’s not all that clearly documented. Chapter 7.11 is titled “In rooms and regions”, and talks about the “in” clause as “this location”. But it doesn’t make a direct connection to “the location of the player” (back in 3.25, if the reader remembers that, which they probably don’t.) Nor does it explain that the “in” clause doesn’t generalize to vehicles and chairs and whatnot.

(I think the I7 documention is generally very good. But it definitely encourages you to look at its concrete examples and then generalize all sorts of wild variations that you might use in your code. Which often work! The way you expect! But not always.)

I agree!

1 Like

Hmmm. I see your point, and I agree that it’s not the compiler’s job to determine whether your code is doing what you think it should be doing. However, this feels different to me for reasons I’m having trouble pinning down precisely. Maybe this: The code generated is obviously “valid” when considered as I6, but the translation to I6 doesn’t seem valid to me.

I rooted through the documentation (general index), and I found WWI 6.11 and WWI 7.11 as the applicable sections; if you’re thinking of others, please let me know. (I see that zarf already mentioned 7.11.)

WWI 6.11 A word about in makes an effort to differentiate between containment and enclosure, and between containment and regional-containment (even though it avoids mentioning the relations explicitly this early in the text). That discussion applies only to <thing> is in <thing>, <thing> is in <room> or <thing> is in <region> constructions.

If I’m not misunderstanding, the construction

After waiting while the player is in Limbo:
[I6 translation: ((((action ==##Wait) &&  (actor==player) && (self=actor,true) && (((I126_limbo == ContainerOf(player)))))))]

is handled partly as an action pattern (waiting) and partly as a condition (the player is in Limbo).

WWI 7.11 In rooms and regions discusses the in <room> and in <region> constructions. It does not have any examples indicating that in <thing> is a valid pattern, but neither does it have any note warning that such a construction can’t possibly work correctly, nor does it explain how the underlying I6 comparison is generated so that it’s more obvious that using it that way is incorrect.

Again, if I’m not misunderstanding, the construction

After waiting in Limbo:
[I6 translation: ((((action ==##Wait) &&  (actor==player) && ((real_location == I126_limbo) && (true)))))]

is handled as a single action pattern (waiting in Limbo).

Although now deprecated and therefore not mentioned outside of the vestigial note in WorldModel.i6t, with the pre-6L02 phrase to decide whether in (O - object) in place the compiler would also understand

After waiting while in Limbo:
[I6 translation: ((((action ==##Wait) &&  (actor==player) && (self=actor,true) && (((WhetherIn(I126_limbo)))))))]

which would again be handled as partly an action pattern (waiting) and partly a condition (in Limbo).

I share Zed’s stance that the ability to detect semantic problems is of value. And, in fact, the I7 compiler does this all the time for phrases set up by the Standard Rules and your own code. I’m assuming that in this case it’s because the compiler is working at a lower level than phrases (and because it wants to be able to accept in <description of objects>) that it’s even allowing an action pattern like waiting in west. Still, right now it’s somehow noticing the usage <action> in <region> and responding in a type-appropriate way (vs. the apparent default of a comparison to real_location), so it certainly seems to me that it should do the something appropriate for <action> in <thing> for the sake of consistency.

I think that the way WWI 6.11 examples cover regions, rooms and things while the WWI 7.11 examples cover regions and rooms (but not things) creates a contrast that highlights what I perceive as a significant gap in functionality. It’s because it’s an inconsistency in the range of acceptable parameters between two highly similar (if different) sets of patterns – and because that gap results in what I think of as a significant translation error, where translation is the core function of a compiler – that I think of that gap as a bug. If the compiler were currently rejecting After waiting in the box: then I would unhesitatingly agree that it would be a feature request to want it to produce meaningful I6 instead.

That said, I think I better understand the reasons for the current behavior now, and I definitely better understand some subtleties of how the compiler sees rule preambles, so thank you very much for the discussion.

1 Like