View Issue Details

IDProjectCategoryView StatusLast Update
0000666LDMud 3.5Runtimepublic2017-10-04 21:35
Reporterzesstra Assigned Tozesstra  
Status resolvedResolutionopen 
Target Version3.5.0 
Summary0000666: RfC: Type-checking at run-time
DescriptionCurrently, types are checked at compile-time (if checked at all). One consequence is, that it is not possible to ensure that functions are only called with arguments of the correct type. Which is the reason for the existence of
if (!stringp(arg) && !closurep(arg))
   handle_error(); // e.g. raise_error().
at the beginning of many functions to ensure correct input data ('garbage in - garbage out').

I would like to discuss an optional type-checking at run-time (restricted to stuff which can't be checked at compile-time, e.g. function arguments during call_other()), which can be enabled by a pragma 'runtime_type_check' (Prequesite is save_types, which could IMHO be implicitly enabled then.)
This way programmers don't have to check their arguments themselves but just rely on the correct types if they use '#pragma runtime_type_check'.
We may have to check how big the run-time impact of such an optional check (for programs without that pragma) is. (If programs use the pragma and don't check in LPC, we improve performance.)
TagsNo tags attached.


related to 0000072 closed clash between several features re. type-safety 



2009-06-24 10:09

manager   ~0001233

I think it's a good idea, especially for library code.

Do we want to check only call_others or internal calls as well?
For call_others the overhead is insignificant, but for internal calls it might have a small impact. On the other side the compile-time type checks are insufficient for internal calls (because of 'mixed' variables or overriding functions with other argument types).

Do we want to check unsafe assignments to variables (a mixed or unknown value to a variable with a type != mixed)?


2009-06-24 10:20

updater   ~0001234

I agree that this would be a major improvement.

It would get rid of a lot of typechecking that is being done explicitely in the LPC-code right now. It would be very nice to have it for internal calls as well if it does not have a too severe impact on performance - nevertheless for call_other it seems even more important to me (for all that stuff that is handled by central applications that receive calls from a lot of programs of different coders).

I personally like the idea of warnings at unsafe assignments though I would do that only if strong_types or even strict_types has been declared.


2009-06-24 11:08

administrator   ~0001235

Yes, I would also use it especially for library code. :-)

Originally I thought, a check for inter-object calls would sufficient, but because of the behaviour Gnomi mentioned, Im afraid, we should check intra-object calls as well. Which contradicts my wish to decrease function call overhead for intra-object calls. :-( So I have no definite position on this yet.

Warnings for 'unsafe' assignments might be a good idea, but we should do that in a third step and after evaluating the run-time impact. (I would see function calls/arguments as first step and definitely wrong assignments as second step.)

I agree, that all of this should only be in effect for strong_types/strict_types, for programs using weak_types it is probably anyway useless (too much work).


2009-06-24 19:28

reporter   ~0001236

Last edited: 2009-06-24 19:32

The really interesting question about performance is: would it be slower than the existing "if( !stringp(foo) && !closurep(foo) )"s?

Another point: take the example above: foo would be declared as 'mixed' here, like eg.

void bar(mixed foo) { if(...) ... }

How shall the driver check a type against mixed? Strictly speaking if we want to get rid of that if() we would have to declare the function somewhat like

void bar(string|closure foo) { ... }

For any other type than mixed checking would be a nice feature, but OTOH it would be rather inconsistent simple because there *is* a 'mixed' type at all, that has to be checked (if at all) manually anyways.

Looking at it that way the existing checks (e.g. at assignments from call_other) seem sufficient; the mere existence of 'mixed' makes LPC not inherently typesafe, thus there is no "clean" way to introduce type checking...


2009-06-25 04:04

administrator   ~0001237

Doing the check in C will be significantly faster (at least an order of magnitude, I guess) than in LPC. However, not all functions check their argument types. Some wizards forget it, some are too lazy (both would profit in theory), some don't think it is necessary for the specific function.

I admit, my example above it not the best one, because that function argument would be mixed and then checks of the driver are pointless/not possible.
A syntax like void bar(string|closure foo) would be nice for this, but (because IMHO only a tiny fraction of functions use 'mixed' arguments) not a prerequisite for a run-time type check to be useful.

I don't think it is inconsistent. If you deliberately use 'mixed' you just tell the driver that you don't care (this time) and that the driver should not care, thats all. Why should it be inconsistent to check all other types, only because you have a type which can hold any type? If I don't use mixed (I think I wrote only 5-10 functions accepting 'mixed' in the last years) and have a string argument, I would still be happy if the driver takes care that my function is only called with a string argument.


2009-06-25 19:22

reporter   ~0001238

We use mixed very often for functions that offer different ways of calling. I agree this is not 100% clean either, but our coders are used to it :-) And of course in that case typechecking is up to the coder anyways.


2009-06-26 02:46

reporter   ~0001239

What about the zero? (as an unitialized variable or destructed object)
Should it be treated like an int like in many error messages or as a special case of the declared argument variable type?


void dance(object who);

object ob;

dance(&ob); // unitialized, but maybe I want to get a referenced object back
ob= ...;
dance(ob); // destructed object, syntactically this is still type 'object'


2009-06-26 02:56

manager   ~0001240

Zero has to be considered as an element of every type. Otherwise this would break a lot of code.


2009-09-04 11:57

reporter   ~0001250

I really really really really want this ;-) My whole LIB contains a LOT of checks just for types. We introduced a system like this for every lib method in this way:

void dance(object who, int foo)
    check(who, IS_OBJECT, foo, IS_INT);


It produces something like 'Bad argument 2 to dance(): expected 'int' but got 'object'.'

its OK, but i would love to get rid of these ;-)


2009-09-04 12:14

administrator   ~0001251

Actually, I started working on these checks yesterday on a private branch. There is still quite a lot to discuss and decide, but some first patches will probably find their way into trunk during the next weeks.
The first problem is that svalues and vartype_s/fulltype_s (typeid_t, typeflags_t) use very different schemes to encode types and modifiers.


2009-10-28 11:28

administrator   ~0001568

I had a look today at the possibility for type-checks on variable assignments. This is not so easy, because the compiler just generates F_ASSIGN/F_VOID_ASSIGN, which transfer/copy data from one svalue to another. In this case, there is no data available to decide, which type the destination svalue should have.
In the case of global variables, this data at leasts exists as variable_t.type in ob->prog->variables, but in case of local vars, this information is lost after compilation.
So we have to make the type info available first. Therefore I will concentrate first on type-checks upon function calls.


2009-10-31 10:23

administrator   ~0001575

ok. experimentally in 3.5. (trunk) now:
type checks at runtime are enabled by a pragma 'rtt_checks'. Currently the argument types upon function calls are checked. The checks depend on the pragma set in the defining program, not in the inheritee.
If you like to test it, I am happy about feedback. But keep in mind, that it is experimental and there are surely errors.


2009-11-01 16:44

administrator   ~0001579

I added checks for the correct return types when returning from lfuns and sefuns.
Interestingly, this caused more errors in our mudlib so far than the checks for function arguments... ;-)


2009-11-02 16:22

administrator   ~0001583

Bardioc notified me that it would be better to accept structs having the declared one as a base. ;-) So r2793 changed that behaviour and does not check to type identity anymore.


2009-11-04 18:22

administrator   ~0001602

restore_object() now checks the types of restored variables (if the restored entity from the savefile has the same type as the variable the data was restored to).
Similarly restore_struct() checks the types of the restored data with the declared types of its members.

Issue History

Date Modified Username Field Change
2009-06-24 08:13 zesstra New Issue
2009-06-24 08:13 zesstra Relationship added related to 0000072
2009-06-24 10:09 Gnomi Note Added: 0001233
2009-06-24 10:20 Sorcerer Note Added: 0001234
2009-06-24 11:08 zesstra Note Added: 0001235
2009-06-24 19:28 invisible Note Added: 0001236
2009-06-24 19:32 invisible Note Edited: 0001236
2009-06-25 04:04 zesstra Note Added: 0001237
2009-06-25 19:22 invisible Note Added: 0001238
2009-06-26 02:46 _xtian_ Note Added: 0001239
2009-06-26 02:56 Gnomi Note Added: 0001240
2009-09-04 11:57 Bardioc Note Added: 0001250
2009-09-04 12:14 zesstra Note Added: 0001251
2009-10-28 11:28 zesstra Note Added: 0001568
2009-10-28 11:28 zesstra Assigned To => zesstra
2009-10-28 11:28 zesstra Status new => assigned
2009-10-31 10:23 zesstra Note Added: 0001575
2009-11-01 16:44 zesstra Note Added: 0001579
2009-11-02 16:22 zesstra Note Added: 0001583
2009-11-04 18:22 zesstra Note Added: 0001602
2010-03-27 06:22 zesstra Target Version => 3.5.0
2017-10-04 21:35 zesstra Status assigned => resolved