Boom! Fooled by your own compiler: https://quality.embarcadero.com/browse/RSP-11184

Boom! Fooled by your own compiler: https://quality.embarcadero.com/browse/RSP-11184

Now wouldn't it be nice if the compiler would raise a W1035 Return value of function might be undefined for managed types?

I think that issue has been brought up "recently" (not listing the dozen duplicates of that issue) 
http://qc.embarcadero.com/wc/qcmain.aspx?d=894


Oh, btw, FixInsight finds these errors! ;)

Comments

  1. I remember it during the Delphi 2 beta.

    ReplyDelete
  2. Yes this is highly annoying. How does it work on mobile, do you get the warning at all?

    ReplyDelete
  3. Yet another shoutout to FixInsight / Roman Yankovsky. Btw, this missing warning is a personal favourite bugbear of mine, and I actually suggested it as something to add to FI :)

    Marco Cantù Any chance of getting fixes like this into Delphi? Things like this are quality improvements...

    ReplyDelete
  4. Yep, almost got bitten by this bug recently, but fixinsight found it for me :)

    ReplyDelete
  5. I agree this is important, but I think I already asked to get this fixed in the past, and someone told me it was more tricky than it seems (but probably not as much as the issues around returning interfaces ;-)

    I'll definitely ask again, and higher the bug priority.

    ReplyDelete
  6. Expect a lot more of this bugs (f.i. REST.Json.TJson.Format)

    ReplyDelete
  7. Allow me to explain this issue a little more. This isn't an excuse or justification because I agree that we should be handling this better.

    When the compiler is allocating space for local variables and even where the Result variable will live, it keeps track of what variables are "live". A "live" variable is one that has been assigned a value or is a value passed in a parameter. Take a simple function such as: function Add(L, R: Integer): Integer; upon entry to the function, L and R are "live". They are assumed to contain valid values (you can't get to this function without providing some value). The "Result" variable is "dead" because no value has been assigned to it yet. If you don't explicitly add code to set Result's value, the compiler knows that when it is finalizing the function, Result is still "dead" so it reports it as a warning.

    Now lets look at a function which returns a managed type.
    The "Result" variable isn't allocated by the function being called, rather it is turned into an implicit "var" parameter that the caller provides, which can be a compiler generated temporary variable. Now that Result is a "parameter" instead of merely a value, it enters the function as "live". This is because the caller is responsible for allocating where the value should live. Since the code generator's live-variable analysis phase doesn't distinguish between an implicit "var" Result parameter and any other passed in parameters, it merely assumes it is "live". To the backend, a function returning a managed type looks like a procedure with a "var" parameter. IOW, it doesn't have a "result".

    Let's extend this to managed local variables and why don't they warn the use of use before assignment? The same logic is in play as well. In this case, on entry the local variables are allocated onto the stack or even a register for some simpler local variable types like an Integer. All those local variables are now "dead", including the variables that are managed types. Further in while building the entry-point prologue code, it notices that there are managed types that need to be tracked for finalization purposes. They also need to be set to an initial known value (this is nil or 0) so that the epilogue knows which variables need to be finalized and which ones don't have any value. It is this initialization process that now marks the variables as "live". To the backend, they're merely stack locations of a certain size. For this reason, they are treated just like any other simple type. It is the front-end that places the extra "managed" semantics onto those variables.

    Trying to teach the backend more about the semantics of these variables isn't easy... especially when we are dealing with multiple backends now. There is the traditional x86 backend, the newer x64 backend, and now the LLVM-based backend. Each one goes through a slightly different node-to-IR (intermediate representation) pass which is then handed off to the backend in order to perform further optimizations and generate the actual machine code.

    ReplyDelete
  8. Allen Bauer Now this one I'll agree ain't trivial ;) But yeah, it's definitely something which would catch a lot of potential bugs so would be great to get the warning working in these cases.

    ReplyDelete
  9. And this, ladies and gentlemen, is what they call technical debt :(

    Thanks for the explanation, Allen.

    ReplyDelete

Post a Comment