Generate type provider fails to create custom operation for computation expression
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
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)
If you'd like to help address this problem, please submit a PR to dotnet/fsharp