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

Stubborn warning FS0193 on valid code #385

Closed
drvink opened this issue Apr 26, 2015 · 14 comments
Closed

Stubborn warning FS0193 on valid code #385

drvink opened this issue Apr 26, 2015 · 14 comments
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Milestone

Comments

@drvink
Copy link
Contributor

drvink commented Apr 26, 2015

The following code typechecks and runs without issue, but generates a spurious warning about a missing type parameter constraint, and there seems to be no obvious solution to satisfy the compiler. This is happening on Visual F# 3.1.2.

#!/usr/bin/env fsharpi

open System

type CShow = class
  static member inline show (x : int) = sprintf "%d" x
  static member inline show (x : string) = sprintf "\"%s\"" x
end

type Instance<'a, 'b, 'c when
             (^a or ^b) : (static member show : ^b -> ^c)> = class
  static member inline instance (b : ^b) =
    ((^a or ^b) : (static member show : ^b -> ^c) b)
end

type IShow = interface
  abstract show : unit -> string
end

let inline Show arg = {
  new IShow with
  member __.show () = Instance<CShow, _, string>.instance arg }

let inline cout (x : string) = Console.WriteLine x
let inline show (x : #IShow) = x.show ()

let _ =
  [Show 1; Show "arsdf"]
  |> List.iter (show >> cout)

The warning is: Blarg.fsx(13,5): warning FS0193: A type parameter is missing a constraint 'when ( ^a or ^b) : (static member show : ^b -> 'c)' (the line with the static member invocation).

@latkin
Copy link
Contributor

latkin commented Apr 27, 2015

I can repro in 4.0

@latkin latkin added the Bug label Apr 27, 2015
@dsyme
Copy link
Contributor

dsyme commented Apr 28, 2015

Yes, I repro. FWIW you will almost certainly be able to workaround this by disassociating the member that causes the constraint from the type:

open System

type Instance< 'a, 'b, 'c>() =  class end

module Instance = 
  let inline instance (b : ^b) : ^c =
    (((^a or ^b) : (static member show : ^b -> ^c) b) : ^c)

@drvink
Copy link
Contributor Author

drvink commented Apr 28, 2015

Hi Don,

That workaround does fix the warning, but how would it be used with the given example, i.e. so that CShow is used as part of resolving the constraint? The following works, but results in a warning saying that "instance" should not be given explicit type arguments:

type Instance<'a, 'b, 'c> = class end
module Instance = begin
  let inline instance (b : ^b) : ^c =
    (((^a or ^b) : (static member show : ^b -> ^c) b) : ^c)
end

let inline Show arg = {
  new IShow with
  member __.show () = Instance.instance<_, CShow, _> arg }

@dsyme
Copy link
Contributor

dsyme commented Apr 28, 2015

How about this?

type IShow = 
  abstract show : unit -> string

type CShow() = 
  static member show (x : int) = sprintf "%d" x
  static member show (x : string) = sprintf "\"%s\"" x

let inline instance< ^a,^b, ^c when (^a or ^b) : (static member show : ^b -> ^c)  > (b : ^b) : ^c = 
    ((((^a or ^b) : (static member show : ^b -> ^c) b)) : ^c)

let inline Show arg =  
    { new IShow with
          member __.show() = instance<CShow, _, _> arg }

@drvink
Copy link
Contributor Author

drvink commented Apr 28, 2015

Ah, right, of course. I assume that being able to write <^a, ^b, ^c when (^a or ^b) ...> is an F# 4.0 syntax cleanup? (I have to write <'a, 'b, 'c when (^a or ^b) ...>)

@dsyme
Copy link
Contributor

dsyme commented Apr 28, 2015

Hmm... What exact version of F# are you using?

@drvink
Copy link
Contributor Author

drvink commented Apr 28, 2015

VS says Visual F# 3.1.2 and fsi's banner says 12.0.30815.0

@dsyme
Copy link
Contributor

dsyme commented Apr 28, 2015

I'm on the same and can define this:

type CShow< ^a, ^b, ^c when (^a or ^b) : (static member show : ^b -> ^c)  >() = class end

@drvink
Copy link
Contributor Author

drvink commented Apr 28, 2015

Ah, I guess <^ lexes as one token: type CShow< ^a is OK, type CShow<^a is not.

@dsyme
Copy link
Contributor

dsyme commented Apr 28, 2015

Yes :) Sorry, I made that adjustment instinctively.

@dsyme dsyme added the pri-3 label May 9, 2015
@KevinRansom KevinRansom removed the pri-3 label Dec 4, 2015
@dsyme dsyme added Area-Compiler Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. labels Jan 9, 2016
@cartermp cartermp added this to the Unknown milestone Aug 25, 2018
@cartermp cartermp modified the milestones: Unknown / not bug, Backlog May 23, 2019
@Happypig375
Copy link
Member

This is still reproing on F# Interactive version 11.4.2.0.

@ghost
Copy link

ghost commented Aug 8, 2021

I don't want to distract anyone on an at-worst irritating bug, but I have a funny reproduction in F# 5.0 (in .NET Standard 2.0) that might shed some light on this strange behavior.

The following simple example raises a warning like the ones above:

type TwoDPoint< ^T when ^T : (static member (+) : ^T * ^T -> ^T ) > = 
    {xpt: ^T; ypt: ^T}
with
    static member inline (+) (a : TwoDPoint< ^T>, b : TwoDPoint< ^T>) : TwoDPoint< ^T>  = 
        {xpt = a.xpt + b.xpt; 
         ypt = a.ypt + b.ypt}

The specific warning: A type parameter is missing a constraint 'when ( ^T or ^?769536) : (static member ( + ) : ^T * ^?769536 -> ^T)' (the random number usually indicates to me that the compiler is being fussy for some reason)

However, this works without any warnings:

type TwoDPoint< ^T when ^T : (static member (+) : ^T * ^T -> ^T ) > = 
        {xpt: ^T; ypt: ^T}
with
    static member inline (+) (a,b) : TwoDPoint< ^T>  = 
        {xpt = a.xpt + b.xpt; 
         ypt = a.ypt + b.ypt}

IntelliSense shows that F# is interpolating the types correctly. So for some reason F# doesn't like the type annotations in this specific case. Funnily, I have this hideous type defined later which works flawlessly:

type ColumnVector< ^T when 
       ^T : (static member (+) :  ^T * ^T -> ^T ) and 
       ^T : (static member (-) : ^T * ^T -> ^T ) and 
       ^T : (static member (*) : ^T * ^T -> ^T) and 
       ^T : (static member (/) : ^T * ^T -> ^T) and
       ^T : (static member Zero : ^T) and 
       ^T : (static member Sqrt : ^T -> ^T) and
       ^T : struct and
       ^T : equality> = 
   static member inline (+) 
       ((vec1: ColumnVector< ^T >), (vec2: ColumnVector< ^T> )) : ColumnVector< ^T> = // no problem
   static member inline (*) ((s: ^T),(v: ColumnVector< ^T>)) : ColumnVector< ^T> = // no problem
   // and so on

So this style of chaining together static member constraints can work really well at creating robust polymorphic code. It's just a head scratcher when the compiler complains about it in seemingly arbitrary places.

@smoothdeveloper
Copy link
Contributor

@dsyme, seeing the other "generic type pretty printing" related issues getting coallesced with this older one (makes sense), and on the topic of the pretty printing approach becoming easier to use to contributors, I was wondering if by gleaning more intuition about how this code evolved and ideally would be supposed to work, you may be in a place to improve that infrastructure with stronger invariants / making it easier for contributors to leverage?

I remember the pretty printing with those concerns was a bit of a sticking point I had to grok based on your input, when doing the overload resolution failure error message work, I didn't feel super confident using the right APIs and in my refactorings to enable what I ended up using in the implementation.

It would be great if someone had a clear idea on how we could architecturally solve this (consuming the code being correct 99% of time just by construction, and refactoring/extending the infrastructure feeling less "what am I going to break" inducing, at least for casual contributors).

It could be there is just a missing call to "convert unnamed identifier to generic identifier for pretty printing" in many/some places, but just fixing it with current infrastructure may not address the potential for the issue to show when adding code, if there is already some knowledge about feasibility of addressing the later, it peaks my curiosity that some of this knowledge would write itself in this issue until a plan is put by the community to address it in the code.

@dsyme dsyme added Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen and removed Area-Compiler labels Mar 31, 2022
@vzarytovskii
Copy link
Member

I don't see the original issue in latest FSI anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Projects
Archived in project
Development

No branches or pull requests

8 participants