-
Notifications
You must be signed in to change notification settings - Fork 821
Friend assemblies ("InternalsVisibleTo") can leak via automatic inlining cause runtime error #7422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I think this is a duplicate of #7110 |
@dsyme This talks about a runtime error, #7110 about a compile time error. #7110 is arguably by design but this looks like a faulty optimization by the compiler (=bug). |
Ah yes.
As mentioned in #7110, the intention is that the compiler doesn't inline in this case. So there may be a bug here |
Sample of this bug... Clone the repository https://github.com/manofstick/Cistern.Linq Go to branch Load solution from Execute and get the following error at runtime:
To workaround, uncomment the |
As promised in fscheck/FsCheck#549 I made a simple example repository to reproduce this issue. It is constructed the same way as the scenario which caused this issue in FsCheck. When looking at the generated IL, it indeed emits the open assembly_A_optimized.referenceModulePublicA
open assembly_B_not_optimized.referenceModulePublicB
[<EntryPoint>]
let main _ =
makeExceptionA ()
makeExceptionAggressiveInliningA ()
makeExceptionNoInliningA ()
makeExceptionB ()
makeExceptionAggressiveInliningB ()
makeExceptionNoInliningB ()
0 compiles into (optimization on for using program): // Location: C:\git\inlined_internal\usage_optimized\bin\Debug\netcoreapp3.1\usage_optimized.dll
// Sequence point data from C:\git\inlined_internal\usage_optimized\bin\Debug\netcoreapp3.1\usage_optimized.pdb
.class public abstract sealed auto ansi
ProgramOptimized
extends [System.Runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags)
= (01 00 07 00 00 00 00 00 ) // ........
// int32(7) // 0x00000007
.method public static int32
main(
string[] _arg1
) cil managed
{
.entrypoint
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [6 5 - 6 22]
IL_0000: ldc.i4.0
IL_0001: brfalse.s IL_000b
IL_0003: ldnull
IL_0004: unbox.any [FSharp.Core]Microsoft.FSharp.Core.Unit
IL_0009: br.s IL_0011
IL_000b: newobj instance void [assembly_A_optimized]assembly_A_optimized.referenceModuleInternalA/TestExceptionA::.ctor()
IL_0010: throw
IL_0011: pop
// [7 5 - 7 40]
IL_0012: ldc.i4.0
IL_0013: brfalse.s IL_001d
IL_0015: ldnull
IL_0016: unbox.any [FSharp.Core]Microsoft.FSharp.Core.Unit
IL_001b: br.s IL_0023
IL_001d: newobj instance void [assembly_A_optimized]assembly_A_optimized.referenceModuleInternalA/TestExceptionA::.ctor()
IL_0022: throw
IL_0023: pop
// [8 5 - 8 29]
IL_0024: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_A_optimized]assembly_A_optimized.referenceModulePublicA::makeExceptionNoInliningA<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0029: pop
// [10 5 - 10 19]
IL_002a: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_002f: pop
// [11 5 - 11 37]
IL_0030: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionAggressiveInliningB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0035: pop
// [12 5 - 12 29]
IL_0036: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionNoInliningB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_003b: pop
// [14 5 - 14 6]
IL_003c: ldc.i4.0
IL_003d: ret
} // end of method ProgramOptimized::main
} // end of class ProgramOptimized and with optimization off for the using program, this compiles into: // Location: C:\git\inlined_internal\usage_not_optimized\bin\Debug\netcoreapp3.1\usage_not_optimized.dll
// Sequence point data from C:\git\inlined_internal\usage_not_optimized\bin\Debug\netcoreapp3.1\usage_not_optimized.pdb
.class public abstract sealed auto ansi
ProgramNotOptimized
extends [System.Runtime]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags)
= (01 00 07 00 00 00 00 00 ) // ........
// int32(7) // 0x00000007
.method public static int32
main(
string[] _arg1
) cil managed
{
.entrypoint
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.EntryPointAttribute::.ctor()
= (01 00 00 00 )
.maxstack 3
.locals init (
[0] string[] V_0
)
IL_0000: ldarg.0 // _arg1
IL_0001: stloc.0 // V_0
// [7 5 - 7 22]
IL_0002: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_A_optimized]assembly_A_optimized.referenceModulePublicA::makeExceptionA<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0007: pop
// [8 5 - 8 40]
IL_0008: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_A_optimized]assembly_A_optimized.referenceModulePublicA::makeExceptionAggressiveInliningA<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_000d: pop
// [9 5 - 9 32]
IL_000e: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_A_optimized]assembly_A_optimized.referenceModulePublicA::makeExceptionNoInliningA<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0013: pop
// [11 5 - 11 22]
IL_0014: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0019: pop
// [12 5 - 12 40]
IL_001a: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionAggressiveInliningB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_001f: pop
// [13 5 - 13 32]
IL_0020: call !!0/*class [FSharp.Core]Microsoft.FSharp.Core.Unit*/ [assembly_B_not_optimized]assembly_B_not_optimized.referenceModulePublicB::makeExceptionNoInliningB<class [FSharp.Core]Microsoft.FSharp.Core.Unit>()
IL_0025: pop
// [15 5 - 15 6]
IL_0026: ldc.i4.0
IL_0027: ret
} // end of method ProgramNotOptimized::main
} // end of class ProgramNotOptimized Thing to notice is that both the reference assembly and the 'using' program must be compiled with optimization on for this to occur. When the using program does not enable optimization all calls emit correct IL code. When the using program has optimization enabled only the function calls to the optimized reference assembly have inlined internal calls. Also I hope this helps. |
Assembly A exposed it's internal to Assembly B via the
InternalsVisibleTo
attribute. Assembly B provides a simple method to Assembly C, but because it's a simple method, it is inlined by the f# compiler into Assembly C. This results in Runtime error.Expected behavior
The FSharp compiler shouldn't inline methods that have calls to methods that have been exposed via InternalsVisibleTo.
Actual behavior
Runtime error.
Known workarounds
As per #5178, the use of
[<MethodImpl(MethodImplOptions.NoInlining)>]
stops the f# compiler from inling the method, but has the side effect that the JIT also stops inlining the call.Related information
The text was updated successfully, but these errors were encountered: