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

Generate type provider fails to create custom operation for computation expression #386

Open
DragonSA opened this issue Aug 7, 2022 · 2 comments
Labels

Comments

@DragonSA
Copy link

DragonSA commented Aug 7, 2022

Description

Using a generative type provider fails to create a computation expression builder with a custom operation. The compiler fails to recognise the method as a custom operation.

Note that the IL appears to be practically equivalent for the generated type and the manually created builder (that does compile successfully).

Repro steps

See the repo ProvidedCustomOperation for a minimal reproduction. Key snippets below:

The generative type provider that creates a computation expression builder with a custom operation defined.

[<TypeProvider>]
type Provider(config) as this =
    inherit TypeProviderForNamespaces(config)

    let ns = "Provider"

    do
        let provider = ProvidedTypeDefinition(Assembly.GetExecutingAssembly(), ns, "BuilderProvider", Some typeof<obj>, isErased = false)
        provider.DefineStaticParameters([ ProvidedStaticParameter("_", typeof<bool>) ], fun typeName _ ->
            let asm = ProvidedAssembly()
            let builder = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<CommonBuilder>, isErased = false)
            builder.AddMember(ProvidedConstructor([ ], fun _ -> <@@ () @@>))
            let method = ProvidedMethod(
                methodName = "Add",
                parameters = [ ProvidedParameter("x", typeof<int list>); ProvidedParameter("a", typeof<int>) ],
                returnType = typeof<int list>,
                invokeCode = (fun [ _; x; a ] -> <@@ ((%%a : int) + 5)::(%%x : int list) @@>))
            method.AddCustomAttribute({
                new CustomAttributeData() with
                    member _.Constructor = typeof<CustomOperationAttribute>.GetConstructor([| typeof<string> |])
                    member _.ConstructorArguments = [| CustomAttributeTypedArgument(typeof<string>, "add") |]
                    member _.NamedArguments = [| CustomAttributeNamedArgument(typeof<CustomOperationAttribute>.GetProperty("MaintainsVariableSpaceUsingBind"), true) |]
            })
            builder.AddMember(method)
            asm.AddTypes([ builder ])
            builder)
        this.AddNamespace(ns, [ provider ])

The equivalent builder:

type ExplicitBuilder() =
    inherit CommonBuilder()

    [<CustomOperation("add", MaintainsVariableSpaceUsingBind=true)>]
    member _.Add(x, a) = (a + 5)::x

Expected behavior

The computation expression compiles successfully:

type ProvidedBuilder = BuilderProvider<false>
let y = ProvidedBuilder()
assert (y {
    add 1
    add 2
    yield 3
} = [7; 6; 3])

Actual behavior

Compilation error:

ProvidedCustomOperation/Usage/Program.fs(12,5): error FS0039: The value or constructor 'add' is not defined. [ProvidedCustomOperation/Usage/Usage.fsproj]
ProvidedCustomOperation/Usage/Program.fs(13,5): error FS0039: The value or constructor 'add' is not defined. [ProvidedCustomOperation/Usage/Usage.fsproj]

Related information

  • Operating system: Darwin Davids-iMac.local 20.6.0 Darwin Kernel Version 20.6.0: Tue Jun 21 20:50:28 PDT 2022; root:xnu-7195.141.32~1/RELEASE_X86_64 x86_64
  • Branch: NuGet 7.0.3
  • .NET Runtime: 6.0.301
@dsyme
Copy link
Contributor

dsyme commented Aug 23, 2022

This is not currently supported by the F# tooling. In particular TryBindMethInfoAttribute is incomplete for provided methods:

From fsharp\src\Compiler\Checking\AttributeChecking.fs:

let TryBindMethInfoAttribute g (m: range) (AttribInfo(atref, _) as attribSpec) minfo f1 f2 f3 = 
#if NO_TYPEPROVIDERS
    // to prevent unused parameter warning
    ignore f3
#endif
    BindMethInfoAttributes m minfo 
        (fun ilAttribs -> TryDecodeILAttribute atref ilAttribs |> Option.bind f1)
        (fun fsAttribs -> TryFindFSharpAttribute g attribSpec fsAttribs |> Option.bind f2)
#if !NO_TYPEPROVIDERS
        (fun provAttribs -> 
            match provAttribs.PUntaint((fun a -> a.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), atref.FullName)), m) with
            | Some args -> f3 args
            | None -> None)  
#else
        (fun _provAttribs -> None)

@dsyme
Copy link
Contributor

dsyme commented Aug 23, 2022

If you'd like to help address this problem, please submit a PR to dotnet/fsharp

@dsyme dsyme added the external label Aug 23, 2022
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

2 participants