Skip to content
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

F# Interactive v14 backward incompatibility: Error FS0193 when calling inlined Array.init #486

Closed
gbaydin opened this issue Jun 6, 2015 · 2 comments
Labels
Milestone

Comments

@gbaydin
Copy link

gbaydin commented Jun 6, 2015

Hi. I have Visual Studio Community 2013 Update 4 and 2015 RC on my system. While I was experimenting with F# 4.0, I encountered a problem with the new F# Interactive (v14.0.22810.0).

I did my best to isolate the problem into a minimal example. Here are the steps to reproduce:

  1. Compile an F# library with the code
namespace Library1

module Test =
    let inline init1 n f = Array.init n f
    let init2 n f = Array.init n f

targeting FSharp.Core 4.3.1.0.

  1. Have a script file where you call Test.init1
#r "./bin/Debug/Library1.dll"
open Library1

let a = Test.init1 4 (fun _ -> 4.)

and run it within F# Interactive 14.0.22810.0.

You get:

error FS0193: The module/namespace 'ErrorStrings' from compilation unit 'FSharp.Core' did not contain the val '{MemberParentMangledName = null;
 MemberIsOverride = false;
 LogicalName = "InputMustBeNonNegativeString";
 TotalArgCount = 0;}'

whereas if you run it with F# Interactive 12.0.30815.0 it runs fine:

val a : float [] = [|4.0; 4.0; 4.0; 4.0|]

Here is some extra information:

  1. If you call Test.init2, which has the same type with Test.init1, with F# Interactive 14.0.22810.0, there is no error and it runs fine. So, there is something to do with inline.

  2. When you call Test.init1 from an F# console application

open Library1

[<EntryPoint>]
let main argv = 

    let a = Test.init1 4 (fun _ -> 4.)
    printfn "%A" a

there is no error. So, the error is about F# Interactive.

  1. When you compile the library code targeting FSharp.Core version 4.4.0.0, there is no error with F# Interactive 14.0.22810.0. So, this is a backward compatibility problem of the new F# Interactive with existing libraries compiled for FSharp.Core 4.3.1.0, I think.
@dsyme
Copy link
Contributor

dsyme commented Jun 6, 2015

@gbaydin - thank you for this timely bug report.

This commit broke the binary compatibility of FSharp.Core: 52a7252

because the "module let" bindings were changed to "static member" bindings, and as far as F# metadata is concerned these are different. This obviously needs to be fixed for FSharp.Core 4.4.0.0.

These items are referred to in the inlined implementations of Array.init and other functions.

@enricosada
Copy link
Contributor

that commit was mine, so i think i should help 😄 , i'll fix this

Possible fix:

  1. revert the old ErrorStrings module (backward compatibility).
  2. add the static methods as functions in ErrorStrings module (or another module like Resources maybe? so we can reuse it for all the FSharp.Core resources if needed? )

But @dsyme can you help me to understand better the problem?

The root problem i think is: FSharp.Core.SR.resources is private/internal, so functions like FSharp.Core.SR.resources.GetString or SR.GetString(SR.arrayWasEmpty) etc, cannot be inlined, but some methods require it ( like average ) so we use a public wrapper ErrorStrings

That's ok, we can inline some functions, but not others unless we wrap the (SR.GetString(SR.arrayWasEmpty) as functions inside ErrorStrings.

The problem is:
Currently is difficult to inline more functions, because we break backward compatibility each time (like this bug).
If we add a new functions in ErrorStrings, reference the old version break for a simple refactoring (and maybe is a good perf improvement like an inline).

Maybe is already discussed before but cant we make a wrapper of FSharp.Core.SR.resources public instead? And use inlined functions for type safety access of resource strings?

module internal SR =

    //public wrapper
    let resourceManager: ResuorceManager = resources

module internal Resources =

   let inline GetString(name:System.String) : System.String = 
        SR.resourceManager.GetString(name, System.Globalization.CultureInfo.CurrentUICulture)

   let inline arrayWasEmpty () = GetString(SR.arrayWasEmpty)

inside the code we can do Resources.arrayWasEmpty () and that is compiled as SR.resourceManager.GetString("arrayWasEmpty", System.Globalization.CultureInfo.CurrentUICulture)

Another options is to use a GetString: string -> Culture option -> string wrapper instead to expose the ResourceManager class, but dont know, localization and resources are complicated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants