-
Notifications
You must be signed in to change notification settings - Fork 789
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
[WIP, RFC FS-1043] Extension members visible to trait constraints #6286
Conversation
…sualfsharp into extensionconstraints
…o extensionconstraints
…o extensionconstraints
…o extensionconstraints
@alfonsogarciacaro It is critical that we update the TAST (and the FCS API) to include solutions (witnesses) for trait constraints. This has long been a flaw in the TAST and our code generation, leading to horrors where we rediscover the solution during code generation and likewise where we rediscover solutions using reflection in FSHarp.Core. I need to finally address this problem and record solutions to trait constraints in the TAST itself, and possibly pass them as witnesses in the underlying generic code used for reflection calls and quotation evaluation. This is very tricky and awkward as we can't break compat, I need to prototype carefully. Much of the compiler cleanup and documentation I've been doing has been preparing to storm this particular castle, or die doing it. |
testfiles\test.fs does not compile:
|
@dsyme Not sure what the best way is to help with this one. So I'll just outline what I found here: In ConstraintSolver.fs /// Only consider overload resolution if canonicalizing or all the types are now nominal.
/// That is, don't perform resolution if more nominal information may influence the set of available overloads
and GetRelevantMethodsForTrait (csenv:ConstraintSolverEnv) permitWeakResolution nm (TTrait(tys, _, memFlags, argtys, rty, soln, extSlns, ad) as traitInfo) : MethInfo list =
let results =
if permitWeakResolution || MemberConstraintSupportIsReadyForDeterminingOverloads csenv traitInfo then
let m = csenv.m
let minfos =
match memFlags.MemberKind with
| MemberKind.Constructor ->
tys |> List.map (GetIntrinsicConstructorInfosOfType csenv.SolverState.InfoReader m)
| _ ->
tys |> List.map (GetIntrinsicMethInfosOfType csenv.SolverState.InfoReader (Some nm, AccessibleFromSomeFSharpCode, AllowMultiIntfInstantiations.Yes) IgnoreOverrides m)
// Merge the sets so we don't get the same minfo from each side
// We merge based on whether minfos use identical metadata or not.
let minfos = List.reduce (ListSet.unionFavourLeft MethInfo.MethInfosUseIdenticalDefinitions) minfos
// Get the extension method that may be relevant to solving the constraint as MethInfo objects.
// Extension members are not used when canonicalizing prior to generalization (permitWeakResolution=true)
let extMInfos =
if MemberConstraintSupportIsReadyForDeterminingOverloads csenv traitInfo then
GetRelevantExtensionMethodsForTrait csenv.m csenv.amap traitInfo
else []
let extMInfos = extMInfos |> ListSet.setify MethInfo.MethInfosUseIdenticalDefinitions
let minfos = minfos @ extMInfos
/// Check that the available members aren't hiding a member from the parent (depth 1 only)
let relevantMinfos = minfos |> List.filter(fun minfo -> not minfo.IsDispatchSlot && not minfo.IsVirtual && minfo.IsInstance)
minfos
|> List.filter(fun minfo1 ->
not(minfo1.IsDispatchSlot &&
relevantMinfos
|> List.exists (fun minfo2 -> MethInfosEquivByNameAndSig EraseAll true csenv.g csenv.amap m minfo2 minfo1)))
else
[]
// The trait name "op_Explicit" also covers "op_Implicit", so look for that one too.
if nm = "op_Explicit" then
results @ GetRelevantMethodsForTrait csenv permitWeakResolution "op_Implicit" (TTrait(tys, "op_Implicit", memFlags, argtys, rty, soln, extSlns, ad))
else
results With this change the testfile now has three errors:
|
The three remaining errors can be reproduced by type MyType =
| MyType of int
/// Locally extending an F# type with a wide range of standard operators
module FSharpTypeWithExtrinsicOperators =
[<AutoOpen>]
module Extensions =
type MyType with
static member (|||)(MyType (x : int), MyType (y : int)) = MyType (x ||| y)
static member (&&&)(MyType x, MyType y) = MyType (x &&& y)
static member (^^^)(MyType x, MyType y) = MyType (x ^^^ y) Here, Going back to the test file this leaves one last error on
Where
With a type annotation ( |
Using open System.Runtime.CompilerServices
[<Extension>]
type Ext2() =
[<Extension>]
static member inline (!@)(a : string) = a.Length
!@"324234"
|
@kevmal Super work, thanks. Could you submit the first change as a PR to my branch? Thanks |
append extension methods in GetRelevantMethodsForTrait
open System.Runtime.CompilerServices
[<Extension>]
type Ext2() =
[<Extension>]
static member Bleh(a : string) = a.Length
let inline bleh s = (^a : (member Bleh : unit -> int) s) works. The last operator example, as the error states, is an instance member and shouldn't work. Currently I think that's okay because type System.Int32 with
static member (!@)(a:string) = a.Length
!@"32423" also works. My only concern is it would make sense to restrict the above (you must extend string with string operators). But it's important to me to be able to do something like (not exactly this, just staying on the same theme) type System.Int32 with
static member inline (!@)(a) = (^a : (member Length : int) a) where, in similar cases, I'd usually have to use the With that said there seems to be an issue: type System.Int32 with
static member inline (+)(a, b) = Array.map2 (+) a b
[|1;2;3|] + [|2;3;4|] //Okay
[|TimeSpan.Zero|] + [|TimeSpan.Zero|] //Okay
[|1m|] + [|2m|] //Okay
[|1uy|] + [|2uy|] //Okay
[|1L|] + [|2L|] //Okay
[|1I|] + [|2I|] //Okay
[| [|1 ; 1|]; [|2|] |] + [| [|2; 2|]; [|3|] |] //Okay
[|"1"|] + [|"2"|] //error FS0001
[|1.f|] + [|2.f|] //error FS0001
[|1.0|] + [|2.0|] //error FS0001 Where the errors (for float/single/string) are:
|
Yes, I remember reverting this during integration, thanks, I will fix it now |
Re this:
I think this should be an error. |
@kevmal Thanks for your help! I've pushed the required fixes to the branch and If you'd like to contribute a bit of code, then moving |
@dsyme With that change (wildcard match on minfos) all solution tests passed. I think the overall issue relates to the last TODO you listed. So in the context of the previous issue I brought up... With integertypes we hit this branch
Since the check minfos passes for integer types it's fine but for double/float/string the invalid method (array extension op) causes this branch to skip and error on the available method. In the case of Though with that said I'm not sure why the above would not be |
With
It compiles to
|
Closed in favour of #6805 |
RFC https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md
This is work by @TobyShaw and myself to implement RFC FS-1043. This PR brings #3582 up-to-date with master
TODO items from the code
extSlns // TODO: do we need to remap here???
extSlns starts empty. TODO: check the ramifications of this when inlining solved trait calls from other assemblies
// TODO: consider what happens when the expression refers to extSlns that have become hidden
None
for TraitFreshener, e.g. https://github.com/Microsoft/visualfsharp/pull/3582/files#diff-5b9ab9dd9d7133aaf23add1048742031R576// TODO: check the use of 'allPairs' - not all these extensions apply to each type variable.
Things to test
Things from previous PR