1
votes

Having a go at writing a type provider and I've come across a problem I can't quite get past specifically with regard to adding existing interfaces with generic parameters that are ProvidedTypes. If anyone knows how to add existing interfaces with generic parameters onto the generated types?

What I want is to generate a type similar to the following with both Bar and Foo being generated types. Obviously the below is a contrived example.

type Bar() =
    member val List: IEnumerable<Foo> with get, set

Unfortunately I'm getting an error when trying to use a ProvidedType that implements the interface typeof<IEnumerable<Foo>>. I suspect it is because a generated type is being used as a generic parameter.

The exception message is something like:

The operation 'Module' on item 'Foo' should not be called on provided type, member or parameter

EDIT: I've added simple code to reproduce the error (called in DefineStaticParameters):

let buildType() = 
    let providedType1 = ProvidedTypeDefinition("ParentType", Some typeof<obj>, IsErased = false)
    let providedType2 = ProvidedTypeDefinition("DataType", Some typeof<obj>, IsErased = false)

    let symbolType = ProvidedTypeBuilder.MakeGenericType(typedefof<IEnumerable<_>>, [providedType2])

    let providedField = ProvidedField("propertyField", symbolType)
    providedType1.AddMember(providedField)

    let providedProperty = ProvidedProperty("TestProperty", symbolType)
    providedProperty.GetterCode <- fun args -> Expr.FieldGet(args.[0], providedField)
    providedType1.AddMember providedProperty

    let pconstructor = ProvidedConstructor([ProvidedParameter("dataList", symbolType)])
    pconstructor.InvokeCode <- fun args -> <@@ () @@>
    providedType1.AddMember pconstructor

    let nestedProperty = ProvidedProperty("Data", typeof<string>)
    nestedProperty.GetterCode <- fun _ -> <@@ "TEST SUCCESS" @@>
    providedType2.AddMember nestedProperty

    let interfaceType = ProvidedTypeBuilder.MakeGenericType(typedefof<IEquatable<_>>, [providedType2])
    providedType2.AddInterfaceImplementation interfaceType

    let equalsParameter = ProvidedParameter("other", providedType2)
    let providedMethodEquals = ProvidedMethod("Equals", [ equalsParameter ], typeof<bool>)
    providedMethodEquals.InvokeCode <- fun args ->
        let propertyGet x = Expr.PropertyGet(x, nestedProperty)
        let currentEq = propertyGet args.[0]
        let otherEq = propertyGet args.[1]
        <@@ %%currentEq = %%otherEq @@>

    // Add these to the generated type with a namespace outside this method
    [ providedType1; providedType2 ]

Stack trace looks like:

at Microsoft.FSharp.Core.Operators.Raise[T](Exception exn) at ProviderImplementation.ProvidedTypes.Misc.notRequired[a](String opname, String item) ProvidedTypes.fs:line 58\ at ProviderImplementation.ProvidedTypes.ProvidedTypeDefinition.get_Module() in ProvidedTypes.fs:line 1683 at System.Reflection.Emit.AssemblyBuilder.CheckContext(Type[] types) at System.Reflection.Emit.ModuleBuilder.GetTypeTokenWorkerNoLock(Type type, Boolean getGenericDefinition) at System.Reflection.Emit.ModuleBuilder.GetTypeTokenInternal(Type type, Boolean getGenericDefinition) at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst) at System.Reflection.Emit.SignatureHelper.GetType SigToken(Module mod, Type type) at System.Reflection.Emit.ModuleBuilder.GetTypeTokenWorkerNoLock(Type type, Boolean getGenericDefinition) at System.Reflection.Emit.ModuleBuilder.GetTypeTokenInternal(Type type, Boolean getGenericDefinition) at System.Reflection.Emit.TypeBuilder.AddInterfaceImplementation(Type interfaceType)

1
You might see if using ProvidedTypeBuilder.MakeGenericType helps. - kvb
It gets me further but now I'm having a problem with the ProvidedSymbolType of IEnumerable<Foo> - where it is not complaining GetAttributeFlagsImpl should not be called. I'm not sure whether I should attempt to implement the method on ProvidedSymbolType or if I'm doing something else wrong. - akara
Creating a custom SymbolType that implements GetAttributeFlagsImpl getting its type attributes from the generic IEnumerable<> type passed in to build it (altered ProvidedTypeBuilder.MakeGenericType captures this) gets me back to the original error in the question. Not sure how I should populate the Module property. - akara
After tracing it down I suspect that the module created in AssemblyGenerator needs to be set on the provided types similar to how the assembly in this class is set on every ProvidedType when added to the assembly. - akara

1 Answers

0
votes

After further investigation it seems as if the Generated Types API inside the type provider starter pack does not support this functionality as of yet. I've raised an issue to cover this case. My suspicion is that the interface types added to the TypeBuilder need to be converted from the ProvidedTypeDefinition to actual Reflection.Emit TypeBuilders before being added as interfaces to other generated types including all generic type arguments of the interface types.

i.e for IEnumerable< GeneratedType > - we need to convert generic type argument GeneratedType first, then make a new type instance that refers to the converted type (IEnumerable < GeneratedType >) then add it as an interface implementation. The code that adds interfaces to the types is in AssemblyGenerator in the ProvidedTypes.fs file.