Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

compilation unit 'FSharp.Core' did not contain the namespace, module or type 'StringModule' #343

Closed
aggieben opened this issue Jun 21, 2020 · 8 comments
Labels

Comments

@aggieben
Copy link

aggieben commented Jun 21, 2020

Description

I wrote a simple type provider that references String.length in the untyped quotation used to provide a constructor implementation. The type provider library builds, but a consuming project fails to build with the following error:

FSC : error FS0193: The module/namespace 'Microsoft.FSharp.Collections' from compilation unit 'FSharp.Core' did not contain the namespace, module or type 'StringModule' [/Users/ben/proj/oss/constrainedtypes/test/ConstrainedTypes.Test/ConstrainedTypes.Test.fsproj]

The type provider is implemented as follows:

namespace ConstrainedTypesopen System.IO
open System.Reflection
open FSharp.Quotations
open ProviderImplementation
open ProviderImplementation.ProvidedTypes
open FSharp.Quotations
open Microsoft.FSharp.Quotations[<TypeProvider>]
type ConstrainedStringProvider (config : TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces (config, addDefaultProbingLocation=true)let rootNs = "ConstrainedTypes"
    let thisAssembly = Assembly.GetExecutingAssembly()
    let boundedStringProvider = ProvidedTypeDefinition(thisAssembly, rootNs, "BoundedString", Some typeof<string>)do
        boundedStringProvider.DefineStaticParameters([ProvidedStaticParameter("Length", typeof<int>)], fun name args ->
            let length = args.[0] :?> int
            let provided = ProvidedTypeDefinition(thisAssembly, rootNs, name, Some typeof<string>)
​
            ProvidedConstructor(
                [ProvidedParameter("value", typeof<string>)],
                fun args ->
                    <@@
                        printfn "checking bound: %d" length
                        if (length < String.length %%(args.[0])) then
                            sprintf "provided value exceeded the bounds: '%s' > %d"
                                %%(args.[0]) length
                            |> invalidArg "value"
                    @@>
            )
            |> provided.AddMember
​
            provided
        )
​
        this.AddNamespace(rootNs, [boundedStringProvider])[<TypeProviderAssembly>]
do ()

This implementation can be cloned here: https://github.com/aggieben/constrainedtypes

Repro steps

See implementation above. Clone the linked implementation and build the ConstrainedTypes.Test project to repro.

Expected behavior

I expected the use of the provided type BoundedString<10> to compile.

Actual behavior

The type provider fails at consumer compile time.

Known workarounds

None

Related information

  • MacOS Catalina
  • .NET Core 3.1.100
@sergey-tihon
Copy link
Member

Can you please try to create TP from the template?

dotnet new -i FSharp.TypeProviders.Templates
dotnet new typeprovider -n ConstrainedTypes

Update samples provider with our code, but does not remove assemblyReplacementMap https://github.com/fsprojects/FSharp.TypeProviders.SDK/blob/master/templates/content/basic/src/MyProvider.DesignTime/MyProvider.DesignTime.fs#L20 and then

cd ConstrainedTypes

dotnet tool restore
dotnet paket update
dotnet build -c release

dotnet test -c release

@aggieben
Copy link
Author

Update samples provider with our code, but does not remove assemblyReplacementMap

@sergey-tihon I'm not sure I understand what this means. Are you asking me to take the provider code from the template and update it with my implementation, but be sure to leave the assemblyReplacementMap parameter in the constructor?

@aggieben
Copy link
Author

aggieben commented Jun 22, 2020

@sergey-tihon I've reimplemented my TP from the template as you suggested here:

https://github.com/aggieben/constrainedtypes/tree/tpsdk-343

It fails in the same way.

FSC : error FS0193: The module/namespace 'Microsoft.FSharp.Collections' from compilation unit 'FSharp.Core' did not contain the namespace, module or type 'StringModule' [C:\Users\Ben\proj\oss\constrainedtypes\tests\constrainedtypes.Tests\constrainedtypes.Tests.fsproj]

@sergey-tihon
Copy link
Member

Thank you, it definitely looks like a bug of mapping types from quotation to target runtime types.

It is a good practice to keep quotations as small as possible. You can move method implementation into runtime assembly and just call it from quotation.

[<AutoOpen>]
module internal Utilities =

    let x = 1

    let ctorBoundedString length str = 
        if (length < String.length str) then
            sprintf "provided value exceeds the bounds: '%s' > %d"
                str length
            |> invalidArg "value"

simple quotation that call implementation and pass parameters

            ProvidedConstructor(
                [ProvidedParameter("value", typeof<string>)],
                fun args -> <@@ ctorBoundedString length %%(args.[0]) @@>
            )
            |> boundedStringType.AddMember

@aggieben
Copy link
Author

@sergey-tihon alright, thanks. I'll give that a go. Is it necessary to have separate runtime and design-time components?

@sergey-tihon
Copy link
Member

It it not mandatory, so it should be possible to keep both components in one assembly.
But yes, I believe that it is recommended. You can read about design details here
Most of recent/popular type providers separate them, as it is done in template.

  • It allow you to minimize runtime dependencies (design-time component may references assemblies that does not needed in runtime)
  • Runtime component compiled as usual assembly with all optimization for as many runtimes as you want to support.
  • Design time component become simpler and if you minimize number of quotations you significantly simplify development and debug process.

@aggieben
Copy link
Author

@sergey-tihon I moved the bounds-checking function to the utility module in the runtime component as you suggested, but the tests now fail with this error:

System.MethodAccessException : Attempt by method 'constrainedtypesTests.Default constructor should create instance()' to access method 'ConstrainedTypes.Utilities.ensureBoundedString(Int32, System.String)' failed.

This same error is emitted from both the combined provider (on my master branch) and the bifurcated provider (on my tpsdk-340 branch).

@sergey-tihon
Copy link
Member

ops, sorry, my typo
As error message says issue is in method accessability.
You need to remove internal modifier from module declaration

Replace

module internal Utilities =

to

module Utilities =

@dsyme dsyme added the bug label Sep 21, 2021
@fsprojects fsprojects locked and limited conversation to collaborators Sep 21, 2021
@dsyme dsyme closed this as completed Sep 21, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Projects
None yet
Development

No branches or pull requests

3 participants