Friday, February 15, 2019

Be carefull with inline vars.

The new inline var is a great feature. You can save time and your code looks more cleaner.

But are there any problems with inline vars and the auto-type functionality?

Let's take a look at a small example.

Procedure Foo
var
  i : int32;
  x : uint32;
begin
  x := 0;
  for i:=0 to x-1 do
    Bar(i); // Bar would never be called.     
end;

Procedure Foo;
var
  i : int32;
begin
  for i:=0 to MyList.Count - 1 do
    DoSomeThing(MyList[i]); // Should work as expected
end;

Procedure Foo;
begin
  for var I := 0 to MyList.Count - 1 do
    DoSimeThing(MyList[i]); // Are you sure this is working?
end;

At this point you are unable to decide!!! You have to take a look at the function result of Count. if the result is an uint32 this is not working, because the compiler takes an uint32 for i and the for is running "forever" ( from 0 to $FFFFFFFF ).

If your implementation of this list auto-creates elements on read access you have a problem...

Edit:
Sorry about the late comments - I did not know I have to approve them.

7 comments:

  1. So it's type inference that is the problem here, not inline vars. (Not that inline vars don't have their own problems.)
    If you declare it like this:

    for var i: Int32 = 0 to MyList.Count -1 do

    it should work as expected.

    ReplyDelete
  2. Ok, I really had to read this a lot until I got your problem and why we don't have this problem (we're using the inline var alot).
    1. All of our lists are TList descendants (or TObjectList), so Count is an Integer and not unsigned int
    2. We never use it in this way, because we have only generic lists, thus the last loop looks like that

    for var Element in MyList do
    DoSomething(Element);

    and creating objects on read access sounds not really like a good problem solver (aka it's bad code).

    ReplyDelete
  3. When using inline variables, you don't need to rely on type inference, you can specify the tye explicitly, eg:

    Procedure Foo;
    begin
    for var I: int32 := 0 to MyList.Count - 1 do
    DoSimeThing(MyList[i]);
    end;

    ReplyDelete
  4. First: Isn't that why all the Count properties are Integer and not some unsigned typed like Word or UInt32?

    Also: Your code raises and integer overflow for me. If an unsigned integer variable is already zero, you cannot subtract any further 😉

    ReplyDelete
  5. So the real issue is that Count returns a UInt32. It shouldn't. Did this actually happen to you?

    ReplyDelete
    Replies
    1. Yes, of course I simplified the example. I had a function returning an uint32 in my game engine and it took me 3h to find the error - that's why I wrote this blog post.

      Delete
  6. You might also be willing to have a look at https://www.linkedin.com/pulse/inline-variables-delphi-rio-ivelin-nikolaev/

    ReplyDelete