In code that I help maintain, I have found multiple examples of code that looks like this:
Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
I expected this to crash when Widget was nil, but when I tested it, it worked perfectly.
If I recompile it with "Code inlining control" turned off in Project - Options - Compiler, I do get an Access Violation.
It seems that, because IfThen is marked as inline, the compiler is normally not evaluating Widget.Description if Widget is nil.
Is there any reason that the code should be "fixed", as it doesn't seem to be broken? They don't want the code changed unnecessarily.
Is it likely to bite them?
I have tested it with Delphi XE2 and XE6.
Personally, I hate to rely on a behavior that isn't contractual.
The inline directive is a suggestion to the compiler.
If I understand correctly what I read, your code would also crash if you build using runtime packages.
inlining never occurs across package boundaries
Like Uli Gerhardt commented, it could be considered a bug that it works in the first place. Since the behavior isn't contractual, it can change at any time.
If I was to make any recommendation, I would flag that as a low priority "fix". I'm pretty sure some would argue that if the code works, it doesn't need fixing, there is no bug. At that point, it becomes more of a philosophical question (If a tree falls in a forest and no one is around to hear it, does it make a sound?)
Is there any reason that the code should be "fixed", as it doesn't seem to be broken?
That's really a question that only you can answer. However, to answer it then you need to understand fully the implications of reliance on this behaviour. There are two main issues that I perceive:
Inlining of functions is not guaranteed. The compiler may choose not to inline, and in the case of runtime packages or DLLs, a function in another package cannot be inlined.
Skipping evaluation of an argument only occurs when the compiler is sure that there are no side effects associated with evaluation of the argument. For instance, if the argument involved a function call, the compiler will ensure that it is always evaluated.
To expand on point 2, consider the statement in your question:
Description := IfThen(Assigned(Widget), Widget.Description, 'No Widget');
Now, if Widget.Description is a field, or is a property with a getter that reads a field, then the compiler decides that evaluation has no side effects. This evaluation can safely be skipped.
On the other hand, if Widget.Description is a function, or property with a getter function, then the compiler determines that there may be side effects. And so it ensures that Widget.Description is evaluated exactly once.
So, armed with this knowledge, here are a couple of ways for your code to fail:
You move to runtime packages, or the compiler decides not to inline the function.
You change the Description property getter from a field getter to a function getter.
If it were me, I would not like to rely on this behaviour. But as I said right at the top, ultimately it is your decision.
Finally, the behaviour has been changed from XE7. All arguments to inline functions are evaluated exactly once. This is in keeping with other languages and means that observable behaviour is no longer affected by inlining decisions. I would regard the change in XE7 as a bug fix.
It already has been fixed - in XE7 and confirmed that this was supposed to be wrong behavior.
See https://quality.embarcadero.com/browse/RSP-11531
Related
This is a common question for other compilers (C#, VC++, GCC.) I would like to know the same thing for the Delphi compiler (any version; I'm currently using 2010 and XE2 and will use XE4 soon.)
I have a situation in high-performance code I'm writing where a condition has to be checked, but in most cases no action needs to be taken:
if UnlikelyCondition then
HandleUnlikelyCondition
else
HandleLikelyCondition
end;
Often nothing needs to be done for the likely case:
if UnlikelyCondition then
HandleUnlikelyCondition
else
Exit
end;
I would like to hint to the compiler that the second branch of the if statement is the one to optimize for. How can I do this in Delphi?
Current code
Currently, I have written my code assuming that the if statement's condition equalling true is the best thing to optimise for:
if LikelyCondition then
HandleLikelyCondition
else
HandleUnlikelyCondition
end;
or
if LikelyCondition then Exit;
HandleUnlikelyCondition;
In a test just now using the first of these two examples, I get a 50% extra performance boost restructuring my if statements like this, ie assuming the if statement's condition is true. Perhaps another way of phrasing the question is, is this the best I can do?
If you have not encountered branch misprediction before, this epic answer is an illuminating read.
There is nothing in the language or compiler that allows you to supply hints for branch prediction. And in any case, modern architectures would ignore those hints even if the compiler emitted object code that contained hints.
My delphi application runs scripts using JvInterpreter (from the Jedi project).
A feature I use is runtime evaluation of expressions.
Script Example:
[...]
ShowMessage(X_SomeName);
[...]
JvInterpreter doesn't know X_SomeName.
When X_SomeName's value is required the scripter calls its OnGetValue-callback.
This points to a function I handle. There I lookup X_SomeName's value and return it.
Then JvInterpreter calls ShowMessage with the value I provided.
Now I consider switching to DelphiWebScript since it has a proper debug-interface and should also be faster than JvInterpreter.
Problem: I didn't find any obvious way to implement what JvInterpreter does with its OnGetValue/OnSetValue functions, though.
X_SomeName should be considered (and actually is, most of the time) a variable which is handled by the host application.
Any Ideas?
Thanks!
You can do that through the language extension mechanism, which has a FindUnknownName method that allows to register symbols on the spot.
It is used in the asm lib module demo, and you can also check the new "AutoExternalValues" test case in ULanguageExtensionTests, which should be closer to what you're after.
I found this directive declared in Controls.pas (and also in other units) and I'll be glad to know what does it mean.
{$C PRELOAD}
As far as I know $C means assertions control but what is the PRELOAD keyword ? Is it something like "assert me at preloading time" ?
I found this in Delphi 2009
Thank you
The $C directive is called Code segment attribute and in conjuntion with the keywords MOVEABLE, FIXED, DEMANDLOAD, PRELOAD, DISCARDABLE, PERMANENT changues the attributes of a code segment.
{$C MOVEABLE DEMANDLOAD DISCARDABLE} // this is setting Code Segment Attribute.
if you use the $C directive with a + or - you are using enabling or disabling the generation of code for assertions.
example :
{$C+} { Assertions - On }
{$C+} and {$C-} are for assertions. {$C PRELOAD} is a carryover from 16-bit programming, where it preloaded the unit's code segment into memory immediately at runtime instead of waiting for the segment to be accessed first. That became unnecessary in Delphi 2 when 32-bit programming came around, so I don't know why the VCL source is still using it.
I'm trying to get rid of some hints(*) the Delphi compiler emits. Browsing through the ToolsAPI I see a IOTAToolsFilter that looks like it might help me accomplish this through it's Notifier, but I'm not sure how to invoke this (through what xxxServices I can access the filter).
Can anyone tell me if I´m on the right track here? Thanks!
(*) In particular, H2365 about overridden methods not matching the case of the parent. Not so nice when you have about 5 million lines of active code with a slightly different code convention than Embarcadero's. We've been working without hints for months now, and we kinda miss 'm. :-)
Even if you could query BorlandIDEServices for IOTAToolsFilter, that interface isn't going to help you do what you're asking. That interface was introduced as part of a mechanism for adding additional build tools (compilers, etc.) to the IDE (before the IDE used MSBuild). It allowed you to write a custom "filter" to handle output from a particular build tool, but it would not let you apply a filter to one of the built-in tools (like the delphi compiler).
The reason the Supports(BorlandIDEServices, IOTAToolsFilter, OTAToolsFilter) call fails in Delphi2010 is that once MSBuild support was added to the IDE, the old way of adding build tools to the IDE was disabled, and the BorlandIDEServices interface no longer supported IOTAToolsFilter.
The declaration of IOTAToolsFilter should probably have been marked deprecated in ToolsAPI.pas (or least it should have been mentioned in the source code comment that it is no longer supported).
As far as your desire to filter a particular hint, I'm not aware of a way to do that via the ToolsAPI. It seems like a reasonable thing that can be added to IOTAMessageServices (the ability to enumerate, filter, and possibly change the messages in the IDE's Message View). I would enter a request in QualityCentral for that.
Also, please vote for QC #35774 (http://qc.embarcadero.com/wc/qcmain.aspx?d=35774), as if that were implemented, you would not need to use the ToolsAPI for this sort of thing.
According to http://docwiki.embarcadero.com/RADStudio/en/Obtaining_Tools_API_Services it should be possible to access it directly using BorlandIDEServices, eg:
var
OTAToolsFilter: IOTAToolsFilter;
begin
if Supports(BorlandIDEServices, IOTAToolsFilter, OTAToolsFilter) then
ShowMessage('supports IOTAToolsFilter')
else
ShowMessage('IOTAToolsFilter NOT supported');
end;
However this doesn't return the desired interface in Delphi 2010 (you'll get the not supported message), so there's either an error in the documentation, or an error in BorlandIDEServices not returning the correct interface.
I am using Delphi 2007 with all patches and updates.
I have a file which is used by two different projects. In that file is a procedure, which I will simplify as follows:
procedure MyProcedure;
const
UniqueValue: integer = 0;
begin
//some code
Inc(UniqueValue);
//some more code
end;
The Inc() command should fail, because you cannot assign to a constant. In one project, I get an error to that effect (I'll call that project "Accurate"). In the other project, I don't get any errors (I'll call it "Bogus"). I also don't get any warnings. I can't figure out why the compiler lets this incorrect code through.
Here's what I've tried in project Bogus:
1 - Introduce an obvious error, like typing "slkdjflskdjf" in the middle of a line
Result: I get an error,which proves that it is really trying to compile this file.
2 - Delete the .DCU, and rebuild the project
Result: The .DCU is re-generated, again proving that the project is truly compiling this erroneous code.
Does anyone have thoughts on why this behavior would occur? And more specifically, why it would occur in one project but not another? Is there some obscure compiler option to permit assigning to constants?
One final note: Both projects are converted from Delphi 5. Under Delphi 5 with similar code, they both compile fine.
Edit: Thanks for all your help. After changing the assignable typed constants directive, I can get consistent behavior across both projects. I learned something new today...
There is an option for this called "Assignable typed constants" in Compiler Options. It can also be enabled with "{$J+}" in your code.
Could it be that those projects differ in the setting of the $J compiler directive?
I'd suggest to check the Project Options, section Compiler and see if you can spot any differences. Maybe you have option Assignable typed constants enabled in the Bogus project.
As others have said it's almost certainly the {$J+} directive.
If you have a diff tool you can compare the project files to see how they differ - they're just text files - to solve similar problems in the future.