Running through some of my code last night, putting them through the new XE4 compiler, threw up a real oddity: Some code that used to compile just fine, which no longer compiles in XE4 and which should not have compiled before!
It’s an odd one, because the code previously compiled – and worked – as intended, and the old “bug” that seems to have now been fixed could well have instead been embraced as a feature (although I can understand why it was not and am not suggesting it should have, just observing).
I have to point out at this juncture that there is nothing much to learn from this post I’m afraid. It is a bit of a compiler curiosity, that is all. But even so, I found it interesting and thought others might as well. And who knows, someone else might have the same problem lurking in their older library code, unbeknownst to them, in which case this might save a bit of head scratching for them.
It involves the referencing of an enum in a unit that is not referenced in the uses clause, where the reference is qualified by the unit name in which the enum is declared and where the unit containing the reference uses some other unit that in turn does use the unit that contains the enum.
Got all that ? Clear as mud ? 🙂
Ok, here’s the simplest possible test case that will show what I mean. 3 files – a DPR and 2 units. Obviously in the real project the DPR and the units involved were a sight more complex (I suspect that the “error” crept in during a refactoring where the unit containing the enum was decomposed into 3 smaller units).
In any event, here’s the sample DPR. The code doesn’t do anything, as such, it just exercises the necessary parts of the compiler:
program unused; uses Foo.Foo in 'Foo.Foo.pas'; var c: Integer; begin c := Ord(Foo.enumBAR); end.
Here we can see that the DPR uses the unit Foo.Foo, but references an enum member – enumBAR – qualified with a different unit name, Foo, which is not being “used” at all.
Here is the Foo.Foo unit:
unit Foo.Foo; interface uses Foo; implementation end.
And finally the Foo unit containing the enum declaration with the enumBAR member:
unit Foo; interface type TEnum = (enumBAR); implementation end.
This project compiles just fine in Delphi 2010, but fails in XE4 with the compiler complaining about the undeclared “Foo” identifier on the line:
Again, in the actual case in my actual project, the reference was not in the DPR but in another unit in the project. All units involved were in the DPR uses list, but other than that, the scenario was the same as illustrated by the sample.
The solution is obviously to simply add the required unit to the uses list, so no great shakes.
I haven’t tried the sample specifically in other versions of Delphi, but this project has previously been tested in Delphi versions 7 thru XE2, though I cannot honestly recall if it has been tested in those versions since the refactoring that might have led to this situation. The point being that I think if I were to do that testing, I would find that the behaviour in this area might have changed as a result of the introduction of SCOPEDENUMS (since this case involves a qualified enum reference, albeit unit qualified rather than type-name qualified).
Obviously whether it existed in any other version of Delphi besides 2010 or not, the previous behaviour was technically wrong. It should not have been possible to reference members of units that are not being “used”. But then again, given than “using” a unit is a formal declaration that brings the contents of that unit into scope, it is arguable that you could conceivably extend the syntax to allow formal qualification of specific members in a unit without requiring that unit to be used to bring all it’s members into scope for unqualified use.
I don’t think you should. The declaration of which units are being used is useful self-documentation imho. As I said at the outset, it is interesting to think that it might not (and apparently did not always used to be :)) necessary.
In any event, this code now compiles correctly. Or, more accurately, correctly fails to compile. 🙂