Is Result variable defined from first line in a function? - delphi

I need a clarification of this case.
According my tests the Result variable is defined to:
Boolean=False, Integer=0, String='', Object=nil etc from the first line.
But I have never seen an official reference for this.
It also make sense as this gives the hint.
[DCC Warning] Unit1.pas(35): H2077 Value assigned to 'TForm1.Test' never used
function TForm1.Test: Boolean;
begin
Result := False;
// Some arbitrary code here
Result := True;
end;
But what happens if I comment out the first line and there is an exception somewhere before last line? Is Result = False ?
If Result is undefined this means that I always have to start every function by defining Result in case of exception later. And this make no sense for me.

As stated by the official Delphi documentation, the result is either:
CPU register(s) (AL / AX / EAX / RAX / EAX:EDX) for ordinal values and elements contained in a register;
FPU register (st(0) / XMM1);
An additional variable passed as a latest parameter.
The general rule is that no result value is defined by default. You'll have to set it. The compiler will warn you about any missing result set.
For a string, dynamic array, method pointer, or variant result, the
effects are the same as if the function result were declared as an
additional var parameter following the declared parameters. In other
words, the caller passes an additional 32-bit pointer that points to a
variable in which to return the function result.
To be accurate, the var parameter is not only for managed types, but only for record or object results, which are allocated on the stack before calling, so are subject to the same behavior.
That is, for instance, if your result is a string, it will passed as an additional var parameter. So it will contain by default the value before the call. It will be '' at first, then if you call the function several times, it will contain the previous value.
function GetString: string;
// is compiled as procedure GetString(var result: string);
begin
if result='' then
result := 'test' else
writeln('result=',result);
end;
function GetRaise: string;
// is compiled as procedure GetRaise(var result: string);
begin
result := 'toto';
raise Exception.Create('Problem');
end;
var s: string;
begin
// here s=''
s := GetString; // called as GetString(s);
// here s='test'
s := GetString; // called as GetString(s);
// will write 'result=test' on the console
try
s := GetRaise; // called as GetRaise(s);
finally
// here s='toto'
end;
end;
So my advices are:
Fix all compiler warning about unset result;
Do not assume that a result string is initialized to '' (it may be at first, but not at 2nd call) - this is passed as a var parameter, not as a out parameter;
Any exception will be processed as usual, that is, the running flow will jump to the next finally or except block - but if you have a result transmitted as a var parameter, and something has been already assigned to result, the value will be set;
It is not because in most cases, an unset result ordinal value (e.g. a boolean) is 0 (because EAX=0 in asm code just before the return), that it will be next time (I've seen random issues on customer side because of such unset result variables: it works most time, then sometimes code fails...);
You can use the exit() syntax to return a value, on newer versions of Delphi.

You state:
If Result is undefined this means that I always have to start every function by defining Result in case of exception later.
You are concerned that the return value of a function is undefined if the function raises an exception. But that should not matter. Consider the following code:
x := fn();
If the body of the function fn raises an exception then, back at the call site, x should not be assigned to. Logically the one-liner above can be thought of as a two-liner:
call fn()
assign return value to x
If an exception is raised in line 1 then line 2 never happens and x should never be assigned to.
So, if an exception is raised before you have assigned to Result then that is simply not a problem because a function's return value should never be used if the function raises an exception.
What you should in fact be concerned about is a related issue. What if you assign to Result and then an exception is raised? Is it possible for the value you assigned to Result to propagate outside of the function? Sadly the answer is yes.
For many result types (e.g. Integer, Boolean etc.) the value you assign to Result does not propagate outside the function if that function raises an exception. So far, so good.
But for some result types (strings, dynamic arrays, interface references, variants etc.) there is an implementation detail that complicates matters. The return value is passed to the function as a var parameter. And it turns out that you can initialise the return value from outside the function. Like this:
s := 'my string';
s := fn();
When the body of fn begins execution, Result has the value 'my string'. It is as if fn is declared like this:
procedure fn(var Result: string);
And this means that you can assign to the Result variable and see modifications at the call site, even if your function subsequently raises an exception. There is no clean way to work around it. The best you can do is to assign to a local variable in the function and only assign Result as the final act of the function.
function fn: string;
var
s: string;
begin
s := ...
... blah blah, maybe raise exception
Result := s;
end;
The lack of a C style return statement is felt strongly here.
It is surprising hard to state accurately which type of result variables will be susceptible to the problem described above. Initially I thought that the problem just affected managed types. But Arnaud states in a comment that records and objects are affected too. Well, that is true if the record or object is stack allocated. If it is a global variable, or heap allocated (e.g. member of a class) then the compiler treats it differently. For heap allocated records, an implicit stack allocated variable is used to return the function result. Only when the function returns is this copied to the heap allocated variable. So the value to which you assign the function result variable at the call site affects the semantics of the function itself!
In my opinion this is all a very clear illustration of why it was a dreadful mistake, in the language design, for function return values to have var semantics as opposed to having out semantics.

No, Result has no (guaranteed) default value. It is undefined unless you give it a value. This is implied by the documentation, which states
If the function exits without assigning a value to Result or the
function name, then the function's return value is undefined.
I just tried
function test: integer;
begin
ShowMessage(IntToStr(result));
end;
and got a message with the text 35531136.

Related

How can I use a variable used in another unit within a different method?

I have a method that requires a local variable processed in another unit, but I don't know how to reference it correctly. Depending on the result of the if condition, the variable obtained changes.
function Connecting.SendSomething(Var1 : Boolean;Var2 : boolean) : integer;
begin
Result := 0;
if 2+2 = 4 then
Result := 1
else
Result := 2;
...
end;
Result is only mentioned there. Nowhere else in the unit code was it used, so I'm guessing that's the integer variable. The point is that I want to use Result inside an if conditional in a different unit's method.
Please bear in mind that the secondary unit is correctly included on top, the names of the variables have been changed to put them here and the code is simplified as to omit sensitive data.
Over there I'm using Connecting.SendSomething() inside a basic button's method with (TRUE, TRUE) and (TRUE, FALSE) values as parameters, I just don't know how to "reel in" Result.
Result refers to the integer that SendSomething() returns:
Procedures and Functions (Delphi): Function Declarations
The statement block of the function is governed by the same rules that apply to procedures. Within the statement block, you can use variables and other identifiers declared in the localDeclarations part of the function, parameter names from the parameter list, and any identifiers within whose scope the function declaration falls. In addition, the function name itself acts as a special variable that holds the return value of the function, as does the predefined variable Result.
As long as extended syntax is enabled ({$X+}), Result is implicitly declared in every function. Do not try to redeclare it.
So, only SendSomething() and its immediate caller can access the Result value, eg
if Connecting.SendSomething(true, true) = 1 then
Unless SendSomething() explicitly passes its Result as a parameter to another procedure/function, eg:
function Connecting.SendSomething(Var1 : Boolean;Var2 : boolean) : integer;
begin
Result := 0;
if 2+2 = 4 then
Result := 1
else
Result := 2;
...
DoSomethingWith(Result);
...
end;

Can't use default parametr values with comparer in Spring4D

I'm not sure if this is some generic problem or it's because of Spring4D implementation, but I can't use default parameter values for creating comparer.
type
TMyClass = class
class function MyComparer(AParam: Boolean = False): IComparer<TMyClass>;
end;
implementation
class function TMyClass.MyComparer(AParam: Boolean): IComparer<TMyClass>;
begin
Result := TComparer<TMyClass>.Construct(
function (const L, R: TMyClass): Integer
begin
Result := 0;
end);
end;
When I create a list without the specified parameter, I get an error message about missing parameters.
TCollections.CreateSortedObjectList<TMyClass>(TMyClass.MyComparer);
E2035 Not enough actual parameters
However without any parameters or with all parameters specified it works. Is there any reason why I can't do that?
I don't have Spring4D to hand to test, but I'm guessing that what's happening is something similar to this where Delphi's syntax rules allowing omission of parentheses when executing a method which takes no parameters introduces ambiguity. Here, where you do :
TCollections.CreateSortedObjectList<TMyClass>(TMyClass.MyComparer);
...the compiler can't be sure if you mean to pass the method MyComparer directly (to the overload of CreateSortedObjectList which takes a method pointer type TComparison<T>) or whether you mean to execute the method and pass the return value. In this case you want to do the latter, so you can be explicit for the compiler and include the parentheses
TCollections.CreateSortedObjectList<TMyClass>(TMyClass.MyComparer());

Is it risky to use exceptions like this?

I have the following function
function calculate(s:string):integer;
begin
try
strtoint(s);
do_something;
except
result:=-1;
end;
end;
Is there any consequence of using exceptions like this?
If the intent is to check the validity of the input string then there are better methods than an exception handler. To convert from string to integer and detect invalid input use TryStrToInt:
var
value: Integer;
...
if TryStrToInt(s, Value) then
Result := ...
else
Result := -1;
This function returns True if the string can be converted to an integer, and if so that integer is returned via the second argument, an out parameter. Otherwise the function returns False. In your code you ignore the converted integer value but it is available if you need it.
This is to be preferred over an exception handler, in my view, because it is more explicit and concise. Not to mention avoiding the performance overhead of raising and catching an exception.
Of course, your code will detect failures other than an invalid string. One potential source for errors is your do_something procedure. If you really want to swallow any exceptions raised by that procedure then an exception handler is needed. However, I rather suspect that it is more likely that your original intent was to catch invalid string input only. In which case your code was incorrect.
Another source of errors is if s happens to be an invalid string variable. But if that happens then the entire basis of your program is pulled from under you and I personally don't think you should expect to handle such scenarios gracefully.
Part of the problem with advising you is that your code is probably not truly representative. For instance it does not appear to set the return value if the input is valid. And we don't know for sure what the intent of your exception handler is. I'm guessing that you mean to trap errors in the call to StrToInt but I cannot tell that for sure.
It very much depends on the specification of the Calculate method.
If the specification stipulates that -1 is returned in the event of any and all exceptions then this is fine and (formatting issues aside) does the job as concisely as possible.
If the contract is only that the method returns -1 for invalid (non-numeric) strings then there is a potential issue that DoSomething itself might raise other exceptions (including possibly unrelated conversion errors) which will then be incorrectly handled, to yield -1 from the Calculate method (making it impossible to distinguish between invalid values of s and other errors.
In the latter case it would be more correct to handle the specific contract with respect to the string parameter in a way that avoids relying on an exception (since an error in this case is not "exceptional" but a specific input case to be handled) and allow exceptions from DoSomething to be handled by the caller, if appropriate.
To test for a valid numeric string you can use TryStrToInt and only call DoSomething if s is determined to be a valid integer. Assuming that the result of the Calculate function is -1 for invalid inputs and the result of the DoSomething operation on the numeric value of s otherwise:
function calculate(s: String):integer;
begin
if NOT TryStrToInt(s, result) then
result := -1;
else
result := DoSomething(result);
end;
The use of result in this way is a matter of personal preference. Some would argue that you should not use result for working storage like this and that you should use a separate local variable to hold the integer value of s, named accordingly:
function calculate(s: String): Integer;
var
inValue: Integer;
begin
if NOT TryStrToInt(s, inValue) then
result := -1;
else
result := DoSomething(inValue);
end;
FWIW: I personally would favour the latter. :)
Why don't you simply go for something like this:
function calculate(s:string):integer;
begin
result:=-1;
strtoint(s);
do_something;
result:=.... <--- whatever value you want to return or maybe result:=do_something as Deltics shows
end;

Initialise string function result?

I've just been debugging a problem with a function that returns a string that has got me worried. I've always assumed that the implicit Result variable for functions that return a string would be empty at the start of the function call, but the following (simplified) code produced an unexpected result:
function TMyObject.GenerateInfo: string;
procedure AppendInfo(const AppendStr: string);
begin
if(Result > '') then
Result := Result + #13;
Result := Result + AppendStr;
end;
begin
if(ACondition) then
AppendInfo('Some Text');
end;
Calling this function multiple times resulted in:
"Some Text"
the first time,
"Some Text"
"Some Text"
the second time,
"Some Text"
"Some Text"
"Some Text"
the third time, etc.
To fix it I had to initialise the Result:
begin
Result := '';
if(ACondition) then
AppendInfo('Some Text');
end;
Is it necessary to initialise a string function result? Why (technically)? Why does the compiler not emit a warning "W1035 Return value of function 'xxx' might be undefined" for string functions? Do I need to go through all my code to make sure a value is set as it is not reliable to expect an empty string from a function if the result is not explicitly set?
I've tested this in a new test application and the result is the same.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
S := GenerateInfo;
ShowMessage(S); // 5 lines!
end;
This is not a bug, but "feature":
For a string, dynamic array, method
pointer, or variant result, the
effects are the same as if the
function result were declared as an
additional var parameter following the
declared parameters. In other words,
the caller passes an additional 32-bit
pointer that points to a variable in
which to return the function result.
I.e. your
function TMyObject.GenerateInfo: string;
Is really this:
procedure TMyObject.GenerateInfo(var Result: string);
Note "var" prefix (not "out" as you may expect!).
This is SUCH un-intuitive, so it leads to all kind of problems in the code. Code in question - just one example of results of this feature.
See and vote for this request.
We've run into this before, I think maybe as far back as Delphi 6 or 7. Yes, even though the compiler doesn't bother to give you a warning, you do need to initialize your string Result variables, for precisely the reason you ran into. The string variable is getting initialized -- it doesn't start as a garbage reference -- but it doesn't seem to get reinitialized when you expect it to.
As for why it happens... not sure. It's a bug, so it doesn't necessarily need a reason. We only saw it happen when we called the function repeatedly in a loop; if we called it outside a loop, it worked as expected. It looked like the caller was allocating space for the Result variable (and reusing it when it called the same function repeatedly, thus causing the bug), rather than the function allocating its own string (and allocating a new one on each call).
If you were using short strings, then the caller does allocate the buffer -- that's long-standing behavior for large value types. But that doesn't make sense for AnsiString. Maybe the compiler team just forgot to change the semantics when they first implemented long strings in Delphi 2.
This is not a Bug. By definition no variable inside function is initialized, including Result.
So your Result is undefind on first call, and can hold anything. How it is implemented in compiler is irrelevant, and you can have different results in different compilers.
It seems like your function should be simplified like this:
function TMyObject.GenerateInfo: string;
begin
if(ACondition) then
Result := 'Some Text'
else
Result := '';
end;
You typically don't want to use Result on the right side of an assignment in a function.
Anyway, strictly for illustrative purposes, you could also do this, though not recommended:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
begin
S := ''; // Clear before you call
S := GenerateInfo;
end;
ShowMessage(S); // 5 lines!
end;
This looks like a bug in D2007. I just tested it in Delphi 2010 and got the expected behavior. (1 line instead of 5.)
If you think that some automatic management of strings are made to make your life easier, you're only partly right. All such things are also done to make string logic consistent and side-effects free.
In plenty of places there are string passed by reference, passed by value, but all these lines are expecting VALID strings in whose memory-management counter is some valid, not a garbage value. So in order to keep strings valid the only thing for sure is that they should be initialized when they firstly introduced. For example, for any local variable string this is a necessity since this is the place a string is introduced. All other string usage including function(): string (that actually procedure(var Result: string) as Alexander correctly pointed out) just expects valid strings on the stack, not initialized. And validness here comes from the fact that (var Result: string) construction says that "I'm waiting for a valid variable that definetly was introduced before". UPDATE: Because of that the actual contents of Result is unexpected, but due to the same logic, if it's the only call to this function that has a local variable at the left, the emptiness of the string in this case is guaranteed.
Alex's answer is nearly always right and it answers why I was seeing the strange behaviour that I was, but it isn't the whole story.
The following, compiled without optimisation, produces the expected result of sTemp being an empty string. If you swap the function out for the procedure call you get a different result.
There seems to be a different rule for the actual program unit.
Admittedly this is a corner case.
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
function PointlessFunction: string;
begin
end;
procedure PointlessProcedure(var AString: string);
begin
end;
var
sTemp: string;
begin
sTemp := '1234';
sTemp := PointlessFunction;
//PointlessProcedure(sTemp);
WriteLn('Result:' + sTemp);
ReadLn;
end.

How to know what type is a var?

TypeInfo(Type) returns the info about the specified type, is there any way to know the typeinfo of a var?
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= TypeInfo(S);
Info:= TypeInfo(Instance);
Info:= TypeInfo(Obj);
end
This code returns:
[DCC Error] Unit1.pas(354): E2133 TYPEINFO standard function expects a type identifier
I know a non instantiated var is only a pointer address.
At compile time, the compiler parses and do the type safety check.
At run time, is there any way to know a little more about a var, only passing its address?
No.
First, there's no such thing as a "non-instantiated variable." You instantiate it by the mere act of typing its name and type into your source file.
Second, you already know all there is to know about a variable by looking at it in your source code. The variable ceases to exist once your program is compiled. After that, it's all just bits.
A pointer only has a type at compile time. At run time, everything that can be done to that address has already been determined. The compiler checks for that, as you already noted. Checking the type of a variable at run time is only useful in languages where a variable's type could change, as in dynamic languages. The closest Delphi comes to that is with its Variant type. The type of the variable is always Variant, but you can store many types of values in it. To find out what it holds, you can use the VarType function.
Any time you could want to use TypeInfo to get the type information of the type associated with a variable, you can also directly name the type you're interested in; if the variable is in scope, then you can go find its declaration and use the declared type in your call to TypeInfo.
If you want to pass an arbitrary address to a function and have that function discover the type information for itself, you're out of luck. You will instead need to pass the PTypeInfo value as an additional parameter. That's what all the built-in Delphi functions do. For example, when you call New on a pointer variable, the compiler inserts an additional parameter that holds the PTypeInfo value for the type you're allocating. When you call SetLength on a dynamic array, the compiler inserts a PTypeInfo value for the array type.
The answer that you gave suggests that you're looking for something other than what you asked for. Given your question, I thought you were looking for a hypothetical function that could satisfy this code:
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= GetVariableTypeInfo(#S);
Assert(Info = TypeInfo(string));
Info:= GetVariableTypeInfo(#Instance);
Assert(Info = TypeInfo(IObjectType));
Info:= GetVariableTypeInfo(#Obj);
Assert(Info = TypeInfo(TDBGrid));
end;
Let's use the IsClass and IsObject functions from the JCL to build that function:
function GetVariableTypeInfo(pvar: Pointer): PTypeInfo;
begin
if not Assigned(pvar) then
Result := nil
else if IsClass(PPointer(pvar)^) then
Result := PClass(pvar).ClassInfo
else if IsObject(PPointer(pvar)^) then
Result := PObject(pvar).ClassInfo
else
raise EUnknownResult.Create;
end;
It obviously won't work for S or Instance above, but let's see what happens with Obj:
Info := GetVariableTypeInfo(#Obj);
That should give an access violation. Obj has no value, so IsClass and IsObject both will be reading an unspecified memory address, probably not one that belongs to your process. You asked for a routine that would use a variable's address as its input, but the mere address isn't enough.
Now let's take a closer look at how IsClass and IsObject really behave. Those functions take an arbitrary value and check whether the value looks like it might be a value of the given kind, either object (instance) or class. Use it like this:
// This code will yield no assertion failures.
var
p: Pointer;
o: TObject;
a: array of Integer;
begin
p := TDBGrid;
Assert(IsClass(p));
p := TForm.Create(nil);
Assert(IsObject(p));
// So far, so good. Works just as expected.
// Now things get interesting:
Pointer(a) := p;
Assert(IsObject(a));
Pointer(a) := nil;
// A dynamic array is an object? Hmm.
o := nil;
try
IsObject(o);
Assert(False);
except
on e: TObject do
Assert(e is EAccessViolation);
end;
// The variable is clearly a TObject, but since it
// doesn't hold a reference to an object, IsObject
// can't check whether its class field looks like
// a valid class reference.
end;
Notice that the functions tell you nothing about the variables, only about the values they hold. I wouldn't really consider those functions, then, to answer the question of how to get type information about a variable.
Furthermore, you said that all you know about the variable is its address. The functions you found do not take the address of a variable. They take the value of a variable. Here's a demonstration:
var
c: TClass;
begin
c := TDBGrid;
Assert(IsClass(c));
Assert(not IsClass(#c)); // Address of variable
Assert(IsObject(#c)); // Address of variable is an object?
end;
You might object to how I'm abusing these functions by passing what's obviously garbage into them. But I think that's the only way it makes sense to talk about this topic. If you know you'll never have garbage values, then you don't need the function you're asking for anyway because you already know enough about your program to use real types for your variables.
Overall, you're asking the wrong question. Instead of asking how you determine the type of a variable or the type of a value in memory, you should be asking how you got yourself into the position where you don't already know the types of your variables and your data.
With generics, it is now possible to get the type info without specifying it.
Certain users indicated the following code doesn't compile without errors.
As of Delphi 10 Seattle, version 23.0.20618.2753, it compiles without errors, as seen below in the screenshot.
program TypeInfos;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.TypInfo;
type
TTypeInfo = class
class procedure ShowTypeInfo<T>(const X: T);
end;
{ TTypeInfo }
class procedure TTypeInfo.ShowTypeInfo<T>(const X: T);
var
LTypeInfo: PTypeInfo;
begin
LTypeInfo := TypeInfo(T);
WriteLn(LTypeInfo.Name);
end;
var
L: Exception;
B: Boolean;
begin
// Console output
TTypeInfo.ShowTypeInfo(L); // Exception
TTypeInfo.ShowTypeInfo(B); // Boolean
end.
Not that I know of. You can get RTTI (Run Time Type Information) on published properties of a class, but not for "normal" variables like strings and integers and so forth. The information is simply not there.
Besides, the only way you could pass a var without passing a type is to use either a generic TObject parameter, a generic type (D2008, as in ), or as an untyped parameter. I can't think of another way of passing it that would even compile.

Resources