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

Static arguments for type providers cannot be of type "obj" #18316

Open
IS4Code opened this issue Feb 14, 2025 · 3 comments
Open

Static arguments for type providers cannot be of type "obj" #18316

IS4Code opened this issue Feb 14, 2025 · 3 comments

Comments

@IS4Code
Copy link

IS4Code commented Feb 14, 2025

A static parameter for a provided type that has the type obj (or any other type that is not a direct type of a constant expression that does not involve upcasting) cannot ever be given an explicit value.

Repro steps

namespace Providers

open ProviderImplementation.ProvidedTypes
open FSharp.Core.CompilerServices
open System.Reflection

[<assembly: TypeProviderAssembly>]do()

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

  do
    let param = ProvidedStaticParameter("value", typeof<obj>);
    let thisAssembly = Assembly.GetExecutingAssembly()
    let namespaceName = typeof<ConstantTypeProvider>.Namespace
    let definition = ProvidedTypeDefinition(thisAssembly, namespaceName, "Const", Some typeof<obj>)

    let makeType (typeName: string) (parameterValues: obj array) =
        let definition = ProvidedTypeDefinition(thisAssembly, namespaceName, typeName, Some typeof<obj>)
        let value = parameterValues[0]
        //failwith (sprintf "%A" parameterValues[0])
        definition.AddMember(ProvidedField.Literal("Value", value.GetType(), value))
        this.AddNamespace(namespaceName, [definition])
        definition

    definition.DefineStaticParameters([param], makeType)
    this.AddNamespace(namespaceName, [definition])
[<Literal>]
let v = Providers.Const<const(2:obj)>.Value

printf "%A" v

Expected behavior

The program should compile and "2" should be printed.

Actual behavior

error FS3047: Unknown static argument kind 'System.Object' when resolving a reference to a provided type or method 'Const,value="2"'
error FS1109: A reference to the type 'Providers.Const,value="2"' in assembly 'Providers' was found, but the type could not be found in that assembly

Known workarounds

I am not aware of any workarounds that could produce a provided type based on a constant expression of an arbitrary type.

Related information

This or a similar issue prevents the following static arguments from being accepted:

  • const (expr:obj).
  • Same but with ValueType, Enum or any less specific type than those permitted by constant expressions, or, in general, const (upcast expr) when the static parameter has a less specific type.
  • null (any type).

I am led to believe that this is a bug for the following reasons:

  • The specification does not seem to mention any restriction on the types of static arguments, so I presume it should be able to be anything that can be given to ApplyStaticArguments.
  • Implicit arguments (those where the parameter is optional and no value is given) work fine and the value is correctly passed to ApplyStaticArguments (although any type works there).
  • ApplyStaticArguments is actually still called here, even with the errors. This can be verified by uncommenting failwith and observing that "2" is thrown.
  • There are other contexts where constant expressions can be treated as obj, such as in custom attributes.

From what I can tell, at least a part of the issue is caused here, where the type is hard-checked against a list of concrete types.

Being able to accept a type argument of an arbitrary type can have all sorts of nice use cases, from specifying values in queries, formatting, hashing, serialization, or other operations on constant expressions.

FSharpObjectConstExample.zip

@T-Gro
Copy link
Member

T-Gro commented Feb 14, 2025

I think this should be possible - upcasting an already supported constant expression to obj.

@kerams
Copy link
Contributor

kerams commented Feb 14, 2025

The specification does not seem to mention any restriction on the types of static arguments

https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf
page 266

type-arg =
   …
   static-parameter

static-parameter =
    static-parameter-value
    id = static-parameter-value

static-parameter-value =
    const expr // <---- objects are not compile-time constants
    simple-constant-expression // <-------------- see page 60 - Simple Constant Expressions

@IS4Code
Copy link
Author

IS4Code commented Feb 14, 2025

@kerams That is just about syntax as far as I can tell, so const expr should, I believe, cover this case. But thank you for mentioning this, since now I realized even this is not working, while permitted by the other syntax that is not relying on const:

[<Literal>]
let nullobj: obj = null

[<Literal>]
let v = Providers.Const<nullobj>.Value

printf "%A" v
error FS3045: Invalid static argument to provided type. Expected an argument of kind 'obj'.

But it is an argument of kind obj! (And just to be sure, I verified that it works fine for a parameter and argument of a concrete type).

While investigating this, I also discovered another compiler issue: #18319. If such literals are even supposed to be valid, they also cannot be used as static arguments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: New
Development

No branches or pull requests

4 participants