View Issue Details
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0000340||LDMud 3.5||LPC Compiler/Preprocessor||public||2005-01-11 10:59||2010-07-13 15:40|
|Target Version||3.5.0||Fixed in Version||3.5.0|
|Summary||0000340: "obsolete" modifier|
|Description||An "obsolete" modifier for lfuns, sefuns which throws a compilation/runtime warning if the so marked function is called (compiled function call as well as as call_others at runtime).|
This is another item, that would help handling big libs with a lot of historical accumulated nonesense.
|Additional Information||note that this behaviour can be simulated by explicitely notifying wizards, writing into a bug-database, etc.|
But I would favour an easy, fast, standarized way to handle this.
|Tags||No tags attached.|
||ah, and it could help MUDs prepare their switch to 3.3 by masking the efuns that are to vanish with an "obsolete" sefun|
||come to think about it "deprecated" probably wouldn't sound that harsh.|
||Oh, BTW: I like that idea (Although it might be difficult to find a bit for that flag in the function header.) We have plenty of old nonsense floating around... I am surprised that I did not comment earlier.|
Years ago, I wrapped each obsoleted efun by an sefun of the same name, giving a warning each time it was used. In the second stage, the sefun throws an error, pointing to the particular successor function.
This way it's quite comfortable to switch to a more recent driver version with "vanished" efuns.
Yes, that works good for efuns. But for them we have such a mechanism in the driver source which we also use (e.g. cat(), tail() in 3.3.x).
But in this case (lfuns, sefuns) it does not really work. You might define sefuns for obsolete lfuns, but you will have difficulties to defer from the sefun to private/protected lfuns. And if it triggers a warning on each call you might be flooded with scroll.
Additionally it does not catch call_others. You might define a call_other sefun and check on each call. But that introduces a signifcant slowdown for each call_other...
I think, the driver would be better suited, because it can do a lot things at compile-time and the remaining (call_other) much more efficient.
||I would love to have this modifier! Even though EverLIB is not old, parts of the domains are, and this would help alot!|
I worked on this a bit. It is not committed to the driver sources yet, because I would like to have some comments first.
I added a modifier 'deprecated' for functions (in theory it could be extended to variables at some point), which will set TYPE_MOD_DEPRECATED in the function flags. Upon calling such a function (intra-object + sefuns) the compiler will issue a warning at compile-time. Callothers and lfun closure evaluations will cause a warning at runtime (this could trigger a huge number of warnings, be warned).
There are still two caveats to be solved:
1) the warning in case of lfun closures will not include the correct line number + defining program (the correct solution would be to check before pushing a new control stack frame, but therefore I need a possibility to get the function flags indepedent of setup_new_frame1()).
2) If you call a not-defined function and this will be defined later by inheritance/cross-definition the compiler will miss any deprecated modifier, because the call was generated when the flag was unknown.
You can find the changeset at: http://github.com/zesstra/ldmud-dev/compare/deprecated_mod and the deprecated_mod branch is at http://github.com/zesstra/ldmud-dev/tree/deprecated_mod. Comments welcome.
BTW: Should it be possible to 'loose' the deprecated flags by redefining the function in inheritees?
Pro: I deprecate a specific function (e.g core lib) and don't care about redefined function as long as they don't call me.
Contra: Maybe I want to deprecate an interface as a whole, not only the function I define myself...
We would use "deprecated" if we want to change or eliminate parts of APIs. In that case we don't even want another user to keep on redefining that function - which he surely only did redefine in the first place to extend the inherited API.
In that case redefining the function will not work any more as expected and the user should be notified of this.
So: Pro: "deprecated" should not go away.
Concerning 2) (external declarations of functions):
In uni-libs, at least if we haven't diverged that much, we use external declarations quite a bit for the core living and player objects (the code is deployed over several inherits), so this limitation might be a bit hindering.
Could a work-around be to issue a warning if the compiler encounters a redefinition where not all declarations are "deprecated"?
I disagree. I might have a program with a function fun() that is perfectly valid and will stay. And just because some other program had a fun() that is now deprecated, this doesn't make my fun() invalid or deprecated as well, just because both programs were inherited together in another program. And I'd rather not differentiate between redefining a function and cross-defining it to another inherited program (because it's basically the same and moving a function to an inherited program should be a valid refactoring step, so it should behave similarly).
In other words, the deprecated flag should warn whenever the removal of that function declaration with this flag alters the behavior at the point of the warning (might not compile anymore or might call a function that was hidden until now). And when a deprecated function is redefined by a non-deprecated function the removal of the deprecated function changes nothing for the caller (which still calls the non-deprecated one), so no warning there.
Right, so to summarize the two different wishes:
a) Gnomi wishes to deprecate functions
b) Xtian wishes to deprecate interfaces
Unfortunately both are mutually exclusive. Gnomi offered the opinion, that we probably can't do b) consistently. One issue is, that we can't prevent somebody from implementing a deprecated interface himself and not inheriting the original program defining the interface at all. Although that is IMHO very rare (might in theory be excluded by code-style conventions).
Actually I could probably use both a) and b) independently in our lib... ;-) Usually I would deprecate functions. But there are some exceptions. e.g. we have an interface do_wear() which is obsolete and we do things differently now. If a wizard redefined do_wear() and did not call ::do_wear() there would be no warning, but his do_wear() will a) do the wrong things to wear a piece of clothing or b) his object will completely miss when a player wears the object (e.g fail to enforce restrictions).
So far, I guess you would have to use first deprecated, then a deprecated+nomask empty function to deprecate interfaces.
Yes, my wish (point b) can actually be realized by using "deprecated" (as Gnomi argued) and "nomask", or even the "warn_nomask" (http://mantis.bearnip.com/view.php?id=734) idea.
Please note, that we once proposed a "required" modifier which guaranteed that a inherited function would call ::fun() (http://mantis.bearnip.com/view.php?id=352). All these suggestions come from the frustrating experiences of maintaining an API over decades and inexperienced programmers, btw so all have real used cases. (yes, I remember that "required" still had some difficulties)
||Yes, I fully share that frustration, we also have our share of ancient APIs and wizards who just copy a function from the core lib and the copy is never maintained anymore, gets no updates or bugfixes, until some ugly enough bug happens. (BTW: that suggestion about 'required' is still here in Mantis. Although I guess, we can't make it happen until we have a compiler which can analyze the execution flow. :-()|
Ah, concerning the issue with the cross-defined functions: IMHO it not so big as it seems.
If you have a (matching) prototype with deprecated, the driver will warn. If you don't have a prototype, AFAIK you can only call the function if the calling function is compiled without type testing (it has no function type, which is not allowed with anything other than weak_types.
If you don't call a function without prototype yourself, that leaves call_other and symbol_function at runtime. And at runtime, the flags from the cross-defined functions are available (and would contain the deprecated flag).
||Updated my test branch for this: the flag is now checked upon creation of closures, not at evaluation time (symbol_function, compiler (#'fun, #'::fun)).|
||I added checks for usages of deprecated global variables and committed my patch series to the trunk of 3.5. for testing.|
||Ok, no complaints so far. Closing this one for now.|
|2005-01-11 10:59||_xtian_||New Issue|
|2005-01-11 11:16||_xtian_||Note Added: 0000298|
|2005-01-19 06:15||_xtian_||Note Added: 0000310|
|2010-02-12 19:14||zesstra||Note Added: 0001718|
|2010-03-03 18:00||Coogan||Note Added: 0001749|
|2010-03-03 18:11||zesstra||Note Added: 0001750|
|2010-03-03 18:14||zesstra||Project||LDMud => LDMud 3.5|
|2010-03-03 18:15||zesstra||Relationship added||related to 0000703|
|2010-03-05 14:15||Bardioc||Note Added: 0001767|
|2010-03-07 12:02||zesstra||Note Added: 0001768|
|2010-03-07 12:02||zesstra||Status||new => assigned|
|2010-03-07 12:02||zesstra||Assigned To||=> zesstra|
|2010-03-10 16:29||zesstra||Note Added: 0001776|
|2010-03-10 17:22||_xtian_||Note Added: 0001778|
|2010-03-10 17:29||_xtian_||Note Added: 0001779|
|2010-03-11 04:55||Gnomi||Note Added: 0001784|
|2010-03-11 05:33||zesstra||Note Added: 0001785|
|2010-03-11 14:01||_xtian_||Note Added: 0001787|
|2010-03-11 14:26||zesstra||Note Added: 0001788|
|2010-03-14 10:54||zesstra||Note Added: 0001794|
|2010-03-14 11:40||zesstra||Note Added: 0001797|
|2010-03-21 12:25||zesstra||Note Added: 0001811|
|2010-03-21 12:25||zesstra||Relationship deleted||related to 0000703|
|2010-03-27 06:22||zesstra||Target Version||=> 3.5.0|
|2010-04-29 14:08||zesstra||Relationship added||child of 0000749|
|2010-07-13 15:40||zesstra||Note Added: 0001870|
|2010-07-13 15:40||zesstra||Status||assigned => resolved|
|2010-07-13 15:40||zesstra||Fixed in Version||=> 3.5.0|
|2010-07-13 15:40||zesstra||Resolution||open => fixed|