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

Improve Error Reporting: "could not be generalized because it would escape its scope" #3302

Open
Tracked by #1103
isaacabraham opened this issue Jul 4, 2017 · 2 comments
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Feature Improvement Theme-Simple-F# A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language.
Milestone

Comments

@isaacabraham
Copy link
Contributor

What

This error crops up when creating SRTP members but forgetting to inline them (example here taken from this SO question): -

type MyType<'T when ^T: (static member ( + ) : ^T * ^T -> ^T)> =
    member this.F a b = a + b

leads to

error FS0670: This code is not sufficiently generic. The type variable ^T when ^T : (static member ( + ) : ^T * ^T -> ^T) could not be generalized because it would escape its scope.

Why

It seems that the error could not be generalized because it would escape its scope normally can be solved by inlining the member definition. There are a few posts / questions out there on SO etc. regarding this, all with that answer.

I know that SRTP is a somewhat advanced F# topic but nonetheless the error could be improved since there's often the same fix that can be applied. Also, the current error message could explain itself a little better - what does "it would escape its scope" mean in this context? Why would inlining fix it? etc.

How

I'm not entirely sure, but perhaps a start would be to at least offer the solution: -

error FS0670: This code is not sufficiently generic. The type variable ^T when ^T : (static member ( + ) : ^T * ^T -> ^T) could not be generalized because it would escape its scope. Consider inlining the member to fix this error e.g. "member inline this.F" ....

However, this still doesn't clearly explain the "why"; someone seeing this for the first (or second!) time won't know why the error is occuring, but rather than a tried-and-tested solution that makes the error go away. Other ideas include: -

  • Showing an alternate way to define the member which (apparently?) gets the constraint for free:-
type MyType() =
    member inline this.F a b = a + b
  • Another answer that I rather like from the SO post above contains a much more : -
    "Member constrains need statically resolved type parameters. But statically resolved type parameters are not allowed on types, only for inline functions and inline methods.". This at least tries to address the why the error occurs, although it could still be improved upon IMHO.
@Martin521
Copy link
Contributor

Martin521 commented Sep 27, 2017

Here is another case of this error message (on B<'T> below), where also the above mentioned improvements are not helping.

type A<'T>() = class end
module AA = let CreateA<'T>() = A()
type B<'T>() = let a: A<'T> = AA.CreateA()

Edit, six years later, accidentally coming across this old post of mine.
The above is actually simply fixed by adding another annotation.

type A<'T>() = class end
module AA = let CreateA<'T>() = A<'T>()
type B<'T>() = let a: A<'T> = AA.CreateA()

@cartermp cartermp added this to the Unknown milestone Aug 25, 2018
@dsyme dsyme added the Theme-Simple-F# A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language. label Sep 16, 2021
@dsyme dsyme added Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen and removed Area-Compiler labels Mar 31, 2022
@vzarytovskii vzarytovskii moved this to Not Planned in F# Compiler and Tooling Jun 17, 2022
@Hermholtz
Copy link

Hermholtz commented Nov 16, 2023

Same with the following function:

let draw<'T when 'T : (member Draw : unit -> unit)> (x: 'T) =
  x.Draw()

or

let draw (x: 'T when 'T : (member Draw : unit -> unit)) =
  x.Draw()

Bonus question/suggestion: maybe the language could be improved to not require inlining in these cases? Maybe the compiler could generate proxy functions or types that would be "inlined" under the hood to satisfy Statically Resolved Type Parameters, but allow us to use non-inlined functions as well? Inlining may not always be desirable.

Consider this example:

// Business logic processors
type FirstTypeThatCanDraw() =
  member x.Draw() = printfn "I'm drawing!"

type AntherTypeThatCanDraw() =
  member x.Draw() = printfn "I'm drawing, too!"
  member x.Bark() = printfn "Barking!"

let o = new FirstTypeThatCanDraw()
let p = new AntherTypeThatCanDraw()

// The new features in question (not working today without "inline")
// let draw (x: 'T when 'T : (member Draw : unit -> unit)) =
//   x.Draw()

// The new compiler would generate this under the hood (not inlined),
// one for each concrete argument type.
let ``draw$generated_proxy_for_FirstTypeThatCanDraw`` (x: 'T when 'T : (member Draw : unit -> unit)) =
  x.Draw();

let ``draw$generated_proxy_for_AntherTypeThatCanDraw`` (x: 'T when 'T : (member Draw : unit -> unit)) =
  x.Draw();

// my code would be:
// draw o
// draw p
// but under the hood it would get converted to:
``draw$generated_proxy_for_FirstTypeThatCanDraw`` o
``draw$generated_proxy_for_AntherTypeThatCanDraw`` p

The difference is that we would have one body of the function per type instead of one body per call site.

Maybe we could use an attribute on the function with constraints to hint compiler about our intent? I'm not sure.

The above code generates 2 warnings of the kind: "Warning: this construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to FirstTypeThatCanDraw." but I hope compiler could handle that, too.

Thanks.

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 Feature Improvement Theme-Simple-F# A cross-community initiative called "Simple F#", keeping people in the sweet spot of the language.
Projects
Archived in project
Development

No branches or pull requests

7 participants