To issue run-time problem messages, and to perform some run-time type checking which may issue such messages.


§1. Reporting. All RTPs are produced by calling IssueRTP. This does not necessarily issue the problem right away, since reporting of these can be temporarily suspended, and we also want to avoid long cascades of repeated RTPs.

The only compulsory argument is name, which is used by the Inform GUI apps to notice that RTPs have been issued and to display an appropriate page of explanatory documentation. The apps notice RTPs by looking for a particular pattern of text, so do not reformat RTPs without coordinating with the apps.

It is usual also to give desc and set. desc is a description, which can either be a string or a function which takes the three parameters and prints out a line of description instead. set should be a constant identifying which kit of code issued the RTP: for example, BasicInformKitRTPs. If an RTP does not belong to the right set, the GUI apps won't be able to display helpful pages about it.

The arguments par1, par2 and par3 are optional parameters which are passed to the desc function, if there is one.

Array LatestRTPData -->
    (-1)    -->0 = Name of current RTP, or -1 for none, or -2 for issued already
    0       -->1 = par1 value
    0       -->2 = par2 value
    0       -->3 = par3 value
    0       -->4 = description string or function
    0       -->5 = set value
    false;  -->6 = suspension flag (true if suspended, false if not)

[ IssueRTP name desc set par1 par2 par3;
    if ((name ofclass String) &&
        ((desc ofclass String) || (desc ofclass Routine) || (desc == -1)) &&
        (set ofclass String)) {
        if (LatestRTPData-->0 == -1) {
            LatestRTPData-->0 = name;
            LatestRTPData-->1 = par1;
            LatestRTPData-->2 = par2;
            LatestRTPData-->3 = par3;
            LatestRTPData-->4 = desc;
            LatestRTPData-->5 = set;
        }
        ShowRTP();
    } else {
        "*** Improper call to IssueRTP ***";
    }
];
[ ClearRTP;
    LatestRTPData-->0 = -1;
    LatestRTPData-->6 = false;
];
[ SuspendRTP;
    LatestRTPData-->6 = true;
];
[ ResumeRTP;
    LatestRTPData-->6 = false;
];
[ ShowRTP   name par1 par2 par3 desc suspended;
    if (LatestRTPData-->0 == -1 or -2) return;
    if (LatestRTPData-->6) return;

    name = LatestRTPData-->0;
    par1 = LatestRTPData-->1;
    par2 = LatestRTPData-->2;
    par3 = LatestRTPData-->3;
    LatestRTPData-->0 = -2;

#Ifdef TARGET_GLULX;
    suspended = SuspendTextInput(Main_Window);
#Endif;

    print "^*** Run-time problem ", (string) name;
    #ifdef DEBUG;
    if (LatestRTPData-->5) { print ": ", (string) LatestRTPData-->5; }
    #endif;
    print "^";

    desc = LatestRTPData-->4;
    if (desc ~= -1) {
        print "*** ";
        if (desc ofclass String) print (string) desc, "^";
        else if (desc ofclass Routine) desc(par1, par2, par3);
    }

#Ifdef TARGET_GLULX;
    if (suspended) {
        ResumeTextInput(Main_Window);
    }
#Endif;
];

§2. The VeneerError RTP. The following function, confusingly called RunTimeError and not to be confused with IssueRTP above, is a residue from the old I6 library. Most of its possible messages can't be seen in I7 use — for instance I7 has no timers or daemons, so error 4 is out of the question. We retain it only because the veneer code added by I6 requires it to be present.

Constant MAX_TIMERS = 0;
[ RunTimeError n p1 p2;
    IssueRTP("VeneerError", "Low level error.", BasicInformKitRTPs);
    print "*** Veneer error ", n, " (", p1, ",", p2, ")^";
    #Ifdef DEBUG;
    print "*** ";
    switch (n) {
      1:    print "preposition not found (this should not occur)";
      2:    print "Property value not routine or string: ~", (property) p2, "~ of ~", (name) p1,
                  "~ (", p1, ")";
      3:    print "Entry in property list not routine or string: ~", (property) p2, "~ list of ~",
                  (name) p1, "~ (", p1, ")";
      4:    print "Too many timers/daemons are active simultaneously.
                  The limit is the library constant MAX_TIMERS (currently ",
                  MAX_TIMERS, ") and should be increased";
      5:    print "Object ~", (name) p1, "~ has no ~time_left~ property";
      7:    print "The object ~", (name) p1, "~ can only be used as a player object if it has
                  the ~number~ property";
      8:    print "Attempt to take random entry from an empty table array";
      9:    print p1, " is not a valid direction property number";
      10:   print "The player-object is outside the object tree";
      11:   print "The room ~", (name) p1, "~ has no ~description~ property";
      12:   print "Tried to set a non-existent pronoun using SetPronoun";
      13:   print "A 'topic' token can only be followed by a preposition";
      default: print "(unexplained)";
    }
    print "^";
    #Endif; DEBUG
];

§3. RTPs issued by the compiler. Or properly speaking, by code generated by the Inform 7 compiler, usually in response to runtime type-checking having failed. Rather than calling IssueRTP directly, it calls one of the following.

[ IssueTypecheckingRTP line file;
    IssueRTP("PhraseAppliedToIncompatibleValue",
        "Phrase applied to an incompatible kind of value.", BasicInformKitRTPs);
    rtrue;
];

[ IssueIterationRTP what;
    IssueRTP("RepeatedThroughUnenumerableKind",
        "Could not iterate through values of this kind.", BasicInformKitRTPs);
    print "*** This arose from a description which started out here - ~",
        (string) what, "~.^";
    rtrue;
];

[ IssueSetVariableRTP val setting kind;
    IssueRTP("SetVariableToWrongKindOfObject",
        "Tried to set a variable to the wrong kind of object.", BasicInformKitRTPs);
    print "*** You wrote '", (string) setting, "', which sets the value to ", (the) val,
        " - but that doesn't have the kind '", (string) kind, "'.^";
    rtrue;
];

[ IssueChangedRelationRTP X Y rel;
    IssueRTP("ChangedRelationOutsideDomain",
        "Tried to change a relation for objects with the wrong kinds.", BasicInformKitRTPs);
    print "*** The relation was set up as ~",
        (string) RlnGetF(rel, RR_DESCRIPTION), "~, but you tried to ",
        "relate (or unrelate) ", (the) X, " to ", (the) Y, ".^";
    rtrue;
];

[ IssueAbstractRelationRTP task rel;
    IssueRTP("UsedAbstractRelation",
        "Tried to make use of an abstract relation.", BasicInformKitRTPs);
    rtrue;
];

§4. Return Type Checking Failed. Similarly, though in a more restricted set of circumstances. Inform can usually prove that the value returned by a phrase matches its supposed kind, but because of the deliberately weak type-checking within the kind of value "object" it will allow a broader kind of object to be returned when the requirement is for a narrower one. In such cases, it checks the result with the following routine. The value V is a valid "object", but that means it can be nothing, and we must check that it matches a given I7 kind K (where nothing is not valid).

[ CheckKindReturned V K;
    if (V ofclass K) return V;
    if (V == nothing)
        IssueRTP("DecidedOnNothing",
            "Attempt to 'decide on nothing'.", BasicInformKitRTPs);
    else
        IssueRTP("DecidedOnWrongKindOfObject",
            "Attempt to 'decide on V' where V is the wrong kind of object.",
            BasicInformKitRTPs);
    return V;
];