scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

MSIL control flow requires empty stack after try block #312

Closed scabug closed 13 years ago

scabug commented 16 years ago

Scala code:

class DynamicVariable {
  def withValue: Int = {
    try { 10 } finally {}
  }
}

Generated MSIL:

        .method public virtual hidebysig instance int32 'withValue'() cil managed
        {
            .maxstack   2
            .locals init (
                class [mscorlib]'System.Exception' 'exc1')
            .try {
                .line       5
                IL_0000:    ldc.i4.s    10
                IL_0002:    leave       IL_000f
            }
            catch [mscorlib]'System.Exception' {
                IL_0007:    stloc.0
                IL_0008:    ldloc.0
                IL_0009:    throw
                IL_000a:    leave       IL_000f
            }
            IL_000f:    ret
        }

Output from PEVerify:

Microsoft (R) .NET Framework PE Verifier.  Version  2.0.50727.42
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: [D:\projects\SketcherEngine\SketcherEngine\test.dll : test.DynamicVariable::withValue][offset 0x0000000F] Return value missing on the stack.
1 Error Verifying test.dll

From the spec CLR architecture spec says in section 9.5, bullet point (4.b):

leave from the body of a try or catch (in this case the destination of the leave must have an empty evaluation stack and the leave instruction has the side-effect of emptying the evaluation stack).

To fix this, we must save the result from the stack in a local variable, then reload when the ret is made:

Correct MSIL:

        .method public virtual hidebysig instance int32 'withValue'() cil managed
        {
            .maxstack   2
            .locals init (
                class [mscorlib]'System.Exception' 'exc1', int32 x)
            .try {
                .line       5
                IL_0000:    ldc.i4.s    10
                stloc.1
                IL_0002:    leave       IL_000f
            }
            catch [mscorlib]'System.Exception' {
                IL_0007:    stloc.0
                IL_0008:    ldloc.0
                IL_0009:    throw
                IL_000a:    leave       IL_000f
            }
            IL_000f:    
               ldloc.1
               ret
        }

I guess this means we have to get icode to stop leaving things on the stack....

scabug commented 16 years ago

Imported From: https://issues.scala-lang.org/browse/SI-312?orig=1 Reporter: @mcdirmid

scabug commented 16 years ago

@dragos said: There is support for that already. The uncurry phase is lifting try-catches to methods when there are values on the stack /before/ entering the try-catch. An exception inside the try-catch would clean the stack, including any values pushed on the stack before the block.

The only thing that needs to be done is relax the condition under which this lifting is performed.

scabug commented 16 years ago

Sebastian Hack (shack) said: Fixed in r13762.

Try expressions that return a value are now generally transformed to store the resulting value in a local variable.

scabug commented 15 years ago

@odersky said: Milestone 2.7.0 deleted