csharpfftfsharpintegrationinterpolationlinear-algebramathdifferentiationmatrixnumericsrandomregressionstatisticsmathnet
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1010 lines
49 KiB
1010 lines
49 KiB
// (c) Microsoft Corporation 2005-2009.
|
|
|
|
#light
|
|
|
|
/// Generate the hash/compare functions we add to user-defined types by default.
|
|
module internal Microsoft.FSharp.Compiler.Augment
|
|
open Internal.Utilities
|
|
open Microsoft.FSharp.Compiler
|
|
open Microsoft.FSharp.Compiler.AbstractIL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.IL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
|
|
open Microsoft.FSharp.Compiler.Tast
|
|
open Microsoft.FSharp.Compiler.Tastops
|
|
open Microsoft.FSharp.Compiler.Ast
|
|
open Microsoft.FSharp.Compiler.ErrorLogger
|
|
open Microsoft.FSharp.Compiler.PrettyNaming
|
|
open Microsoft.FSharp.Compiler.Lib
|
|
open Microsoft.FSharp.Compiler.Env
|
|
|
|
let mk_IComparable_CompareTo_slotsig g =
|
|
TSlotSig("CompareTo",g.mk_IComparable_ty, [],[], [[TSlotParam(Some("obj"),g.obj_ty,false,false,false,[])]],Some g.int_ty)
|
|
|
|
let mk_IStructuralComparable_CompareTo_slotsig g =
|
|
TSlotSig("CompareTo",g.mk_IStructuralComparable_ty,[],[],[[TSlotParam(None,(mk_tuple_ty [g.obj_ty ; g.mk_IComparer_ty]),false,false,false,[])]], Some g.int_ty)
|
|
|
|
let mk_IStructuralEquatable_Equals_slotsig g =
|
|
TSlotSig("Equals",g.mk_IStructuralEquatable_ty,[],[],[[TSlotParam(None,(mk_tuple_ty [g.obj_ty ; g.mk_IEqualityComparer_ty]),false,false,false,[])]], Some g.bool_ty)
|
|
|
|
let mk_IStructuralEquatable_GetHashCode_slotsig g =
|
|
TSlotSig("GetHashCode",g.mk_IStructuralEquatable_ty,[],[],[[TSlotParam(None,g.mk_IEqualityComparer_ty,false,false,false,[])]], Some g.int_ty)
|
|
|
|
let mk_GetHashCode_slotsig g =
|
|
TSlotSig("GetHashCode", g.obj_ty, [],[], [],Some g.int_ty)
|
|
|
|
let mk_Equals_slotsig g =
|
|
TSlotSig("Equals", g.obj_ty, [],[], [[TSlotParam(Some("obj"),g.obj_ty,false,false,false,[])]],Some g.bool_ty)
|
|
|
|
|
|
let mspec_Object_GetType ilg = IL.mk_nongeneric_instance_mspec_in_nongeneric_boxed_tref(ilg.tref_Object,"GetType",[],ilg.typ_Type)
|
|
let mspec_Object_ToString ilg = IL.mk_nongeneric_instance_mspec_in_nongeneric_boxed_tref(ilg.tref_Object,"ToString",[],ilg.typ_String)
|
|
let mk_call_Object_GetType_GetString g m e1 =
|
|
mk_asm ([ IL.mk_normal_callvirt(mspec_Object_ToString g.ilg) ], [],
|
|
[ mk_asm ([ IL.mk_normal_callvirt(mspec_Object_GetType g.ilg) ], [], [ e1; ], [ g.int_ty ], m) ], [ g.string_ty ], m)
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Helpers associated with code-generation of comparison/hash augmentations
|
|
//-------------------------------------------------------------------------
|
|
|
|
let mk_this_typ g ty = if is_struct_typ g ty then mk_byref_typ g ty else ty
|
|
|
|
let mk_compare_obj_typ g ty = (mk_this_typ g ty) --> (g.obj_ty --> g.int_ty)
|
|
let mk_compare_typ g ty = (mk_this_typ g ty) --> (ty --> g.int_ty)
|
|
let mk_compare_withc_typ g ty = (mk_this_typ g ty) --> ((mk_tuple_ty [g.obj_ty ; g.mk_IComparer_ty]) --> g.int_ty)
|
|
|
|
let mk_equals_obj_typ g ty = (mk_this_typ g ty) --> (g.obj_ty --> g.bool_ty)
|
|
let mk_equals_typ g ty = (mk_this_typ g ty) --> (ty --> g.bool_ty)
|
|
let mk_equals_withc_typ g ty = (mk_this_typ g ty) --> ((mk_tuple_ty [g.obj_ty ; g.mk_IEqualityComparer_ty]) --> g.bool_ty)
|
|
|
|
let mk_hash_withc_typ g ty = (mk_this_typ g ty) --> (g.mk_IEqualityComparer_ty --> g.int_ty)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Polymorphic comparison
|
|
//-------------------------------------------------------------------------
|
|
|
|
let mk_rel_binop g op m e1 e2 = mk_asm ([ IL.I_arith op ],[], [e1; e2],[g.bool_ty],m)
|
|
let mk_clt g m e1 e2 = mk_rel_binop g IL.AI_clt m e1 e2
|
|
let mk_cgt g m e1 e2 = mk_rel_binop g IL.AI_cgt m e1 e2
|
|
|
|
//-------------------------------------------------------------------------
|
|
// REVIEW: make this a .constrained call, not a virtual call.
|
|
//-------------------------------------------------------------------------
|
|
|
|
// for creating and using FSharpComparer objects and for creating and using
|
|
// IStructuralComparable objects (Eg, Calling CompareTo(obj o, IComparer comp))
|
|
let icomparer_iltref g = g.tcref_System_Collections_IComparer.CompiledRepresentationForTyrepNamed
|
|
let icomparer_ilt g = mk_boxed_typ (icomparer_iltref g) []
|
|
let istructuralcomparable_iltref g = g.tcref_System_IStructuralComparable.CompiledRepresentationForTyrepNamed
|
|
|
|
let iequalitycomparer_iltref g = (g.tcref_System_Collections_IEqualityComparer).CompiledRepresentationForTyrepNamed
|
|
let iequalitycomparer_ilt g = mk_boxed_typ (iequalitycomparer_iltref g) []
|
|
let istructuralequatable_iltref g = (g.tcref_System_IStructuralEquatable).CompiledRepresentationForTyrepNamed
|
|
let langprim_iltref g = (g.tcref_LanguagePrimitives).CompiledRepresentationForTyrepNamed
|
|
let langprim_ilt g = mk_boxed_typ (langprim_iltref g) []
|
|
|
|
let mspec_getComparer g = mk_static_nongeneric_mspec_in_typ (langprim_ilt g, "FSharpComparer",[],icomparer_ilt g)
|
|
let mk_call_GetComparer g m =
|
|
mk_asm([IL.mk_normal_call(mspec_getComparer g)], [], [], [g.mk_IComparer_ty], m)
|
|
|
|
let mk_thisv g m ty = mk_local m "this" (mk_this_typ g ty)
|
|
|
|
let mk_shl g m acce n = mk_asm([ IL.mk_ldc_i32 n; IL.I_arith IL.AI_shl ],[],[acce],[g.int_ty],m)
|
|
let mk_shr g m acce n = mk_asm([ IL.mk_ldc_i32 n; IL.I_arith IL.AI_shr ],[],[acce],[g.int_ty],m)
|
|
let mk_add g m e1 e2 = mk_asm([ IL.I_arith IL.AI_add ],[],[e1;e2],[g.int_ty],m)
|
|
|
|
let add_to_hash_acc g m e accv acce =
|
|
mk_val_set m accv (mk_add g m (mk_int g m 0x9e3779b9)
|
|
(mk_add g m e
|
|
(mk_add g m (mk_shl g m acce 6) (mk_shr g m acce 2))))
|
|
|
|
|
|
let mk_combine_all_hash_generators g m exprs accv acce =
|
|
List.fold_left
|
|
(fun tm e -> mk_compgen_seq m (add_to_hash_acc g m e accv acce) tm)
|
|
acce
|
|
exprs
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Build comparison functions for union, record and exception types.
|
|
//-------------------------------------------------------------------------
|
|
|
|
let mk_thisv_thatv g m ty =
|
|
let thisv,thise = mk_thisv g m ty
|
|
let thatv,thate = mk_local m "obj" (mk_this_typ g ty)
|
|
thisv,thatv,thise,thate
|
|
|
|
let bind_thatv g m ty thatv expr =
|
|
if is_struct_typ g ty then
|
|
let thatv2,_ = mk_mut_compgen_local m "obj" ty
|
|
thatv2,mk_compgen_let m thatv (mk_val_addr m (mk_local_vref thatv2)) expr
|
|
else thatv,expr
|
|
|
|
let mk_thatcast g m ty =
|
|
if is_struct_typ g ty then
|
|
mk_mut_compgen_local m "thatCast" (mk_byref_typ g ty)
|
|
else
|
|
mk_compgen_local m "thatCast" ty
|
|
|
|
let bind_thatcast g m ty thatcastv thatv thate expr =
|
|
if is_struct_typ g ty then
|
|
mk_compgen_let m thatcastv (mk_val_addr m (mk_local_vref thatv)) expr
|
|
else
|
|
mk_compgen_let m thatcastv thate expr
|
|
|
|
let mk_compare_test_conjs g m exprs =
|
|
match exprs with
|
|
| [] -> mk_zero g m
|
|
| [h] -> h
|
|
| l ->
|
|
let a,b = List.frontAndBack l
|
|
(a,b) ||> List.foldBack (fun e acc ->
|
|
let nv,ne = mk_local m "n" g.int_ty
|
|
mk_compgen_let m nv e
|
|
(mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.int_ty
|
|
(mk_clt g m ne (mk_zero g m))
|
|
ne
|
|
(mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.int_ty
|
|
(mk_cgt g m ne (mk_zero g m))
|
|
ne
|
|
acc)))
|
|
|
|
let mk_equals_test_conjs g m exprs =
|
|
match exprs with
|
|
| [] -> mk_one g m
|
|
| [h] -> h
|
|
| l ->
|
|
let a,b = List.frontAndBack l
|
|
List.foldBack (fun e acc -> mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.bool_ty e acc (mk_false g m)) a b
|
|
|
|
/// note: 'x == y' does not imply 'x = y' for NaN
|
|
let mk_physical_equality_equals_test g m tycon thise thate expr = expr
|
|
|
|
let minimal_type g (tcref:TyconRef) =
|
|
if tcref.Deref.IsExceptionDecl then [], g.exn_ty
|
|
else generalize_tcref tcref
|
|
|
|
/// Build the comparison implementation for a record type
|
|
let mk_recd_compare g tcref (tycon:Tycon) =
|
|
let m = tycon.Range
|
|
let fields = tycon.AllInstanceFieldsAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thatv,thise,thate = mk_thisv_thatv g m ty
|
|
let compe = mk_call_GetComparer g m
|
|
let mk_test (fspec:RecdField) =
|
|
let fty = fspec.FormalType
|
|
let fref = rfref_of_rfield tcref fspec
|
|
let m = fref.Range
|
|
mk_call_generic_comparison_withc_outer g m fty
|
|
compe
|
|
(mk_recd_field_get_via_expra(thise, fref, tinst, m))
|
|
(mk_recd_field_get_via_expra(thate, fref, tinst, m))
|
|
let expr = mk_compare_test_conjs g m (List.map mk_test fields)
|
|
let thatv,expr = bind_thatv g m ty thatv expr
|
|
thisv,thatv, expr
|
|
|
|
|
|
/// Build the comparison implementation for a record type when parameterized by a comparer
|
|
let mk_recd_compare_withc g tcref (tycon:Tycon) (thisv,thise) (_,thate) compe =
|
|
let m = tycon.Range
|
|
let fields = tycon.AllInstanceFieldsAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let tcv,tce = mk_compgen_local m "tempCast" ty // let tcv = thate
|
|
let thatcastv,thatcaste = mk_thatcast g m ty // let thatcastv = &tcv, if a struct
|
|
|
|
let mk_test (fspec:RecdField) =
|
|
let fty = fspec.FormalType
|
|
let fref = rfref_of_rfield tcref fspec
|
|
let m = fref.Range
|
|
let e1 = mk_recd_field_get_via_expra(thise, fref, tinst, m)
|
|
let e2 = mk_recd_field_get_via_expra(thatcaste, fref, tinst, m)
|
|
|
|
mk_call_generic_comparison_withc_outer g m fty
|
|
compe
|
|
(mk_recd_field_get_via_expra(thise, fref, tinst, m))
|
|
(mk_recd_field_get_via_expra(thatcaste, fref, tinst, m))
|
|
let expr = mk_compare_test_conjs g m (List.map mk_test fields)
|
|
|
|
let expr = bind_thatcast g m ty thatcastv tcv tce expr
|
|
// will be optimized away if not necessary
|
|
let expr = mk_compgen_let m tcv thate expr
|
|
expr
|
|
|
|
|
|
/// Build the equality implementation wrapper for a record type
|
|
let mk_recd_equality g tcref (tycon:Tycon) =
|
|
let m = tycon.Range
|
|
let fields = tycon.AllInstanceFieldsAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thatv,thise,thate = mk_thisv_thatv g m ty
|
|
let mk_test (fspec:RecdField) =
|
|
let fty = fspec.FormalType
|
|
let fref = rfref_of_rfield tcref fspec
|
|
let m = fref.Range
|
|
mk_call_generic_equality_outer g m fty
|
|
(mk_recd_field_get_via_expra(thise, fref, tinst, m))
|
|
(mk_recd_field_get_via_expra(thate, fref, tinst, m))
|
|
let expr = mk_equals_test_conjs g m (List.map mk_test fields)
|
|
let expr = mk_physical_equality_equals_test g m tycon thise thate expr
|
|
let thatv,expr = bind_thatv g m ty thatv expr
|
|
thisv,thatv,expr
|
|
|
|
/// Build the equality implementation for a record type when parameterized by a comparer
|
|
let mk_recd_equality_withc g tcref (tycon:Tycon) (thisv,thise) (thatv,thate) compe =
|
|
let m = tycon.Range
|
|
let fields = tycon.AllInstanceFieldsAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thatcastv,thatcaste = mk_thatcast g m ty
|
|
let tcv,tce = mk_compgen_local m "tempCast" ty
|
|
|
|
let mk_test (fspec:RecdField) =
|
|
let fty = fspec.FormalType
|
|
let fref = rfref_of_rfield tcref fspec
|
|
let m = fref.Range
|
|
let e1 = mk_recd_field_get_via_expra(thise, fref, tinst, m)
|
|
let e2 = mk_recd_field_get_via_expra(thatcaste, fref, tinst, m)
|
|
|
|
mk_call_generic_equality_withc_outer g m fty
|
|
compe
|
|
(mk_recd_field_get_via_expra(thise, fref, tinst, m))
|
|
(mk_recd_field_get_via_expra(thatcaste, fref, tinst, m))
|
|
let expr = mk_equals_test_conjs g m (List.map mk_test fields)
|
|
let expr = mk_physical_equality_equals_test g m tycon thise thatcaste expr
|
|
let expr = bind_thatcast g m ty thatcastv tcv tce expr
|
|
// will be optimized away if not necessary
|
|
let expr = mk_compgen_let m tcv thate expr
|
|
thisv,thatv,expr
|
|
|
|
|
|
/// Build the comparison implementation for an exception definition
|
|
let mk_exnconstr_compare g exnref (exnc:Tycon) =
|
|
let m = exnc.Range
|
|
let thatv,thate = mk_local m "obj" g.exn_ty
|
|
let thisv,thise = mk_thisv g m g.exn_ty
|
|
let compe = mk_call_GetComparer g m
|
|
let mk_test i (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
mk_call_generic_comparison_withc_outer g m fty
|
|
compe
|
|
(mk_exnconstr_field_get(thise, exnref, i, m))
|
|
(mk_exnconstr_field_get(thate, exnref, i, m))
|
|
let expr = mk_compare_test_conjs g m (List.mapi mk_test (exnc.AllInstanceFieldsAsList))
|
|
let expr =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let dtree =
|
|
TDSwitch(thate,
|
|
[ mk_case(TTest_isinst(g.exn_ty,mk_tyapp_ty exnref []),
|
|
mbuilder.AddResultTarget(expr,SuppressSequencePointAtTarget)) ],
|
|
// OK, this is gross - we are comparing types by comparing strings. We should be able to do this another way.
|
|
Some(mbuilder.AddResultTarget
|
|
(mk_call_string_compare g m
|
|
(mk_call_Object_GetType_GetString g m thise)
|
|
(mk_call_Object_GetType_GetString g m thate),
|
|
SuppressSequencePointAtTarget)),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.int_ty)
|
|
thisv,thatv,expr
|
|
|
|
|
|
/// Build the comparison implementation for an exception definition when parameterized by a comparer
|
|
let mk_exnconstr_compare_withc g exnref (exnc:Tycon) (thisv,thise) (thatv,thate) compe =
|
|
let m = exnc.Range
|
|
let thatcastv,thatcaste = mk_thatcast g m g.exn_ty
|
|
let mk_test i (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
let e1 = mk_exnconstr_field_get(thise, exnref, i, m)
|
|
let e2 = mk_exnconstr_field_get(thatcaste, exnref, i, m)
|
|
|
|
mk_call_generic_comparison_withc_outer g m fty
|
|
compe
|
|
(mk_exnconstr_field_get(thise, exnref, i, m))
|
|
(mk_exnconstr_field_get(thatcaste, exnref, i, m))
|
|
let expr = mk_compare_test_conjs g m (List.mapi mk_test (exnc.AllInstanceFieldsAsList))
|
|
let expr =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let dtree =
|
|
TDSwitch(thatcaste,
|
|
[ mk_case(TTest_isinst(g.exn_ty,mk_tyapp_ty exnref []),
|
|
mbuilder.AddResultTarget(expr,SuppressSequencePointAtTarget)) ],
|
|
// OK, this is gross - we are comparing types by comparing strings. We should be able to do this another way.
|
|
Some(mbuilder.AddResultTarget
|
|
(mk_call_string_compare g m
|
|
(mk_call_Object_GetType_GetString g m thise)
|
|
(mk_call_Object_GetType_GetString g m thatcaste),
|
|
SuppressSequencePointAtTarget)),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.int_ty)
|
|
let expr = bind_thatcast g m g.exn_ty thatcastv thatv thate expr
|
|
expr
|
|
|
|
|
|
/// Build the equality implementation for an exception definition
|
|
let mk_exnconstr_equality g exnref (exnc:Tycon) =
|
|
let m = exnc.Range
|
|
let thatv,thate = mk_local m "obj" g.exn_ty
|
|
let thisv,thise = mk_thisv g m g.exn_ty
|
|
let mk_test i (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
mk_call_generic_equality_outer g m fty
|
|
(mk_exnconstr_field_get(thise, exnref, i, m))
|
|
(mk_exnconstr_field_get(thate, exnref, i, m))
|
|
let expr = mk_equals_test_conjs g m (List.mapi mk_test (exnc.AllInstanceFieldsAsList))
|
|
let existential_tested =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let dtree =
|
|
TDSwitch(thate,
|
|
[ mk_case(TTest_isinst(g.exn_ty,mk_tyapp_ty exnref []),
|
|
mbuilder.AddResultTarget(expr,SuppressSequencePointAtTarget)) ],
|
|
Some(mbuilder.AddResultTarget(mk_false g m,SuppressSequencePointAtTarget)),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.bool_ty)
|
|
let eqTested = mk_physical_equality_equals_test g m exnc thise thate existential_tested
|
|
thisv,thatv, eqTested
|
|
|
|
|
|
/// Build the equality implementation for an exception definition when parameterized by a comparer
|
|
let mk_exnconstr_equality_withc g exnref (exnc:Tycon) (thisv,thise) (thatv,thate) compe =
|
|
let m = exnc.Range
|
|
let thatcastv,thatcaste = mk_thatcast g m g.exn_ty
|
|
let mk_test i (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
let e1 = mk_exnconstr_field_get(thise, exnref, i, m)
|
|
let e2 = mk_exnconstr_field_get(thatcaste, exnref, i, m)
|
|
mk_call_generic_equality_withc_outer g m fty
|
|
compe
|
|
(mk_exnconstr_field_get(thise, exnref, i, m))
|
|
(mk_exnconstr_field_get(thatcaste, exnref, i, m))
|
|
let expr = mk_equals_test_conjs g m (List.mapi mk_test (exnc.AllInstanceFieldsAsList))
|
|
let existential_tested =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let dtree =
|
|
TDSwitch(thatcaste,
|
|
[ mk_case(TTest_isinst(g.exn_ty,mk_tyapp_ty exnref []),
|
|
mbuilder.AddResultTarget(expr,SuppressSequencePointAtTarget)) ],
|
|
Some(mbuilder.AddResultTarget(mk_false g m,SuppressSequencePointAtTarget)),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.bool_ty)
|
|
let eqTested = mk_physical_equality_equals_test g m exnc thise thatcaste existential_tested
|
|
let eqTested = bind_thatcast g m g.exn_ty thatcastv thatv thate eqTested
|
|
thisv,thatv, eqTested
|
|
|
|
/// Build the comparison implementation for a union type
|
|
let mk_union_compare g tcref (tycon:Tycon) =
|
|
let m = tycon.Range
|
|
let ucases = tycon.UnionCasesAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thise = mk_local m "this" ty
|
|
let thatv,thate = mk_local m "obj" ty
|
|
let thistagv,thistage = mk_compgen_local m "thisTag" g.int_ty
|
|
let thattagv,thattage = mk_compgen_local m "thatTag" g.int_ty
|
|
let compe = mk_call_GetComparer g m
|
|
|
|
let expr1 =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let mk_constr_case ucase =
|
|
let cref = ucref_of_ucase tcref ucase
|
|
let m = cref.Range
|
|
let thisucv,thisucve = mk_compgen_local m "thisCast" (mk_proven_ucase_typ cref tinst)
|
|
let thatucv,thatucve = mk_compgen_local m "thatCast" (mk_proven_ucase_typ cref tinst)
|
|
let mk_test j (argty:RecdField) =
|
|
mk_call_generic_comparison_withc_outer g m argty.FormalType
|
|
compe
|
|
(mk_ucase_field_get_proven(thisucve, cref, tinst, j, m))
|
|
(mk_ucase_field_get_proven(thatucve, cref, tinst, j, m))
|
|
let rfields = ucase.RecdFields
|
|
if isNil rfields then None else
|
|
Some (mk_case(TTest_unionconstr(cref,tinst),
|
|
mbuilder.AddResultTarget
|
|
(mk_compgen_let m thisucv (mk_ucase_proof(thise,cref,tinst,m))
|
|
(mk_compgen_let m thatucv (mk_ucase_proof(thate,cref,tinst,m))
|
|
(mk_compare_test_conjs g m (List.mapi mk_test rfields))),
|
|
SuppressSequencePointAtTarget)))
|
|
|
|
let nullary,nonNullary = List.partition isNone (List.map mk_constr_case ucases)
|
|
if isNil nonNullary then mk_zero g m else
|
|
let dtree =
|
|
TDSwitch(thise,
|
|
(nonNullary |> List.map (function (Some c) -> c | None -> failwith "mk_union_compare")),
|
|
(if isNil nullary then None
|
|
else Some (mbuilder.AddResultTarget(mk_zero g m,SuppressSequencePointAtTarget))),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.int_ty)
|
|
|
|
let getTags =
|
|
if ucases.Length = 1 then expr1 else
|
|
let tagsEqTested =
|
|
mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.int_ty
|
|
(mk_ceq g m thistage thattage)
|
|
expr1
|
|
(mk_asm ([ IL.I_arith IL.AI_sub ],[], [thistage; thattage],[g.int_ty],m))in
|
|
mk_compgen_let m thistagv
|
|
(mk_ucase_tag_get (thise,tcref,tinst,m))
|
|
(mk_compgen_let m thattagv
|
|
(mk_ucase_tag_get (thate,tcref,tinst,m))
|
|
tagsEqTested)
|
|
|
|
let nullTestedThat = mk_nonnull_cond g m g.int_ty thate getTags (mk_one g m)
|
|
let nullTestedThis = mk_nonnull_cond g m g.int_ty thise nullTestedThat (mk_minus_one g m)
|
|
thisv,thatv, nullTestedThis
|
|
|
|
|
|
/// Build the comparison implementation for a union type when parameterized by a comparer
|
|
let mk_union_compare_withc g tcref (tycon:Tycon) (thisv,thise) (thatv,thate) compe =
|
|
let m = tycon.Range
|
|
let ucases = tycon.UnionCasesAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thistagv,thistage = mk_compgen_local m "thisTag" g.int_ty
|
|
let thattagv,thattage = mk_compgen_local m "thatTag" g.int_ty
|
|
let thatcastv,thatcaste = mk_thatcast g m ty
|
|
|
|
let expr1 =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let mk_constr_case ucase =
|
|
let cref = ucref_of_ucase tcref ucase
|
|
let m = cref.Range
|
|
let thisucv,thisucve = mk_compgen_local m "thisCastu" (mk_proven_ucase_typ cref tinst)
|
|
let thatucv,thatucve = mk_compgen_local m "thatCastu" (mk_proven_ucase_typ cref tinst)
|
|
let mk_test j (argty:RecdField) =
|
|
let e1 = mk_ucase_field_get_proven(thisucve, cref, tinst, j, m)
|
|
let e2 = mk_ucase_field_get_proven(thatucve, cref, tinst, j, m)
|
|
mk_call_generic_comparison_withc_outer g m argty.FormalType
|
|
compe
|
|
(mk_ucase_field_get_proven(thisucve, cref, tinst, j, m))
|
|
(mk_ucase_field_get_proven(thatucve, cref, tinst, j, m))
|
|
let rfields = ucase.RecdFields
|
|
if isNil rfields then None else
|
|
Some (mk_case(TTest_unionconstr(cref,tinst),
|
|
mbuilder.AddResultTarget
|
|
(mk_compgen_let m thisucv (mk_ucase_proof(thise,cref,tinst,m))
|
|
(mk_compgen_let m thatucv (mk_ucase_proof(thatcaste,cref,tinst,m))
|
|
(mk_compare_test_conjs g m (List.mapi mk_test rfields))),
|
|
SuppressSequencePointAtTarget)))
|
|
|
|
let nullary,nonNullary = List.partition isNone (List.map mk_constr_case ucases)
|
|
if isNil nonNullary then mk_zero g m else
|
|
let dtree =
|
|
TDSwitch(thise,
|
|
(nonNullary |> List.map (function (Some c) -> c | None -> failwith "mk_union_compare")),
|
|
(if isNil nullary then None
|
|
else Some (mbuilder.AddResultTarget(mk_zero g m,SuppressSequencePointAtTarget))),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.int_ty)
|
|
|
|
let getTags =
|
|
if ucases.Length = 1 then expr1 else
|
|
let tagsEqTested =
|
|
mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.int_ty
|
|
(mk_ceq g m thistage thattage)
|
|
expr1
|
|
(mk_asm ([ IL.I_arith IL.AI_sub ],[], [thistage; thattage],[g.int_ty],m))in
|
|
mk_compgen_let m thistagv
|
|
(mk_ucase_tag_get (thise,tcref,tinst,m))
|
|
(mk_compgen_let m thattagv
|
|
(mk_ucase_tag_get (thatcaste,tcref,tinst,m))
|
|
tagsEqTested)
|
|
|
|
let nullTestedThat = mk_nonnull_cond g m g.int_ty thatcaste getTags (mk_one g m)
|
|
let nullTestedThis = mk_nonnull_cond g m g.int_ty thise nullTestedThat (mk_minus_one g m)
|
|
let eqTested = bind_thatcast g m ty thatcastv thatv thate nullTestedThis
|
|
eqTested
|
|
|
|
|
|
/// Build the equality implementation for a union type
|
|
let mk_union_equality g tcref (tycon:Tycon) =
|
|
let m = tycon.Range
|
|
let ucases = tycon.UnionCasesAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thise = mk_local m "this" ty
|
|
let thatv,thate = mk_local m "obj" ty
|
|
let thistagv,thistage = mk_compgen_local m "thisTag" g.int_ty
|
|
let thattagv,thattage = mk_compgen_local m "thatTag" g.int_ty
|
|
|
|
let expr1 =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let mk_constr_case ucase =
|
|
let cref = ucref_of_ucase tcref ucase
|
|
let m = cref.Range
|
|
let thisucv,thisucve = mk_compgen_local m "thisCast" (mk_proven_ucase_typ cref tinst)
|
|
let thatucv,thatucve = mk_compgen_local m "thatCast" (mk_proven_ucase_typ cref tinst)
|
|
let mk_test j (argty:RecdField) =
|
|
mk_call_generic_equality_outer g m argty.FormalType
|
|
(mk_ucase_field_get_proven(thisucve, cref, tinst, j, m))
|
|
(mk_ucase_field_get_proven(thatucve, cref, tinst, j, m))
|
|
let rfields = ucase.RecdFields
|
|
if isNil rfields then None else
|
|
Some (mk_case(TTest_unionconstr(cref,tinst),
|
|
mbuilder.AddResultTarget
|
|
(mk_compgen_let m thisucv (mk_ucase_proof(thise,cref,tinst,m))
|
|
(mk_compgen_let m thatucv (mk_ucase_proof(thate,cref,tinst,m))
|
|
(mk_equals_test_conjs g m (List.mapi mk_test rfields))),
|
|
SuppressSequencePointAtTarget)))
|
|
|
|
let nullary,nonNullary = List.partition isNone (List.map mk_constr_case ucases)
|
|
if isNil nonNullary then mk_true g m else
|
|
let dtree =
|
|
TDSwitch(thise,List.map (function (Some c) -> c | None -> failwith "mk_union_equality") nonNullary,
|
|
(if isNil nullary then None else Some (mbuilder.AddResultTarget(mk_true g m,SuppressSequencePointAtTarget))),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.bool_ty)
|
|
|
|
let getTags =
|
|
if ucases.Length = 1 then expr1 else
|
|
let tagsEqTested =
|
|
mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.bool_ty
|
|
(mk_ceq g m thistage thattage)
|
|
expr1
|
|
(mk_false g m)
|
|
|
|
mk_compgen_let m thistagv
|
|
(mk_ucase_tag_get (thise,tcref,tinst,m))
|
|
(mk_compgen_let m thattagv
|
|
(mk_ucase_tag_get (thate,tcref,tinst,m))
|
|
tagsEqTested)
|
|
let nullTestedThat = mk_nonnull_cond g m g.bool_ty thate getTags (mk_false g m)
|
|
let nullTestedThis = mk_nonnull_cond g m g.bool_ty thise nullTestedThat (mk_false g m)
|
|
let eqTested =
|
|
if is_unit_typ g ty then mk_true g m else
|
|
mk_physical_equality_equals_test g m tycon thise thate nullTestedThis
|
|
thisv,thatv, eqTested
|
|
|
|
|
|
/// Build the equality implementation for a union type when parameterized by a comparer
|
|
let mk_union_equality_withc g tcref (tycon:Tycon) (thisv,thise) (thatv,thate) compe =
|
|
let m = tycon.Range
|
|
let ucases = tycon.UnionCasesAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thistagv,thistage = mk_compgen_local m "thisTag" g.int_ty
|
|
let thattagv,thattage = mk_compgen_local m "thatTag" g.int_ty
|
|
let thatcastv,thatcaste = mk_thatcast g m ty
|
|
|
|
let expr1 =
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let mk_constr_case ucase =
|
|
let cref = ucref_of_ucase tcref ucase
|
|
let m = cref.Range
|
|
let thisucv,thisucve = mk_compgen_local m "thisCastu" (mk_proven_ucase_typ cref tinst)
|
|
let thatucv,thatucve = mk_compgen_local m "thatCastu" (mk_proven_ucase_typ cref tinst)
|
|
let mk_test j (argty:RecdField) =
|
|
let e1 = mk_ucase_field_get_proven(thisucve, cref, tinst, j, m)
|
|
let e2 = mk_ucase_field_get_proven(thatucve, cref, tinst, j, m)
|
|
mk_call_generic_equality_withc_outer g m argty.FormalType
|
|
compe
|
|
(mk_ucase_field_get_proven(thisucve, cref, tinst, j, m))
|
|
(mk_ucase_field_get_proven(thatucve, cref, tinst, j, m))
|
|
let rfields = ucase.RecdFields
|
|
if isNil rfields then None else
|
|
Some (mk_case(TTest_unionconstr(cref,tinst),
|
|
mbuilder.AddResultTarget
|
|
(mk_compgen_let m thisucv (mk_ucase_proof(thise,cref,tinst,m))
|
|
(mk_compgen_let m thatucv (mk_ucase_proof(thatcaste,cref,tinst,m))
|
|
(mk_equals_test_conjs g m (List.mapi mk_test rfields))),
|
|
SuppressSequencePointAtTarget)))
|
|
|
|
let nullary,nonNullary = List.partition isNone (List.map mk_constr_case ucases)
|
|
if isNil nonNullary then mk_true g m else
|
|
let dtree =
|
|
TDSwitch(thise,List.map (function (Some c) -> c | None -> failwith "mk_union_equality") nonNullary,
|
|
(if isNil nullary then None else Some (mbuilder.AddResultTarget(mk_true g m,SuppressSequencePointAtTarget))),
|
|
m)
|
|
mbuilder.Close(dtree,m,g.bool_ty)
|
|
|
|
let getTags =
|
|
if ucases.Length = 1 then expr1 else
|
|
let tagsEqTested =
|
|
mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.bool_ty
|
|
(mk_ceq g m thistage thattage)
|
|
expr1
|
|
(mk_false g m)
|
|
|
|
mk_compgen_let m thistagv
|
|
(mk_ucase_tag_get (thise,tcref,tinst,m))
|
|
(mk_compgen_let m thattagv
|
|
(mk_ucase_tag_get (thatcaste,tcref,tinst,m))
|
|
tagsEqTested)
|
|
let nullTestedThat = mk_nonnull_cond g m g.bool_ty thatcaste getTags (mk_false g m)
|
|
let nullTestedThis = mk_nonnull_cond g m g.bool_ty thise nullTestedThat (mk_false g m)
|
|
let eqTested =
|
|
if is_unit_typ g ty then mk_true g m else
|
|
mk_physical_equality_equals_test g m tycon thise thatcaste nullTestedThis
|
|
let eqTested = bind_thatcast g m ty thatcastv thatv thate eqTested
|
|
thisv,thatv, eqTested
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Build hashing functions for union, record and exception types.
|
|
// Hashing functions must respect the "=" and comparison operators.
|
|
//-------------------------------------------------------------------------
|
|
|
|
/// Structural hash implementation for record types when parameterized by a comparer
|
|
let mk_recd_hash_withc g tcref (tycon:Tycon) compe =
|
|
let m = tycon.Range
|
|
let fields = tycon.AllInstanceFieldsAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thise = mk_thisv g m ty
|
|
let mk_field_hash (fspec:RecdField) =
|
|
let fty = fspec.FormalType
|
|
let fref = rfref_of_rfield tcref fspec
|
|
let m = fref.Range
|
|
let e = mk_recd_field_get_via_expra(thise, fref, tinst, m)
|
|
|
|
mk_call_generic_hash_withc_outer g m fty compe e
|
|
|
|
let accv,acce = mk_mut_compgen_local m "i" g.int_ty
|
|
let stmt = mk_combine_all_hash_generators g m (List.map mk_field_hash fields) (mk_local_vref accv) acce
|
|
let expr = mk_compgen_let m accv (mk_zero g m) stmt
|
|
thisv,expr
|
|
|
|
/// Structural hash implementation for exception types when parameterized by a comparer
|
|
let mk_exnconstr_hash_withc g exnref (exnc:Tycon) compe =
|
|
let m = exnc.Range
|
|
let thisv,thise = mk_thisv g m g.exn_ty
|
|
|
|
let mk_hash i (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
let e = mk_exnconstr_field_get(thise, exnref, i, m)
|
|
|
|
mk_call_generic_hash_withc_outer g m fty compe e
|
|
|
|
let accv,acce = mk_mut_compgen_local m "i" g.int_ty
|
|
let stmt = mk_combine_all_hash_generators g m (List.mapi mk_hash (exnc.AllInstanceFieldsAsList)) (mk_local_vref accv) acce
|
|
let expr = mk_compgen_let m accv (mk_zero g m) stmt
|
|
thisv,expr
|
|
|
|
/// Structural hash implementation for union types when parameterized by a comparer
|
|
let mk_union_hash_withc g tcref (tycon:Tycon) compe =
|
|
let m = tycon.Range
|
|
let ucases = tycon.UnionCasesAsList
|
|
let tinst,ty = minimal_type g tcref
|
|
let thisv,thise = mk_thisv g m ty
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m )
|
|
let accv,acce = mk_mut_compgen_local m "i" g.int_ty
|
|
let mk_constr_case i ucase1 =
|
|
let c1ref = ucref_of_ucase tcref ucase1
|
|
let ucv,ucve = mk_compgen_local m "unionCase" (mk_proven_ucase_typ c1ref tinst)
|
|
let m = c1ref.Range
|
|
let mk_hash j (rfield:RecdField) =
|
|
let fty = rfield.FormalType
|
|
let e = mk_ucase_field_get_proven(ucve, c1ref, tinst, j, m)
|
|
mk_call_generic_hash_withc_outer g m fty compe e
|
|
mk_case(TTest_unionconstr(c1ref,tinst),
|
|
mbuilder.AddResultTarget
|
|
(mk_compgen_let m ucv
|
|
(mk_ucase_proof(thise,c1ref,tinst,m))
|
|
(mk_compgen_seq m
|
|
(mk_val_set m (mk_local_vref accv) (mk_int g m i))
|
|
(mk_combine_all_hash_generators g m (List.mapi mk_hash ucase1.RecdFields) (mk_local_vref accv) acce)),
|
|
SuppressSequencePointAtTarget))
|
|
let dtree = TDSwitch(thise,List.mapi mk_constr_case ucases, None,m)
|
|
let stmt = mbuilder.Close(dtree,m,g.unit_ty)
|
|
let expr = mk_compgen_let m accv (mk_zero g m) stmt
|
|
thisv,expr
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// The predicate that determines which types implement the
|
|
// pre-baked IStructuralHash and IComparable semantics associated with F#
|
|
// types. Note abstract types are not _known_ to implement these interfaces,
|
|
// though the interfaces may be discoverable via type tests.
|
|
//-------------------------------------------------------------------------
|
|
|
|
let isNominalExnc (exnc:Tycon) =
|
|
match exnc.ExceptionInfo with
|
|
| TExnAbbrevRepr _ | TExnNone | TExnAsmRepr _ -> false
|
|
| TExnFresh _ -> true
|
|
|
|
let isTrueFSharpStructTycon g (tycon: Tycon) =
|
|
(tycon.IsFSharpStructTycon && not tycon.IsFSharpEnumTycon)
|
|
|
|
let canBeAugmented g (tycon:Tycon) =
|
|
tycon.IsUnionTycon ||
|
|
tycon.IsRecordTycon ||
|
|
(tycon.IsExceptionDecl && isNominalExnc tycon) ||
|
|
isTrueFSharpStructTycon g tycon
|
|
|
|
let augmentation_attribs g (tycon:Tycon) =
|
|
canBeAugmented g tycon,
|
|
TryFindBoolAttrib g g.attrib_ReferenceEqualityAttribute tycon.Attribs,
|
|
TryFindBoolAttrib g g.attrib_StructuralEqualityAttribute tycon.Attribs,
|
|
TryFindBoolAttrib g g.attrib_StructuralComparisonAttribute tycon.Attribs
|
|
|
|
let CheckAugmentationAttribs g (tycon:Tycon)=
|
|
let m = tycon.Range
|
|
let attribs = augmentation_attribs g tycon
|
|
match attribs with
|
|
|
|
(* THESE ARE THE LEGITIMATE CASES *)
|
|
|
|
| true, Some(true), None , None ->
|
|
if isTrueFSharpStructTycon g tycon then
|
|
errorR(Error("The 'ReferenceEquality' attribute may not be used on structs. Consider using the 'StructuralEquality' attribute instead, or implement an override for 'System.Object.Equals(obj)'", m))
|
|
else ()
|
|
|
|
(* [< >] *)
|
|
| _, None, None, None
|
|
(* [<ReferenceEquality(true)>] *)
|
|
(* [<StructuralEquality(true); StructuralComparison(true)>] *)
|
|
| true, None , Some(true), Some(true)
|
|
(* [<StructuralEquality(false); StructuralComparison(false)>] *)
|
|
| true, None , Some(false), Some(false)
|
|
(* [<StructuralEquality(true); StructuralComparison(false)>] *)
|
|
| true, None , Some(true), Some(false) ->
|
|
()
|
|
|
|
(* THESE ARE THE ERROR CASES *)
|
|
|
|
(* [<ReferenceEquality(false); ...>] *)
|
|
| _, Some(false), _ , _ ->
|
|
errorR(Error("The 'ReferenceEquality' attribute may not be 'false'. Consider using the 'StructuralEquality' attribute instead", m))
|
|
(* [<StructuralEquality(false); ...>] *)
|
|
| _, _, Some(false), None
|
|
| _, _, Some(false), Some(true) ->
|
|
errorR(Error("The 'StructuralEquality' attribute may not be 'false' unles the 'StructuralComparison' attribute is also false", m))
|
|
(* [<StructuralComparison(_)>] *)
|
|
| true, None , None , Some(_) ->
|
|
errorR(Error("The 'StructuralComparison' attribute must be used in conjunction with the 'StructuralEquality' attribute", m))
|
|
(* [<StructuralEquality(_)>] *)
|
|
| true, None , Some(true), None ->
|
|
errorR(Error("The 'StructuralEquality' attribute must be used in conjunction with the 'StructuralComparison' attribute", m))
|
|
|
|
(* [<ReferenceEquality; StructuralEquality>] *)
|
|
| true, Some(_) , Some(_) , _
|
|
(* [<ReferenceEquality; StructuralComparison(_) >] *)
|
|
| true, Some(_), _, Some(_) ->
|
|
errorR(Error("A type may not have both the 'ReferenceEquality' and 'StructuralEquality' or 'StructuralComparison' attributes", m))
|
|
|
|
(* non augmented type, [<ReferenceEquality; ... >] *)
|
|
| false, Some(_), _, _
|
|
(* non augmented type, [<StructuralEquality; ... >] *)
|
|
| false, _, Some(_) , _
|
|
(* non augmented type, [<StructuralComparison(_); ... >] *)
|
|
| false, _, _ , Some(_) ->
|
|
errorR(Error("Only record, union, exception and struct types may be augmented with the 'ReferenceEquality', 'StructuralEquality' and 'StructuralComparison' attributes", m))
|
|
let tcaug = tycon.TypeContents
|
|
|
|
let hasExplicitICompare =
|
|
isNone tcaug.tcaug_compare
|
|
let hasExplicitIStructuralCompare =
|
|
isNone tcaug.tcaug_compare_withc
|
|
let hasExplicitEquals =
|
|
isNone tcaug.tcaug_equals && tcaug_has_override g tcaug "Equals" [g.obj_ty]
|
|
|
|
match attribs with
|
|
(* [<ReferenceEquality(true)>] *)
|
|
| _, Some(true), _, _ when hasExplicitEquals ->
|
|
errorR(Error("A type with attribute 'ReferenceEquality' may not have an explicit implementation of 'Object.Equals(obj)'", m))
|
|
| _, _, Some(true), _ when hasExplicitEquals ->
|
|
errorR(Error("A type with attribute 'StructuralEquality' may not have an explicit implementation of 'Object.Equals(obj)'", m))
|
|
// already caught the case where ReferenceEquality is true
|
|
| _, None, _, Some(true) when hasExplicitICompare ->
|
|
errorR(Error("A type with attribute 'StructuralComparison' may not have an explicit implementation of 'System.IComparable'", m))
|
|
| _, Some(false), _, Some(true) when hasExplicitICompare ->
|
|
errorR(Error("A type with attribute 'StructuralComparison' may not have an explicit implementation of 'System.IComparable'", m))
|
|
| _ -> ()
|
|
|
|
let TyconIsAugmentedWithCompare g (tycon:Tycon) =
|
|
// This type gets defined in prim-types, before we can add attributes to F# type definitions
|
|
let isUnit = g.compilingFslib && tycon.DisplayName = "Unit"
|
|
not isUnit &&
|
|
|
|
match augmentation_attribs g tycon with
|
|
(* [< >] *)
|
|
| true, None, None , None
|
|
(* [<StructuralEquality(true); StructuralComparison(true)>] *)
|
|
| true, None, Some(true), Some(true) -> true
|
|
(* other cases *)
|
|
| _ -> false
|
|
|
|
let TyconIsAugmentedWithEquals g (tycon:Tycon) =
|
|
// This type gets defined in prim-types, before we can add attributes to F# type definitions
|
|
let isUnit = g.compilingFslib && tycon.DisplayName = "Unit"
|
|
not isUnit &&
|
|
|
|
match augmentation_attribs g tycon with
|
|
(* [< >] *)
|
|
| true, None, None , _
|
|
(* [<StructuralEquality(true); _ >] *)
|
|
(* [<StructuralEquality(true); StructuralComparison(true)>] *)
|
|
| true, None, Some(true), _ -> true
|
|
(* other cases *)
|
|
| _ -> false
|
|
|
|
let TyconIsAugmentedWithHash g tycon = TyconIsAugmentedWithEquals g tycon
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* Make values that represent the implementations of the
|
|
* IComparable semantics associated with F# types.
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let slotImplMethod (final,ilnm,c,slotsig) =
|
|
{ ImplementedSlotSigs=[slotsig];
|
|
MemberFlags=
|
|
{ OverloadQualifier=None;
|
|
MemberIsInstance=true;
|
|
MemberIsVirtual=false;
|
|
MemberIsDispatchSlot=false;
|
|
MemberIsFinal=final;
|
|
MemberIsOverrideOrExplicitImpl=true;
|
|
MemberKind=MemberKindMember};
|
|
IsImplemented=false;
|
|
CompiledName=ilnm;
|
|
ApparentParent=c}
|
|
|
|
let nonVirtualMethod (ilnm,c) =
|
|
{ ImplementedSlotSigs=[];
|
|
MemberFlags={ OverloadQualifier=None;
|
|
MemberIsInstance=true;
|
|
MemberIsVirtual=false;
|
|
MemberIsDispatchSlot=false;
|
|
MemberIsFinal=false;
|
|
MemberIsOverrideOrExplicitImpl=false;
|
|
MemberKind=MemberKindMember};
|
|
IsImplemented=false;
|
|
CompiledName=ilnm;
|
|
ApparentParent=c}
|
|
|
|
let mk_vspec g (tcref:TyconRef) isStronglyTyped tmty cpath vis methn slotsig ilnm ty tuparg =
|
|
let m = tcref.Range
|
|
let tps = tcref.Typars(m)
|
|
let id = mksyn_id m methn
|
|
let final = is_union_typ g tmty or is_recd_typ g tmty or is_struct_typ g tmty
|
|
let membInfo = match slotsig with None -> nonVirtualMethod (ilnm,tcref) | Some(slotsig) -> slotImplMethod(final,ilnm,tcref,slotsig)
|
|
let inl = OptionalInline
|
|
let args = if tuparg then [TopValInfo.unnamedTopArg; [TopValInfo.unnamedTopArg1;TopValInfo.unnamedTopArg1]] else [TopValInfo.unnamedTopArg;TopValInfo.unnamedTopArg]
|
|
let topValInfo = Some (TopValInfo (TopValInfo.InferTyparInfo tps,args,TopValInfo.unnamedRetVal))
|
|
NewVal (id,ty,Immutable,true,topValInfo,cpath,vis,ValNotInRecScope,Some(membInfo),NormalVal,[],inl,emptyXmlDoc,true,false,false,false,None,Parent(tcref))
|
|
|
|
let MakeValsForCompareAugmentation g (tcref:TyconRef) =
|
|
let _,tmty = minimal_type g tcref
|
|
let tps = tcref.Typars(tcref.Range)
|
|
let vis = tcref.TypeReprAccessibility
|
|
mk_vspec g tcref false tmty tcref.CompilationPathOpt vis (tcref.MangledName^".CompareToOverride" ) (Some(mk_IComparable_CompareTo_slotsig g)) "CompareTo" (tps +-> (mk_compare_obj_typ g tmty)) false,
|
|
mk_vspec g tcref true tmty tcref.CompilationPathOpt vis (tcref.MangledName^".CompareTo" ) None "CompareTo" (tps +-> (mk_compare_typ g tmty)) false
|
|
|
|
let MakeValsForCompareWithComparerAugmentation g (tcref:TyconRef) =
|
|
let _,tmty = minimal_type g tcref
|
|
let tps = tcref.Typars(tcref.Range)
|
|
let vis = tcref.TypeReprAccessibility
|
|
mk_vspec g tcref false tmty tcref.CompilationPathOpt vis (tcref.MangledName^".StructuralCompareTo") (Some(mk_IStructuralComparable_CompareTo_slotsig g)) "CompareTo" (tps +-> (mk_compare_withc_typ g tmty)) true
|
|
|
|
let MakeValsForEqualsAugmentation g (tcref:TyconRef) =
|
|
let _,tmty = minimal_type g tcref
|
|
let vis = tcref.TypeReprAccessibility
|
|
let tps = tcref.Typars(tcref.Range)
|
|
let obj_vspec = mk_vspec g tcref false tmty tcref.CompilationPathOpt vis (tcref.MangledName^".EqualsOverride" ) (Some(mk_Equals_slotsig g)) "Equals" (tps +-> (mk_equals_obj_typ g tmty)) false
|
|
let vspec = mk_vspec g tcref true tmty tcref.CompilationPathOpt vis (tcref.MangledName^".Equals" ) None "Equals" (tps +-> (mk_equals_typ g tmty)) false
|
|
obj_vspec,vspec
|
|
|
|
let MakeValsForEqualityWithComparerAugmentation g (tcref:TyconRef) =
|
|
let _,tmty = minimal_type g tcref
|
|
let vis = tcref.TypeReprAccessibility
|
|
let tps = tcref.Typars(tcref.Range)
|
|
let hsh = mk_vspec g tcref false tmty tcref.CompilationPathOpt vis (tcref.MangledName^".StructuralGetHashCode") (Some(mk_IStructuralEquatable_GetHashCode_slotsig g)) "GetHashCode" (tps +-> (mk_hash_withc_typ g tmty)) false
|
|
let eq = mk_vspec g tcref false tmty tcref.CompilationPathOpt vis (tcref.MangledName^".StructuralEquals") (Some(mk_IStructuralEquatable_Equals_slotsig g)) "Equals" (tps +-> (mk_equals_withc_typ g tmty)) true
|
|
hsh,eq
|
|
|
|
let MakeBindingsForCompareAugmentation g (tycon:Tycon) =
|
|
let tcref = mk_local_tcref tycon
|
|
let m = tycon.Range
|
|
let tps = tycon.Typars(tycon.Range)
|
|
let mk_compare comparef =
|
|
match tycon.TypeContents.tcaug_compare with
|
|
| None -> []
|
|
| Some (vref1,vref2) ->
|
|
let vspec1 = deref_val vref1
|
|
let vspec2 = deref_val vref2
|
|
(* this is the body of the override *)
|
|
let rhs1 =
|
|
let tinst,ty = minimal_type g tcref
|
|
|
|
let thisv,thise = mk_thisv g m ty
|
|
let thatobjv,thatobje = mk_local m "obj" g.obj_ty
|
|
let comparee =
|
|
if is_unit_typ g ty then mk_zero g m else
|
|
|
|
mk_appl g ((expr_for_vref m vref2,vref2.Type), (if isNil tinst then [] else [tinst]), [thise;mk_coerce(thatobje,ty,m,g.obj_ty)], m)
|
|
|
|
mk_lambdas m tps [thisv;thatobjv] (comparee,g.int_ty)
|
|
let rhs2 =
|
|
let thisv,thatv,comparee = comparef g tcref tycon
|
|
mk_lambdas m tps [thisv;thatv] (comparee,g.int_ty)
|
|
[ // This one must come first because it may be inlined into the second
|
|
mk_compgen_bind vspec2 rhs2;
|
|
mk_compgen_bind vspec1 rhs1; ]
|
|
if tycon.IsUnionTycon then mk_compare mk_union_compare
|
|
elif tycon.IsRecordTycon or tycon.IsStructTycon then mk_compare mk_recd_compare
|
|
elif tycon.IsExceptionDecl then mk_compare mk_exnconstr_compare
|
|
else []
|
|
|
|
let MakeBindingsForCompareWithComparerAugmentation g (tycon:Tycon) =
|
|
let tcref = mk_local_tcref tycon
|
|
let m = tycon.Range
|
|
let tps = tycon.Typars(tycon.Range)
|
|
let mk_compare comparef =
|
|
match tycon.TypeContents.tcaug_compare_withc with
|
|
| None -> []
|
|
| Some (vref) ->
|
|
let vspec = deref_val vref
|
|
let tinst,ty = minimal_type g tcref
|
|
|
|
let compv,compe = mk_local m "comp" g.mk_IComparer_ty
|
|
|
|
let thisv,thise = mk_thisv g m ty
|
|
let thatobjv,thatobje = mk_local m "obj" g.obj_ty
|
|
let thate = mk_coerce(thatobje,ty,m,g.obj_ty)
|
|
|
|
let rhs =
|
|
let comparee = comparef g tcref tycon (thisv,thise) (thatobjv,thate) compe
|
|
let comparee = if is_unit_typ g ty then mk_zero g m else comparee
|
|
mk_multi_lambdas m tps [[thisv];[thatobjv;compv]] (comparee,g.int_ty)
|
|
[mk_compgen_bind vspec rhs]
|
|
if tycon.IsUnionTycon then mk_compare mk_union_compare_withc
|
|
elif tycon.IsRecordTycon or tycon.IsStructTycon then mk_compare mk_recd_compare_withc
|
|
elif tycon.IsExceptionDecl then mk_compare mk_exnconstr_compare_withc
|
|
else []
|
|
|
|
let MakeBindingsForEqualityWithComparerAugmentation g (tycon:Tycon) =
|
|
let tcref = mk_local_tcref tycon
|
|
let m = tycon.Range
|
|
let tps = tycon.Typars(tycon.Range)
|
|
let mk_structural_equatable hashf equalsf =
|
|
match tycon.TypeContents.tcaug_hash_and_equals_withc with
|
|
| None -> []
|
|
| Some (vref1, vref2) ->
|
|
let vspec1 = deref_val vref1
|
|
let vspec2 = deref_val vref2
|
|
let tinst,ty = minimal_type g tcref
|
|
let compv,compe = mk_local m "comp" g.mk_IEqualityComparer_ty
|
|
|
|
// build the hash rhs
|
|
let rhs_hash =
|
|
let thisv,hashe = hashf g tcref tycon compe
|
|
mk_lambdas m tps [thisv;compv] (hashe,g.int_ty)
|
|
|
|
// build the equals rhs
|
|
let rhs_equals =
|
|
let thisv,thise = mk_thisv g m ty
|
|
let thatv,thate = mk_local m "obj" g.obj_ty
|
|
let thate = mk_coerce(thate,ty,m,g.obj_ty)
|
|
let thisv,thatv,equalse = equalsf g tcref tycon (thisv,thise) (thatv,thate) compe
|
|
mk_multi_lambdas m tps [[thisv];[thatv;compv]] (equalse,g.bool_ty)
|
|
|
|
[(mk_compgen_bind vspec1 rhs_hash) ; (mk_compgen_bind vspec2 rhs_equals)]
|
|
if tycon.IsUnionTycon then mk_structural_equatable mk_union_hash_withc mk_union_equality_withc
|
|
elif (tycon.IsRecordTycon || tycon.IsStructTycon) then mk_structural_equatable mk_recd_hash_withc mk_recd_equality_withc
|
|
elif tycon.IsExceptionDecl then mk_structural_equatable mk_exnconstr_hash_withc mk_exnconstr_equality_withc
|
|
else []
|
|
|
|
let MakeBindingsForEqualsAugmentation g (tycon:Tycon) =
|
|
let tcref = mk_local_tcref tycon
|
|
let m = tycon.Range
|
|
let tps = tycon.Typars(m)
|
|
let mk_equals equalsf =
|
|
match tycon.TypeContents.tcaug_equals with
|
|
| None -> []
|
|
| Some (vref1,vref2) ->
|
|
// this is the body of the override
|
|
let rhs1 =
|
|
let tinst,ty = minimal_type g tcref
|
|
|
|
let thisv,thise = mk_thisv g m ty
|
|
let thatobjv,thatobje = mk_local m "obj" g.obj_ty
|
|
let equalse =
|
|
if is_unit_typ g ty then mk_true g m else
|
|
|
|
let thatv,thate = mk_local m "that" ty
|
|
mk_isinst_cond g m ty thatobje thatv
|
|
(mk_appl g ((expr_for_vref m vref2,vref2.Type), (if isNil tinst then [] else [tinst]), [thise;thate], m))
|
|
(mk_false g m)
|
|
|
|
mk_lambdas m tps [thisv;thatobjv] (equalse,g.bool_ty)
|
|
// this is the body of the real strongly typed implementation
|
|
let rhs2 =
|
|
let thisv,thatv,equalse = equalsf g tcref tycon
|
|
mk_lambdas m tps [thisv;thatv] (equalse,g.bool_ty)
|
|
|
|
[ mk_compgen_bind (deref_val vref2) rhs2;
|
|
mk_compgen_bind (deref_val vref1) rhs1; ]
|
|
if tycon.IsExceptionDecl then mk_equals mk_exnconstr_equality
|
|
elif tycon.IsUnionTycon then mk_equals mk_union_equality
|
|
elif tycon.IsRecordTycon or tycon.IsStructTycon then mk_equals mk_recd_equality
|
|
else []
|
|
|