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.
8134 lines
396 KiB
8134 lines
396 KiB
// (c) Microsoft Corporation 2005-2009.
|
|
|
|
#light
|
|
|
|
/// Derived expression manipulation and construction functions.
|
|
module (* internal *) Microsoft.FSharp.Compiler.Tastops
|
|
|
|
open Internal.Utilities
|
|
open Internal.Utilities.Pervasives
|
|
open Microsoft.FSharp.Compiler.AbstractIL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.IL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX
|
|
open Microsoft.FSharp.Compiler
|
|
|
|
module Illib = Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
|
|
module Ilprint = Microsoft.FSharp.Compiler.AbstractIL.AsciiWriter
|
|
|
|
open Microsoft.FSharp.Compiler.AbstractIL.IL
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
|
|
open Microsoft.FSharp.Compiler.Range
|
|
open Microsoft.FSharp.Compiler.Ast
|
|
open Microsoft.FSharp.Compiler.ErrorLogger
|
|
open Microsoft.FSharp.Compiler.Tast
|
|
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
|
|
open Microsoft.FSharp.Compiler.Lib
|
|
open Microsoft.FSharp.Compiler.Env
|
|
open Microsoft.FSharp.Compiler.Layout
|
|
open Microsoft.FSharp.Compiler.PrettyNaming
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Standard orderings, e.g. for order set/map keys
|
|
//---------------------------------------------------------------------------
|
|
|
|
let val_spec_order (v1:Val) (v2:Val) = compare v1.Stamp v2.Stamp
|
|
let tycon_spec_order (tc1:Tycon) (tc2:Tycon) = compare tc1.Stamp tc2.Stamp
|
|
let rfref_order (RFRef(tcref1,nm1)) (RFRef(tcref2,nm2)) =
|
|
let c = tycon_spec_order (deref_tycon tcref1) (deref_tycon tcref2)
|
|
if c <> 0 then c else
|
|
compare nm1 nm2
|
|
|
|
let ucref_order (UCRef(tcref1,nm1)) (UCRef(tcref2,nm2)) =
|
|
let c = tycon_spec_order (deref_tycon tcref1) (deref_tycon tcref2)
|
|
if c <> 0 then c else
|
|
compare nm1 nm2
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Make some common types
|
|
//---------------------------------------------------------------------------
|
|
|
|
let mk_fun_ty d r = TType_fun (d,r)
|
|
let (-->) d r = mk_fun_ty d r
|
|
let mk_forall_ty d r = TType_forall (d,r)
|
|
let try_mk_forall_ty d r = if isNil d then r else mk_forall_ty d r
|
|
let (+->) d r = try_mk_forall_ty d r
|
|
let mk_tuple_ty l = TType_tuple l
|
|
let mk_iterated_fun_ty dl r = List.foldBack (-->) dl r
|
|
|
|
let fake_mk_tupled_ty m tys =
|
|
match tys with
|
|
| [] -> error(InternalError("fake_mk_tupled_ty",m))
|
|
| [h] -> h
|
|
| _ -> mk_tuple_ty tys
|
|
|
|
let type_of_lambda_arg m vs = fake_mk_tupled_ty m (types_of_vals vs)
|
|
let mk_multi_lambda_ty m vs rty = mk_fun_ty (type_of_lambda_arg m vs) rty
|
|
let mk_lambda_ty tps tys rty = try_mk_forall_ty tps (mk_iterated_fun_ty tys rty)
|
|
|
|
/// When compiling FSharp.Core.dll we have to deal with the non-local references into
|
|
/// the library arising from env.ml. Part of this means that we have to be able to resolve these
|
|
/// references. This function artificially forces the existence of a module or namespace at a
|
|
/// particular point in order to do this.
|
|
let ensure_fslib_has_submodul_at (ccu:ccu) path (CompPath(_,cpath)) xml =
|
|
let scoref = ccu.ILScopeRef
|
|
let rec loop prior_cpath (path:ident list) cpath (modul:ModuleOrNamespace) =
|
|
let mtype = modul.ModuleOrNamespaceType
|
|
match path,cpath with
|
|
| (hpath::tpath),((_,mkind)::tcpath) ->
|
|
let modName = hpath.idText
|
|
if not (Map.mem modName mtype.AllEntities) then
|
|
let smodul = NewModuleOrNamespace (Some(CompPath(scoref,prior_cpath))) taccessPublic hpath xml [] (notlazy (empty_mtype mkind))
|
|
mtype.AddModuleOrNamespaceByMutation(smodul);
|
|
let modul = Map.find modName mtype.AllEntities
|
|
loop (prior_cpath@[(modName,Namespace)]) tpath tcpath modul
|
|
|
|
| _ -> ()
|
|
|
|
loop [] path cpath ccu.Contents
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Primitive destructors
|
|
//---------------------------------------------------------------------------
|
|
|
|
/// Look through the TExpr_link nodes arising from type inference
|
|
let rec strip_expr e =
|
|
match e with
|
|
| TExpr_link eref -> strip_expr !eref
|
|
| _ -> e
|
|
|
|
let discrim_of_case (TCase(d,_)) = d
|
|
let dest_of_case (TCase(_,d)) = d
|
|
let mk_case (a,b) = TCase(a,b)
|
|
|
|
let is_tuple e = match e with TExpr_op(TOp_tuple,_,_,_) -> true | _ -> false
|
|
let try_dest_tuple e = match e with TExpr_op(TOp_tuple,_,es,_) -> es | _ -> [e]
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Debug info for expressions
|
|
//---------------------------------------------------------------------------
|
|
|
|
let rec range_of_expr x =
|
|
match x with
|
|
| TExpr_val (_,_,m) | TExpr_op (_,_,_,m) | TExpr_const (_,m,_) | TExpr_quote (_,_,m,_)
|
|
| TExpr_obj (_,_,_,_,_,_,m,_) | TExpr_app(_,_,_,_,m) | TExpr_seq (_,_,_,_,m)
|
|
| TExpr_static_optimization (_,_,_,m) | TExpr_lambda (_,_,_,_,m,_,_)
|
|
| TExpr_tlambda (_,_,_,m,_,_)| TExpr_tchoose (_,_,m) | TExpr_letrec (_,_,m,_) | TExpr_let (_,_,m,_) | TExpr_match (_,_,_,_,m,_,_)
|
|
-> m
|
|
| TExpr_link(eref) -> range_of_expr !eref
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Build nodes in decision graphs
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
let prim_mk_match(spBind,exprm,tree,targets,matchm,ty) = TExpr_match (spBind,exprm,tree,targets,matchm,ty,SkipFreeVarsCache())
|
|
|
|
type MatchBuilder(spBind,inpRange: Range.range) =
|
|
|
|
let targets = new ResizeArray<_>(10)
|
|
member x.AddTarget(tg) =
|
|
let n = targets.Count
|
|
targets.Add(tg);
|
|
n
|
|
|
|
member x.AddResultTarget(e,spTarget) = TDSuccess(FlatList.empty, x.AddTarget(TTarget(FlatList.empty,e,spTarget)))
|
|
|
|
member x.CloseTargets() = targets |> ResizeArray.to_list
|
|
|
|
member x.Close(dtree,m,ty) = prim_mk_match (spBind,inpRange,dtree,ResizeArray.to_array targets,m,ty)
|
|
|
|
let mk_bool_switch m g t e = TDSwitch(g,[TCase(TTest_const(TConst_bool(true)),t)],Some e,m)
|
|
|
|
let mk_cond spBind spTarget m ty e1 e2 e3 =
|
|
let mbuilder = new MatchBuilder(spBind,m)
|
|
let dtree = mk_bool_switch m e1 (mbuilder.AddResultTarget(e2,spTarget)) (mbuilder.AddResultTarget(e3,spTarget))
|
|
mbuilder.Close(dtree,m,ty)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// These make local/non-local references to values according to whether
|
|
// the item is globally stable ("published") or not.
|
|
//---------------------------------------------------------------------------
|
|
|
|
let mk_local_vref (v:Val) = VRef_private v
|
|
let mk_local_modref (v:ModuleOrNamespace) = ERef_private v
|
|
let mk_local_tcref (v:Tycon) = ERef_private v
|
|
let mk_local_ecref (v:Tycon) = ERef_private v
|
|
|
|
let mk_nonlocal_ccu_top_tcref ccu (x:Tycon) = mk_nonlocal_tcref_preresolved x (nlpath_of_ccu ccu) x.MangledName
|
|
|
|
let mk_vref_in_modref (cref:TyconRef) (x:Val) : ValRef =
|
|
match cref with
|
|
| ERef_private _ -> mk_local_vref x
|
|
| ERef_nonlocal nlr ->
|
|
let (NLPath(ccu,p)) = nlr.nlr_nlpath
|
|
mk_nonlocal_vref_preresolved x (NLPath(ccu, Array.append p [| nlr.nlr_item |])) x.MangledName
|
|
|
|
let MakeNestedTcref (cref:TyconRef) (x:Entity) : TyconRef =
|
|
match cref with
|
|
| ERef_private _ -> mk_local_tcref x
|
|
| ERef_nonlocal nlr ->
|
|
let (NLPath(ccu,p)) = nlr.nlr_nlpath
|
|
mk_nonlocal_tcref_preresolved x (NLPath(ccu, Array.append p [| nlr.nlr_item |])) x.MangledName
|
|
|
|
let mk_rfref_in_tcref (x:ModuleOrNamespaceRef) tycon (rf:ident) : RecdFieldRef = mk_rfref (MakeNestedTcref x tycon) rf.idText
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Primitive constructors
|
|
//---------------------------------------------------------------------------
|
|
|
|
let expr_for_vref m vref = TExpr_val(vref,NormalValUse,m)
|
|
let expr_for_val m v = expr_for_vref m (mk_local_vref v)
|
|
let gen_mk_local m s ty mut compgen =
|
|
let thisv = NewVal(ident(s,m),ty,mut,compgen,None,None,taccessPublic,ValNotInRecScope,None,NormalVal,[],OptionalInline,emptyXmlDoc,false,false,false,false,None,ParentNone)
|
|
thisv,expr_for_val m thisv
|
|
|
|
let mk_local m s ty = gen_mk_local m s ty Immutable false
|
|
let mk_compgen_local m s ty = gen_mk_local m s ty Immutable true
|
|
let mk_mut_compgen_local m s ty = gen_mk_local m s ty Mutable true
|
|
|
|
|
|
(* Type gives return type. For type-lambdas this is the formal return type. *)
|
|
let mk_multi_lambda m vs (b,rty) = TExpr_lambda (new_uniq(), None,vs,b,m, rty, SkipFreeVarsCache())
|
|
let mk_basev_multi_lambda m basevopt vs (b,rty) = TExpr_lambda (new_uniq(), basevopt,vs,b,m, rty, SkipFreeVarsCache())
|
|
let mk_lambda m v (b,rty) = mk_multi_lambda m [v] (b,rty)
|
|
let mk_tlambda m vs (b,tau_ty) = match vs with [] -> b | _ -> TExpr_tlambda (new_uniq(), vs,b,m,tau_ty, SkipFreeVarsCache())
|
|
let mk_tchoose m vs b = match vs with [] -> b | _ -> TExpr_tchoose (vs,b,m)
|
|
|
|
let mk_obj_expr (ty,basev,basecall,overrides,iimpls,m) =
|
|
TExpr_obj (new_uniq(),ty,basev,basecall,overrides,iimpls,m,SkipFreeVarsCache())
|
|
|
|
let mk_lambdas m tps (vs:Val list) (b,rty) =
|
|
mk_tlambda m tps (List.foldBack (fun v (e,ty) -> mk_lambda m v (e,ty), v.Type --> ty) vs (b,rty))
|
|
let mk_multi_lambdas_core m vsl (b,rty) =
|
|
List.foldBack (fun v (e,ty) -> mk_multi_lambda m v (e,ty), type_of_lambda_arg m v --> ty) vsl (b,rty)
|
|
let mk_multi_lambdas m tps vsl (b,rty) =
|
|
mk_tlambda m tps (mk_multi_lambdas_core m vsl (b,rty) )
|
|
|
|
let mk_basev_multi_lambdas_core m basevopt vsl (b,rty) =
|
|
match basevopt with
|
|
| None -> mk_multi_lambdas_core m vsl (b,rty)
|
|
| _ ->
|
|
match vsl with
|
|
| [] -> error(InternalError("mk_basev_multi_lambdas_core: can't attach a basev to a non-lambda expression",m))
|
|
| h::t ->
|
|
let b,rty = mk_multi_lambdas_core m t (b,rty)
|
|
(mk_basev_multi_lambda m basevopt h (b,rty), (type_of_lambda_arg m h --> rty))
|
|
|
|
let mk_basev_multi_lambdas m tps basevopt vsl (b,rty) =
|
|
mk_tlambda m tps (mk_basev_multi_lambdas_core m basevopt vsl (b,rty) )
|
|
|
|
let mk_multi_lambda_bind v letSeqPtOpt m tps vsl (b,rty) =
|
|
TBind(v,mk_multi_lambdas m tps vsl (b,rty),letSeqPtOpt)
|
|
|
|
let mk_bind seqPtOpt v e = TBind(v,e,seqPtOpt)
|
|
let mk_compgen_bind v e = TBind(v,e,NoSequencePointAtStickyBinding)
|
|
|
|
/// Make bindings that are compiler generated (though the variables may not be - e.g. they may be lambda arguments in a beta reduction)
|
|
let mk_compgen_binds vs es =
|
|
if List.length vs <> List.length es then failwith "mk_compgen_binds: invalid argument";
|
|
List.map2 mk_compgen_bind vs es |> FlatList.of_list
|
|
|
|
(* n.b. type gives type of body *)
|
|
let mk_let_bind m bind body = TExpr_let(bind,body, m, NewFreeVarsCache())
|
|
let mk_lets_bind m binds body = List.foldBack (mk_let_bind m) binds body
|
|
let mk_lets_from_Bindings m binds body = FlatList.foldBack (mk_let_bind m) binds body
|
|
let mk_let seqPtOpt m v x body = mk_let_bind m (mk_bind seqPtOpt v x) body
|
|
let mk_compgen_let m v x body = mk_let_bind m (mk_compgen_bind v x) body
|
|
|
|
let mk_invisible_bind v e = TBind(v,e,NoSequencePointAtInvisibleBinding)
|
|
let mk_invisible_let m v x body = mk_let_bind m (mk_invisible_bind v x) body
|
|
let mk_invisible_binds vs es =
|
|
if List.length vs <> List.length es then failwith "mk_invisible_binds: invalid argument";
|
|
List.map2 mk_invisible_bind vs es
|
|
|
|
let mk_invisible_FlatBindings vs es =
|
|
if FlatList.length vs <> FlatList.length es then failwith "mk_invisible_FlatBindings: invalid argument";
|
|
FlatList.map2 mk_invisible_bind vs es
|
|
|
|
let mk_invisible_lets m vs xs body = mk_lets_bind m (mk_invisible_binds vs xs) body
|
|
let mk_invisible_lets_from_Bindings m vs xs body = mk_lets_from_Bindings m (mk_invisible_FlatBindings vs xs) body
|
|
|
|
let mk_letrec_binds m binds body = if FlatList.isEmpty binds then body else TExpr_letrec(binds,body, m, NewFreeVarsCache())
|
|
let mk_letrec_binds_typed m binds (body,ty) = mk_letrec_binds m binds body, ty
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Type schemes...
|
|
//-------------------------------------------------------------------------
|
|
|
|
|
|
type TypeScheme =
|
|
TypeScheme of
|
|
typars (* the truly generalized type parameters *)
|
|
* typars (* free choice type parameters from a recursive block where this value only generalizes a subsest of the overall set of type parameters generalized *)
|
|
* typ (* the 'tau' type forming the body of the generalized type *)
|
|
|
|
let mk_poly_bind_rhs m typeScheme bodyExpr =
|
|
let (TypeScheme(generalizedTypars,freeChoiceTypars,tauType)) = typeScheme
|
|
mk_tlambda m generalizedTypars (mk_tchoose m freeChoiceTypars bodyExpr, tauType)
|
|
|
|
let is_being_generalized tp typeScheme =
|
|
let (TypeScheme(generalizedTypars,_,_)) = typeScheme
|
|
ListSet.mem tpspec_eq tp generalizedTypars
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Build conditional expressions...
|
|
//-------------------------------------------------------------------------
|
|
|
|
let mk_lazy_and g m e1 e2 = mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.bool_ty e1 e2 (TExpr_const(TConst_bool false,m,g.bool_ty))
|
|
let mk_lazy_or g m e1 e2 = mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m g.bool_ty e1 (TExpr_const(TConst_bool true,m,g.bool_ty)) e2
|
|
|
|
let mk_byref_typ g ty = TType_app (g.byref_tcr, [ty])
|
|
let mk_multi_dim_array_typ g n ty =
|
|
if n = 1 then TType_app (g.il_arr1_tcr, [ty])
|
|
elif n = 2 then TType_app (g.il_arr2_tcr, [ty])
|
|
elif n = 3 then TType_app (g.il_arr3_tcr, [ty])
|
|
elif n = 4 then TType_app (g.il_arr4_tcr, [ty])
|
|
else failwith "F# supports a maxiumum .NET array dimension of 4"
|
|
|
|
let mk_unit_typ g = g.unit_ty
|
|
let mk_nativeint_typ g = g.nativeint_ty
|
|
|
|
let mk_coerce(e,to_ty,m,from_ty) = TExpr_op(TOp_coerce,[to_ty;from_ty],[e],m)
|
|
|
|
let mk_asm(code,tinst,args,rettys,m) = TExpr_op(TOp_asm(code,rettys),tinst,args,m)
|
|
let mk_ucase(uc,tinst,args,m) = TExpr_op(TOp_ucase uc,tinst,args,m)
|
|
let mk_exnconstr(uc,args,m) = TExpr_op(TOp_exnconstr uc,[],args,m)
|
|
let mk_tuple_field_get(e,tinst,i,m) = TExpr_op(TOp_tuple_field_get(i), tinst, [e],m)
|
|
|
|
let mk_recd_field_get_via_expra(e,fref,tinst,m) = TExpr_op(TOp_rfield_get(fref), tinst, [e],m)
|
|
let mk_recd_field_get_addr_via_expra(e,fref,tinst,m) = TExpr_op(TOp_field_get_addr(fref), tinst, [e],m)
|
|
|
|
let mk_static_rfield_get_addr(fref,tinst,m) = TExpr_op(TOp_field_get_addr(fref), tinst, [],m)
|
|
let mk_static_rfield_get(fref,tinst,m) = TExpr_op(TOp_rfield_get(fref), tinst, [],m)
|
|
let mk_static_rfield_set(fref,tinst,e,m) = TExpr_op(TOp_rfield_set(fref), tinst, [e],m)
|
|
|
|
let mk_recd_field_set_via_expra(e1,fref,tinst,e2,m) = TExpr_op(TOp_rfield_set(fref), tinst, [e1;e2],m)
|
|
|
|
let mk_ucase_tag_get(e1,cref,tinst,m) = TExpr_op(TOp_ucase_tag_get(cref), tinst, [e1],m)
|
|
let mk_ucase_proof(e1,cref,tinst,m) = TExpr_op(TOp_ucase_proof(cref), tinst, [e1],m)
|
|
|
|
/// Build a 'get' expression for something we've already determined to be a particular union case, and where the
|
|
/// input expression has 'TType_ucase', which is an F# compiler internal "type"
|
|
let mk_ucase_field_get_proven(e1,cref,tinst,j,m) = TExpr_op(TOp_ucase_field_get(cref,j), tinst, [e1],m)
|
|
|
|
/// Build a 'get' expression for something we've already determined to be a particular union case, but where
|
|
/// the static type of the input is not yet proven to be that particular union case. This requires a type
|
|
/// cast to 'prove' the condition.
|
|
let mk_ucase_field_get_unproven(e1,cref,tinst,j,m) = mk_ucase_field_get_proven(mk_ucase_proof(e1,cref,tinst,m),cref,tinst,j,m)
|
|
|
|
let mk_ucase_field_set(e1,cref,tinst,j,e2,m) = TExpr_op(TOp_ucase_field_set(cref,j), tinst, [e1;e2],m)
|
|
|
|
let mk_exnconstr_field_get(e1,ecref,j,m) = TExpr_op(TOp_exnconstr_field_get(ecref,j), [],[e1],m)
|
|
let mk_exnconstr_field_set(e1,ecref,j,e2,m) = TExpr_op(TOp_exnconstr_field_set(ecref,j), [],[e1;e2],m)
|
|
|
|
let mk_dummy_lambda g (e,ety) =
|
|
let m = (range_of_expr e)
|
|
mk_lambda m (fst (mk_compgen_local m "unitVar" g.unit_ty)) (e,ety)
|
|
|
|
let mk_while g (spWhile,e1,e2,m) =
|
|
TExpr_op(TOp_while spWhile,[] ,[mk_dummy_lambda g (e1,g.bool_ty);mk_dummy_lambda g (e2,g.unit_ty)],m)
|
|
|
|
let mk_for g (spFor,v,e1,dir,e2,e3,m) =
|
|
TExpr_op(TOp_for (spFor,dir) ,[] ,[mk_dummy_lambda g (e1,g.int_ty) ;mk_dummy_lambda g (e2,g.int_ty);mk_lambda (range_of_expr e3) v (e3,g.unit_ty)],m)
|
|
|
|
let mk_try_catch g (e1,vf,ef,vh,eh,m,ty,spTry,spWith) =
|
|
TExpr_op(TOp_try_catch(spTry,spWith),[ty],[mk_dummy_lambda g (e1,ty);mk_lambda (range_of_expr ef) vf (ef,ty);mk_lambda (range_of_expr eh) vh (eh,ty)],m)
|
|
|
|
let mk_try_finally g (e1,e2,m,ty,spTry,spFinally) =
|
|
TExpr_op(TOp_try_finally(spTry,spFinally),[ty],[mk_dummy_lambda g (e1,ty);mk_dummy_lambda g (e2,g.unit_ty)],m)
|
|
|
|
let mk_ilzero (m,ty) = TExpr_const(TConst_zero,m,ty)
|
|
|
|
|
|
let rec split_after_acc n l1 l2 = if n <= 0 then List.rev l1,l2 else split_after_acc (n-1) ((List.hd l2):: l1) (List.tl l2)
|
|
let split_after n l = split_after_acc n [] l
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Maps tracking extra information for values
|
|
//--------------------------------------------------------------------------
|
|
|
|
[<Struct>]
|
|
type ValMap<'a> =
|
|
val imap: I64map.t<'a>
|
|
new (imap) = {imap=imap}
|
|
|
|
// REVIEW: convert these to OO
|
|
let vspec_map_find (v: Val) (m:ValMap<_>) = I64map.find v.Stamp m.imap
|
|
let vspec_map_tryfind (v: Val) (m:ValMap<_>) = I64map.tryfind v.Stamp m.imap
|
|
let vspec_map_mem (v: Val) (m:ValMap<_>) = I64map.mem v.Stamp m.imap
|
|
let vspec_map_add (v: Val) x (m:ValMap<_>) = ValMap (I64map.add v.Stamp x m.imap)
|
|
let vspec_map_remove (v: Val) (m:ValMap<_>) = ValMap (I64map.remove v.Stamp m.imap)
|
|
let vspec_map_empty () = ValMap (I64map.empty ())
|
|
let vspec_map_is_empty (m:ValMap<_>) = m.imap.IsEmpty
|
|
let vspec_map_of_list vs = List.foldBack (fun (x,y) acc -> vspec_map_add x y acc) vs (vspec_map_empty())
|
|
|
|
type ValHash<'a> = VSpecHash of System.Collections.Generic.Dictionary<stamp,'a>
|
|
let vspec_hash_find (VSpecHash t) (v:Val) = t.[v.Stamp]
|
|
let vspec_hash_tryfind (VSpecHash t) (v:Val) = let i = v.Stamp in if t.ContainsKey(i) then Some(t.[i]) else None
|
|
let vspec_hash_mem (VSpecHash t) (v:Val) = let i = v.Stamp in t.ContainsKey(i)
|
|
let vspec_hash_add (VSpecHash t) (v:Val) x = let i = v.Stamp in t.[i] <- x
|
|
let vspec_hash_remove (VSpecHash t) (v:Val) = let i = v.Stamp in t.Remove(i) |> ignore
|
|
let vspec_hash_create() = VSpecHash (new System.Collections.Generic.Dictionary<_,_>(11))
|
|
|
|
type ValMultiMap<'a> = ValMap<'a list>
|
|
let vspec_mmap_find v (m: ValMultiMap<'a>) = if vspec_map_mem v m then vspec_map_find v m else []
|
|
let vspec_mmap_add v x (m: ValMultiMap<'a>) = vspec_map_add v (x :: vspec_mmap_find v m) m
|
|
let vspec_mmap_empty () : ValMultiMap<'a> = vspec_map_empty()
|
|
|
|
type TyparMap<'a> = TPMap of I64map.t<'a>
|
|
let tpmap_find (v: Typar) (TPMap m) = I64map.find v.Stamp m
|
|
let tpmap_mem (v: Typar) (TPMap m) = I64map.mem v.Stamp m
|
|
let tpmap_add (v: Typar) x (TPMap m) = TPMap (I64map.add v.Stamp x m)
|
|
let tpmap_empty () = TPMap (I64map.empty ())
|
|
|
|
type TcrefMap<'a> = TCRefMap of I64map.t<'a>
|
|
let tcref_map_find (v: TyconRef) (TCRefMap m) = I64map.find v.Stamp m
|
|
let tcref_map_tryfind (v: TyconRef) (TCRefMap m) = I64map.tryfind v.Stamp m
|
|
let tcref_map_mem (v: TyconRef) (TCRefMap m) = I64map.mem v.Stamp m
|
|
let tcref_map_add (v: TyconRef) x (TCRefMap m) = TCRefMap (I64map.add v.Stamp x m)
|
|
let tcref_map_empty () = TCRefMap (I64map.empty ())
|
|
let tcref_map_is_empty (TCRefMap m) = Zmap.is_empty m
|
|
let tcref_map_of_list vs = List.foldBack (fun (x,y) acc -> tcref_map_add x y acc) vs (tcref_map_empty())
|
|
|
|
type TcrefMultiMap<'a> = 'a list TcrefMap
|
|
let tcref_mmap_find v (m: TcrefMultiMap<'a>) = if tcref_map_mem v m then tcref_map_find v m else []
|
|
let tcref_mmap_add v x (m: TcrefMultiMap<'a>) = tcref_map_add v (x :: tcref_mmap_find v m) m
|
|
let tcref_mmap_empty () : TcrefMultiMap<'a> = tcref_map_empty()
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Substitute for type variables and remap type constructors
|
|
//--------------------------------------------------------------------------
|
|
|
|
type TyparInst = (Typar * typ) list
|
|
type tpenv = TyparInst
|
|
|
|
type TyconRefRemap = TyconRef TcrefMap
|
|
type ValRemap = ValMap<ValRef>
|
|
|
|
let empty_tpenv = ([] : tpenv)
|
|
let empty_tpinst = ([] : TyparInst)
|
|
|
|
type Remap =
|
|
{ tpinst : TyparInst;
|
|
vspec_remap: ValRemap;
|
|
tcref_remap : TyconRefRemap }
|
|
|
|
type tyenv = Remap
|
|
|
|
let empty_tcref_remap : TyconRefRemap = tcref_map_empty()
|
|
let empty_vref_remap : ValRemap = vspec_map_empty()
|
|
let empty_remap = { tpinst = empty_tpinst; tcref_remap =empty_tcref_remap;vspec_remap =empty_vref_remap }
|
|
let empty_tyenv = empty_remap
|
|
|
|
//--------------------------------------------------------------------------
|
|
// renamings
|
|
//--------------------------------------------------------------------------
|
|
|
|
let tmenv_add_tcref_remap tcref1 tcref2 tmenv =
|
|
{tmenv with tcref_remap=tcref_map_add tcref1 tcref2 tmenv.tcref_remap}
|
|
|
|
let remap_vref tmenv vref =
|
|
match vspec_map_tryfind (deref_val vref) tmenv.vspec_remap with
|
|
| None -> vref
|
|
| Some res -> res
|
|
|
|
let remap_is_empty tyenv =
|
|
isNil tyenv.tpinst &&
|
|
tcref_map_is_empty tyenv.tcref_remap &&
|
|
vspec_map_is_empty tyenv.vspec_remap
|
|
|
|
let inst_tpref tpinst ty tp =
|
|
if ListAssoc.containsKey typar_ref_eq tp tpinst then ListAssoc.find typar_ref_eq tp tpinst
|
|
else ty (* avoid re-allocation of TType_app node in the common case *)
|
|
|
|
let inst_tpref_unit tpinst measure (tp:Typar) =
|
|
if tp.Kind = KindType then failwith "inst_tpref_unit: kind=Type";
|
|
if ListAssoc.containsKey typar_ref_eq tp tpinst then
|
|
match ListAssoc.find typar_ref_eq tp tpinst with
|
|
| TType_measure measure -> measure
|
|
| _ -> failwith "inst_tpref_unit incorrect kind";
|
|
else measure
|
|
|
|
let remap_tcref tcmap tcr =
|
|
match tcref_map_tryfind tcr tcmap with
|
|
| Some tcr -> tcr
|
|
| None -> tcr
|
|
|
|
let remap_ucref tcmap (UCRef(tcref,nm)) = UCRef(remap_tcref tcmap tcref,nm)
|
|
let remap_rfref tcmap (RFRef(tcref,nm)) = RFRef(remap_tcref tcmap tcref,nm)
|
|
|
|
let mk_typar_inst (typars: typars) tyargs =
|
|
#if CHECKED
|
|
if List.length typars <> List.length tyargs then
|
|
failwith ("mk_typar_inst: invalid type" ^ (sprintf " %d <> %d" (List.length typars) (List.length tyargs)));
|
|
#endif
|
|
(List.zip typars tyargs : TyparInst)
|
|
|
|
let generalize_typar tp = mk_typar_ty tp
|
|
let generalize_typars tps = List.map generalize_typar tps
|
|
|
|
let rec remap_typeA (tyenv : tyenv) (ty:typ) =
|
|
let ty = strip_tpeqns ty
|
|
match ty with
|
|
| TType_var tp as ty -> inst_tpref tyenv.tpinst ty tp
|
|
| TType_app (tcr,tinst) as ty ->
|
|
match tcref_map_tryfind tcr tyenv.tcref_remap with
|
|
| Some tcr' -> TType_app (tcr',remap_typesA tyenv tinst)
|
|
| None ->
|
|
match tinst with
|
|
| [] -> ty (* optimization to avoid re-allocation of TType_app node in the common case *)
|
|
| _ ->
|
|
(* avoid reallocation on idempotent *)
|
|
let tinst' = remap_typesA tyenv tinst
|
|
if tinst === tinst' then ty else
|
|
TType_app (tcr,tinst')
|
|
|
|
| TType_ucase (UCRef(tcr,n),tinst) as ty ->
|
|
match tcref_map_tryfind tcr tyenv.tcref_remap with
|
|
| Some tcr' -> TType_ucase (UCRef(tcr',n),remap_typesA tyenv tinst)
|
|
| None -> TType_ucase (UCRef(tcr,n),remap_typesA tyenv tinst)
|
|
|
|
| TType_tuple l as ty ->
|
|
let l' = remap_typesA tyenv l
|
|
if l === l' then ty else
|
|
TType_tuple (l')
|
|
| TType_fun (d,r) as ty ->
|
|
let d' = remap_typeA tyenv d
|
|
let r' = remap_typeA tyenv r
|
|
if d === d' && r === r' then ty else
|
|
TType_fun (d', r')
|
|
| TType_forall (tps,ty) ->
|
|
let tps',tyenv = copy_remap_and_bind_typars tyenv tps
|
|
TType_forall (tps', remap_typeA tyenv ty)
|
|
| TType_modul_bindings -> ty
|
|
| TType_measure measure ->
|
|
TType_measure (remap_measureA tyenv measure)
|
|
|
|
and remap_measureA tyenv measure =
|
|
match measure with
|
|
| MeasureOne -> measure
|
|
| MeasureCon tcr ->
|
|
match tcref_map_tryfind tcr tyenv.tcref_remap with
|
|
| Some tcr -> MeasureCon tcr
|
|
| None -> measure
|
|
| MeasureProd(u1,u2) -> MeasureProd(remap_measureA tyenv u1, remap_measureA tyenv u2)
|
|
| MeasureInv u -> MeasureInv(remap_measureA tyenv u)
|
|
| MeasureVar tp as measure ->
|
|
match tp.Solution with
|
|
| None ->
|
|
if ListAssoc.containsKey typar_ref_eq tp tyenv.tpinst then
|
|
match ListAssoc.find typar_ref_eq tp tyenv.tpinst with
|
|
| TType_measure measure -> measure
|
|
| _ -> failwith "remap_measureA: incorrect kinds"
|
|
else measure
|
|
| Some (TType_measure measure) -> remap_measureA tyenv measure
|
|
| Some ty -> failwithf "incorrect kinds: %A" ty
|
|
and remap_typesA tyenv types = List.mapq (remap_typeA tyenv) types
|
|
and remap_typar_constraintsA tyenv cs =
|
|
cs |> List.choose (fun x ->
|
|
match x with
|
|
| TTyparCoercesToType(ty,m) ->
|
|
Some(TTyparCoercesToType (remap_typeA tyenv ty,m))
|
|
| TTyparMayResolveMemberConstraint(traitInfo,m) ->
|
|
Some(TTyparMayResolveMemberConstraint (remap_traitA tyenv traitInfo,m))
|
|
| TTyparDefaultsToType(priority,ty,m) -> Some(TTyparDefaultsToType(priority,remap_typeA tyenv ty,m))
|
|
| TTyparIsEnum(uty,m) ->
|
|
Some(TTyparIsEnum(remap_typeA tyenv uty,m))
|
|
| TTyparIsDelegate(uty1,uty2,m) ->
|
|
Some(TTyparIsDelegate(remap_typeA tyenv uty1,remap_typeA tyenv uty2,m))
|
|
| TTyparSimpleChoice(tys,m) -> Some(TTyparSimpleChoice(remap_typesA tyenv tys,m))
|
|
| TTyparSupportsNull _ | TTyparIsNotNullableValueType _
|
|
| TTyparIsReferenceType _ | TTyparRequiresDefaultConstructor _ -> Some(x))
|
|
|
|
and remap_traitA tyenv (TTrait(typs,nm,mf,argtys,rty,slnCell)) =
|
|
let slnCell =
|
|
match !slnCell with
|
|
| None -> None
|
|
| Some sln ->
|
|
let sln =
|
|
match sln with
|
|
| ILMethSln(typ,extOpt,mref,minst) ->
|
|
ILMethSln(remap_typeA tyenv typ,extOpt,mref,remap_typesA tyenv minst)
|
|
| FSMethSln(typ, vref,minst) ->
|
|
FSMethSln(remap_typeA tyenv typ, remap_vref tyenv vref,remap_typesA tyenv minst)
|
|
| BuiltInSln ->
|
|
BuiltInSln
|
|
Some sln
|
|
// Note: we reallocate a new solution cell on every traversal of a trait constraint
|
|
// This feels incorrect for trait constraints that are quantified: it seems we should have
|
|
// formal binders for trait constraints when they are quantified, just as
|
|
// we have formal binders for type variables.
|
|
//
|
|
// The danger here is that a solution for one syntactic occurrence of a trait constraint won't
|
|
// be propagated to other, "linked" solutions. However trait constraints don't appear in any algebrra
|
|
// in the same way as types
|
|
TTrait(remap_typesA tyenv typs,nm,mf,remap_typesA tyenv argtys, Option.map (remap_typeA tyenv) rty,ref slnCell)
|
|
|
|
|
|
and bind_typars tps tyargs tpinst =
|
|
match tps with
|
|
| [] -> tpinst
|
|
| _ -> List.map2 (fun tp tyarg -> (tp,tyarg)) tps tyargs @ tpinst
|
|
|
|
(* This version is used to remap most type parameters, e.g. ones bound at tycons, vals, records *)
|
|
(* See notes below on remap_type_full for why we have a function that accepts remap_attribs as an argument *)
|
|
and copy_remap_and_bind_typars_full remap_attrib tyenv tps =
|
|
match tps with
|
|
| [] -> tps,tyenv
|
|
| _ ->
|
|
let tps' = CopyTypars tps
|
|
let tyenv = { tyenv with tpinst = bind_typars tps (generalize_typars tps') tyenv.tpinst }
|
|
(tps,tps') ||> List.iter2 (fun tporig tp ->
|
|
fixup_typar_constraints tp (remap_typar_constraintsA tyenv tporig.Constraints);
|
|
tp.Data.typar_attribs <- tporig.Data.typar_attribs |> List.map remap_attrib) ;
|
|
tps',tyenv
|
|
|
|
(* copies bound typars, extends tpinst *)
|
|
and copy_remap_and_bind_typars tyenv tps =
|
|
copy_remap_and_bind_typars_full (fun _ -> failwith "Unexpected attribute in first-class Type_forall") tyenv tps
|
|
|
|
let remap_type tyenv x =
|
|
if remap_is_empty tyenv then x else
|
|
remap_typeA tyenv x
|
|
|
|
let remap_types tyenv x =
|
|
if remap_is_empty tyenv then x else
|
|
remap_typesA tyenv x
|
|
|
|
/// Use this one for any type that may be a forall type where the type variables may contain attributes
|
|
/// Logically speaking this is mtuually recursive with remap_attrib defined much later in this file,
|
|
/// because types may contain forall types that contain attributes, which need to be remapped.
|
|
/// We currently break the recursion by passing in remap_attrib as a function parameter.
|
|
/// Use this one for any type that may be a forall type where the type variables may contain attributes
|
|
let remap_type_full remap_attrib tyenv ty =
|
|
if remap_is_empty tyenv then ty else
|
|
match strip_tpeqns ty with
|
|
| TType_forall(tps,tau) ->
|
|
let tps',tyenvinner = copy_remap_and_bind_typars_full remap_attrib tyenv tps
|
|
TType_forall(tps',remap_type tyenvinner tau)
|
|
| _ ->
|
|
try remap_type tyenv ty
|
|
with e -> failwith "error in remap_type_full"
|
|
|
|
let remap_param tyenv (TSlotParam(nm,typ,fl1,fl2,fl3,attribs) as x) =
|
|
if remap_is_empty tyenv then x else
|
|
TSlotParam(nm,remap_typeA tyenv typ,fl1,fl2,fl3,attribs)
|
|
|
|
let remap_slotsig remap_attrib tyenv (TSlotSig(nm,typ, ctps,methTypars,paraml, rty) as x) =
|
|
if remap_is_empty tyenv then x else
|
|
let typ' = remap_typeA tyenv typ
|
|
let ctps',tyenvinner = copy_remap_and_bind_typars_full remap_attrib tyenv ctps
|
|
let methTypars',tyenvinner = copy_remap_and_bind_typars_full remap_attrib tyenvinner methTypars
|
|
TSlotSig(nm,typ', ctps',methTypars',List.mapSquared (remap_param tyenvinner) paraml,Option.map (remap_typeA tyenvinner) rty)
|
|
|
|
let mk_inst_tyenv tpinst = { tcref_remap= empty_tcref_remap; tpinst=tpinst; vspec_remap=empty_vref_remap }
|
|
|
|
(* entry points for "typar -> typ" instantiation *)
|
|
let InstType tpinst x = if isNil tpinst then x else remap_typeA (mk_inst_tyenv tpinst) x
|
|
let inst_types tpinst x = if isNil tpinst then x else remap_typesA (mk_inst_tyenv tpinst) x
|
|
let inst_trait tpinst x = if isNil tpinst then x else remap_traitA (mk_inst_tyenv tpinst) x
|
|
let inst_typar_constraints tpinst x = if isNil tpinst then x else remap_typar_constraintsA (mk_inst_tyenv tpinst) x
|
|
let inst_slotsig tpinst ss = remap_slotsig (fun _ -> failwith "Unexpected attribute in first-class Type_forall") (mk_inst_tyenv tpinst) ss
|
|
let copy_slotsig ss = remap_slotsig (fun _ -> failwith "Unexpected attribute in first-class Type_forall") empty_remap ss
|
|
|
|
let mk_typar_to_typar_renaming tpsorig tps =
|
|
let tinst = generalize_typars tps
|
|
mk_typar_inst tpsorig tinst,tinst
|
|
|
|
let mk_tycon_inst (tycon:Tycon) tinst = mk_typar_inst tycon.TyparsNoRange tinst
|
|
let mk_tcref_inst tcref tinst = mk_tycon_inst (deref_tycon tcref) tinst
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Remove inference equations and abbreviations from units
|
|
//---------------------------------------------------------------------------
|
|
|
|
let reduce_tcref_abbrev_measure (tcref:TyconRef) =
|
|
let abbrev = tcref.TypeAbbrev
|
|
match abbrev with
|
|
| Some (TType_measure abbrev_measure) -> abbrev_measure
|
|
| _ -> invalid_arg "reduce_tcref_abbrev_measure: no abbreviation or incorrect kind"
|
|
|
|
let rec strip_tpeqns_and_tcabbrevsA_measure canShortcut measure =
|
|
match strip_upeqnsA canShortcut measure with
|
|
| MeasureCon tcref when tcref.IsTypeAbbrev ->
|
|
strip_tpeqns_and_tcabbrevsA_measure canShortcut (reduce_tcref_abbrev_measure tcref)
|
|
| m -> m
|
|
|
|
let strip_tpeqns_and_tcabbrevs_measure m = strip_tpeqns_and_tcabbrevsA_measure false m
|
|
|
|
let tcref_eq g tcref1 tcref2 = prim_tcref_eq g.compilingFslib g.fslibCcu tcref1 tcref2
|
|
let tycon_eq (tycon1:Entity) (tycon2:Entity) = (tycon1.Stamp = tycon2.Stamp)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Basic unit stuff
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
/// What is the contribution of unit-of-measure constant ucref to unit-of-measure expression measure?
|
|
let rec MeasureConExponent g abbrev ucref unt =
|
|
match (if abbrev then strip_tpeqns_and_tcabbrevs_measure unt else strip_upeqns unt) with
|
|
| MeasureCon ucref' -> if tcref_eq g ucref' ucref then 1 else 0
|
|
| MeasureInv unt' -> -(MeasureConExponent g abbrev ucref unt')
|
|
| MeasureProd(unt1,unt2) -> MeasureConExponent g abbrev ucref unt1 + MeasureConExponent g abbrev ucref unt2
|
|
| _ -> 0
|
|
|
|
/// What is the contribution of unit-of-measure constant ucref to unit-of-measure expression measure
|
|
/// after remapping tycons?
|
|
let rec MeasureConExponentAfterRemapping g r ucref unt =
|
|
match strip_tpeqns_and_tcabbrevs_measure unt with
|
|
| MeasureCon ucref' -> if tcref_eq g (r ucref') ucref then 1 else 0
|
|
| MeasureInv unt' -> -(MeasureConExponentAfterRemapping g r ucref unt')
|
|
| MeasureProd(unt1,unt2) -> MeasureConExponentAfterRemapping g r ucref unt1 + MeasureConExponentAfterRemapping g r ucref unt2
|
|
| _ -> 0
|
|
|
|
/// What is the contribution of unit-of-measure variable tp to unit-of-measure expression unt?
|
|
let rec MeasureVarExponent tp unt =
|
|
match strip_tpeqns_and_tcabbrevs_measure unt with
|
|
| MeasureVar tp' -> if typar_ref_eq tp tp' then 1 else 0
|
|
| MeasureInv unt' -> -(MeasureVarExponent tp unt')
|
|
| MeasureProd(unt1,unt2) -> MeasureVarExponent tp unt1 + MeasureVarExponent tp unt2
|
|
| _ -> 0
|
|
|
|
/// List the *literal* occurrences of unit variables in a unit expression, without repeats
|
|
let ListMeasureVarOccs unt =
|
|
let rec gather acc unt =
|
|
match strip_tpeqns_and_tcabbrevs_measure unt with
|
|
MeasureVar tp -> if List.exists (typar_ref_eq tp) acc then acc else tp::acc
|
|
| MeasureProd(unt1,unt2) -> gather (gather acc unt1) unt2
|
|
| MeasureInv unt' -> gather acc unt'
|
|
| _ -> acc
|
|
gather [] unt
|
|
|
|
/// List the *observable* occurrences of unit variables in a unit expression, without repeats, paired with their non-zero exponents
|
|
let ListMeasureVarOccsWithNonZeroExponents untexpr =
|
|
let rec gather acc unt =
|
|
match strip_tpeqns_and_tcabbrevs_measure unt with
|
|
MeasureVar tp -> if List.exists (fun (tp', _) -> typar_ref_eq tp tp') acc then acc
|
|
else let e = MeasureVarExponent tp untexpr in if e=0 then acc else (tp,e)::acc
|
|
| MeasureProd(unt1,unt2) -> gather (gather acc unt1) unt2
|
|
| MeasureInv unt' -> gather acc unt'
|
|
| _ -> acc
|
|
gather [] untexpr
|
|
|
|
/// List the *observable* occurrences of unit constants in a unit expression, without repeats, paired with their non-zero exponents
|
|
let ListMeasureConOccsWithNonZeroExponents g eraseAbbrevs untexpr =
|
|
let rec gather acc unt =
|
|
match (if eraseAbbrevs then strip_tpeqns_and_tcabbrevs_measure unt else strip_upeqns unt) with
|
|
| MeasureCon c -> if List.exists (fun (c', _) -> tcref_eq g c c') acc then acc
|
|
else let e = MeasureConExponent g eraseAbbrevs c untexpr in if e=0 then acc else (c,e)::acc
|
|
| MeasureProd(unt1,unt2) -> gather (gather acc unt1) unt2
|
|
| MeasureInv unt' -> gather acc unt'
|
|
| _ -> acc
|
|
gather [] untexpr
|
|
|
|
/// List the *literal* occurrences of unit constants in a unit expression, without repeats,
|
|
/// and after applying a remapping function r to tycons
|
|
let ListMeasureConOccsAfterRemapping g r unt =
|
|
let rec gather acc unt =
|
|
match (strip_tpeqns_and_tcabbrevs_measure unt) with
|
|
| MeasureCon c -> if List.exists (tcref_eq g (r c)) acc then acc else r c::acc
|
|
| MeasureProd(unt1,unt2) -> gather (gather acc unt1) unt2
|
|
| MeasureInv unt' -> gather acc unt'
|
|
| _ -> acc
|
|
|
|
gather [] unt
|
|
|
|
/// Construct a measure expression representing the n'th power of a measure
|
|
let rec MeasurePower u n =
|
|
if n=0 then MeasureOne
|
|
elif n=1 then u
|
|
elif n<0 then MeasureInv (MeasurePower u (-n))
|
|
else MeasureProd(u,MeasurePower u (n-1))
|
|
|
|
/// Construct a measure expression representing the product of a list of measures
|
|
let ProdMeasures ms = List.foldBack (fun m1 m2 -> MeasureProd (m1,m2)) ms MeasureOne
|
|
|
|
let is_dimensionless g tyarg =
|
|
match strip_tpeqns tyarg with
|
|
| TType_measure unt ->
|
|
isNil (ListMeasureVarOccsWithNonZeroExponents unt) &&
|
|
isNil (ListMeasureConOccsWithNonZeroExponents g true unt)
|
|
| _ -> false
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Tuple compilation (types)
|
|
//------------------------------------------------------------------------
|
|
|
|
let maxTuple = 8
|
|
let goodTupleFields = maxTuple-1
|
|
|
|
let is_tuple_tcref g tcref =
|
|
match tcref with
|
|
| x when g.tuple1_tcr = x || g.tuple2_tcr = x || g.tuple3_tcr = x || g.tuple4_tcr = x || g.tuple5_tcr = x || g.tuple6_tcr = x || g.tuple7_tcr = x || g.tuple8_tcr = x -> true
|
|
| _ -> false
|
|
|
|
let compiled_tuple_tcref g tys =
|
|
let n = List.length tys
|
|
if n = 1 then g.tuple1_tcr
|
|
elif n = 2 then g.tuple2_tcr
|
|
elif n = 3 then g.tuple3_tcr
|
|
elif n = 4 then g.tuple4_tcr
|
|
elif n = 5 then g.tuple5_tcr
|
|
elif n = 6 then g.tuple6_tcr
|
|
elif n = 7 then g.tuple7_tcr
|
|
elif n = 8 then g.tuple8_tcr
|
|
else failwithf "compiled_tuple_tcref, n = %d" n
|
|
|
|
let rec compiled_tuple_ty g tys =
|
|
let n = List.length tys
|
|
if n < maxTuple then TType_app (compiled_tuple_tcref g tys, tys)
|
|
else
|
|
let tysA,tysB = split_after goodTupleFields tys
|
|
TType_app (g.tuple8_tcr, tysA@[compiled_tuple_ty g tysB])
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Remove inference equations and abbreviations from types
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
let apply_tycon_abbrev abbrev_ty tycon tyargs =
|
|
if isNil tyargs then abbrev_ty
|
|
else InstType (mk_tycon_inst tycon tyargs) abbrev_ty
|
|
|
|
let reduce_tycon_abbrev (tycon:Tycon) tyargs =
|
|
let abbrev = tycon.TypeAbbrev
|
|
match abbrev with
|
|
| None -> invalidArg "tycon" "this type definition is not an abbreviation";
|
|
| Some abbrev_ty ->
|
|
apply_tycon_abbrev abbrev_ty tycon tyargs
|
|
|
|
let reduce_tcref_abbrev (tcref:TyconRef) tyargs =
|
|
reduce_tycon_abbrev tcref.Deref tyargs
|
|
|
|
let reduce_tycon_measureable (tycon:Tycon) tyargs =
|
|
let repr = tycon.TypeReprInfo
|
|
match repr with
|
|
| Some (TMeasureableRepr ty) ->
|
|
if isNil tyargs then ty else InstType (mk_tycon_inst tycon tyargs) ty
|
|
| _ -> invalidArg "tc" "this type definition is not a refinement"
|
|
|
|
let reduce_tcref_measureable (tcref:TyconRef) tyargs =
|
|
reduce_tycon_measureable tcref.Deref tyargs
|
|
|
|
let rec strip_tpeqns_and_tcabbrevsA g canShortcut ty =
|
|
let ty = strip_tpeqnsA canShortcut ty
|
|
match ty with
|
|
| TType_app (tcref,tinst) ->
|
|
let tycon = tcref.Deref
|
|
match tycon.TypeAbbrev with
|
|
| Some abbrev_ty ->
|
|
strip_tpeqns_and_tcabbrevsA g canShortcut (apply_tycon_abbrev abbrev_ty tycon tinst)
|
|
| None ->
|
|
if tycon.IsMeasureableReprTycon && List.forall (is_dimensionless g) tinst then
|
|
strip_tpeqns_and_tcabbrevsA g canShortcut (reduce_tycon_measureable tycon tinst)
|
|
else ty
|
|
| ty -> ty
|
|
|
|
let strip_tpeqns_and_tcabbrevs g ty = strip_tpeqns_and_tcabbrevsA g false ty
|
|
|
|
/// This erases outermost occurences of inference equations, type abbreviations and measureable types (float<_>).
|
|
/// It also optionally erases all "compilation representations", i.e. function and
|
|
/// tuple types, and also "nativeptr<'T> --> System.IntPtr"
|
|
let rec strip_tpeqns_and_tcabbrevs_and_erase eraseFuncAndTuple g ty =
|
|
let ty = strip_tpeqns_and_tcabbrevs g ty
|
|
match ty with
|
|
| TType_app (tcref,args) ->
|
|
let tycon = tcref.Deref
|
|
if tycon.IsMeasureableReprTycon then
|
|
strip_tpeqns_and_tcabbrevs_and_erase eraseFuncAndTuple g (reduce_tycon_measureable tycon args)
|
|
elif tcref_eq g tcref g.nativeptr_tcr && eraseFuncAndTuple then
|
|
strip_tpeqns_and_tcabbrevs_and_erase eraseFuncAndTuple g g.nativeint_ty
|
|
else
|
|
ty
|
|
| TType_fun(a,b) when eraseFuncAndTuple -> TType_app(g.fastFunc_tcr,[ a; b])
|
|
| TType_tuple(l) when eraseFuncAndTuple -> compiled_tuple_ty g l
|
|
| ty -> ty
|
|
|
|
let strip_tpeqns_and_tcabbrevs_and_measureable g ty =
|
|
strip_tpeqns_and_tcabbrevs_and_erase false g ty
|
|
|
|
type Erasure = EraseAll | EraseMeasures | EraseNone
|
|
|
|
let strip_tpeqns_and_tcabbrevs_wrt_erasure erasureFlag g ty =
|
|
match erasureFlag with
|
|
| EraseAll -> strip_tpeqns_and_tcabbrevs_and_erase true g ty
|
|
| EraseMeasures -> strip_tpeqns_and_tcabbrevs_and_erase false g ty
|
|
| _ -> strip_tpeqns_and_tcabbrevs g ty
|
|
|
|
|
|
let rec strip_eqns_from_ecref (eref:TyconRef) =
|
|
let exnc = deref_tycon eref
|
|
match exnc.ExceptionInfo with
|
|
| TExnAbbrevRepr eref -> strip_eqns_from_ecref eref
|
|
| _ -> exnc
|
|
|
|
let dest_unpar_measure g unt =
|
|
let vs = ListMeasureVarOccsWithNonZeroExponents unt
|
|
let cs = ListMeasureConOccsWithNonZeroExponents g true unt
|
|
match vs, cs with
|
|
| [(v,1)], [] -> v
|
|
| _, _ -> failwith "dest_unpar_measure: not a unit-of-measure parameter"
|
|
|
|
let is_unpar_measure g unt =
|
|
let vs = ListMeasureVarOccsWithNonZeroExponents unt
|
|
let cs = ListMeasureConOccsWithNonZeroExponents g true unt
|
|
|
|
match vs, cs with
|
|
| [(_,1)], [] -> true
|
|
| _, _ -> false
|
|
|
|
|
|
let prim_dest_forall_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_forall (tyvs,tau) -> (tyvs,tau) | _ -> failwith "prim_dest_forall_typ: not a forall type")
|
|
let dest_fun_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_fun (tyv,tau) -> (tyv,tau) | _ -> failwith "dest_fun_typ: not a function type")
|
|
let dest_tuple_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_tuple l -> l | _ -> failwith "dest_tuple_typ: not a tuple type")
|
|
let dest_typar_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_var v -> v | _ -> failwith "dest_typar_typ: not a typar type")
|
|
let dest_anypar_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_var v -> v | TType_measure unt -> dest_unpar_measure g unt | _ -> failwith "dest_anypar_typ: not a typar or unpar type")
|
|
let dest_measure_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_measure m -> m | _ -> failwith "dest_measure_typ: not a unit-of-measure type")
|
|
let is_fun_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_fun _ -> true | _ -> false)
|
|
let is_forall_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_forall _ -> true | _ -> false)
|
|
let is_tuple_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_tuple _ -> true | _ -> false)
|
|
let is_union_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcr,_) -> tcr.IsUnionTycon | _ -> false)
|
|
let is_repr_hidden_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcr,_) -> tcr.IsHiddenReprTycon | _ -> false)
|
|
let is_fsobjmodel_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcr,_) -> tcr.IsFSharpObjectModelTycon | _ -> false)
|
|
let is_recd_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcr,_) -> tcr.IsRecordTycon | _ -> false)
|
|
let is_typar_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_var _ -> true | _ -> false)
|
|
let is_anypar_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_var _ -> true | TType_measure unt -> is_unpar_measure g unt | _ -> false)
|
|
let is_measure_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_measure _ -> true | _ -> false)
|
|
|
|
// WARNING: If you increase this you must make the corresponding types in FSharp.Core.dll structs
|
|
#if TUPLE_STRUXT
|
|
let highestTupleStructType = 2
|
|
let is_tuple_struct_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_tuple l -> l.Length <= highestTupleStructType | _ -> false)
|
|
#else
|
|
let is_tuple_struct_typ (g:TcGlobals) (ty:typ) = false
|
|
#endif
|
|
|
|
|
|
let is_proven_ucase_typ ty = match ty with TType_ucase _ -> true | _ -> false
|
|
|
|
let mk_tyapp_ty tcref tyargs = TType_app(tcref,tyargs)
|
|
let mk_proven_ucase_typ ucref tyargs = TType_ucase(ucref,tyargs)
|
|
let is_stripped_tyapp_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app _ -> true | _ -> false)
|
|
let dest_stripped_tyapp_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcref,tinst) -> tcref,tinst | _ -> failwith "dest_stripped_tyapp_typ")
|
|
let tcref_of_stripped_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcref,_) -> tcref | _ -> failwith "tcref_of_stripped_typ")
|
|
let try_tcref_of_stripped_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(tcref,_) -> Some tcref | _ -> None)
|
|
let tinst_of_stripped_typ g ty = ty |> strip_tpeqns_and_tcabbrevs g |> (function TType_app(_,tinst) -> tinst | _ -> [])
|
|
let tycon_of_stripped_typ g ty = deref_tycon (tcref_of_stripped_typ g ty)
|
|
|
|
let mk_inst_for_stripped_typ g typ =
|
|
if is_stripped_tyapp_typ g typ then
|
|
let tcref,tinst = dest_stripped_tyapp_typ g typ
|
|
mk_tcref_inst tcref tinst
|
|
else []
|
|
|
|
let domain_of_fun_typ g ty = fst(dest_fun_typ g ty)
|
|
let range_of_fun_typ g ty = snd(dest_fun_typ g ty)
|
|
|
|
let contains_measures_typ g ty =
|
|
let rec contains ty =
|
|
match strip_tpeqns_and_tcabbrevs g ty with
|
|
| TType_tuple l -> List.exists contains l
|
|
| TType_app (_,tinst) -> List.exists contains tinst
|
|
| TType_ucase (_,tinst) -> List.exists contains tinst
|
|
| TType_fun (d,r) -> contains d || contains r
|
|
| TType_var r -> match r.Kind with KindMeasure -> true | _ -> false
|
|
| TType_forall (tps,r) -> contains r
|
|
| TType_modul_bindings -> failwith "contains_measures_typ: naked struct"
|
|
| TType_measure unt -> true
|
|
contains ty
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Type information about records, constructors etc.
|
|
//---------------------------------------------------------------------------
|
|
|
|
let typ_of_rfield inst (fspec:RecdField) = InstType inst fspec.FormalType
|
|
|
|
let typs_of_rfields inst rfields = List.map (typ_of_rfield inst) rfields
|
|
|
|
let typs_of_instance_rfields_of_tcref inst (tcref:TyconRef) = tcref.AllInstanceFieldsAsList |> typs_of_rfields inst
|
|
|
|
let rfield_tables_of_ucref (x:UnionCaseRef) = x.UnionCase.ucase_rfields
|
|
let rfields_of_ucref x = (rfield_tables_of_ucref x).AllFieldsAsList
|
|
let rfield_of_ucref_by_idx x n = (rfield_tables_of_ucref x).FieldByIndex n
|
|
|
|
let rty_of_ucref (x:UnionCaseRef) = x.UnionCase.ucase_rty
|
|
let typs_of_ucref_rfields inst x = typs_of_rfields inst (rfields_of_ucref x)
|
|
|
|
let typ_of_ucref_rfield_by_idx (x:UnionCaseRef) tinst j =
|
|
let tcref = x.TyconRef
|
|
let inst = mk_tcref_inst tcref tinst
|
|
typ_of_rfield inst (rfield_of_ucref_by_idx x j)
|
|
|
|
let rty_of_uctyp (x:UnionCaseRef) tinst =
|
|
let tcref = x.TyconRef
|
|
let inst = mk_tcref_inst tcref tinst
|
|
InstType inst (rty_of_ucref x)
|
|
|
|
let rfields_of_ecref x = (strip_eqns_from_ecref x).TrueInstanceFieldsAsList
|
|
let rfield_of_ecref_by_idx x n = (strip_eqns_from_ecref x).GetFieldByIndex n
|
|
|
|
let typs_of_ecref_rfields x = typs_of_rfields [] (rfields_of_ecref x)
|
|
let typ_of_ecref_rfield x j = typ_of_rfield [] (rfield_of_ecref_by_idx x j)
|
|
|
|
(* REVIEW: these could be faster, e.g. by storing the index in the NameMap *)
|
|
let ucref_index (UCRef(tcref,id)) = try tcref.UnionCasesArray |> Array.find_index (fun ucspec -> ucspec.DisplayName = id) with Not_found -> error(InternalError(Printf.sprintf "constructor %s not found in type %s" id tcref.MangledName, tcref.Range))
|
|
let rfref_index (RFRef(tcref,id)) = try tcref.AllFieldsArray |> Array.find_index (fun rfspec -> rfspec.Name = id) with Not_found -> error(InternalError(Printf.sprintf "field %s not found in type %s" id tcref.MangledName, tcref.Range))
|
|
|
|
let ucrefs_of_tcref (tcref:TyconRef) = tcref.UnionCasesAsList |> List.map (ucref_of_ucase tcref)
|
|
let instance_rfrefs_of_tcref (tcref:TyconRef) = tcref.TrueInstanceFieldsAsList |> List.map (rfref_of_rfield tcref)
|
|
let all_rfrefs_of_tcref (tcref:TyconRef) = tcref.AllFieldsAsList |> List.map (rfref_of_rfield tcref)
|
|
|
|
let actual_typ_of_rfield tycon tinst (fspec:RecdField) =
|
|
InstType (mk_tycon_inst tycon tinst) fspec.FormalType
|
|
|
|
let actual_rtyp_of_rfref (fref:RecdFieldRef) tinst =
|
|
actual_typ_of_rfield fref.Tycon tinst fref.RecdField
|
|
|
|
let formal_typ_of_tcref g (tcref:TyconRef) =
|
|
TType_app(tcref,List.map mk_typar_ty tcref.TyparsNoRange)
|
|
|
|
let enclosing_formal_typ_of_val g (v:Val) = formal_typ_of_tcref g v.MemberApparentParent
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Apply type functions to types
|
|
//---------------------------------------------------------------------------
|
|
|
|
let NormalizeDeclaredTyparsForEquiRecursiveInference g tps =
|
|
match tps with
|
|
| [] -> []
|
|
| tps ->
|
|
tps |> List.map (fun tp ->
|
|
let ty = mk_typar_ty tp
|
|
if is_anypar_typ g ty then dest_anypar_typ g ty else tp)
|
|
|
|
let dest_forall_typ g ty =
|
|
let tps,tau = prim_dest_forall_typ g ty
|
|
// tps may be have been equated to other tps in equi-recursive type inference
|
|
// and unit type inference. Normalize them here
|
|
let tps = NormalizeDeclaredTyparsForEquiRecursiveInference g tps
|
|
tps,tau
|
|
|
|
let try_dest_forall_typ g ty =
|
|
if is_forall_typ g ty then dest_forall_typ g ty else ([],ty)
|
|
|
|
|
|
let rec strip_fun_typ g ty =
|
|
if is_fun_typ g ty then
|
|
let (d,r) = dest_fun_typ g ty
|
|
let more,rty = strip_fun_typ g r
|
|
d::more, rty
|
|
else [],ty
|
|
|
|
let reduce_forall_typ g ty tyargs =
|
|
let tps,tau = dest_forall_typ g ty
|
|
InstType (mk_typar_inst tps tyargs) tau
|
|
|
|
let reduce_iterated_fun_ty g ty args =
|
|
List.fold (fun ty _ ->
|
|
if not (is_fun_typ g ty) then failwith "reduce_iterated_fun_ty";
|
|
snd (dest_fun_typ g ty)) ty args
|
|
|
|
let apply_types g functy (tyargs,argtys) =
|
|
let after_tyapp_ty = if is_forall_typ g functy then reduce_forall_typ g functy tyargs else functy
|
|
reduce_iterated_fun_ty g after_tyapp_ty argtys
|
|
|
|
let formal_apply_types g functy (tyargs,args) =
|
|
reduce_iterated_fun_ty g
|
|
(if isNil tyargs then functy else snd (dest_forall_typ g functy))
|
|
args
|
|
|
|
let rec strip_fun_typ_upto g n ty =
|
|
assert (n >= 0);
|
|
if n > 0 && is_fun_typ g ty then
|
|
let (d,r) = dest_fun_typ g ty
|
|
let more,rty = strip_fun_typ_upto g (n-1) r in d::more, rty
|
|
else [],ty
|
|
|
|
|
|
let try_dest_tuple_typ g ty =
|
|
if is_tuple_typ g ty then dest_tuple_typ g ty else [ty]
|
|
|
|
type UncurriedArgInfos = (typ * TopArgInfo) list
|
|
type CurriedArgInfos = (typ * TopArgInfo) list list
|
|
|
|
(* A 'tau' type is one with its type paramaeters stripped off *)
|
|
let GetTopTauTypeInFSharpForm g (curriedArgInfos: TopArgInfo list list) tau m =
|
|
let argtys,rty = strip_fun_typ_upto g curriedArgInfos.Length tau
|
|
if curriedArgInfos.Length <> argtys.Length then
|
|
error(Error("Invalid member signature encountered because of an earlier error",m))
|
|
let argtysl =
|
|
(curriedArgInfos,argtys) ||> List.map2 (fun argInfos argty ->
|
|
match argInfos with
|
|
| [] -> [ (g.unit_ty, TopValInfo.unnamedTopArg1) ]
|
|
| [argInfo] -> [ (argty, argInfo) ]
|
|
| _ -> List.zip (dest_tuple_typ g argty) argInfos)
|
|
argtysl,rty
|
|
|
|
let dest_top_forall_type g (TopValInfo (ntps,argInfos,retInfo) as topValInfo) ty =
|
|
let tps,tau = (if isNil ntps then [],ty else try_dest_forall_typ g ty)
|
|
#if CHECKED
|
|
if tps.Length <> kinds.Length then failwith (sprintf "dest_top_forall_type: internal error, #tps = %d, #ntps = %d" (List.length tps) ntps);
|
|
#endif
|
|
(* tps may be have been equated to other tps in equi-recursive type inference. Normalize them here *)
|
|
let tps = NormalizeDeclaredTyparsForEquiRecursiveInference g tps
|
|
tps,tau
|
|
|
|
let GetTopValTypeInFSharpForm g (TopValInfo(_,argInfos,retInfo) as topValInfo) ty m =
|
|
let tps,tau = dest_top_forall_type g topValInfo ty
|
|
let argtysl,rty = GetTopTauTypeInFSharpForm g argInfos tau m
|
|
tps,argtysl,rty,retInfo
|
|
|
|
let IsCompiledAsStaticValue g (v:Val) =
|
|
(isSome v.TopValInfo &&
|
|
match GetTopValTypeInFSharpForm g v.TopValInfo.Value v.Type v.Range with
|
|
| [],[], _,_ when not v.IsMember -> true
|
|
| _ -> false)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Multi-dimensional array types...
|
|
//-------------------------------------------------------------------------
|
|
|
|
let is_il_arr_tcref g tcr =
|
|
tcref_eq g tcr g.il_arr1_tcr ||
|
|
tcref_eq g tcr g.il_arr2_tcr ||
|
|
tcref_eq g tcr g.il_arr3_tcr ||
|
|
tcref_eq g tcr g.il_arr4_tcr
|
|
|
|
let rank_of_il_arr_tcref g tcr =
|
|
if tcref_eq g tcr g.il_arr1_tcr then 1
|
|
elif tcref_eq g tcr g.il_arr2_tcr then 2
|
|
elif tcref_eq g tcr g.il_arr3_tcr then 3
|
|
elif tcref_eq g tcr g.il_arr4_tcr then 4
|
|
else failwith "rank_of_il_arr_tcref: unsupported array rank"
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Misc functions on F# types
|
|
//-------------------------------------------------------------------------
|
|
|
|
let get_array_element_typ (g:TcGlobals) ty =
|
|
let tcr,tinst = dest_stripped_tyapp_typ g ty
|
|
match tinst with
|
|
| [ty] -> ty
|
|
| _ -> failwith "get_array_element_typ";
|
|
|
|
let is_il_arr_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> is_il_arr_tcref g tcref
|
|
|
|
let is_il_arr1_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g tcref g.il_arr1_tcr
|
|
|
|
let dest_il_arr1_typ g ty = get_array_element_typ g ty
|
|
|
|
let is_compat_array_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.array_tcr tcref
|
|
|
|
let is_unit_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.unit_tcr_canon tcref
|
|
|
|
let is_obj_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.system_Object_tcref tcref
|
|
|
|
let is_void_typ (g:TcGlobals) ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.system_Void_tcref tcref
|
|
|
|
let is_il_named_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref.IsILTycon
|
|
|
|
|
|
let is_il_class_typ g ty =
|
|
(is_il_named_typ g ty &&
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
(tcref.ILTyconRawMetadata.tdKind = TypeDef_class))
|
|
|
|
let is_il_interface_typ g ty =
|
|
(is_il_named_typ g ty &&
|
|
let tcr,tinst = dest_stripped_tyapp_typ g ty
|
|
(tcr.ILTyconRawMetadata.tdKind = TypeDef_interface))
|
|
|
|
let is_il_ref_typ g ty =
|
|
(is_il_named_typ g ty &&
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
not (is_value_or_enum_tdef tcref.ILTyconRawMetadata)) ||
|
|
is_il_arr_typ g ty
|
|
|
|
|
|
let is_il_enum_tycon (tycon:Tycon) =
|
|
(tycon.IsILTycon && tycon.ILTyconRawMetadata.IsEnum)
|
|
|
|
let is_il_interface_tycon (tycon:Tycon) =
|
|
(tycon.IsILTycon && (tycon.ILTyconRawMetadata.tdKind = TypeDef_interface))
|
|
|
|
let is_il_delegate_tcref (tcref:TyconRef) =
|
|
if tcref.IsILTycon then
|
|
match tcref.ILTyconRawMetadata.tdKind with
|
|
| TypeDef_delegate -> true
|
|
| _ -> false
|
|
else false
|
|
|
|
let is_any_array_typ g ty = is_il_arr_typ g ty || is_compat_array_typ g ty
|
|
let dest_any_array_typ g ty = get_array_element_typ g ty
|
|
let rank_of_any_array_typ g ty =
|
|
if is_il_arr_typ g ty then
|
|
rank_of_il_arr_tcref g (tcref_of_stripped_typ g ty)
|
|
else 1
|
|
|
|
let is_fsobjmodel_ref_typ g ty =
|
|
is_fsobjmodel_typ g ty &&
|
|
let tcr,tinst = dest_stripped_tyapp_typ g ty
|
|
match tcr.FSharpObjectModelTypeInfo.fsobjmodel_kind with
|
|
| TTyconClass | TTyconInterface | TTyconDelegate _ -> true
|
|
| TTyconStruct | TTyconEnum -> false
|
|
|
|
let is_tycon_kind_struct k =
|
|
match k with
|
|
| TTyconClass | TTyconInterface | TTyconDelegate _ -> false
|
|
| TTyconStruct | TTyconEnum -> true
|
|
|
|
let is_tycon_kind_enum k =
|
|
match k with
|
|
| TTyconStruct | TTyconClass | TTyconInterface | TTyconDelegate _ -> false
|
|
| TTyconEnum -> true
|
|
|
|
let is_fsobjmodel_class_tycon (x:Tycon) =
|
|
x.IsFSharpObjectModelTycon &&
|
|
match x.FSharpObjectModelTypeInfo.fsobjmodel_kind with TTyconClass -> true | _ -> false
|
|
|
|
let is_fsobjmodel_class_typ g ty = is_stripped_tyapp_typ g ty && is_fsobjmodel_class_tycon (tycon_of_stripped_typ g ty)
|
|
let is_fsobjmodel_struct_typ g ty = is_stripped_tyapp_typ g ty && (tycon_of_stripped_typ g ty).IsFSharpStructTycon
|
|
let is_fsobjmodel_interface_typ g ty = is_stripped_tyapp_typ g ty && (tycon_of_stripped_typ g ty).IsFSharpInterfaceTycon
|
|
let is_fsobjmodel_delegate_typ g ty = is_stripped_tyapp_typ g ty && (tycon_of_stripped_typ g ty).IsFSharpDelegateTycon
|
|
|
|
let is_delegate_typ g ty =
|
|
is_fsobjmodel_delegate_typ g ty ||
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| Some tcref -> is_il_delegate_tcref tcref
|
|
| _ -> false
|
|
|
|
let is_interface_typ g ty =
|
|
is_il_interface_typ g ty ||
|
|
is_fsobjmodel_interface_typ g ty
|
|
|
|
let is_class_typ g ty =
|
|
is_il_class_typ g ty ||
|
|
is_fsobjmodel_class_typ g ty
|
|
|
|
let is_ref_typ g ty =
|
|
is_union_typ g ty ||
|
|
is_compat_array_typ g ty ||
|
|
(is_tuple_typ g ty && not (is_tuple_struct_typ g ty)) ||
|
|
is_recd_typ g ty ||
|
|
is_il_ref_typ g ty ||
|
|
is_fun_typ g ty ||
|
|
is_repr_hidden_typ g ty ||
|
|
is_fsobjmodel_ref_typ g ty ||
|
|
is_unit_typ g ty
|
|
|
|
let is_struct_typ g ty =
|
|
(is_stripped_tyapp_typ g ty && (tycon_of_stripped_typ g ty).IsStructTycon) || is_tuple_struct_typ g ty
|
|
|
|
let is_enum_tycon x =
|
|
is_il_enum_tycon x || x.IsFSharpEnumTycon
|
|
|
|
let is_interface_tycon x =
|
|
is_il_interface_tycon x || x.IsFSharpInterfaceTycon
|
|
|
|
let is_enum_tcref tcref = is_enum_tycon (deref_tycon tcref)
|
|
let is_struct_tcref (tcref:TyconRef) = tcref.IsStructTycon
|
|
let is_interface_tcref tcref = is_interface_tycon (deref_tycon tcref)
|
|
|
|
let is_enum_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> is_enum_tcref tcref
|
|
|
|
let actual_rty_of_slotsig parentTyInst methTyInst (TSlotSig(_,_,parentFormalTypars,methFormalTypars,_,formalRetTy)) =
|
|
let methTyInst = mk_typar_inst methFormalTypars methTyInst
|
|
let parentTyInst = mk_typar_inst parentFormalTypars parentTyInst
|
|
Option.map (InstType (parentTyInst @ methTyInst)) formalRetTy
|
|
|
|
let slotsig_has_void_rty (TSlotSig(_,_,_,_,_,formalRetTy)) =
|
|
isNone formalRetTy
|
|
|
|
let rty_of_tmethod g (TObjExprMethod((TSlotSig(_,parentTy,_,_,_,_) as ss),methFormalTypars,_,_,m)) =
|
|
let tinst = tinst_of_stripped_typ g parentTy
|
|
let methTyInst = generalize_typars methFormalTypars
|
|
actual_rty_of_slotsig tinst methTyInst ss
|
|
|
|
/// Is the type 'abstract' in C#-speak
|
|
let is_partially_implemented_tycon (tycon:Tycon) =
|
|
if tycon.IsFSharpObjectModelTycon then
|
|
not tycon.IsFSharpDelegateTycon &&
|
|
tycon.TypeContents.tcaug_abstract
|
|
else
|
|
(tycon.IsILTycon && tycon.ILTyconRawMetadata.IsAbstract)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Find all type variables in a type, apart from those that have had
|
|
// an equation assigned by type inference.
|
|
//---------------------------------------------------------------------------
|
|
|
|
let empty_free_locvals = Zset.empty val_spec_order
|
|
let union_free_locvals s1 s2 =
|
|
if s1 == empty_free_locvals then s2
|
|
elif s2 == empty_free_locvals then s1
|
|
else Zset.union s1 s2
|
|
|
|
let empty_free_rfields = Zset.empty rfref_order
|
|
let union_free_rfields s1 s2 =
|
|
if s1 == empty_free_rfields then s2
|
|
elif s2 == empty_free_rfields then s1
|
|
else Zset.union s1 s2
|
|
|
|
let empty_free_ucases = Zset.empty ucref_order
|
|
let union_free_ucases s1 s2 =
|
|
if s1 == empty_free_ucases then s2
|
|
elif s2 == empty_free_ucases then s1
|
|
else Zset.union s1 s2
|
|
|
|
let empty_free_loctycons = Zset.empty tycon_spec_order
|
|
let union_free_loctycons s1 s2 =
|
|
if s1 == empty_free_loctycons then s2
|
|
elif s2 == empty_free_loctycons then s1
|
|
else Zset.union s1 s2
|
|
|
|
let typar_spec_order (v1:Typar) (v2:Typar) = compare v1.Stamp v2.Stamp (* type instanced *)
|
|
|
|
let empty_free_loctypars = Zset.empty typar_spec_order
|
|
let union_free_loctypars s1 s2 =
|
|
if s1 == empty_free_loctypars then s2
|
|
elif s2 == empty_free_loctypars then s1
|
|
else Zset.union s1 s2
|
|
|
|
let empty_free_tyvars =
|
|
{ FreeTycons=empty_free_loctycons;
|
|
/// The summary of values used as trait solutions
|
|
FreeTraitSolutions=empty_free_locvals;
|
|
FreeTypars=empty_free_loctypars}
|
|
|
|
let union_free_tyvars fvs1 fvs2 =
|
|
if fvs1 == empty_free_tyvars then fvs2 else
|
|
if fvs2 == empty_free_tyvars then fvs1 else
|
|
{ FreeTycons = union_free_loctycons fvs1.FreeTycons fvs2.FreeTycons;
|
|
FreeTraitSolutions = union_free_locvals fvs1.FreeTraitSolutions fvs2.FreeTraitSolutions;
|
|
FreeTypars = union_free_loctypars fvs1.FreeTypars fvs2.FreeTypars }
|
|
|
|
type FreeVarOptions =
|
|
{ canCache: bool;
|
|
collectInTypes: bool
|
|
includeLocalTycons: bool;
|
|
includeTypars: bool;
|
|
includeLocalTyconReprs: bool;
|
|
includeRecdFields : bool;
|
|
includeUnionCases : bool;
|
|
includeLocals : bool }
|
|
|
|
let CollectAllNoCaching =
|
|
{ canCache=false;
|
|
collectInTypes=true;
|
|
includeLocalTycons=true;
|
|
includeLocalTyconReprs=true;
|
|
includeRecdFields =true;
|
|
includeUnionCases=true;
|
|
includeTypars=true;
|
|
includeLocals=true }
|
|
|
|
let CollectTyparsNoCaching =
|
|
{ canCache=false;
|
|
collectInTypes=true;
|
|
includeLocalTycons=false;
|
|
includeTypars=true;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false;
|
|
includeLocals=false }
|
|
|
|
let CollectLocalsNoCaching =
|
|
{ canCache=false;
|
|
collectInTypes=false;
|
|
includeLocalTycons=false;
|
|
includeTypars=false;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false;
|
|
includeLocals=true }
|
|
|
|
let CollectTyparsAndLocalsNoCaching =
|
|
{ canCache=false;
|
|
collectInTypes=true;
|
|
includeLocalTycons=false;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false;
|
|
includeTypars=true;
|
|
includeLocals=true }
|
|
|
|
let CollectAll =
|
|
{ canCache=false;
|
|
collectInTypes=true;
|
|
includeLocalTycons=true;
|
|
includeLocalTyconReprs=true;
|
|
includeRecdFields =true;
|
|
includeUnionCases=true;
|
|
includeTypars=true;
|
|
includeLocals=true }
|
|
|
|
let CollectTyparsAndLocals = // CollectAll
|
|
{ canCache=true; // only cache for this one
|
|
collectInTypes=true;
|
|
includeTypars=true;
|
|
includeLocals=true;
|
|
includeLocalTycons=false;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false; }
|
|
|
|
|
|
let CollectTypars = CollectTyparsAndLocals
|
|
(*
|
|
{ canCache=false;
|
|
collectInTypes=true;
|
|
includeTypars=true;
|
|
includeLocals=false;
|
|
includeLocalTycons=false;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false;}
|
|
*)
|
|
|
|
let CollectLocals = CollectTyparsAndLocals
|
|
(*
|
|
{ canCache=false;
|
|
collectInTypes=false;
|
|
includeLocalTycons=false;
|
|
includeLocalTyconReprs=false;
|
|
includeRecdFields =false;
|
|
includeUnionCases=false;
|
|
includeTypars=false;
|
|
includeLocals=true }
|
|
*)
|
|
|
|
|
|
let acc_free_loctycon opts x acc =
|
|
if not opts.includeLocalTycons then acc else
|
|
if Zset.mem x acc.FreeTycons then acc else
|
|
{acc with FreeTycons = Zset.add x acc.FreeTycons }
|
|
|
|
let acc_free_tycon opts (tcr:TyconRef) acc =
|
|
if not opts.includeLocalTycons then acc else
|
|
match tcr.IsLocalRef with
|
|
| true -> acc_free_loctycon opts tcr.PrivateTarget acc
|
|
| _ -> acc
|
|
|
|
let rec bound_typars opts tps acc =
|
|
// Bound type vars form a recursively-referential set due to constraints, e.g. A : I<B>, B : I<A>
|
|
// So collect up free vars in all constraints first, then bind all variables
|
|
let acc = List.foldBack (fun (tp:Typar) acc -> acc_free_in_typar_constraints opts tp.Constraints acc) tps acc
|
|
List.foldBack (fun tp acc -> {acc with FreeTypars = Zset.remove tp acc.FreeTypars}) tps acc
|
|
|
|
and acc_free_in_typar_constraints opts cxs acc =
|
|
List.foldBack (acc_free_in_typar_constraint opts) cxs acc
|
|
|
|
and acc_free_in_typar_constraint opts tpc acc =
|
|
match tpc with
|
|
| TTyparCoercesToType(typ,m) -> acc_free_in_type opts typ acc
|
|
| TTyparMayResolveMemberConstraint (traitInfo,_) -> acc_free_in_trait opts traitInfo acc
|
|
| TTyparDefaultsToType(_,rty,_) -> acc_free_in_type opts rty acc
|
|
| TTyparSimpleChoice(tys,_) -> acc_free_in_types opts tys acc
|
|
| TTyparIsEnum(uty,m) -> acc_free_in_type opts uty acc
|
|
| TTyparIsDelegate(aty,bty,m) -> acc_free_in_type opts aty (acc_free_in_type opts bty acc)
|
|
| TTyparSupportsNull _ | TTyparIsNotNullableValueType _ | TTyparIsReferenceType _
|
|
| TTyparRequiresDefaultConstructor _ -> acc
|
|
|
|
and acc_free_in_trait opts (TTrait(typs,_,_,argtys,rty,sln)) acc =
|
|
Option.fold_right (acc_free_in_trait_sln opts) sln.Value
|
|
(acc_free_in_types opts typs
|
|
(acc_free_in_types opts argtys
|
|
(Option.fold_right (acc_free_in_type opts) rty acc)))
|
|
|
|
and acc_free_in_trait_sln opts sln acc =
|
|
match sln with
|
|
| ILMethSln(typ,extOpt,mref,minst) ->
|
|
acc_free_in_type opts typ
|
|
(acc_free_in_types opts minst acc)
|
|
| FSMethSln(typ, vref,minst) ->
|
|
acc_free_in_type opts typ
|
|
(acc_free_trait_sln_vref opts vref
|
|
(acc_free_in_types opts minst acc))
|
|
| BuiltInSln -> acc
|
|
|
|
and acc_free_trait_sln_locval opts v fvs =
|
|
if Zset.mem v fvs.FreeTraitSolutions then fvs
|
|
else
|
|
let fvs = acc_free_in_val opts v fvs
|
|
{fvs with FreeTraitSolutions=Zset.add v fvs.FreeTraitSolutions}
|
|
and acc_free_trait_sln_vref opts (vref:ValRef) fvs =
|
|
match vref.IsLocalRef with
|
|
| true -> acc_free_trait_sln_locval opts vref.PrivateTarget fvs
|
|
// non-local values do not contain free variables
|
|
| _ -> fvs
|
|
|
|
and acc_free_tpref opts (tp:Typar) acc =
|
|
if not opts.includeTypars then acc else
|
|
if Zset.mem tp acc.FreeTypars then acc
|
|
else
|
|
acc_free_in_typar_constraints opts tp.Constraints
|
|
{acc with FreeTypars=Zset.add tp acc.FreeTypars}
|
|
|
|
and acc_free_in_type opts ty acc =
|
|
match strip_tpeqns ty with
|
|
| TType_tuple l -> acc_free_in_types opts l acc
|
|
| TType_app (tc,tinst) ->
|
|
let acc = acc_free_tycon opts tc acc
|
|
match tinst with
|
|
| [] -> acc // optimization to avoid unneeded call
|
|
| _ -> acc_free_in_types opts tinst acc
|
|
| TType_ucase (UCRef(tc,_),tinst) -> acc_free_in_types opts tinst (acc_free_tycon opts tc acc)
|
|
| TType_fun (d,r) -> acc_free_in_type opts d (acc_free_in_type opts r acc)
|
|
| TType_var r -> acc_free_tpref opts r acc
|
|
| TType_forall (tps,r) -> union_free_tyvars (bound_typars opts tps (free_in_type opts r)) acc
|
|
| TType_modul_bindings -> failwith "acc_free_in_type opts: naked struct"
|
|
| TType_measure unt -> acc_free_in_unit opts unt acc
|
|
and acc_free_in_unit opts unt acc = List.foldBack (fun (tp,_) acc -> acc_free_tpref opts tp acc) (ListMeasureVarOccsWithNonZeroExponents unt) acc
|
|
and acc_free_in_types opts tys acc =
|
|
match tys with
|
|
| [] -> acc
|
|
| h :: t -> acc_free_in_type opts h (acc_free_in_types opts t acc)
|
|
and free_in_type opts ty = acc_free_in_type opts ty empty_free_tyvars
|
|
|
|
and acc_free_in_val opts (v:Val) acc = acc_free_in_type opts v.Data.val_type acc
|
|
|
|
let free_in_types opts tys = acc_free_in_types opts tys empty_free_tyvars
|
|
let free_in_val opts v = acc_free_in_val opts v empty_free_tyvars
|
|
let free_in_typar_constraints opts v = acc_free_in_typar_constraints opts v empty_free_tyvars
|
|
let acc_free_tprefs opts tps acc = List.foldBack (acc_free_tpref opts) tps acc
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Free in type, left-to-right order preserved. This is used to determine the
|
|
// order of type variables for top-level definitions based on their signature,
|
|
// so be careful not to change the order. We accumulate in reverse
|
|
// order.
|
|
//--------------------------------------------------------------------------
|
|
|
|
let empty_free_typars_lr = []
|
|
let union_free_typars_lr fvs1 fvs2 = ListSet.unionFavourRight typar_ref_eq fvs1 fvs2
|
|
|
|
let rec bound_typars_lr g cxFlag thruFlag acc tps =
|
|
(* Bound type vars form a recursively-referential set due to constraints, e.g. A : I<B>, B : I<A> *)
|
|
(* So collect up free vars in all constraints first, then bind all variables *)
|
|
let acc = List.fold (fun acc (tp:Typar) -> acc_free_in_typar_constraints_lr g cxFlag thruFlag acc tp.Constraints) tps acc
|
|
List.foldBack (ListSet.remove typar_ref_eq) tps acc
|
|
|
|
and acc_free_in_typar_constraints_lr g cxFlag thruFlag acc cxs =
|
|
List.fold (acc_free_in_typar_constraint_lr g cxFlag thruFlag) acc cxs
|
|
|
|
and acc_free_in_typar_constraint_lr g cxFlag thruFlag acc tpc =
|
|
match tpc with
|
|
| TTyparCoercesToType(typ,m) -> acc_free_in_type_lr g cxFlag thruFlag acc typ
|
|
| TTyparMayResolveMemberConstraint (traitInfo,_) -> acc_free_in_trait_lr g cxFlag thruFlag acc traitInfo
|
|
| TTyparDefaultsToType(_,rty,_) -> acc_free_in_type_lr g cxFlag thruFlag acc rty
|
|
| TTyparSimpleChoice(tys,_) -> acc_free_in_types_lr g cxFlag thruFlag acc tys
|
|
| TTyparIsEnum(uty,m) -> acc_free_in_type_lr g cxFlag thruFlag acc uty
|
|
| TTyparIsDelegate(aty,bty,m) -> acc_free_in_type_lr g cxFlag thruFlag (acc_free_in_type_lr g cxFlag thruFlag acc aty) bty
|
|
| TTyparSupportsNull _ | TTyparIsNotNullableValueType _ | TTyparIsReferenceType _
|
|
| TTyparRequiresDefaultConstructor _ -> acc
|
|
|
|
and acc_free_in_trait_lr g cxFlag thruFlag acc (TTrait(typs,_,_,argtys,rty,_)) =
|
|
let acc = acc_free_in_types_lr g cxFlag thruFlag acc typs
|
|
let acc = acc_free_in_types_lr g cxFlag thruFlag acc argtys
|
|
let acc = Option.fold_left (acc_free_in_type_lr g cxFlag thruFlag) acc rty
|
|
acc
|
|
|
|
and acc_free_tpref_lr g cxFlag thruFlag acc (tp:Typar) =
|
|
if ListSet.mem typar_ref_eq tp acc
|
|
then acc
|
|
else
|
|
let acc = (ListSet.insert typar_ref_eq tp acc)
|
|
if cxFlag then
|
|
acc_free_in_typar_constraints_lr g cxFlag thruFlag acc tp.Constraints
|
|
else
|
|
acc
|
|
|
|
and acc_free_in_type_lr g cxFlag thruFlag acc ty =
|
|
if verbose then dprintf "--> acc_free_in_type_lr \n";
|
|
match (if thruFlag then strip_tpeqns_and_tcabbrevs g ty else strip_tpeqns ty) with
|
|
| TType_tuple l -> acc_free_in_types_lr g cxFlag thruFlag acc l
|
|
| TType_app (_,tinst) -> acc_free_in_types_lr g cxFlag thruFlag acc tinst
|
|
| TType_ucase (_,tinst) -> acc_free_in_types_lr g cxFlag thruFlag acc tinst
|
|
| TType_fun (d,r) -> acc_free_in_type_lr g cxFlag thruFlag (acc_free_in_type_lr g cxFlag thruFlag acc d ) r
|
|
| TType_var r -> acc_free_tpref_lr g cxFlag thruFlag acc r
|
|
| TType_forall (tps,r) -> union_free_typars_lr (bound_typars_lr g cxFlag thruFlag tps (acc_free_in_type_lr g cxFlag thruFlag empty_free_typars_lr r)) acc
|
|
| TType_modul_bindings -> failwith "acc_free_in_type_lr: naked struct"
|
|
| TType_measure unt -> List.foldBack (fun (tp,_) acc -> acc_free_tpref_lr g cxFlag thruFlag acc tp) (ListMeasureVarOccsWithNonZeroExponents unt) acc
|
|
|
|
and acc_free_in_types_lr g cxFlag thruFlag acc tys =
|
|
match tys with
|
|
| [] -> acc
|
|
| h :: t -> acc_free_in_types_lr g cxFlag thruFlag (acc_free_in_type_lr g cxFlag thruFlag acc h) t
|
|
|
|
let free_in_type_lr g thruFlag ty = acc_free_in_type_lr g true thruFlag empty_free_typars_lr ty |> List.rev
|
|
let free_in_types_lr g thruFlag ty = acc_free_in_types_lr g true thruFlag empty_free_typars_lr ty |> List.rev
|
|
let free_in_types_lr_no_cxs g ty = acc_free_in_types_lr g false true empty_free_typars_lr ty |> List.rev
|
|
|
|
let var_of_bind (b:Binding) = b.Var
|
|
let rhs_of_bind (b:Binding) = b.Expr
|
|
let vars_of_Bindings (binds:Bindings) = binds |> FlatList.map (fun b -> b.Var)
|
|
let vars_of_binds (binds:Binding list) = binds |> List.map (fun (b:Binding) -> b.Var)
|
|
|
|
let bind_order (v1:Binding) (v2:Binding) = val_spec_order v1.Var v2.Var
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Equivalence of types up to alpha-equivalence
|
|
//---------------------------------------------------------------------------
|
|
|
|
type TypeEquivEnv =
|
|
{ ae_typars: TyparMap<typ>;
|
|
ae_tcrefs: TyconRefRemap}
|
|
|
|
let tyeq_env_empty = { ae_typars=tpmap_empty(); ae_tcrefs=empty_tcref_remap }
|
|
|
|
let bind_tyeq_env_types tps1 tys2 aenv =
|
|
{aenv with ae_typars=List.fold_right2 tpmap_add tps1 tys2 aenv.ae_typars}
|
|
|
|
let bind_tyeq_env_typars tps1 tps2 aenv =
|
|
bind_tyeq_env_types tps1 (List.map mk_typar_ty tps2) aenv
|
|
|
|
let bind_tyeq_env_tpinst tpinst aenv =
|
|
let tps,tys = List.unzip tpinst
|
|
bind_tyeq_env_types tps tys aenv
|
|
|
|
let mk_tyeq_env tps1 tps2 = bind_tyeq_env_typars tps1 tps2 tyeq_env_empty
|
|
|
|
let rec traits_aequiv_aux erasureFlag g aenv (TTrait(typs1,nm,mf1,argtys,rty,_)) (TTrait(typs2,nm2,mf2,argtys2,rty2,_)) =
|
|
ListSet.equals (type_aequiv_aux erasureFlag g aenv) typs1 typs2 &&
|
|
mf1 = mf2 &&
|
|
return_types_aequiv_aux erasureFlag g aenv rty rty2 &&
|
|
List.lengthsEqAndForall2 (type_aequiv_aux erasureFlag g aenv) argtys argtys2 &&
|
|
nm = nm2
|
|
|
|
and return_types_aequiv_aux erasureFlag g aenv rty rty2 =
|
|
match rty,rty2 with
|
|
| None,None -> true
|
|
| Some t1,Some t2 -> type_aequiv_aux erasureFlag g aenv t1 t2
|
|
| _ -> false
|
|
|
|
|
|
and typarConstraints_aequiv_aux erasureFlag g aenv tpc1 tpc2 =
|
|
match tpc1,tpc2 with
|
|
| TTyparCoercesToType(acty,_),
|
|
TTyparCoercesToType(fcty,_) ->
|
|
type_aequiv_aux erasureFlag g aenv acty fcty
|
|
|
|
| TTyparMayResolveMemberConstraint(trait1,_),
|
|
TTyparMayResolveMemberConstraint(trait2,_) ->
|
|
traits_aequiv_aux erasureFlag g aenv trait1 trait2
|
|
|
|
| TTyparDefaultsToType(_,acty,_),
|
|
TTyparDefaultsToType(_,fcty,_) ->
|
|
type_aequiv_aux erasureFlag g aenv acty fcty
|
|
|
|
| TTyparIsEnum(uty1,_),TTyparIsEnum(uty2,_) ->
|
|
type_aequiv_aux erasureFlag g aenv uty1 uty2
|
|
|
|
| TTyparIsDelegate(aty1,bty1,_),TTyparIsDelegate(aty2,bty2,_) ->
|
|
type_aequiv_aux erasureFlag g aenv aty1 aty2 &&
|
|
type_aequiv_aux erasureFlag g aenv bty1 bty2
|
|
|
|
| TTyparSimpleChoice (tys1,_),TTyparSimpleChoice(tys2,_) ->
|
|
ListSet.equals (type_aequiv_aux erasureFlag g aenv) tys1 tys2
|
|
|
|
| TTyparSupportsNull _ ,TTyparSupportsNull _
|
|
| TTyparIsNotNullableValueType _ ,TTyparIsNotNullableValueType _
|
|
| TTyparIsReferenceType _ ,TTyparIsReferenceType _
|
|
| TTyparRequiresDefaultConstructor _, TTyparRequiresDefaultConstructor _ -> true
|
|
| _ -> false
|
|
|
|
and typarConstraintSets_aequiv_aux erasureFlag g aenv (tp1:Typar) (tp2:Typar) =
|
|
tp1.StaticReq = tp2.StaticReq &&
|
|
ListSet.equals (typarConstraints_aequiv_aux erasureFlag g aenv) tp1.Constraints tp2.Constraints
|
|
|
|
and typar_decls_aequiv_aux erasureFlag g aenv tps1 tps2 =
|
|
List.length tps1 = List.length tps2 &&
|
|
let aenv = bind_tyeq_env_typars tps1 tps2 aenv
|
|
List.for_all2 (typarConstraintSets_aequiv_aux erasureFlag g aenv) tps1 tps2
|
|
|
|
and tcref_aequiv g aenv tc1 tc2 =
|
|
tcref_eq g tc1 tc2 ||
|
|
(tcref_map_mem tc1 aenv.ae_tcrefs && tcref_eq g (tcref_map_find tc1 aenv.ae_tcrefs) tc2)
|
|
|
|
and type_aequiv_aux erasureFlag g aenv ty1 ty2 =
|
|
if verbose then dprintf "--> type_aequiv...\n";
|
|
let ty1 = strip_tpeqns_and_tcabbrevs_wrt_erasure erasureFlag g ty1
|
|
let ty2 = strip_tpeqns_and_tcabbrevs_wrt_erasure erasureFlag g ty2
|
|
match ty1, ty2 with
|
|
| TType_forall(tps1,rty1), TType_forall(tps2,rty2) ->
|
|
typar_decls_aequiv_aux erasureFlag g aenv tps1 tps2 && type_aequiv_aux erasureFlag g (bind_tyeq_env_typars tps1 tps2 aenv) rty1 rty2
|
|
| TType_var tp1, TType_var tp2 when typar_ref_eq tp1 tp2 ->
|
|
true
|
|
| TType_var tp1, _ when tpmap_mem tp1 aenv.ae_typars ->
|
|
type_equiv_aux erasureFlag g (tpmap_find tp1 aenv.ae_typars) ty2
|
|
| TType_app (tc1,b1) ,TType_app (tc2,b2) ->
|
|
tcref_aequiv g aenv tc1 tc2 &&
|
|
types_aequiv_aux erasureFlag g aenv b1 b2
|
|
| TType_ucase (UCRef(tc1,n1),b1) ,TType_ucase (UCRef(tc2,n2),b2) ->
|
|
n1=n2 &&
|
|
tcref_aequiv g aenv tc1 tc2 &&
|
|
types_aequiv_aux erasureFlag g aenv b1 b2
|
|
| TType_tuple l1,TType_tuple l2 ->
|
|
types_aequiv_aux erasureFlag g aenv l1 l2
|
|
| TType_fun (dtys1,rty1),TType_fun (dtys2,rty2) ->
|
|
type_aequiv_aux erasureFlag g aenv dtys1 dtys2 && type_aequiv_aux erasureFlag g aenv rty1 rty2
|
|
| TType_measure m1, TType_measure m2 ->
|
|
match erasureFlag with EraseNone -> measure_aequiv g aenv m1 m2 | _ -> true
|
|
| _ -> false
|
|
|
|
and measure_aequiv g aenv un1 un2 =
|
|
let vars1 = ListMeasureVarOccs un1
|
|
let trans tp1 = if tpmap_mem tp1 aenv.ae_typars then dest_anypar_typ g (tpmap_find tp1 aenv.ae_typars) else tp1
|
|
let remap_tcref tc = if tcref_map_mem tc aenv.ae_tcrefs then tcref_map_find tc aenv.ae_tcrefs else tc
|
|
let vars1' = List.map trans vars1
|
|
let vars2 = ListSet.subtract typar_ref_eq (ListMeasureVarOccs un2) vars1'
|
|
let cons1 = ListMeasureConOccsAfterRemapping g remap_tcref un1
|
|
let cons2 = ListMeasureConOccsAfterRemapping g remap_tcref un2
|
|
|
|
List.forall (fun v -> MeasureVarExponent v un1 = MeasureVarExponent (trans v) un2) vars1 &&
|
|
List.forall (fun v -> MeasureVarExponent v un1 = MeasureVarExponent v un2) vars2 &&
|
|
List.forall (fun c -> MeasureConExponentAfterRemapping g remap_tcref c un1 = MeasureConExponentAfterRemapping g remap_tcref c un2) (cons1@cons2)
|
|
|
|
and types_aequiv_aux erasureFlag g aenv l1 l2 = List.lengthsEqAndForall2 (type_aequiv_aux erasureFlag g aenv) l1 l2
|
|
and type_equiv_aux erasureFlag g ty1 ty2 = type_aequiv_aux erasureFlag g tyeq_env_empty ty1 ty2
|
|
|
|
let type_aequiv g aenv ty1 ty2 = type_aequiv_aux EraseNone g aenv ty1 ty2
|
|
let type_equiv g ty1 ty2 = type_equiv_aux EraseNone g ty1 ty2
|
|
let traits_aequiv g aenv t1 t2 = traits_aequiv_aux EraseNone g aenv t1 t2
|
|
let typarConstraints_aequiv g aenv c1 c2 = typarConstraints_aequiv_aux EraseNone g aenv c1 c2
|
|
let typar_decls_aequiv g aenv d1 d2 = typar_decls_aequiv_aux EraseNone g aenv d1 d2
|
|
let return_types_aequiv g aenv t1 t2 = return_types_aequiv_aux EraseNone g aenv t1 t2
|
|
let measure_equiv g m1 m2 = measure_aequiv g tyeq_env_empty m1 m2
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Values representing member functions on F# types
|
|
//--------------------------------------------------------------------------
|
|
|
|
let GetNumObjArgsOfMember memberFlags =
|
|
if memberFlags.MemberIsInstance then 1 else 0
|
|
|
|
let GetNumObjArgsOfVal (v:Val) =
|
|
match v.MemberInfo with
|
|
| Some membInfo -> GetNumObjArgsOfMember membInfo.MemberFlags
|
|
| None -> 0
|
|
|
|
let GetNumObjArgsOfValRef (vref:ValRef) = GetNumObjArgsOfVal vref.Deref
|
|
|
|
// Pull apart the type for an F# value that represents an object model method. Do not strip off a 'unit' argument.
|
|
// Review: Should GetMemberTypeInFSharpForm have any other direct callers?
|
|
let GetMemberTypeInFSharpForm g memberFlags arities ty m =
|
|
let tps,argInfos,rty,retInfo = GetTopValTypeInFSharpForm g arities ty m
|
|
let numObjArgs = GetNumObjArgsOfMember memberFlags
|
|
|
|
let argInfos =
|
|
if numObjArgs = 1 then
|
|
match argInfos with
|
|
| [] ->
|
|
errorR(InternalError("value does not have a valid member type",m));
|
|
argInfos
|
|
| h::t -> t
|
|
else argInfos
|
|
tps,argInfos,rty,retInfo
|
|
|
|
// Check that an F# value represents an object model method.
|
|
// It will also always have an arity (inferred from syntax).
|
|
let check_member_val membInfo arity m =
|
|
match membInfo, arity with
|
|
| None,_ -> error(InternalError("check_member_val - no membInfo" , m))
|
|
| _,None -> error(InternalError("check_member_val - no arity", m))
|
|
| Some membInfo,Some arity -> (membInfo,arity)
|
|
|
|
let check_member_vref (vref:ValRef) =
|
|
check_member_val vref.MemberInfo vref.TopValInfo vref.Range
|
|
|
|
let GetTopValTypeInCompiledForm g topValInfo typ m =
|
|
let tps,paramArgInfos,rty,retInfo = GetTopValTypeInFSharpForm g topValInfo typ m
|
|
// Eliminate lone single unit arguments
|
|
let paramArgInfos =
|
|
match paramArgInfos, topValInfo.ArgInfos with
|
|
// static member and module value unit argument elimination
|
|
| [[(argType,_)]] ,[[]] ->
|
|
//assert is_unit_typ g argType
|
|
[[]]
|
|
// instance member unit argument elimination
|
|
| [objInfo;[(argType,_)]] ,[[objArg];[]] ->
|
|
//assert is_unit_typ g argType
|
|
[objInfo; []]
|
|
| _ ->
|
|
paramArgInfos
|
|
let rty = (if is_unit_typ g rty then None else Some rty)
|
|
(tps,paramArgInfos,rty,retInfo)
|
|
|
|
// Pull apart the type for an F# value that represents an object model method
|
|
// and see the "member" form for the type, i.e.
|
|
// detect methods with no arguments by (effectively) looking for single argument type of 'unit'.
|
|
// The analysis is driven of the inferred arity information for the value.
|
|
//
|
|
// This is used not only for the compiled form - it's also used for all type checking and object model
|
|
// logic such as determining if abstract methods have been implemented or not, and how
|
|
// many arguments the method takes etc.
|
|
let GetMemberTypeInMemberForm g memberFlags topValInfo typ m =
|
|
let tps,paramArgInfos,rty,retInfo = GetMemberTypeInFSharpForm g memberFlags topValInfo typ m
|
|
// Eliminate lone single unit arguments
|
|
let paramArgInfos =
|
|
match paramArgInfos, topValInfo.ArgInfos with
|
|
// static member and module value unit argument elimination
|
|
| [[(argType,_)]] ,[[]] ->
|
|
assert is_unit_typ g argType
|
|
[[]]
|
|
// instance member unit argument elimination
|
|
| [[(argType,_)]] ,[[objArg];[]] ->
|
|
assert is_unit_typ g argType
|
|
[[]]
|
|
| _ ->
|
|
paramArgInfos
|
|
let rty = (if is_unit_typ g rty then None else Some rty)
|
|
(tps,paramArgInfos,rty,retInfo)
|
|
|
|
let GetTypeOfMemberInMemberForm g (vref:ValRef) =
|
|
//assert (not vref.IsExtensionMember)
|
|
let membInfo,topValInfo = check_member_vref vref
|
|
GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo vref.Type vref.Range
|
|
|
|
let GetTypeOfMemberInFSharpForm g (vref:ValRef) =
|
|
let membInfo,topValInfo = check_member_vref vref
|
|
GetMemberTypeInFSharpForm g membInfo.MemberFlags topValInfo vref.Type vref.Range
|
|
|
|
let GetReturnTypeOMemberInMemberForm g (vref:ValRef) =
|
|
let membInfo,topValInfo = check_member_vref vref
|
|
let _,_,rty,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo vref.Type vref.Range
|
|
rty
|
|
|
|
/// Match up the type variables on an member value with the type
|
|
/// variables on the apparent enclosing type
|
|
let PartitionValTypars g (v:Val) =
|
|
match v.TopValInfo with
|
|
| None -> error(InternalError("PartitionValTypars: not a top value", v.Range))
|
|
| Some arities ->
|
|
let fullTypars,_ = dest_top_forall_type g arities v.Type
|
|
let parent = v.MemberApparentParent
|
|
let parentTypars = parent.TyparsNoRange
|
|
let nparentTypars = parentTypars.Length
|
|
if nparentTypars <= fullTypars.Length then
|
|
let memberParentTypars,memberMethodTypars = List.chop nparentTypars fullTypars
|
|
let memberToParentInst,tinst = mk_typar_to_typar_renaming memberParentTypars parentTypars
|
|
Some(parentTypars,memberParentTypars,memberMethodTypars,memberToParentInst,tinst)
|
|
else None
|
|
|
|
let PartitionValRefTypars g vref = PartitionValTypars g (deref_val vref)
|
|
|
|
/// Get the arguments for an F# value that represents an object model method
|
|
let ArgInfosOfMemberVal g (v:Val) =
|
|
let membInfo,topValInfo = check_member_val v.MemberInfo v.TopValInfo v.Range
|
|
let _,arginfos,_,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo v.Type v.Range
|
|
arginfos
|
|
|
|
let ArgInfosOfMember g vref =
|
|
ArgInfosOfMemberVal g (deref_val vref)
|
|
|
|
let GetFSharpViewOfReturnType g retTy =
|
|
match retTy with
|
|
| None -> g.unit_ty
|
|
| Some retTy -> retTy
|
|
|
|
|
|
/// Get the property "type" (getter return type) for an F# value that represents a getter or setter
|
|
/// of an object model property.
|
|
let ReturnTypeOfPropertyVal g (v:Val) =
|
|
let membInfo,topValInfo = check_member_val v.MemberInfo v.TopValInfo v.Range
|
|
match membInfo.MemberFlags.MemberKind with
|
|
| MemberKindPropertySet ->
|
|
let _,arginfos,_,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo v.Type v.Range
|
|
if not arginfos.IsEmpty && not arginfos.Head.IsEmpty then
|
|
arginfos.Head |> List.last |> fst
|
|
else
|
|
error(Error("this value does not have a valid property setter type", v.Range));
|
|
| MemberKindPropertyGet ->
|
|
let _,_,rty,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo v.Type v.Range
|
|
GetFSharpViewOfReturnType g rty
|
|
| _ -> error(InternalError("vtyp_of_prop_vref",v.Range))
|
|
|
|
|
|
/// Get the property arguments for an F# value that represents a getter or setter
|
|
/// of an object model property.
|
|
let ArgInfosOfPropertyVal g (v:Val) =
|
|
let membInfo,topValInfo = check_member_val v.MemberInfo v.TopValInfo v.Range
|
|
match membInfo.MemberFlags.MemberKind with
|
|
| MemberKindPropertyGet ->
|
|
ArgInfosOfMemberVal g v |> List.concat
|
|
| MemberKindPropertySet ->
|
|
let _,arginfos,_,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags topValInfo v.Type v.Range
|
|
if not arginfos.IsEmpty && not arginfos.Head.IsEmpty then
|
|
arginfos.Head |> List.frontAndBack |> fst
|
|
else
|
|
error(Error("this value does not have a valid property setter type", v.Range));
|
|
| _ ->
|
|
error(InternalError("ArgInfosOfPropertyVal",v.Range))
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Generalize type constructors to types
|
|
//---------------------------------------------------------------------------
|
|
|
|
let generalize_tcref_tinst (tc:TyconRef) = generalize_typars tc.TyparsNoRange
|
|
let generalize_tcref tc =
|
|
let tinst = generalize_tcref_tinst tc
|
|
tinst,TType_app(tc, tinst)
|
|
|
|
let isTTyparSupportsStaticMethod = function TTyparMayResolveMemberConstraint _ -> true | _ -> false
|
|
let isTTyparCoercesToType = function TTyparCoercesToType _ -> true | _ -> false
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Print Signatures/Types - prelude
|
|
//--------------------------------------------------------------------------
|
|
|
|
let prefix_of_static_req s =
|
|
match s with
|
|
| NoStaticReq -> "'"
|
|
| HeadTypeStaticReq -> " ^"
|
|
|
|
let prefix_of_rigid (typar:Typar) =
|
|
if (typar.Rigidity <> TyparRigid) then "_" else ""
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Prettify: PrettyTyparNames/PrettifyTypes - make typar names human friendly
|
|
//---------------------------------------------------------------------------
|
|
|
|
module PrettyTypes = begin
|
|
|
|
let NewPrettyTypar (tp:Typar) nm =
|
|
NewTypar (tp.Kind, tp.Rigidity,Typar(ident(nm, tp.Range),tp.StaticReq,false),false,DynamicReq,[])
|
|
|
|
let NewPrettyTypars renaming tps names =
|
|
let niceTypars = List.map2 NewPrettyTypar tps names
|
|
let renaming = renaming @ mk_typar_inst tps (generalize_typars niceTypars)
|
|
(tps,niceTypars) ||> List.iter2 (fun tp tpnice -> fixup_typar_constraints tpnice (inst_typar_constraints renaming tp.Constraints)) ;
|
|
niceTypars, renaming
|
|
|
|
// We choose names for type parameters from 'a'..'t'
|
|
// We choose names for unit-of-measure from 'u'..'z'
|
|
// If we run off the end of these ranges, we use 'aX' for positive integer X or 'uX' for positive integer X
|
|
// Finally, we skip any names already in use
|
|
let NeedsPrettyTyparName (tp:Typar) = tp.IsCompilerGenerated && (tp.Data.typar_id.idText = unassignedTyparName)
|
|
let PrettyTyparNames pred alreadyInUse tps =
|
|
let rec choose (tps:Typar list) (typeIndex, measureIndex) acc =
|
|
match tps with
|
|
| [] -> List.rev acc
|
|
| tp::tps ->
|
|
|
|
|
|
// Use a particular name, possibly after incrementing indexes
|
|
let useThisName (nm, typeIndex, measureIndex) =
|
|
choose tps (typeIndex, measureIndex) (nm::acc)
|
|
|
|
// Give up, try again with incremented indexes
|
|
let tryAgain (typeIndex, measureIndex) =
|
|
choose (tp::tps) (typeIndex, measureIndex) acc
|
|
|
|
let tryName (nm, typeIndex, measureIndex) f =
|
|
if List.mem nm alreadyInUse then
|
|
f()
|
|
else
|
|
useThisName (nm, typeIndex, measureIndex)
|
|
|
|
if pred tp then
|
|
if NeedsPrettyTyparName tp then
|
|
let (typeIndex, measureIndex, baseName, letters, i) =
|
|
match tp.Kind with
|
|
| KindType -> (typeIndex+1,measureIndex,'a',20,typeIndex)
|
|
| KindMeasure -> (typeIndex,measureIndex+1,'u',6,measureIndex)
|
|
let nm =
|
|
if i < letters then String.make 1 (char(int baseName + i))
|
|
else String.make 1 baseName ^ string (i-letters+1)
|
|
tryName (nm, typeIndex, measureIndex) (fun () ->
|
|
tryAgain (typeIndex, measureIndex))
|
|
|
|
else
|
|
tryName (tp.Name, typeIndex, measureIndex) (fun () ->
|
|
// Use the next index and append it to the natural name
|
|
let (typeIndex, measureIndex, nm) =
|
|
match tp.Kind with
|
|
| KindType -> (typeIndex+1,measureIndex,tp.Name+ string typeIndex)
|
|
| KindMeasure -> (typeIndex,measureIndex+1,tp.Name+ string measureIndex)
|
|
tryName (nm,typeIndex, measureIndex) (fun () ->
|
|
tryAgain (typeIndex, measureIndex)))
|
|
else
|
|
useThisName (tp.Name,typeIndex, measureIndex)
|
|
|
|
|
|
choose tps (0,0) []
|
|
|
|
let PrettifyTypes g foldTys mapTys tys =
|
|
let ftps = (foldTys (acc_free_in_type_lr g true false) empty_free_typars_lr tys)
|
|
(* let ftps = (foldTys (fun x acc -> acc_free_in_type_lr false acc x) tys empty_free_typars_lr) in *)
|
|
let ftps = List.rev ftps
|
|
(* ftps |> List.iter (fun tp -> dprintf "free typar: %d\n" (stamp_of_typar tp)); *)
|
|
let rec computeKeep (keep:typars) change (tps:typars) =
|
|
match tps with
|
|
| [] -> List.rev keep, List.rev change
|
|
| tp :: rest ->
|
|
if not (NeedsPrettyTyparName tp) && (not (keep |> List.exists (fun tp2 -> tp.Name = tp2.Name))) then
|
|
computeKeep (tp :: keep) change rest
|
|
else
|
|
computeKeep keep (tp :: change) rest
|
|
let keep,change = computeKeep [] [] ftps
|
|
|
|
(* change |> List.iter (fun tp -> dprintf "change typar: %s %s %d\n" tp.Name (tp.DisplayName) (stamp_of_typar tp)); *)
|
|
(* keep |> List.iter (fun tp -> dprintf "keep typar: %s %s %d\n" tp.Name (tp.DisplayName) (stamp_of_typar tp)); *)
|
|
let alreadyInUse = keep |> List.map (fun x -> x.Name)
|
|
let names = PrettyTyparNames (fun x -> List.memq x change) alreadyInUse ftps
|
|
|
|
let niceTypars, renaming = NewPrettyTypars [] ftps names
|
|
let prettyTypars = mapTys (InstType renaming) tys
|
|
(* niceTypars |> List.iter (fun tp -> dprintf "nice typar: %d\n" (stamp_of_typar tp)); *)
|
|
let tpconstraints = niceTypars |> List.collect (fun tpnice -> List.map (fun tpc -> tpnice,tpc) tpnice.Constraints)
|
|
|
|
renaming,
|
|
prettyTypars,
|
|
tpconstraints
|
|
|
|
let PrettifyTypes1 g x = PrettifyTypes g (fun f -> f) (fun f -> f) x
|
|
let PrettifyTypes2 g x = PrettifyTypes g (fun f -> foldl'2 (f,f)) (fun f -> map'2 (f,f)) x
|
|
let PrettifyTypesN g x = PrettifyTypes g List.fold List.map x
|
|
let PrettifyTypesN1 g x = PrettifyTypes g (fun f -> foldl'2 (List.fold (foldl1'2 f), f)) (fun f -> map'2 (List.map (map1'2 f),f)) x
|
|
let PrettifyTypesNN1 g x = PrettifyTypes g (fun f -> foldl'3 (List.fold f, List.fold (foldl1'2 f),f)) (fun f -> map'3 (List.map f, List.map (map1'2 f), f)) x
|
|
let PrettifyTypesNM1 g x = PrettifyTypes g (fun f -> foldl'3 (List.fold f, List.fold (List.fold (foldl1'2 f)),f)) (fun f -> map'3 (List.map f, List.mapSquared (map1'2 f), f)) x
|
|
|
|
end
|
|
|
|
|
|
|
|
module SimplifyTypes = begin
|
|
|
|
(* CAREFUL! This function does NOT walk constraints *)
|
|
let rec FoldType f z typ =
|
|
let typ = strip_tpeqns typ
|
|
let z = f z typ
|
|
match typ with
|
|
| TType_forall (tps,body) -> FoldType f z body
|
|
| TType_app (_,tinst) -> List.fold (FoldType f) z tinst
|
|
| TType_ucase (_,tinst) -> List.fold (FoldType f) z tinst
|
|
| TType_tuple typs -> List.fold (FoldType f) z typs
|
|
| TType_fun (s,t) -> FoldType f (FoldType f z s) t
|
|
| TType_var tp -> z
|
|
| TType_modul_bindings -> z
|
|
| TType_measure unt -> z
|
|
|
|
let incM x m =
|
|
if Zmap.mem x m then Zmap.add x (1 + Zmap.find x m) m
|
|
else Zmap.add x 1 m
|
|
|
|
let AccTyparCounts z typ =
|
|
(* Walk type to determine typars and their counts (for pprinting decisions) *)
|
|
FoldType (fun z typ -> match typ with | TType_var tp when tp.Rigidity = TyparRigid -> incM tp z | _ -> z) z typ
|
|
|
|
let emptyTyparCounts = Zmap.empty typar_spec_order
|
|
|
|
(* print multiple fragments of the same type using consistent naming and formatting *)
|
|
let AccTyparCountsMulti acc l = List.fold AccTyparCounts acc l
|
|
|
|
type TypeSimplificationInfo =
|
|
{ singletons : Typar Zset.set;
|
|
inplaceConstraints : Zmap.map<Typar,typ>;
|
|
postfixConstraints : (Typar * TyparConstraint) list; }
|
|
|
|
let typeSimplificationInfo0 =
|
|
{ singletons = Zset.empty typar_spec_order;
|
|
inplaceConstraints = Zmap.empty typar_spec_order;
|
|
postfixConstraints = [] }
|
|
|
|
let CategorizeConstraints simplify m cxs =
|
|
let singletons = if simplify then Zmap.chooseL (fun tp n -> if n=1 then Some tp else None) m else []
|
|
let singletons = Zset.addList singletons (Zset.empty typar_spec_order)
|
|
// Here, singletons are typars that occur once in the type.
|
|
// However, they may also occur in a type constraint.
|
|
// If they do, they are really multiple occurance - so we should remove them.
|
|
let constraintTypars = (free_in_typar_constraints CollectTyparsNoCaching (List.map snd cxs)).FreeTypars
|
|
let usedInTypeConstraint typar = Zset.mem typar constraintTypars
|
|
let singletons = singletons |> Zset.filter (usedInTypeConstraint >> not)
|
|
(* Here, singletons should really be used once *)
|
|
let inplace,postfix =
|
|
cxs |> List.partition (fun (tp,tpc) ->
|
|
simplify &&
|
|
isTTyparCoercesToType tpc &&
|
|
Zset.mem tp singletons &&
|
|
tp.Constraints.Length = 1)
|
|
let inplace = inplace |> List.map (function (tp,TTyparCoercesToType(ty,m)) -> tp,ty | _ -> failwith "not isTTyparCoercesToType")
|
|
|
|
{ singletons = singletons;
|
|
inplaceConstraints = Zmap.of_list typar_spec_order inplace;
|
|
postfixConstraints = postfix;
|
|
}
|
|
let CollectInfo simplify tys cxs =
|
|
CategorizeConstraints simplify (AccTyparCountsMulti emptyTyparCounts tys) cxs
|
|
|
|
end
|
|
|
|
let rec IterType ((fStripped,fTypars,fTraitSolution) as f) typ =
|
|
// We iterate the _solved_ constraints as well, to pick up any record of trait constraint solutions
|
|
// This means we walk _all_ the constraints _everywhere_ in a type, including
|
|
// those attached to _solved_ type variables. This is used by PostTypecheckSemanticChecks to detect uses of
|
|
// values as solutions to trait constraints and determine if inference has caused the value to escape its scope.
|
|
// The only record of these solutions is in the _solved_ constraints of types.
|
|
// In an ideal world we would, instead, record the solutions to these constraints as "witness variables" in expressions,
|
|
// rather than solely in types.
|
|
match typ with
|
|
| TType_var tp when tp.Solution.IsSome ->
|
|
tp.Constraints |> List.iter (fun cx ->
|
|
match cx with
|
|
| TTyparMayResolveMemberConstraint((TTrait(typs,_,_,argtys,rty,soln)),m) ->
|
|
Option.iter fTraitSolution !soln
|
|
| _ -> ())
|
|
| _ -> ()
|
|
|
|
let typ = strip_tpeqns typ
|
|
fStripped typ;
|
|
match typ with
|
|
| TType_forall (tps,body) ->
|
|
IterType f body;
|
|
tps |> List.iter fTypars;
|
|
tps |> List.iter (fun tp -> tp.Constraints |> List.iter (IterTypeConstraint f))
|
|
|
|
| TType_measure unt -> ()
|
|
| TType_app (_,tinst) -> IterTypes f tinst
|
|
| TType_ucase (_,tinst) -> IterTypes f tinst
|
|
| TType_tuple typs -> IterTypes f typs
|
|
| TType_fun (s,t) -> IterType f s; IterType f t
|
|
| TType_var tp -> fTypars tp
|
|
| TType_modul_bindings -> ()
|
|
and IterTypes f tys = List.iter (IterType f) tys
|
|
and IterTypeConstraint ((fStripped,fTypars,fTraitSolution) as f) x =
|
|
match x with
|
|
| TTyparCoercesToType(ty,m) -> IterType f ty
|
|
| TTyparMayResolveMemberConstraint(traitInfo,m) -> IterTraitInfo f traitInfo
|
|
| TTyparDefaultsToType(priority,ty,m) -> IterType f ty
|
|
| TTyparSimpleChoice(tys,m) -> IterTypes f tys
|
|
| TTyparIsEnum(uty,m) -> IterType f uty
|
|
| TTyparIsDelegate(aty,bty,m) -> IterType f aty; IterType f bty
|
|
| TTyparSupportsNull _ | TTyparIsNotNullableValueType _
|
|
| TTyparIsReferenceType _ | TTyparRequiresDefaultConstructor _ -> ()
|
|
and IterTraitInfo ((_,_,fTraitSolution) as f) (TTrait(typs,_,_,argtys,rty,soln)) =
|
|
IterTypes f typs;
|
|
IterTypes f argtys;
|
|
Option.iter (IterType f) rty;
|
|
Option.iter fTraitSolution !soln
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Print Signatures/Types
|
|
//--------------------------------------------------------------------------
|
|
|
|
type DisplayEnv =
|
|
{ html: bool;
|
|
htmlHideRedundantKeywords: bool;
|
|
htmlAssemMap: string NameMap; (* where can the docs for f# assemblies be found? *)
|
|
openTopPaths: (string list) list;
|
|
showObsoleteMembers: bool;
|
|
showTyparBinding: bool;
|
|
showImperativeTyparAnnotations: bool;
|
|
suppressInlineKeyword: bool;
|
|
showMemberContainers:bool;
|
|
shortConstraints:bool;
|
|
showAttributes:bool;
|
|
showOverrides:bool;
|
|
showConstraintTyparAnnotations: bool;
|
|
abbreviateAdditionalConstraints: bool;
|
|
showTyparDefaultConstraints : bool;
|
|
g: TcGlobals;
|
|
contextAccessibility: Accessibility;
|
|
generatedValueLayout:(Val -> layout option);
|
|
}
|
|
|
|
member x.Normalize() =
|
|
{ x with
|
|
openTopPaths =
|
|
x.openTopPaths
|
|
|> List.sortWith (fun p1 p2 -> -(compare p1 p2))
|
|
}
|
|
|
|
let empty_denv tcGlobals =
|
|
{ html=false;
|
|
htmlHideRedundantKeywords=false;
|
|
htmlAssemMap=NameMap.empty;
|
|
openTopPaths=[];
|
|
showObsoleteMembers=true;
|
|
showTyparBinding = false;
|
|
showImperativeTyparAnnotations=false;
|
|
suppressInlineKeyword=false;
|
|
showMemberContainers=false;
|
|
showAttributes=false;
|
|
showOverrides=true;
|
|
showConstraintTyparAnnotations=true;
|
|
abbreviateAdditionalConstraints=false;
|
|
showTyparDefaultConstraints=false;
|
|
shortConstraints=false;
|
|
g=tcGlobals;
|
|
contextAccessibility = taccessPublic;
|
|
generatedValueLayout = (fun v -> None);
|
|
}.Normalize()
|
|
|
|
|
|
let denv_add_open_path path denv =
|
|
{ denv with openTopPaths= path :: denv.openTopPaths}.Normalize()
|
|
|
|
let denv_add_open_modref modref denv =
|
|
let path = demangled_path_of_cpath (full_cpath_of_modul (deref_modul modref))
|
|
denv_add_open_path path denv
|
|
|
|
let denv_scope_access access denv =
|
|
{ denv with contextAccessibility = combineAccess denv.contextAccessibility access }
|
|
|
|
let full_name_of_nlpath (NLPath(ccu,p) : NonLocalPath) = text_of_arr_path p
|
|
let (+.+) s1 s2 = (if s1 = "" then s2 else s1^"."^s2)
|
|
|
|
let full_name_of_parent_of_item_ref ppF (|Ref_private|Ref_nonlocal|) tcref =
|
|
match tcref with
|
|
| Ref_private x ->
|
|
(match ppF x with
|
|
| None -> None
|
|
| Some (PubPath([| |],nm)) -> None
|
|
| Some (PubPath(p,nm)) -> Some(text_of_path (Array.to_list p)))
|
|
| Ref_nonlocal nlr ->
|
|
match nlr.nlr_nlpath with
|
|
| (NLPath(ccu,[| |])) -> None
|
|
| _ -> Some (full_name_of_nlpath nlr.nlr_nlpath)
|
|
|
|
|
|
let full_display_text_of_item_ref nmF ppF (|Ref_private|Ref_nonlocal|) xref =
|
|
match full_name_of_parent_of_item_ref ppF (|Ref_private|Ref_nonlocal|) xref with
|
|
| None -> nmF xref
|
|
| Some pathText -> pathText +.+ nmF xref
|
|
|
|
let full_display_text_of_parent_of_modref r = full_name_of_parent_of_item_ref pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
|
|
let full_display_text_of_modref r = full_display_text_of_item_ref demangled_name_of_modref pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
let full_display_text_of_vref r = full_display_text_of_item_ref (fun (tc:ValRef) -> tc.DisplayName) pubpath_of_val (|VRef_private|VRef_nonlocal|) r
|
|
let full_display_text_of_tcref r = full_display_text_of_item_ref (fun (tc:TyconRef) -> tc.DisplayNameWithUnderscoreTypars) pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
let full_display_text_of_ecref r = full_display_text_of_item_ref (fun (tc:TyconRef) -> tc.DisplayNameWithUnderscoreTypars) pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
|
|
let full_display_text_of_ucref (ucref:UnionCaseRef) = full_display_text_of_tcref ucref.TyconRef +.+ ucref.CaseName
|
|
let full_display_text_of_rfref (rfref:RecdFieldRef) = full_display_text_of_tcref rfref.TyconRef +.+ rfref.FieldName
|
|
|
|
// This is a broken version of 'full_display_text_of_item_ref' that uses a mangled name in the Ref_nonlocal case.
|
|
// It is only used by the FSI generation code, which uses a really unstable and subtle technique to propagate information from
|
|
// the pretty printing code that lays out signautre files to the consuming code in the Language Service.
|
|
let full_mangled_name_of_item_ref_DO_NOT_USE nmF ppF (|Ref_private|Ref_nonlocal|) xref =
|
|
match xref with
|
|
| Ref_private x ->
|
|
match full_name_of_parent_of_item_ref ppF (|Ref_private|Ref_nonlocal|) xref with
|
|
| None -> nmF xref
|
|
| Some pathText -> pathText +.+ nmF xref
|
|
| Ref_nonlocal nlr ->
|
|
let nm = nlr.nlr_item // <<----- BAD BAD BAD - this is a mangled name. The FSI generation code expects exactly this name to be used for non-local references
|
|
match full_name_of_parent_of_item_ref ppF (|Ref_private|Ref_nonlocal|) xref with
|
|
| None -> nm
|
|
| Some pathText -> pathText +.+ nm
|
|
|
|
let approx_full_mangled_name_of_modref r = full_mangled_name_of_item_ref_DO_NOT_USE demangled_name_of_modref pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
let approx_full_mangled_name_of_vref r = full_mangled_name_of_item_ref_DO_NOT_USE (fun (tc:ValRef) -> tc.MangledName) pubpath_of_val (|VRef_private|VRef_nonlocal|) r
|
|
let approx_full_mangled_name_of_tcref r = full_mangled_name_of_item_ref_DO_NOT_USE (fun (tc:TyconRef) -> tc.DisplayName) pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
let approx_full_mangled_name_of_ecref r = full_mangled_name_of_item_ref_DO_NOT_USE (fun (tc:TyconRef) -> tc.DisplayName) pubpath_of_tycon (|ERef_private|ERef_nonlocal|) r
|
|
let approx_full_mangled_name_of_ucref (ucref:UnionCaseRef) = approx_full_mangled_name_of_tcref ucref.TyconRef +.+ ucref.CaseName
|
|
let approx_full_mangled_name_of_rfref (rfref:RecdFieldRef) = approx_full_mangled_name_of_tcref rfref.TyconRef +.+ rfref.FieldName
|
|
|
|
let full_mangled_path_to_tcref (tcref:TyconRef) =
|
|
match tcref with
|
|
| ERef_private ltc -> (match tcref.PublicPath with None -> [| |] | Some (PubPath(p,nm)) -> p)
|
|
| ERef_nonlocal nlr -> path_of_nlpath (nlpath_of_nlref nlr)
|
|
|
|
let qualified_mangled_name_of_tcref tcref nm =
|
|
String.concat "-" (Array.to_list (full_mangled_path_to_tcref tcref) @ [ tcref.MangledName ^ "-" ^ nm ])
|
|
|
|
let rec firstEq p1 p2 =
|
|
match p1 with
|
|
| [] -> true
|
|
| h1::t1 ->
|
|
match p2 with
|
|
| h2::t2 -> h1 = h2 && firstEq t1 t2
|
|
| _ -> false
|
|
|
|
let rec firstRem p1 p2 =
|
|
match p1 with [] -> p2 | h1::t1 -> firstRem t1 (List.tl p2)
|
|
|
|
let trim_path_by_denv denv path =
|
|
let findOpenedNamespace opened_path =
|
|
if firstEq opened_path path then
|
|
let t2 = firstRem opened_path path
|
|
if t2 <> [] then Some(text_of_path t2^".")
|
|
else Some("")
|
|
else None
|
|
match List.tryPick findOpenedNamespace denv.openTopPaths with
|
|
| Some(s) -> s
|
|
| None -> if isNil path then "" else text_of_path path ^ "."
|
|
|
|
|
|
let adhoc_of_tycon (tycon:Tycon) =
|
|
NameMultiMap.range tycon.TypeContents.tcaug_adhoc
|
|
|> List.filter (fun v -> not v.IsCompilerGenerated)
|
|
|
|
let super_of_tycon g (tycon:Tycon) =
|
|
match tycon.TypeContents.tcaug_super with
|
|
| None -> g.obj_ty
|
|
| Some ty -> ty
|
|
|
|
let implements_of_tycon (g:TcGlobals) (tycon:Tycon) =
|
|
tycon.TypeContents.tcaug_implements |> List.map (fun (x,_,_) -> x)
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Detect attributes
|
|
//----------------------------------------------------------------------------
|
|
|
|
// AbsIL view of attributes (we read these from .NET binaries)
|
|
let is_il_attrib (tref:ILTypeRef) attr =
|
|
(attr.customMethod.EnclosingType.TypeSpec.Name = tref.Name) &&
|
|
(attr.customMethod.EnclosingType.TypeSpec.Enclosing = tref.Enclosing)
|
|
|
|
// REVIEW: consider supporting querying on Abstract IL custom attributes.
|
|
// These linear iterations cost us a fair bit when there are lots of attributes
|
|
// on imported types. However this is fairly rare and can also be solved by caching the
|
|
// results of attribute lookups in the TAST
|
|
let ILThingHasILAttrib tref attrs = List.exists (is_il_attrib tref) (dest_custom_attrs attrs)
|
|
|
|
let ILThingDecodeILAttrib g tref attrs =
|
|
attrs |> dest_custom_attrs |> List.tryPick(fun x -> if is_il_attrib tref x then Some(decode_il_attrib_data g.ilg x) else None)
|
|
|
|
// This one is done by name to ensure the compiler doesn't take a dependency on dereferencing a type that only exists in .NET 3.5
|
|
let ILThingHasExtensionAttribute attrs =
|
|
attrs |> dest_custom_attrs |> List.exists (fun attr ->
|
|
attr.customMethod.EnclosingType.TypeSpec.Name = "System.Runtime.CompilerServices.ExtensionAttribute")
|
|
|
|
(* F# view of attributes (these get converted to AbsIL attributes in ilxgen) *)
|
|
let IsMatchingAttrib g (AttribInfo(_,tcref)) (Attrib(tcref2,_,_,_,_)) = tcref_eq g tcref tcref2
|
|
let HasAttrib g tref attrs = List.exists (IsMatchingAttrib g tref) attrs
|
|
let fsthing_find_attrib g tref attrs = List.find (IsMatchingAttrib g tref) attrs
|
|
let TryFindAttrib g tref attrs = List.tryfind (IsMatchingAttrib g tref) attrs
|
|
|
|
let (|SpecificAttribNamedArg|_|) nm = function (AttribNamedArg(nm2,_,_,AttribExpr(_,v))) when nm = nm2 -> Some v | _ -> None
|
|
|
|
let (|AttribInt32Arg|_|) = function AttribExpr(_,TExpr_const (TConst_int32(n),m,_)) -> Some(n) | _ -> None
|
|
let (|AttribBoolArg|_|) = function AttribExpr(_,TExpr_const (TConst_bool(n),m,_)) -> Some(n) | _ -> None
|
|
let (|AttribStringArg|_|) = function AttribExpr(_,TExpr_const (TConst_string(n),m,_)) -> Some(n) | _ -> None
|
|
|
|
let TryFindBoolAttrib g nm attrs =
|
|
match TryFindAttrib g nm attrs with
|
|
| Some(Attrib(_,_,[ ],_,_)) -> Some(true)
|
|
| Some(Attrib(_,_,[ AttribBoolArg(b) ],_,_)) -> Some(b)
|
|
| _ -> None
|
|
|
|
let TryFindUnitAttrib g nm attrs =
|
|
match TryFindAttrib g nm attrs with
|
|
| Some(Attrib(_,_,[ ],_,_)) -> Some()
|
|
| _ -> None
|
|
|
|
let TryFindInt32Attrib g nm attrs =
|
|
match TryFindAttrib g nm attrs with
|
|
| Some(Attrib(_,_,[ AttribInt32Arg(b) ],_,_)) -> Some b
|
|
| _ -> None
|
|
|
|
let ILThingHasAttrib (AttribInfo (atref,_)) attrs =
|
|
ILThingHasILAttrib atref attrs
|
|
|
|
let TyconRefTryBindAttrib g (AttribInfo (atref,_) as args) (tcref:TyconRef) f1 f2 =
|
|
if tcref.IsILTycon then
|
|
match ILThingDecodeILAttrib g atref tcref.ILTyconRawMetadata.tdCustomAttrs with
|
|
| Some attr -> f1 attr
|
|
| _ -> None
|
|
else
|
|
match TryFindAttrib g args tcref.Attribs with
|
|
| Some attr -> f2 attr
|
|
| _ -> None
|
|
|
|
let TyconRefHasAttrib g args tcref =
|
|
TyconRefTryBindAttrib g args tcref (fun _ -> Some()) (fun _ -> Some()) |> isSome
|
|
|
|
/// Detect if a type is definitely known to be non-serializable
|
|
let is_definitely_not_serializable g typ =
|
|
if is_il_arr_typ g typ || is_any_array_typ g typ then
|
|
true
|
|
else
|
|
match try_tcref_of_stripped_typ g typ with
|
|
| None -> false
|
|
| Some tcref ->
|
|
if tcref.IsILTycon then
|
|
not tcref.ILTyconRawMetadata.tdSerializable
|
|
else
|
|
(TryFindBoolAttrib g g.attrib_AutoSerializableAttribute tcref.Attribs = Some(false))
|
|
|
|
//-------------------------------------------------------------------------
|
|
// List and reference types...
|
|
//-------------------------------------------------------------------------
|
|
|
|
let is_byref_typ g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.byref_tcr tcref
|
|
|
|
let dest_byref_typ g ty = if is_byref_typ g ty then List.hd (tinst_of_stripped_typ g ty) else failwith "dest_byref_typ: not a byref type"
|
|
|
|
let is_refcell_ty g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g g.refcell_tcr tcref
|
|
|
|
let dest_refcell_ty g ty = if is_refcell_ty g ty then List.hd (tinst_of_stripped_typ g ty) else failwith "dest_refcell_ty: not a ref type"
|
|
let mk_refcell_ty g ty = TType_app(g.refcell_tcr_nice,[ty])
|
|
|
|
let mk_lazy_ty g ty = TType_app(g.lazy_tcr_canon,[ty])
|
|
|
|
let mk_format_ty g aty bty cty dty ety = TType_app(g.format_tcr, [aty;bty;cty;dty; ety])
|
|
|
|
let mk_option_ty g ty = TType_app (g.option_tcr_nice, [ty])
|
|
let mk_list_ty g ty = TType_app (g.list_tcr_nice, [ty])
|
|
let is_arity1_ty g tcr ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref -> tcref_eq g tcr tcref
|
|
|
|
let is_option_ty g ty = is_arity1_ty g g.option_tcr ty
|
|
|
|
let try_dest_option_ty g ty =
|
|
match tinst_of_stripped_typ g ty with
|
|
| [ty1] when is_option_ty g ty -> Some(ty1)
|
|
| _ -> None
|
|
|
|
let dest_option_ty g ty =
|
|
match try_dest_option_ty g ty with
|
|
| Some(ty) -> ty
|
|
| None -> failwith "dest_option_ty: not an option type"
|
|
|
|
let mk_none_ucref g = mk_ucref g.option_tcr "None"
|
|
let mk_some_ucref g = mk_ucref g.option_tcr "Some"
|
|
|
|
let vref_is_dispatch_slot (vref:ValRef) =
|
|
match vref.MemberInfo with
|
|
| Some membInfo -> membInfo.MemberFlags.MemberIsDispatchSlot
|
|
| None -> false
|
|
|
|
let (|BitwiseOr|_|) g expr =
|
|
match expr with
|
|
| TExpr_app(TExpr_val(vref,_,_),_,_,[arg1;arg2],_) when g.vref_eq vref g.bitwise_or_vref ->
|
|
Some(arg1,arg2)
|
|
// Special workaround, only used when compiling FSharp.Core.dll. Uses of 'a ||| b' occur before the '|||' bitwise or operator
|
|
// is defined. These get through type checking because enums implicitly support the '|||' operator through
|
|
// the automatic resolution of undefined operators (see tc.ml, Item_implicit_op). This then compiles as an
|
|
// application of a lambda to two arguments. We recognize this pattern here
|
|
| TExpr_app(TExpr_lambda _,_,_,[arg1;arg2],_) when g.compilingFslib ->
|
|
Some(arg1,arg2)
|
|
| _ -> None
|
|
|
|
let is_typeof_vref g vref =
|
|
g.vref_eq vref g.typeof_vref
|
|
// There is an internal version of typeof defined in prim-types.fs that needs to be detected
|
|
|| (g.compilingFslib && vref.CompiledName = "typeof")
|
|
|
|
let is_typedefof_vref g vref =
|
|
g.vref_eq vref g.typedefof_vref
|
|
// There is an internal version of typeof defined in prim-types.fs that needs to be detected
|
|
|| (g.compilingFslib && vref.CompiledName = "typedefof")
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Print Signatures/Types
|
|
//--------------------------------------------------------------------------
|
|
|
|
module NicePrint = begin
|
|
open Microsoft.FSharp.Compiler.Layout
|
|
open Microsoft.FSharp.Compiler.PrettyNaming
|
|
open PrettyTypes
|
|
|
|
/// Paths are used in FSI generation and Abstract IL metadata printing
|
|
///
|
|
/// paths in innermost-to-outermost order (e.g., `Foo.Bar.Baz` = ["Baz" ; "Bar" ; "Foo"])
|
|
type Path =
|
|
| Path of string list
|
|
| NoPath
|
|
|
|
static member Empty = Path []
|
|
|
|
member path.Add ident =
|
|
match path with
|
|
| NoPath -> NoPath
|
|
| Path xs -> Path (ident :: xs)
|
|
|
|
let fullySplitTypeRef (tref:ILTypeRef) =
|
|
(List.collect IL.split_namespace (tref.Enclosing @ [IL.ungenericize_tname tref.Name]))
|
|
|
|
let commentL l = wordL "(*" ++ l ++ wordL "*)"
|
|
let comment str = str |> wordL |> commentL
|
|
|
|
let layoutsL (ls : layout list) : layout =
|
|
match ls with
|
|
| [] -> emptyL
|
|
| [x] -> x
|
|
| x :: xs -> List.fold ($$) x xs
|
|
|
|
/// Place the mangled identifier's name at the 'end' of the path; this will make the fully-qualified name later
|
|
///
|
|
/// REVIEW: when FSI generation for assemblies containing quoted identifiers is
|
|
/// implemented, a decision will have to be taken here whether to strip "``" from the
|
|
/// beginning / end of these identifiers; this depends on how the island parser is made to
|
|
/// get "funny" identifiers; the same for operators
|
|
let annotateWithPath ident path mangledIdent =
|
|
let mkPair s = (s, "")
|
|
let node = wordL ident
|
|
match path with
|
|
| NoPath -> node
|
|
| Path path -> Internal.Utilities.StructuredFormat.Layout.Attr ("goto:path", mangledIdent :: path |> List.rev |> List.map mkPair , node)
|
|
|
|
// shortcut to annote elements with a path extended by the identifier.
|
|
let (&==) ident path = annotateWithPath ident path ident
|
|
|
|
|
|
module IlPrint = begin
|
|
|
|
open Microsoft.FSharp.Compiler.AbstractIL.IL
|
|
|
|
let private adjustIlNameInternal (path : Path)(n : string) : string list =
|
|
|
|
// REVIEW: don't hardwire this table like this
|
|
let demangleFSharpBaseTypes n =
|
|
match n with
|
|
| "System.Void" -> "unit"
|
|
| "System.Object" -> "obj"
|
|
| "System.String" -> "string"
|
|
| "System.Single" -> "float32"
|
|
| "System.Double" -> "float"
|
|
| "System.Decimal" -> "decimal"
|
|
| "System.Char" -> "char"
|
|
| "System.Int16" -> "int16"
|
|
| "System.Int32" -> "int"
|
|
| "System.Int64" -> "int64"
|
|
| "System.UInt16" -> "uint16"
|
|
| "System.UInt32" -> "uint32"
|
|
| "System.UInt64" -> "uint64"
|
|
| "System.Boolean" -> "bool"
|
|
| _ -> n
|
|
|
|
let dropCurrentNamespace n =
|
|
match path with
|
|
| NoPath -> n
|
|
| Path xs ->
|
|
match n |> List.rev with
|
|
| (y :: _) as ys when ys = xs -> [ y ] // special case for (recursive) references to the type we're currently defining
|
|
| y :: ys when ys = xs -> [ y ]
|
|
| _ -> n
|
|
n |> demangleFSharpBaseTypes |> SplitNamesForFsiGenerationPath |> ChopUnshowableInFsiGenerationPath |> dropCurrentNamespace |> List.map Lexhelp.Keywords.QuoteIdentifierIfNeeded
|
|
|
|
/// fix up a name coming from IL metadata by:
|
|
/// - translating well-known base types (e.g., System.Int32 -> int)
|
|
/// - chopping off numeric (num-of-params) suffixes
|
|
/// - hiding the current namespace (e.g., if we're in A.B.C.D and we're
|
|
/// showing A.B.C.D.E, we can abbreviate this to E)
|
|
/// - quote "funny" names (keywords, otherwise invalid identifiers)
|
|
let private adjustIlName (path : Path)(n : string) : string =
|
|
n |> adjustIlNameInternal path |> JoinNamesForFsiGenerationPath
|
|
|
|
/// this fixes up a name just like adjustIlName but also handles F#
|
|
/// operators
|
|
let private adjustIlMethodName (path : Path)(n : string) : string =
|
|
let specialdemangleOperatorName s =
|
|
if IsMangledOpName s
|
|
then DemangleOperatorName s
|
|
else s
|
|
n |> adjustIlNameInternal path |> List.map specialdemangleOperatorName |> JoinNamesForFsiGenerationPath
|
|
|
|
|
|
|
|
// our good Haskell friend
|
|
let rec intersperse x lst =
|
|
match lst with
|
|
| [] -> []
|
|
| [y] -> [y]
|
|
| y :: ys -> y :: x :: intersperse x ys
|
|
|
|
let private arrayShapeL (ILArrayShape sh : ILArrayShape) : layout =
|
|
leftL "[" $$ wordL (sh |> List.tl |> List.map (fun _ -> ",") |> String.concat "") $$ rightL "]" // drop off one "," so that a n-dimensional array has n - 1 ","'s
|
|
|
|
let private paramsList (ps : ILGenericParameterDefs) =
|
|
ps |> List.map (fun x -> "'" + x.Name |> wordL)
|
|
|
|
let private paramsL (ps : layout list) : layout =
|
|
match ps with
|
|
| [] -> emptyL
|
|
| _ ->
|
|
let body = ps |> intersperse (sepL ",") |> layoutsL
|
|
sepL "<" $$ body $$ rightL ">"
|
|
|
|
let private pruneParms (name : string)(parms : layout list) =
|
|
let numParms = let rightMost = name |> SplitNamesForFsiGenerationPath |> List.rev |> List.hd
|
|
try rightMost |> int // can't find a way to see the number of generic parameters for *this* class (the GenericParams also include type variables for enclosing classes); this will have to do
|
|
with _ -> 0 // looks like it's non-generic
|
|
parms |> List.rev |> List.take numParms |> List.rev
|
|
|
|
let rec ilILTypeL (denv : DisplayEnv)(path : Path)(parms : layout list)(typ : ILType) : layout =
|
|
match typ with
|
|
| Type_void -> wordL "unit" // These are type-theoretically totally different type-theoretically `void` is Fin 0 and `unit` is Fin (S 0) ... but, this looks like as close as we can get.
|
|
| Type_array (sh, t) -> ilILTypeL denv path parms t $$ arrayShapeL sh
|
|
| Type_value t
|
|
| Type_boxed t -> (adjustIlName path t.Name |> wordL) $$ (t.GenericArgs |> List.map (ilILTypeL denv path parms) |> paramsL)
|
|
| Type_ptr t
|
|
| Type_byref t -> ilILTypeL denv path parms t
|
|
| Type_fptr t -> ilILCallingSignatureL denv path parms None t
|
|
| Type_tyvar n -> List.nth parms <| int n
|
|
| Type_modified (_, _, t) -> ilILTypeL denv path parms t // "Just recurse through them to the contained ILType"--Don
|
|
|
|
/// Layout a function's type signature. We need a special case for
|
|
/// constructors (Their return types are reported as `void`, but this is
|
|
/// incorrect; so if we're dealing with a constructor we require that the
|
|
/// return type be passed along as the `cons` parameter.)
|
|
and ilILCallingSignatureL (denv : DisplayEnv)(path : Path)(parms : layout list)(cons : string option)(signatur : ILCallingSignature) : layout =
|
|
let args = signatur.ArgTypes |> List.map (ilILTypeL denv path parms) |> intersperse (wordL "*")
|
|
let res =
|
|
match cons with
|
|
| Some className -> (adjustIlName path className |> wordL) $$ (pruneParms className parms |> paramsL) // special case for constructor return-type (viz., the class itself)
|
|
| None -> signatur.ReturnType |> ilILTypeL denv path parms
|
|
match args with
|
|
| [] -> wordL "unit" $$ wordL "->" $$ res
|
|
| [x] -> x $$ wordL "->" $$ res
|
|
| x :: xs -> (List.fold ($$) x xs) $$ wordL "->" $$ res
|
|
|
|
/// Layout a method's signature. In the case that we've a constructor, we
|
|
/// pull off the class name from the `path`; naturally, it's the
|
|
/// most-deeply-nested element.
|
|
|
|
let private ilMethodDefL (denv : DisplayEnv)(path : Path)(parms : layout list)(className : string)(m : ILMethodDef) : layout =
|
|
let myParms = m.GenericParams |> paramsList
|
|
let parms = parms @ myParms
|
|
let name = adjustIlMethodName path m.Name
|
|
let (nameL, isCons) =
|
|
match () with
|
|
| _ when m.IsConstructor -> ("new" &== path, Some className) // we need the unadjusted name here to be able to grab the number of generic parameters
|
|
| _ when m.IsStatic -> (wordL "static" $$ wordL "member" $$ (name &== path) $$ (myParms |> paramsL), None)
|
|
| _ -> (wordL "member" $$ (name &== path) $$ (myParms |> paramsL), None)
|
|
let signaturL = callsig_of_mdef m |> ilILCallingSignatureL denv path parms isCons
|
|
nameL $$ wordL ":" $$ signaturL
|
|
|
|
let private ilFieldDefL (denv : DisplayEnv)(path : Path)(parms : layout list)(f : ILFieldDef) : layout =
|
|
let staticL = if f.IsStatic then wordL "static" else emptyL
|
|
let name = adjustIlName path f.Name
|
|
let nameL = name &== path
|
|
let typL = ilILTypeL denv path parms f.Type
|
|
staticL $$ wordL "val" $$ nameL $$ wordL ":" $$ typL
|
|
|
|
let private ilPropertyDefL (denv : DisplayEnv)(path : Path)(parms : layout list)(p : ILPropertyDef) : layout =
|
|
let staticL = if p.propCallconv = CC_static then wordL "static" else emptyL
|
|
let name = adjustIlName path p.Name
|
|
let nameL = name &== path
|
|
|
|
let getterTypeL (getterRef:ILMethodRef) =
|
|
match getterRef.ArgTypes with
|
|
| [] -> ilILTypeL denv path parms getterRef.ReturnType
|
|
| _ -> ilILCallingSignatureL denv path parms None getterRef.CallingSignature
|
|
|
|
let setterTypeL (setterRef:ILMethodRef) =
|
|
let argTypes = setterRef.ArgTypes
|
|
if isNil argTypes then
|
|
emptyL // shouldn't happen
|
|
else
|
|
let frontArgs, lastArg = List.frontAndBack argTypes
|
|
let argsL = frontArgs |> List.map (ilILTypeL denv path parms) |> intersperse (wordL "*") |> List.fold ($$) emptyL
|
|
argsL $$ wordL "->" $$ (ilILTypeL denv path parms lastArg)
|
|
|
|
let typL =
|
|
match p.GetMethod, p.SetMethod with
|
|
| None, None -> ilILTypeL denv path parms p.Type // shouldn't happen
|
|
| Some getterRef, _ -> getterTypeL getterRef
|
|
| None, Some setterRef -> setterTypeL setterRef
|
|
|
|
let specGetSetL =
|
|
match p.GetMethod, p.SetMethod with
|
|
| None,None
|
|
| Some _, None -> emptyL
|
|
| None, Some _ -> wordL "with" $$ wordL "deb set"
|
|
| Some _, Some _ -> wordL "with" $$ wordL "get," $$ wordL "set"
|
|
staticL $$ wordL "member" $$ nameL $$ wordL ":" $$ typL $$ specGetSetL
|
|
|
|
let ilEnumFieldDefL (denv : DisplayEnv) (path : Path) (f : ILFieldDef) : layout =
|
|
let res =
|
|
match f.fdInit with
|
|
| Some init ->
|
|
match init with
|
|
| FieldInit_bool x ->
|
|
if x
|
|
then Some "true"
|
|
else Some "false"
|
|
| FieldInit_char c -> Some ("'" + (char c).ToString () + "'")
|
|
| FieldInit_int16 x -> Some ((x |> int32 |> string) + "s")
|
|
| FieldInit_int32 x -> Some (x |> string)
|
|
| FieldInit_int64 x -> Some ((x |> string) + "L")
|
|
| FieldInit_uint16 x -> Some ((x |> int32 |> string) + "us")
|
|
| FieldInit_uint32 x -> Some ((x |> int64 |> string) + "u")
|
|
| FieldInit_uint64 x -> Some ((x |> int64 |> string) + "UL")
|
|
| FieldInit_single d ->
|
|
let s = d.ToString ("g12", System.Globalization.CultureInfo.InvariantCulture)
|
|
let s =
|
|
if String.for_all (fun c -> System.Char.IsDigit c || c = '-') s
|
|
then s + ".0"
|
|
else s
|
|
Some (s + "f")
|
|
| FieldInit_double d ->
|
|
let s = d.ToString ("g12", System.Globalization.CultureInfo.InvariantCulture)
|
|
if String.for_all (fun c -> System.Char.IsDigit c || c = '-') s
|
|
then Some (s + ".0")
|
|
else Some s
|
|
| _ -> None
|
|
| None -> None
|
|
let initL = match res with
|
|
| None -> wordL "=" $$ (comment "value unavailable")
|
|
| Some s -> wordL "=" $$ wordL s
|
|
|
|
let name = adjustIlName path f.Name
|
|
let nameL = name &== path
|
|
wordL "|" $$ nameL $$ initL
|
|
|
|
// filtering methods for hiding things we oughtn't show
|
|
let private isStaticProperty (p : ILPropertyDef) = match p.GetMethod,p.SetMethod with
|
|
| Some getter,_ -> getter.CallingSignature.CallingConv.IsStatic
|
|
| None,Some setter -> setter.CallingSignature.CallingConv.IsStatic
|
|
| None,None -> true
|
|
let private isPublicMethod (m : ILMethodDef) : bool = m.Access = MemAccess_public
|
|
let private isPublicCtor (m : ILMethodDef) : bool = m.Access = MemAccess_public && m.IsConstructor
|
|
let private isNotSpecialName (m : ILMethodDef) : bool = not m.mdSpecialName
|
|
let private isPublicField (f : ILFieldDef) : bool = f.Access = MemAccess_public
|
|
let private isPublicClass (c : ILTypeDef) : bool =
|
|
match c.Access with
|
|
| TypeAccess_public
|
|
| TypeAccess_nested MemAccess_public -> true
|
|
| _ -> false
|
|
let private isShowEnumField (f : ILFieldDef) : bool = f.Name <> "value__" // this appears to be the hard-coded underlying storage field
|
|
let private isShowBase (n : layout) : bool =
|
|
let noShow = [ "System.Object" ; "System.ValueType" ; "obj" ] // hide certain 'obvious' base classes
|
|
noShow |> List.map wordL |> List.exists ((=) n) |> not
|
|
|
|
let rec ilTypeDefL (denv : DisplayEnv)(path : Path)(t : ILTypeDef) : layout =
|
|
let name = t.Name |> SplitNamesForFsiGenerationPath |> List.rev |> List.hd // dump the qualifiers
|
|
let path' = path // assumption: path's already been augmented when handling the LHS
|
|
let parms = t.GenericParams |> paramsList
|
|
|
|
let baseNameL b =
|
|
match b with
|
|
| Some b -> let baseName = ilILTypeL denv path parms b
|
|
if isShowBase baseName
|
|
then [ wordL "inherit" $$ baseName ]
|
|
else []
|
|
| None -> []
|
|
|
|
let fieldsL parms = dest_fdefs >> List.filter isPublicField >> List.map (ilFieldDefL denv path' parms)
|
|
let enumsL = dest_fdefs >> List.filter isShowEnumField >> List.map (ilEnumFieldDefL denv path')
|
|
let typesL = dest_tdefs >> List.filter isPublicClass >> List.map (ilNestedClassDefL denv path')
|
|
|
|
let renderL pre body post =
|
|
match pre with
|
|
| Some pre ->
|
|
match body with
|
|
| [] -> emptyL // empty type
|
|
| _ -> (pre @@-- aboveListL body) @@ post
|
|
| None -> aboveListL body
|
|
|
|
let classL (t : ILTypeDef) typeWord =
|
|
let pre = Some (wordL typeWord)
|
|
let baseT = baseNameL t.Extends
|
|
let memberBlockLs (fieldDefs:ILFieldDefs,methodDefs:ILMethodDefs,propertyDefs:PropertyDefs) =
|
|
let ctors =
|
|
methodDefs |> dest_mdefs |>
|
|
List.filter isPublicCtor (*|> List.filter isNotSpecialName*) |>
|
|
List.map (ilMethodDefL denv path' parms t.Name)
|
|
let fields = fieldsL parms fieldDefs
|
|
let nProps = propertyDefs |> dest_pdefs |> List.map (fun pd -> pd.Name,ilPropertyDefL denv path' parms pd)
|
|
let nMeths =
|
|
methodDefs |> dest_mdefs |>
|
|
List.filter isPublicMethod |> List.filter isNotSpecialName |>
|
|
List.map (fun md -> md.Name,ilMethodDefL denv path' parms t.Name md)
|
|
let members = (nProps @ nMeths) |> List.sortBy fst |> List.map snd (* (properties and members) are sorted by name *)
|
|
ctors @ fields @ members
|
|
let bodyStatic = memberBlockLs (t.Fields |> dest_fdefs |> List.filter (fun fd -> fd.IsStatic) |> mk_fdefs,
|
|
t.Methods |> dest_mdefs |> List.filter (fun md -> md.IsStatic) |> mk_mdefs,
|
|
t.Properties |> dest_pdefs |> List.filter (fun pd -> isStaticProperty pd) |> mk_properties)
|
|
let bodyInstance = memberBlockLs (t.Fields |> dest_fdefs |> List.filter (fun fd -> fd.IsStatic |> not) |> mk_fdefs,
|
|
t.Methods |> dest_mdefs |> List.filter (fun md -> md.IsStatic |> not) |> mk_mdefs,
|
|
t.Properties |> dest_pdefs |> List.filter (fun pd -> isStaticProperty pd |> not) |> mk_properties)
|
|
let body = bodyInstance @ bodyStatic (* instance "member" before "static member" *)
|
|
let types = typesL t.NestedTypes
|
|
let post = wordL "end"
|
|
renderL pre (baseT @ body @ types) post
|
|
|
|
let delegateL (t : ILTypeDef) =
|
|
let rhs =
|
|
match t.Methods |> dest_mdefs |> List.filter (fun m -> m.Name = "Invoke") with // the delegate delegates to the type of `Invoke`
|
|
| m :: _ -> ilILCallingSignatureL denv path parms None (callsig_of_mdef m)
|
|
| _ -> comment "`Invoke` method could not be found"
|
|
wordL "delegate" $$ wordL "of" $$ rhs
|
|
|
|
match t.tdKind with
|
|
| TypeDef_class -> classL t "class"
|
|
| TypeDef_valuetype -> classL t "struct"
|
|
| TypeDef_interface -> classL t "interface"
|
|
| TypeDef_enum -> renderL None (enumsL t.Fields) emptyL
|
|
| TypeDef_delegate -> delegateL t
|
|
| TypeDef_other _ -> comment "cannot show type"
|
|
|
|
and ilNestedClassDefL (denv : DisplayEnv)(path : Path)(t : ILTypeDef) : layout =
|
|
let name = adjustIlName path t.Name
|
|
let nameL = name &== path
|
|
let parms = t.GenericParams |> paramsList
|
|
let paramsL = pruneParms t.Name parms |> paramsL
|
|
let path' = path.Add name
|
|
let pre = wordL "type" $$ nameL $$ paramsL
|
|
let body = ilTypeDefL denv path' t
|
|
if body = emptyL
|
|
then pre
|
|
else (pre $$ wordL "=") @@-- body
|
|
|
|
end
|
|
|
|
(* Note: We need nice printing of constants in order to print literals and attributes *)
|
|
let constL c =
|
|
let str =
|
|
match c with
|
|
| TConst_bool x -> if x then "true" else "false"
|
|
| TConst_sbyte x -> (x |> string)^"y"
|
|
| TConst_byte x -> (x |> string)^"uy"
|
|
| TConst_int16 x -> (x |> string)^"s"
|
|
| TConst_uint16 x -> (x |> string)^"us"
|
|
| TConst_int32 x -> (x |> string)
|
|
| TConst_uint32 x -> (x |> string)^"u"
|
|
| TConst_int64 x -> (x |> string)^"L"
|
|
| TConst_uint64 x -> (x |> string)^"UL"
|
|
| TConst_nativeint x -> (x |> string)^"n"
|
|
| TConst_unativeint x -> (x |> string)^"un"
|
|
| TConst_float32 d ->
|
|
(let s = d.ToString("g12",System.Globalization.CultureInfo.InvariantCulture)
|
|
if String.for_all (fun c -> System.Char.IsDigit(c) || c = '-') s
|
|
then s + ".0"
|
|
else s) + "f"
|
|
| TConst_float d ->
|
|
let s = d.ToString("g12",System.Globalization.CultureInfo.InvariantCulture)
|
|
if String.for_all (fun c -> System.Char.IsDigit(c) || c = '-') s
|
|
then s + ".0"
|
|
else s
|
|
| TConst_char c -> "'" ^ c.ToString() ^ "'"
|
|
| TConst_string bs -> "\"" ^ bs ^ "\""
|
|
| TConst_unit -> "()"
|
|
| TConst_decimal bs -> string bs ^ "M"
|
|
| TConst_zero -> "default"
|
|
wordL str
|
|
|
|
let bracketIfL x lyt = if x then bracketL lyt else lyt
|
|
let hlinkL (url:string) l = linkL url l
|
|
let squareAngleL x = leftL "[<" $$ x $$ rightL ">]"
|
|
let angleL denv x =
|
|
if denv.html then
|
|
sepL "<" $$ x $$ rightL ">"
|
|
else
|
|
sepL "<" $$ x $$ rightL ">"
|
|
let braceL x = leftL "{" $$ x $$ rightL "}"
|
|
let boolL = function true -> wordL "true" | false -> wordL "false"
|
|
|
|
let accessibilityL (denv:DisplayEnv) accessibility itemL =
|
|
let isInternalCompPath x =
|
|
match x with
|
|
| CompPath(ScopeRef_local,[]) -> true
|
|
| _ -> false
|
|
let (|Public|Internal|Private|) (TAccess p) =
|
|
match p with
|
|
| [] -> Public
|
|
| _ when List.forall isInternalCompPath p -> Internal
|
|
| _ -> Private
|
|
match denv.contextAccessibility,accessibility with
|
|
| Public,Internal -> wordL "internal" ++ itemL // print modifier, since more specific than context
|
|
| Public,Private -> wordL "private" ++ itemL // print modifier, since more specific than context
|
|
| Internal,Private -> wordL "private" ++ itemL // print modifier, since more specific than context
|
|
| _ -> itemL
|
|
|
|
let trefL denv tref =
|
|
let path = fullySplitTypeRef tref
|
|
let p2,n = List.frontAndBack path
|
|
if denv.html then
|
|
hlinkL (text_of_path path ^ ".html") (wordL n)
|
|
else
|
|
leftL (trim_path_by_denv denv p2) $$ wordL n
|
|
|
|
/// Layout a reference to a type or value, perhaps emitting a HTML hyperlink *)
|
|
let tcrefL isExn denv (tcref:TyconRef) =
|
|
let nm = tcref.MangledName
|
|
let isExn = isExn && tcref.IsExceptionDecl
|
|
let demangled = DemangleGenericTypeName nm
|
|
let demangled = if isExn then DemangleExceptionName demangled else demangled
|
|
let tyconTextL = wordL demangled
|
|
let path = demangled_path_of_cpath tcref.CompilationPath
|
|
let arity = tcref.TyparsNoRange.Length
|
|
let arity_suffix = if arity=0 then "" else "-" ^ string_of_int arity
|
|
match tcref with
|
|
| ERef_private _ ->
|
|
if denv.html then
|
|
let nm = (text_of_path (path@["type_" ^ String.underscoreLowercase demangled ^ arity_suffix])) in (* text must tie up with fsc.ml *)
|
|
hlinkL (sprintf "%s.html" nm) tyconTextL
|
|
else
|
|
let pathText = trim_path_by_denv denv path
|
|
(if pathText = "" then tyconTextL else leftL pathText $$ tyconTextL)
|
|
| ERef_nonlocal nlref ->
|
|
if denv.html then
|
|
let href =
|
|
let ccu = ccu_of_nlref nlref
|
|
match ccu.AssemblyName with
|
|
| "mscorlib" | "System" | "System.Windows.Forms"
|
|
| "System.Xml" | "System.Drawing" | "System.Data" ->
|
|
(* cross link to the MSDN 2.0 documentation. Generic types don't seem to have stable names :-( *)
|
|
if demangled = nm then
|
|
Some (sprintf "http://msdn2.microsoft.com/en-us/library/%s.aspx" (text_of_path (path@[nm])))
|
|
else None
|
|
| _ ->
|
|
if ccu.IsFSharp then
|
|
let nm = text_of_path (path@["type_" ^ String.underscoreLowercase demangled ^ arity_suffix]) in (* text must tie up with fsc.ml *)
|
|
match denv.htmlAssemMap.TryFind(ccu.AssemblyName) with
|
|
| Some root -> Some (sprintf "%s/%s.html" root nm)
|
|
(* otherwise assume it is installed parallel to this tree *)
|
|
| None -> Some (sprintf "../%s/%s.html" ccu.AssemblyName nm)
|
|
else
|
|
None
|
|
|
|
match href with
|
|
| Some href -> hlinkL href tyconTextL
|
|
| None -> tyconTextL
|
|
else
|
|
let pathText = trim_path_by_denv denv path
|
|
(if pathText = "" then tyconTextL else leftL pathText $$ tyconTextL)
|
|
|
|
/// Layout the flags of a member *)
|
|
let memFlagsL hide memFlags =
|
|
let stat = if hide || memFlags.MemberIsInstance || (memFlags.MemberKind = MemberKindConstructor) then emptyL else wordL "static"
|
|
let stat = if not memFlags.MemberIsDispatchSlot && memFlags.MemberIsVirtual then stat ++ wordL "virtual"
|
|
elif not hide && memFlags.MemberIsDispatchSlot then stat ++ wordL "abstract"
|
|
elif memFlags.MemberIsOverrideOrExplicitImpl then stat ++ wordL "override"
|
|
else stat
|
|
let stat =
|
|
|
|
if memFlags.MemberIsOverrideOrExplicitImpl then stat
|
|
else
|
|
match memFlags.MemberKind with
|
|
| MemberKindClassConstructor
|
|
| MemberKindConstructor
|
|
| MemberKindPropertyGetSet -> stat
|
|
| MemberKindMember
|
|
| MemberKindPropertyGet
|
|
| MemberKindPropertySet -> stat ++ wordL "member"
|
|
|
|
(* let stat = if memFlags.MemberIsFinal then stat ++ wordL "final" else stat in *)
|
|
stat
|
|
|
|
/// Layout a single attibute arg, following the cases of 'gen_attr_arg' in ilxgen.ml *)
|
|
/// This is the subset of expressions we display in the NicePrint pretty printer *)
|
|
/// See also dataExprL - there is overlap between these that should be removed *)
|
|
let rec attribArgL denv arg =
|
|
match arg with
|
|
| TExpr_const(c,_,ty) ->
|
|
if is_enum_typ denv.g ty then
|
|
wordL "enum" $$ angleL denv (typeL denv ty) $$ bracketL (constL c)
|
|
else
|
|
constL c
|
|
|
|
| TExpr_op(TOp_array,[elemTy],args,m) ->
|
|
leftL "[|" $$ semiListL (List.map (attribArgL denv) args) $$ rightL "|]"
|
|
|
|
(* Detect 'typeof<ty>' calls *)
|
|
| TExpr_app(TExpr_val(vref,_,_),_,[ty],[],m) when is_typeof_vref denv.g vref ->
|
|
leftL "typeof<" $$ typeL denv ty $$ rightL ">"
|
|
|
|
(* Detect 'typedefof<ty>' calls *)
|
|
| TExpr_app(TExpr_val(vref,_,_),_,[ty],[],m) when is_typedefof_vref denv.g vref ->
|
|
leftL "typedefof<" $$ typeL denv ty $$ rightL ">"
|
|
|
|
| TExpr_op(TOp_coerce,[tgTy;_],[arg2],_) ->
|
|
leftL "(" $$ attribArgL denv arg2 $$ wordL ":>" $$ typeL denv tgTy $$ rightL ")"
|
|
|
|
| BitwiseOr denv.g (arg1,arg2) ->
|
|
attribArgL denv arg1 $$ wordL "|||" $$ attribArgL denv arg2
|
|
|
|
(* Detect explicit enum values *)
|
|
| TExpr_app(TExpr_val(vref,_,_),_,_,[arg1],_) when denv.g.vref_eq vref denv.g.enum_vref ->
|
|
wordL "enum" ++ bracketL (attribArgL denv arg1)
|
|
|
|
|
|
| _ -> wordL "(* unsupported attribute argument *)"
|
|
|
|
/// Layout arguments of an attribute 'arg1, ..., argN'
|
|
and attribArgsL denv args =
|
|
sepListL (rightL ",") (List.map (fun (AttribExpr(e1,_)) -> attribArgL denv e1) args)
|
|
|
|
/// Layout an attribute 'Type(arg1, ..., argN)'
|
|
//
|
|
// REVIEW: we are ignoring "props" here
|
|
and attribL denv (Attrib(tcref,k,args,props,m)) =
|
|
let argsL = bracketL (attribArgsL denv args)
|
|
match k with
|
|
| (ILAttrib(mref)) ->
|
|
let trimmedName =
|
|
let name = mref.EnclosingTypeRef.Name
|
|
match String.tryDropSuffix name "Attribute" with
|
|
| Some shortName -> shortName
|
|
| None -> name
|
|
let tref = mref.EnclosingTypeRef
|
|
let tref = ILTypeRef.Create(scope= tref.Scope, enclosing=tref.Enclosing, name=trimmedName)
|
|
trefL denv tref ++ argsL
|
|
|
|
| (FSAttrib(vref)) ->
|
|
(* REVIEW: this is not trimming "Attribute" *)
|
|
let rty = GetReturnTypeOMemberInMemberForm denv.g vref
|
|
let rty = GetFSharpViewOfReturnType denv.g rty
|
|
let tcref = tcref_of_stripped_typ denv.g rty
|
|
tcrefL false denv tcref ++ argsL
|
|
|
|
|
|
/// Layout '[<attribs>]' above another block
|
|
and attribsL denv kind attrs restL =
|
|
|
|
if denv.showAttributes then
|
|
(* Don't display DllImport attributes in generated signatures and/or html *)
|
|
let attrs = if denv.html then attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_OverloadIDAttribute >> not) else attrs
|
|
let attrs = if denv.html then attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_ClassAttribute >> not) else attrs
|
|
let attrs = if denv.html then attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_StructAttribute >> not) else attrs
|
|
let attrs = if denv.html then attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_InterfaceAttribute >> not) else attrs
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_DllImportAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_ContextStaticAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_ThreadStaticAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_EntryPointAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_MarshalAsAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_ReflectedDefinitionAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_StructLayoutAttribute >> not)
|
|
let attrs = attrs |> List.filter (IsMatchingAttrib denv.g denv.g.attrib_AutoSerializableAttribute >> not)
|
|
match attrs with
|
|
| [] -> restL
|
|
| _ -> squareAngleL (sepListL (rightL ";") (List.map (attribL denv) attrs)) @@
|
|
restL
|
|
else
|
|
match kind with
|
|
| KindType -> restL
|
|
| KindMeasure -> squareAngleL (wordL "Measure") @@ restL
|
|
|
|
and typarAttribsL denv kind attrs restL =
|
|
match attrs, kind with
|
|
| [], KindType -> restL
|
|
| _, _ -> squareAngleL (sepListL (rightL ";") ((match kind with KindType -> [] | KindMeasure -> [wordL "Measure"]) @ List.map (attribL denv) attrs)) $$ restL
|
|
|
|
(* NOTE: The primed' functions take an "env" - determines inplace printing of typar and constraints *)
|
|
(* NOTE: "denv" is the DisplayEnv - "env" is the TypeSimplificationInfo *)
|
|
|
|
/// Layout type parameters, taking TypeSimplificationInfo into account *
|
|
and typarDeclsL' denv env nmL prefix (typars:typars) =
|
|
let tpcs = typars |> List.collect (fun tp -> List.map (fun tpc -> tp,tpc) tp.Constraints)
|
|
match typars,tpcs with
|
|
| [],[] ->
|
|
nmL
|
|
|
|
| [h],[] when not prefix ->
|
|
typarL' denv env h --- nmL
|
|
|
|
| _ ->
|
|
let tpcsL = constraintsL denv env tpcs
|
|
let coreL = sepListL (sepL ",") (List.map (typarL' denv env) typars)
|
|
(if prefix or nonNil(tpcs) then nmL $$ angleL denv (coreL --- tpcsL) else bracketL coreL --- nmL)
|
|
|
|
/// Layout a single type parameter declaration, taking TypeSimplificationInfo into account *)
|
|
/// There are several printing-cases for a typar:
|
|
///
|
|
/// 'a - is multiple occurance.
|
|
/// _ - singleton occurance, an underscore prefered over 'b. (OCAML accepts but does not print)
|
|
/// #Type - inplace coercion constraint and singleton.
|
|
/// ('a :> Type) - inplace coercion constraint not singleton.
|
|
/// ('a.opM : S->T) - inplace operator constraint.
|
|
///
|
|
and typarL' denv env (typar:Typar) =
|
|
let varL =
|
|
wordL (sprintf "%s%s%s"
|
|
(if denv.showConstraintTyparAnnotations then prefix_of_static_req typar.StaticReq else "'")
|
|
(if denv.showImperativeTyparAnnotations then prefix_of_rigid typar else "")
|
|
typar.DisplayName)
|
|
let varL = typarAttribsL denv typar.Kind typar.Attribs varL
|
|
|
|
match Zmap.tryfind typar env.SimplifyTypes.inplaceConstraints with
|
|
| Some (typarConstrTyp) ->
|
|
if Zset.mem typar env.SimplifyTypes.singletons then
|
|
leftL "#" $$ typarSubtypeConstraintL denv env typarConstrTyp
|
|
else
|
|
(varL $$ sepL ":>" $$ typarSubtypeConstraintL denv env typarConstrTyp) |> bracketL
|
|
|
|
| _ -> varL
|
|
|
|
|
|
/// Layout type parameter constraints, taking TypeSimplificationInfo into account *)
|
|
and constraintsL denv env cxs =
|
|
|
|
|
|
// Internally member constraints get attached to each type variable in their support.
|
|
// This means we get too many constraints being printed.
|
|
// So we normalize the constraints to eliminate duplicate member constraints
|
|
let cxs =
|
|
cxs
|
|
|> ListSet.setify (fun (_,cx1) (_,cx2) ->
|
|
match cx1,cx2 with
|
|
| TTyparMayResolveMemberConstraint(traitInfo1,_),
|
|
TTyparMayResolveMemberConstraint(traitInfo2,_) -> traits_aequiv denv.g tyeq_env_empty traitInfo1 traitInfo2
|
|
| _ -> false)
|
|
|
|
let cxsL = List.collect (constraintL' denv env) cxs
|
|
match cxsL with
|
|
| [] -> emptyL
|
|
| _ ->
|
|
if denv.abbreviateAdditionalConstraints then
|
|
wordL "when <constraints>"
|
|
elif denv.shortConstraints then
|
|
leftL "(" $$ wordL "requires" $$ sepListL (wordL "and") cxsL $$ rightL ")"
|
|
else
|
|
wordL "when" $$ sepListL (wordL "and") cxsL
|
|
|
|
/// Layout constraints, taking TypeSimplificationInfo into account *)
|
|
and constraintL' denv env (tp,tpc) =
|
|
match tpc with
|
|
| TTyparCoercesToType(tpct,m) ->
|
|
[typarL' denv env tp $$ wordL ":>" --- typarSubtypeConstraintL denv env tpct]
|
|
| TTyparMayResolveMemberConstraint(traitInfo,_) ->
|
|
[traitL denv env traitInfo]
|
|
| TTyparDefaultsToType(_,ty,m) ->
|
|
if denv.showTyparDefaultConstraints then [wordL "default" $$ typarL' denv env tp $$ wordL " :" $$ typeL' denv env ty]
|
|
else []
|
|
| TTyparIsEnum(ty,m) ->
|
|
if denv.shortConstraints then
|
|
[wordL "enum"]
|
|
else
|
|
[typarL' denv env tp $$ wordL ":" $$ tyappL denv env (wordL "enum") 2 true [ty]]
|
|
| TTyparIsDelegate(aty,bty,m) ->
|
|
if denv.shortConstraints then
|
|
[wordL "delegate"]
|
|
else
|
|
[typarL' denv env tp $$ wordL ":" $$ tyappL denv env (wordL "delegate") 2 true [aty;bty]]
|
|
| TTyparSupportsNull(m) ->
|
|
[typarL' denv env tp $$ wordL ":" $$ wordL "null" ]
|
|
| TTyparIsNotNullableValueType(m) ->
|
|
if denv.shortConstraints then
|
|
[wordL "value type"]
|
|
else
|
|
[typarL' denv env tp $$ wordL ":" $$ wordL "struct" ]
|
|
| TTyparIsReferenceType(m) ->
|
|
if denv.shortConstraints then
|
|
[wordL "reference type"]
|
|
else
|
|
[typarL' denv env tp $$ wordL ":" $$ wordL "not struct" ]
|
|
| TTyparSimpleChoice(tys,m) ->
|
|
[typarL' denv env tp $$ wordL ":" $$ bracketL (sepListL (sepL "|") (List.map (typeL' denv env) tys)) ]
|
|
| TTyparRequiresDefaultConstructor(m) ->
|
|
if denv.shortConstraints then
|
|
[wordL "default constructor"]
|
|
else
|
|
[typarL' denv env tp $$ wordL ":" $$ bracketL (wordL "new : unit -> " $$ (typarL' denv env tp))]
|
|
|
|
/// Layout a subtype constraint *)
|
|
and typarSubtypeConstraintL denv env ty = typeL' denv env ty
|
|
|
|
and traitL denv env (TTrait(tys,nm,memFlags,argtys,rty,_)) =
|
|
let nm = DemangleOperatorName nm
|
|
if denv.shortConstraints then
|
|
wordL ("member "^nm)
|
|
else
|
|
let rty = GetFSharpViewOfReturnType denv.g rty
|
|
let stat = memFlagsL denv.htmlHideRedundantKeywords memFlags
|
|
let tys = ListSet.setify (type_equiv denv.g) tys
|
|
let tysL =
|
|
match tys with
|
|
| [ty] -> typeL' denv env ty
|
|
| tys -> bracketL (typesWithPrecL denv env 2 (wordL "or") tys)
|
|
tysL $$ wordL ":" ---
|
|
bracketL (stat ++ wordL nm $$ wordL ":" ---
|
|
((typesWithPrecL denv env 2 (wordL "*") argtys --- wordL "->") --- (typeL' denv env rty)))
|
|
|
|
|
|
/// Layout a unit expression *)
|
|
and auxMeasureL' denv env prec unt =
|
|
let sortVars (vs:(Typar * int) list) = vs |> List.sortBy (fun (v,_) -> v.DisplayName)
|
|
let sortCons (cs:(TyconRef * int) list) = cs |> List.sortBy (fun (c,_) -> c.DisplayName)
|
|
let negvs,posvs = ListMeasureVarOccsWithNonZeroExponents unt |> sortVars |> List.partition (fun (_,e) -> e<0)
|
|
let negcs,poscs = ListMeasureConOccsWithNonZeroExponents denv.g false unt |> sortCons |> List.partition (fun (_,e) -> e<0)
|
|
let unparL (uv:Typar) = wordL ("'" ^ uv.DisplayName)
|
|
let unconL tc = tcrefL false denv tc
|
|
let prefix = spaceListL (List.map (fun (v,e) -> if e=1 then unparL v else unparL v -- wordL (Printf.sprintf "^ %d" e)) posvs @
|
|
List.map (fun (c,e) -> if e=1 then unconL c else unconL c -- wordL (Printf.sprintf "^ %d" e)) poscs)
|
|
let postfix = spaceListL (List.map (fun (v,e) -> if e= -1 then unparL v else unparL v -- wordL (Printf.sprintf "^ %d" (-e))) negvs @
|
|
List.map (fun (c,e) -> if e= -1 then unconL c else unconL c -- wordL (Printf.sprintf "^ %d" (-e))) negcs)
|
|
match (negvs,negcs) with
|
|
| [],[] -> (match posvs,poscs with [],[] -> wordL "1" | _ -> prefix)
|
|
| _ -> prefix $$ sepL "/" $$ (if List.length negvs + List.length negcs > 1 then sepL "(" $$ postfix $$ sepL ")" else postfix)
|
|
|
|
/// Layout type arguments, either NAME<ty,...,ty> or (ty,...,ty) NAME *)
|
|
and tyappL denv env tcL prec prefix args =
|
|
if prefix then
|
|
match args with
|
|
| [] -> tcL
|
|
| [arg] -> tcL $$ sepL (if denv.html then "<" else "<") $$ (typeWithPrecL denv env 4 arg) $$ rightL (if denv.html then ">" else ">")
|
|
| args -> bracketIfL (prec <= 1) (tcL $$ angleL denv (typesWithPrecL denv env 2 (sepL ",") args))
|
|
else
|
|
match args with
|
|
| [] -> tcL
|
|
| [arg] -> typeWithPrecL denv env 2 arg $$ tcL
|
|
| args -> bracketIfL (prec <= 1) (bracketL (typesWithPrecL denv env 2 (sepL ",") args) --- tcL)
|
|
|
|
/// Layout a type, taking precedence into account to insert brackets where needed *)
|
|
and typeWithPrecL denv env prec typ =
|
|
|
|
match strip_tpeqns typ with
|
|
|
|
(* Layout a type application *)
|
|
| TType_app (tc,args) when tc.IsMeasureableReprTycon && List.forall (is_dimensionless denv.g) args ->
|
|
typeWithPrecL denv env prec (reduce_tcref_measureable tc args)
|
|
|
|
| TType_app (tc,args) ->
|
|
tyappL denv env (tcrefL false denv tc) prec tc.IsPrefixDisplay args
|
|
|
|
| TType_ucase (UCRef(tc,_),args) ->
|
|
tyappL denv env (tcrefL false denv tc) prec tc.IsPrefixDisplay args
|
|
|
|
|
|
(* Layout a tuple type *)
|
|
| TType_tuple t ->
|
|
bracketIfL (prec <= 2) (typesWithPrecL denv env 2 (wordL "*") t)
|
|
|
|
(* Layout a first-class generic type. *)
|
|
| TType_forall (tps,tau) ->
|
|
let tauL = typeWithPrecL denv env prec tau
|
|
match tps with
|
|
| [] -> tauL
|
|
| [h] -> typarL' denv env h $$ rightL "." --- tauL
|
|
| (h::t) -> spaceListL (List.map (typarL' denv env) (h::t)) $$ rightL "." --- tauL
|
|
|
|
(* Layout a function type. *)
|
|
| TType_fun (dty,rty) ->
|
|
let rec loop soFarL ty =
|
|
match strip_tpeqns ty with
|
|
| TType_fun (dty,rty) -> loop (soFarL --- (typeWithPrecL denv env 4 dty $$ wordL "->")) rty
|
|
| rty -> soFarL --- typeWithPrecL denv env 5 rty
|
|
bracketIfL (prec <= 4) (loop emptyL typ)
|
|
|
|
(* Layout a type variable . *)
|
|
| TType_var r ->
|
|
typarL' denv env r
|
|
|
|
| TType_modul_bindings -> wordL "<struct>"
|
|
| TType_measure unt -> auxMeasureL' denv env 4 unt
|
|
|
|
/// Layout a list of types, separated with the given separator, either '*' or ',' *)
|
|
and typesWithPrecL denv env prec sep typl =
|
|
sepListL sep (List.map (typeWithPrecL denv env prec) typl)
|
|
|
|
and measureL' denv env unt = auxMeasureL' denv env 5 unt
|
|
|
|
/// Layout a single type, taking TypeSimplificationInfo into account *)
|
|
and typeL' denv env typ =
|
|
typeWithPrecL denv env 5 typ
|
|
|
|
and typeL denv typ =
|
|
typeL' denv SimplifyTypes.typeSimplificationInfo0 typ
|
|
|
|
/// Layout a single type used as the type of a member or value
|
|
let topTypeL denv env argInfos rty cxs =
|
|
if denv.html && List.exists (snd >> isTTyparSupportsStaticMethod) cxs then
|
|
wordL "overloaded"
|
|
else
|
|
// Parenthesize the return type to match the topValInfo
|
|
let rtyL = typeWithPrecL denv env 4 rty
|
|
let cxsL = constraintsL denv env cxs
|
|
match argInfos with
|
|
| [] -> rtyL --- cxsL
|
|
| _ ->
|
|
|
|
// Format each argument, including its name and type
|
|
let argL (ty,TopArgInfo(argAttribs,idOpt)) =
|
|
|
|
// Detect an optional argument
|
|
let isOptionalArg = HasAttrib denv.g denv.g.attrib_OptionalArgumentAttribute argAttribs
|
|
match idOpt, isOptionalArg, try_dest_option_ty denv.g ty with
|
|
// Layout an optional argument
|
|
| Some(id), true, Some(ty) ->
|
|
leftL ("?"^id.idText) $$ sepL ":" $$ typeWithPrecL denv env 2 ty
|
|
// Layout an unnamed argument
|
|
| None, _,_ ->
|
|
typeWithPrecL denv env 2 ty
|
|
// Layout a named argument
|
|
| Some id,_,_ ->
|
|
leftL id.idText $$ sepL ":" $$ typeWithPrecL denv env 2 ty
|
|
|
|
let allArgsL =
|
|
argInfos
|
|
|> List.mapSquared argL
|
|
|> List.map (sepListL (wordL "*"))
|
|
|> List.map (fun x -> (x $$ wordL "->"))
|
|
(List.foldBack (---) allArgsL rtyL) --- cxsL
|
|
|
|
let typarDeclsL denv nmL prefix typars =
|
|
typarDeclsL' denv SimplifyTypes.typeSimplificationInfo0 nmL prefix typars
|
|
|
|
let measureL denv unt =
|
|
measureL' denv SimplifyTypes.typeSimplificationInfo0 unt
|
|
|
|
let constraintL denv typars =
|
|
match constraintL' denv SimplifyTypes.typeSimplificationInfo0 typars with
|
|
| h::_ -> h
|
|
| [] -> emptyL
|
|
|
|
let typesAndConstraintsL denv taus =
|
|
let _,ptaus,cxs = PrettifyTypesN denv.g taus
|
|
let env = SimplifyTypes.CollectInfo true ptaus cxs
|
|
List.map (typeL' denv env) ptaus,constraintsL denv env env.SimplifyTypes.postfixConstraints
|
|
|
|
let topPrettifiedTypesAndConstraintsL denv argInfos tau cxs =
|
|
let env = SimplifyTypes.CollectInfo true (tau:: List.collect (List.map fst) argInfos) cxs
|
|
topTypeL denv env argInfos tau env.SimplifyTypes.postfixConstraints
|
|
|
|
let topTypAndConstraintsL denv argInfos tau =
|
|
let _,(argInfos,tau),cxs = PrettifyTypesN1 denv.g (argInfos,tau)
|
|
topPrettifiedTypesAndConstraintsL denv [argInfos] tau cxs
|
|
|
|
let memberTypeAndConstraintsL denv argInfos retTy parentTyparTys =
|
|
let _,(parentTyparTys,argInfos,retTy),cxs = PrettifyTypesNM1 denv.g (parentTyparTys,argInfos,retTy)
|
|
(* Filter out the parent typars, which don't get shown in the member signature *)
|
|
let cxs = cxs |> List.filter (fun (tp,_) -> not (List.exists (dest_typar_typ denv.g >> typar_ref_eq tp) parentTyparTys))
|
|
topPrettifiedTypesAndConstraintsL denv argInfos retTy cxs
|
|
|
|
(* Layout: type spec - class, datatype, record, abbrev *)
|
|
|
|
let memberTypeCoreL denv memberToParentInst (methTypars:typars,argInfos,retTy) =
|
|
let niceMethodTypars, allTyparInst =
|
|
let methTyparNames = methTypars |> List.mapi (fun i tp -> if (NeedsPrettyTyparName tp) then sprintf "a%d" (List.length memberToParentInst + i) else tp.Name)
|
|
NewPrettyTypars memberToParentInst methTypars methTyparNames
|
|
|
|
let retTy = InstType allTyparInst retTy
|
|
let argInfos = argInfos |> List.map (fun infos -> if isNil infos then [(denv.g.unit_ty,TopValInfo.unnamedTopArg1)] else infos |> List.map (map1'2 (InstType allTyparInst)))
|
|
|
|
(* Also format dummy types corresponding to any type variables on the container to make sure they *)
|
|
(* aren't chosen as names for displayed variables. *)
|
|
let memberParentTypars = List.map fst memberToParentInst
|
|
let parentTyparTys = List.map (mk_typar_ty >> InstType allTyparInst) memberParentTypars
|
|
|
|
niceMethodTypars,memberTypeAndConstraintsL denv argInfos retTy parentTyparTys
|
|
|
|
let memberTypeL denv v tps argInfos retTy =
|
|
match PartitionValTypars denv.g v with
|
|
| Some(_,memberParentTypars,memberMethodTypars,memberToParentInst,_) ->
|
|
memberTypeCoreL denv memberToParentInst (memberMethodTypars, argInfos, retTy)
|
|
| None ->
|
|
[],topTypAndConstraintsL denv (List.concat argInfos) retTy
|
|
|
|
let memberSigL denv (memberToParentInst,nm,methTypars,argInfos,retTy) =
|
|
let niceMethodTypars,tauL = memberTypeCoreL denv memberToParentInst (methTypars, argInfos, retTy)
|
|
let nameL =
|
|
let nameL = wordL (DemangleOperatorName nm)
|
|
let nameL = if denv.showTyparBinding then typarDeclsL denv nameL true niceMethodTypars else nameL
|
|
nameL
|
|
nameL $$ wordL ":" $$ tauL
|
|
|
|
|
|
let memberLP denv (path : Path) (v:Val) =
|
|
let membInfo = the v.MemberInfo
|
|
let topValInfo = the v.TopValInfo
|
|
let id = v.Id
|
|
let ty = v.Type
|
|
let stat = memFlagsL denv.htmlHideRedundantKeywords membInfo.MemberFlags
|
|
let tps,argInfos,rty,_ = GetMemberTypeInFSharpForm denv.g membInfo.MemberFlags topValInfo ty v.Range
|
|
let mkNameL niceMethodTypars name =
|
|
let name = DemangleOperatorName name
|
|
let nameL = if denv.showMemberContainers then tcrefL false denv (tcref_of_stripped_typ denv.g (enclosing_formal_typ_of_val denv.g v)) $$ sepL "." $$ (name &== path) else name &== path
|
|
let nameL = if denv.showTyparBinding then typarDeclsL denv nameL true niceMethodTypars else nameL
|
|
let nameL = accessibilityL denv v.Accessibility nameL
|
|
nameL
|
|
|
|
match membInfo.MemberFlags.MemberKind with
|
|
| MemberKindMember ->
|
|
let niceMethodTypars,tauL = memberTypeL denv v tps argInfos rty
|
|
let nameL = mkNameL niceMethodTypars membInfo.CompiledName
|
|
stat --- (nameL $$ wordL ":" $$ tauL)
|
|
| MemberKindClassConstructor
|
|
| MemberKindConstructor ->
|
|
let niceMethodTypars,tauL = memberTypeL denv v tps argInfos rty
|
|
let newL = accessibilityL denv v.Accessibility (wordL "new")
|
|
stat ++ newL $$ wordL ":" $$ tauL
|
|
| MemberKindPropertyGetSet -> stat
|
|
| MemberKindPropertyGet ->
|
|
if isNil argInfos then
|
|
// use error recovery because intellisense on an incomplete file will show this
|
|
errorR(Error("invalid form for a property getter. At least one '()' argument is required when using the explicit syntax",id.idRange));
|
|
stat --- wordL membInfo.PropertyName --- wordL "with get"
|
|
else
|
|
let argInfos =
|
|
match argInfos with
|
|
| [[(ty,_)]] when is_unit_typ denv.g ty -> []
|
|
| _ -> argInfos
|
|
|
|
let niceMethodTypars,tauL = memberTypeL denv v tps argInfos rty
|
|
let nameL = mkNameL niceMethodTypars membInfo.PropertyName
|
|
stat --- (nameL $$ wordL ":" $$ (if isNil argInfos then tauL else tauL --- wordL "with get"))
|
|
| MemberKindPropertySet ->
|
|
if argInfos.Length <> 1 || isNil argInfos.Head then
|
|
// use error recovery because intellisense on an incomplete file will show this
|
|
errorR(Error("invalid form for a property setter. At least one argument is required",id.idRange));
|
|
stat --- wordL membInfo.PropertyName --- wordL "with set"
|
|
else
|
|
let argInfos,valueInfo = List.frontAndBack argInfos.Head
|
|
let niceMethodTypars,tauL = memberTypeL denv v tps (if isNil argInfos then [] else [argInfos]) (fst valueInfo)
|
|
let nameL = mkNameL niceMethodTypars membInfo.PropertyName
|
|
stat --- (nameL $$ wordL ":" $$ (tauL --- wordL "with set"))
|
|
|
|
let nonMemberValSpecLP denv (path : Path) (tps,v:Val,tau,cxs) =
|
|
let env = SimplifyTypes.CollectInfo true [tau] cxs
|
|
let cxs = env.SimplifyTypes.postfixConstraints
|
|
let argInfos,rty = GetTopTauTypeInFSharpForm denv.g (arity_of_val v).ArgInfos tau v.Range
|
|
(* Drop the names from value arguments when printing them *)
|
|
let argInfos = argInfos |> List.mapSquared (fun (ty,info) -> ty,TopValInfo.unnamedTopArg1)
|
|
let nameL = v.DisplayName &== path
|
|
let nameL = accessibilityL denv v.Accessibility nameL
|
|
let nameL = if v.IsMutable then wordL "mutable" ++ nameL else nameL
|
|
let nameL =
|
|
if not denv.html && v.MustInline && not denv.suppressInlineKeyword then
|
|
wordL "inline" ++ nameL
|
|
else
|
|
nameL
|
|
|
|
let isOverGeneric = List.length (Zset.elements (free_in_type CollectTyparsNoCaching tau).FreeTypars) < List.length tps (* Bug: 1143 *)
|
|
let isTyFunction = v.IsTypeFunction (* Bug: 1143, and innerpoly tests *)
|
|
let typarBindingsL =
|
|
if isTyFunction || isOverGeneric || denv.showTyparBinding then
|
|
typarDeclsL denv nameL true tps
|
|
else nameL
|
|
let valAndTypeL = (wordL "val" $$ typarBindingsL --- wordL ":") --- topTypeL denv env argInfos rty cxs
|
|
match denv.generatedValueLayout v with
|
|
| None -> valAndTypeL
|
|
| Some rhsL -> (valAndTypeL ++ wordL "=") --- rhsL
|
|
|
|
let valLP denv (path : Path) (v:Val) =
|
|
let vL =
|
|
match v.MemberInfo with
|
|
| None ->
|
|
let tps,tau = v.TypeScheme
|
|
|
|
// adjust the type in case this is the 'this' pointer stored in a reference cell
|
|
let tau = if v.BaseOrThisInfo = CtorThisVal && is_refcell_ty denv.g tau then dest_refcell_ty denv.g tau else tau
|
|
|
|
let tprenaming,ptau,cxs = PrettyTypes.PrettifyTypes1 denv.g tau
|
|
let ptps =
|
|
tps
|
|
|> generalize_typars
|
|
// Badly formed code may instantiate rigid declared typars to types, e.g. see bug
|
|
// Hence we double check here that the thing is really a type variable
|
|
|> List.map (InstType tprenaming)
|
|
|> List.filter (is_anypar_typ denv.g)
|
|
|> List.map (dest_anypar_typ denv.g)
|
|
nonMemberValSpecLP denv path (ptps,v,ptau,cxs)
|
|
| Some _ ->
|
|
memberLP denv path v
|
|
attribsL denv KindType v.Attribs vL
|
|
|
|
let valL denv (v:Val) = valLP denv Path.NoPath v
|
|
|
|
let extensionMembersLP denv (path:Path) (vs:Val list) =
|
|
let extensionMemberLP (v:Val) =
|
|
let tycon = v.MemberApparentParent.Deref
|
|
let nameL = wordL tycon.DisplayName
|
|
let nameL = accessibilityL denv tycon.Accessibility nameL // "type-accessibility"
|
|
let tps =
|
|
match PartitionValTypars denv.g v with
|
|
| Some(_,memberParentTypars,memberMethodTypars,_,_) -> memberParentTypars
|
|
| None -> []
|
|
let lhsL = wordL "type" $$ typarDeclsL denv nameL tycon.IsPrefixDisplay tps
|
|
(lhsL $$ wordL "with") @@-- (valLP denv path v)
|
|
aboveListL (List.map extensionMemberLP vs)
|
|
|
|
let ucaseArgTypesL denv argtys =
|
|
sepListL (wordL "*") (List.map (typeWithPrecL denv SimplifyTypes.typeSimplificationInfo0 2) argtys)
|
|
|
|
let ucaseLP denv (path : Path) prefixL ucase =
|
|
let nmL = (DemangleOperatorName ucase.ucase_id.idText) &== path
|
|
//let nmL = accessibilityL denv ucase.Accessibility nmL
|
|
match ucase.RecdFields |> List.map (fun rfld -> rfld.FormalType) with
|
|
| [] -> (prefixL $$ nmL)
|
|
| argtys -> (prefixL $$ nmL $$ wordL "of") --- ucaseArgTypesL denv argtys
|
|
|
|
let ucaseL denv prefixL ucase = ucaseLP denv Path.Empty prefixL ucase
|
|
|
|
let ucasesLP denv (path : Path) ucases =
|
|
let prefixL = wordL "|" // See bug://2964 - always prefix in case preceeded by accessibility modifier
|
|
List.map (ucaseLP denv path prefixL) ucases
|
|
|
|
let rfspecLP addAccess denv (path : Path) (fld:RecdField) =
|
|
let lhs = fld.Name &== path
|
|
let lhs = (if addAccess then accessibilityL denv fld.Accessibility lhs else lhs)
|
|
let lhs = if fld.IsMutable then wordL "mutable" --- lhs else lhs
|
|
(lhs $$ rightL ":") --- typeL denv fld.FormalType
|
|
|
|
let rfspecL addAccess denv fld = rfspecLP addAccess denv Path.Empty fld
|
|
|
|
/// When to force a break? "type tyname = <HERE> repn"
|
|
/// When repn is class or datatype constructors (not single one).
|
|
let breakTypeDefnEqn repr =
|
|
match repr with
|
|
| TFsObjModelRepr _ -> true
|
|
| TFiniteUnionRepr r -> (List.length (r.funion_ucases.UnionCasesAsList) > 1)
|
|
| TRecdRepr _ -> true
|
|
| TAsmRepr _
|
|
| TILObjModelRepr _ -> false
|
|
| TMeasureableRepr _ -> false
|
|
|
|
let tyconLP denv (path : Path) typewordL (tycon:Tycon) =
|
|
let prefix = tycon.IsPrefixDisplay
|
|
let name = tycon.DisplayName
|
|
let nameL = name &== path
|
|
let nameL = accessibilityL denv tycon.Accessibility nameL
|
|
let denv = denv_scope_access tycon.Accessibility denv
|
|
let path = path.Add name // the full names of constructors and , e.g., have their type name as part of the path
|
|
let lhsL =
|
|
let tps = tycon.TyparsNoRange
|
|
let tpsL = typarDeclsL denv nameL prefix tps
|
|
typewordL $$ tpsL
|
|
let memberImplementLs,memberCtorLs,memberInstanceLs,memberStaticLs =
|
|
let tcaug = tycon.TypeContents
|
|
let adhoc =
|
|
adhoc_of_tycon tycon
|
|
|> List.filter (vref_is_dispatch_slot >> not)
|
|
|> List.filter (fun v ->
|
|
match v.MemberInfo.Value.ImplementedSlotSigs with
|
|
| TSlotSig(_,oty,_,_,_,_) :: _ ->
|
|
// Don't print overrides in HTML docs
|
|
denv.showOverrides &&
|
|
// Don't print individual methods forming interface implementations - these are currently never exported
|
|
not (is_interface_typ denv.g oty)
|
|
| [] -> true)
|
|
|> List.filter (fun v -> denv.showObsoleteMembers || not (HasAttrib denv.g denv.g.attrib_SystemObsolete v.Attribs))
|
|
(* sort *)
|
|
let sortKey (v:ValRef) = (not v.IsConstructor, (* constructors before others *)
|
|
v.Id.idText) (* sort by name *)
|
|
let adhoc = adhoc |> List.sortBy sortKey
|
|
let iimpls =
|
|
match tycon.TypeReprInfo with
|
|
| Some (TFsObjModelRepr r) when r.fsobjmodel_kind = TTyconInterface -> []
|
|
| _ -> tcaug.tcaug_implements
|
|
let iimpls = iimpls |> List.filter (fun (ty,compgen,m) -> not compgen)
|
|
(* if TTyconInterface, the iimpls should be printed as inheritted interfaces *)
|
|
let iimplsLs = iimpls |> List.map (fun (ty,compgen,m) -> wordL "interface" --- typeL denv ty)
|
|
let adhocCtorsLs = adhoc |> List.filter (fun v -> v.IsConstructor) |> List.map (fun vref -> valLP denv path (deref_val vref))
|
|
let adhocInstanceLs = adhoc |> List.filter (fun v -> not v.IsConstructor && v.IsInstanceMember) |> List.map (fun vref -> valLP denv path (deref_val vref))
|
|
let adhocStaticLs = adhoc |> List.filter (fun v -> not v.IsConstructor && not v.IsInstanceMember) |> List.map (fun vref -> valLP denv path (deref_val vref))
|
|
iimplsLs,adhocCtorsLs,adhocInstanceLs,adhocStaticLs
|
|
let memberLs = memberImplementLs @ memberCtorLs @ memberInstanceLs @ memberStaticLs
|
|
let addMembersAsWithEnd reprL =
|
|
if isNil memberLs then reprL
|
|
else reprL @@ (wordL "with" @@-- aboveListL memberLs) @@ wordL "end"
|
|
|
|
let reprL =
|
|
match tycon.TypeReprInfo with
|
|
| Some repr ->
|
|
let brk = nonNil memberLs || breakTypeDefnEqn repr
|
|
let rhsL =
|
|
let addReprAccessL l = accessibilityL denv tycon.TypeReprAccessibility l
|
|
let denv = denv_scope_access tycon.TypeReprAccessibility denv
|
|
match repr with
|
|
| TRecdRepr flds ->
|
|
let recdFieldRefL fld = rfspecLP false denv path fld $$ rightL ";"
|
|
let recdL = tycon.TrueFieldsAsList |> List.map recdFieldRefL |> aboveListL |> braceL
|
|
addMembersAsWithEnd (addReprAccessL recdL)
|
|
|
|
| TFsObjModelRepr r ->
|
|
match r.fsobjmodel_kind with
|
|
| TTyconDelegate (TSlotSig(nm,typ, _,_,paraml, rty)) ->
|
|
let rty = GetFSharpViewOfReturnType denv.g rty
|
|
wordL "delegate of" --- topTypeL denv SimplifyTypes.typeSimplificationInfo0 (paraml |> List.mapSquared (fun sp -> (sp.Type, TopValInfo.unnamedTopArg1))) rty []
|
|
| _ ->
|
|
match r.fsobjmodel_kind with
|
|
| TTyconEnum ->
|
|
tycon.TrueFieldsAsList
|
|
|> List.map (fun f ->
|
|
match f.LiteralValue with
|
|
| None -> emptyL
|
|
| Some c -> wordL "| " $$ (f.Name &== path) $$ wordL " = " $$ constL c)
|
|
|> aboveListL
|
|
| _ ->
|
|
let start =
|
|
match r.fsobjmodel_kind with
|
|
| TTyconClass -> "class"
|
|
| TTyconInterface -> "interface"
|
|
| TTyconStruct -> "struct"
|
|
| TTyconEnum -> "enum"
|
|
| _ -> failwith "???"
|
|
let inherits =
|
|
match r.fsobjmodel_kind, tycon.TypeContents.tcaug_super with
|
|
| TTyconClass,Some super -> [wordL "inherit" $$ (typeL denv super)]
|
|
| TTyconInterface,_ ->
|
|
let tcaug = tycon.TypeContents
|
|
tcaug.tcaug_implements
|
|
|> List.filter (fun (ity,compgen,_) -> not compgen)
|
|
|> List.map (fun (ity,compgen,_) -> wordL "inherit" $$ (typeL denv ity))
|
|
| _ -> []
|
|
let vsprs =
|
|
adhoc_of_tycon tycon
|
|
|> List.filter (fun v -> isNil (the(v.MemberInfo)).ImplementedSlotSigs)
|
|
|> List.filter vref_is_dispatch_slot
|
|
|> List.map (fun vref -> valLP denv path (deref_val vref))
|
|
let staticValsLs =
|
|
tycon.TrueFieldsAsList
|
|
|> List.filter (fun f -> f.IsStatic)
|
|
|> List.map (fun f -> wordL "static" $$ wordL "val" $$ rfspecLP true denv path f)
|
|
let instanceValsLs =
|
|
tycon.TrueFieldsAsList
|
|
|> List.filter (fun f -> not f.IsStatic)
|
|
|> List.map (fun f -> wordL "val" $$ rfspecLP true denv path f)
|
|
let alldecls = inherits @ memberImplementLs @ memberCtorLs @ instanceValsLs @ vsprs @ memberInstanceLs @ staticValsLs @ memberStaticLs
|
|
let emptyMeasure = match tycon.TypeOrMeasureKind with KindMeasure -> isNil alldecls | _ -> false
|
|
if emptyMeasure then emptyL else (wordL start @@-- aboveListL alldecls) @@ wordL "end"
|
|
| TFiniteUnionRepr ucases ->
|
|
let ucasesL = tycon.UnionCasesAsList |> ucasesLP denv path |> aboveListL
|
|
addMembersAsWithEnd (addReprAccessL ucasesL)
|
|
| TAsmRepr s ->
|
|
wordL "(# \"<Common IL Type Omitted>\" #)"
|
|
| TMeasureableRepr ty ->
|
|
typeL denv ty
|
|
|
|
| TILObjModelRepr (scoref,enc,td) -> IlPrint.ilTypeDefL denv path td
|
|
|
|
let brk = match tycon.TypeReprInfo with
|
|
| Some (TILObjModelRepr _) -> true
|
|
| _ -> brk
|
|
if rhsL = emptyL
|
|
then lhsL
|
|
else (* only unions and records can have accessibility on their representation *)
|
|
if brk
|
|
then (lhsL $$ wordL "=") @@-- rhsL
|
|
else (lhsL $$ wordL "=") --- rhsL
|
|
|
|
| None ->
|
|
match tycon.TypeAbbrev with
|
|
| None ->
|
|
addMembersAsWithEnd lhsL
|
|
| Some a ->
|
|
(lhsL $$ wordL "=") --- (typeL denv a)
|
|
attribsL denv tycon.TypeOrMeasureKind tycon.Attribs reprL
|
|
|
|
let tyconL denv typewordL tycon = tyconLP denv Path.NoPath typewordL tycon
|
|
|
|
let prettyTypeL denv typ =
|
|
let tprenaming,typ,cxs = PrettyTypes.PrettifyTypes1 denv.g typ
|
|
let env = SimplifyTypes.CollectInfo true [typ] cxs
|
|
let cxsL = constraintsL denv env env.SimplifyTypes.postfixConstraints
|
|
typeWithPrecL denv env 2 typ --- cxsL
|
|
|
|
(* Layout: exception spec *)
|
|
|
|
let exnDefnReprL denv repr =
|
|
match repr with
|
|
| TExnAbbrevRepr ecref -> wordL "=" --- tcrefL true denv ecref
|
|
| TExnAsmRepr tref -> wordL "=" --- wordL "(# ... #)"
|
|
| TExnNone -> emptyL
|
|
| TExnFresh r ->
|
|
match r.TrueFieldsAsList with
|
|
| [] -> emptyL
|
|
| r -> wordL "of" --- ucaseArgTypesL denv (r |> List.map (fun rfld -> rfld.FormalType))
|
|
|
|
let exnDefnLP denv (path : Path) (exnc:Entity) =
|
|
let nm = exnc.DemangledExceptionName
|
|
let nmL = annotateWithPath nm path (mangle_exception_name nm)
|
|
let nmL = accessibilityL denv exnc.TypeReprAccessibility nmL
|
|
let exnL = wordL "exception" $$ nmL // need to tack on the Exception at the right of the name for goto definition
|
|
exnL $$ exnDefnReprL denv exnc.ExceptionInfo
|
|
|
|
let exnDefnL denv exnc = exnDefnLP denv Path.NoPath exnc
|
|
|
|
(* Layout: module spec *)
|
|
|
|
let tycon_specsLP denv (path : Path) (tycons:Tycon list) =
|
|
match tycons with
|
|
| [] -> emptyL
|
|
| [h] when h.IsExceptionDecl -> exnDefnLP denv path h
|
|
| h :: t ->
|
|
let x = tyconLP denv path (wordL "type") h
|
|
let xs = List.map (tyconLP denv path (wordL "and")) t
|
|
aboveListL (x::xs)
|
|
|
|
/// Layout the inferred signature of a compilation unit
|
|
let InferredSigOfModuleExprL showHeader denv expr =
|
|
|
|
let rec isConcreteNamespace x =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
nonNil tycons || not (FlatList.isEmpty binds) || (mbinds |> List.exists (fun (TMBind(x,_)) -> not x.IsNamespace))
|
|
| TMDefLet(bind,m) -> true
|
|
| TMDefDo(e,m) -> true
|
|
| TMDefs(defs) -> defs |> List.exists isConcreteNamespace
|
|
| TMAbstract(TMTyped(_,def,_)) -> isConcreteNamespace def
|
|
|
|
let rec imexprLP denv (path : Path) (TMTyped(mty,def,m)) = imdefLP denv path def
|
|
|
|
and imexprL denv (TMTyped(mty,def,m)) = imexprLP denv Path.Empty (TMTyped(mty,def,m))
|
|
|
|
and imdefsLP denv (path : Path) x = aboveListL (x |> List.map (imdefLP denv path))
|
|
|
|
and imdefLP denv (path : Path) x =
|
|
let filterVal (v:Val) = not v.IsCompilerGenerated && isNone v.MemberInfo
|
|
let filterExtMem (v:Val) = v.IsExtensionMember
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
tycon_specsLP denv path tycons @@
|
|
(binds |> FlatList.to_list |> vars_of_binds |> List.filter filterExtMem |> extensionMembersLP denv path) @@
|
|
(binds |> FlatList.to_list |> vars_of_binds |> List.filter filterVal |> List.map (valLP denv path) |> aboveListL) @@
|
|
(mbinds |> List.map (imbindLP denv path) |> aboveListL)
|
|
| TMDefLet(bind,m) -> ([bind] |> vars_of_binds |> List.filter filterVal |> List.map (valLP denv path) |> aboveListL)
|
|
| TMDefs(defs) -> imdefsLP denv path defs
|
|
| TMDefDo _ -> emptyL
|
|
| TMAbstract(mexpr) -> imexprLP denv path mexpr
|
|
and imbindLP denv (path : Path) (TMBind(mspec, def)) =
|
|
let nm = demangled_name_of_modul mspec
|
|
let innerPath = (full_cpath_of_modul mspec).AccessPath
|
|
let outerPath = mspec.CompilationPath.AccessPath
|
|
let k = mspec.ModuleOrNamespaceType.ModuleOrNamespaceKind
|
|
|
|
let denv = denv_add_open_path (List.map fst innerPath) denv
|
|
if mspec.IsNamespace then
|
|
let basic = imdefLP denv path def
|
|
// Check if this namespace contains anything interesting
|
|
if isConcreteNamespace def then
|
|
// This is a container namespace. We print the header when we get to the first concrete module.
|
|
let headerL = wordL ("namespace " ^ (String.concat "." (innerPath |> List.map fst)))
|
|
headerL @@-- basic
|
|
else
|
|
// This is a namespace that only contains namespaces. Skipt the header
|
|
basic
|
|
else
|
|
// This is a module
|
|
let nmL = accessibilityL denv mspec.Accessibility (nm &== path)
|
|
let denv = denv_scope_access mspec.Accessibility denv
|
|
let basic = imdefLP denv path def
|
|
// Check if its an outer module or a nested module
|
|
if (outerPath |> List.forall (fun (_,istype) -> istype = Namespace) ) then
|
|
// OK, this is an outer module
|
|
if showHeader then
|
|
// OK, we're not in F# Interactive
|
|
// Check if this is an outer module with no namespace
|
|
if isNil outerPath then
|
|
// If so print a "module" declaration
|
|
(wordL "module" $$ nmL) @@ basic
|
|
else
|
|
// Otherwise this is an outer module contained immediately in a namespace
|
|
// We already printed the namespace declaration earlier. So just print the
|
|
// module now.
|
|
((wordL "module" $$ nmL $$ wordL "=" $$ wordL "begin") @@-- basic) @@ wordL "end"
|
|
else
|
|
// OK, wer'e in F# Interactive, presumably the implicit module for each interaction.
|
|
basic
|
|
else
|
|
// OK, this is a nested module
|
|
((wordL "module" $$ nmL $$ wordL "=" $$ wordL "begin") @@-- basic) @@ wordL "end"
|
|
imexprL denv expr
|
|
|
|
type DeclSpec =
|
|
| DVal of Val
|
|
| DTycon of Tycon
|
|
| DException of Tycon
|
|
| DModul of ModuleOrNamespace
|
|
|
|
let rangeOfDeclSpec = function
|
|
| DVal v -> v.Range
|
|
| DTycon t -> t.Range
|
|
| DException t -> t.Range
|
|
| DModul m -> m.Range
|
|
|
|
/// modul - provides (valspec)* - and also types, exns and submodules.
|
|
/// Each defines a decl block on a given range.
|
|
/// Can sort on the ranges to recover the original declaration order.
|
|
let rec ModuleOrNamespaceTypeLP (topLevel : bool)(denv : DisplayEnv)(path : Path)(mtype : ModuleOrNamespaceType) =
|
|
(* REVIEW: consider a better way to keep decls in order. *)
|
|
let decl_specs : DeclSpec list =
|
|
List.concat
|
|
[mtype.AllValuesAndMembers |> NameMap.range |> List.filter (fun v -> not v.IsCompilerGenerated && v.MemberInfo = None) |> List.map DVal;
|
|
mtype.TypeDefinitions |> List.map DTycon;
|
|
mtype.ExceptionDefinitions |> List.map DException;
|
|
mtype.ModuleAndNamespaceDefinitions |> List.map DModul;
|
|
]
|
|
|
|
let decl_specs = List.sortWith (orderOn rangeOfDeclSpec range_ord) decl_specs
|
|
let decl_specL =
|
|
function // only show namespaces / modules at the top level; this is because we've no global namespace
|
|
| DVal vspec when not topLevel -> valLP denv path vspec
|
|
| DTycon tycon when not topLevel -> tyconLP denv path (wordL "type") tycon
|
|
| DException tycon when not topLevel -> exnDefnLP denv path tycon
|
|
| DModul mspec -> ModuleOrNamespaceLP false denv path mspec
|
|
| _ -> emptyL // this catches non-namespace / modules at the top-level
|
|
|
|
aboveListL (List.map decl_specL decl_specs)
|
|
|
|
and ModuleOrNamespaceLP (topLevel : bool)(denv : DisplayEnv)(path : Path)(mspec : ModuleOrNamespace) =
|
|
let istype = mspec.ModuleOrNamespaceType.ModuleOrNamespaceKind
|
|
let nm = demangled_name_of_modul mspec
|
|
let denv = denv_add_open_modref (mk_local_modref mspec) denv
|
|
let nmL = accessibilityL denv mspec.Accessibility (nm &== path)
|
|
let denv = denv_scope_access mspec.Accessibility denv
|
|
let path = path.Add nm // tack on the current module to be used in calls to linearise all subterms
|
|
let body = ModuleOrNamespaceTypeLP topLevel denv path mspec.ModuleOrNamespaceType
|
|
if istype = Namespace
|
|
then (wordL "namespace" $$ nmL) @@-- body
|
|
else (wordL "module" $$ nmL $$ wordL "= begin") @@-- body @@ wordL "end"
|
|
|
|
let ModuleOrNamespaceTypeL (denv : DisplayEnv)(mtype : ModuleOrNamespaceType) = ModuleOrNamespaceTypeLP false denv Path.Empty mtype
|
|
let ModuleOrNamespaceL (denv : DisplayEnv)(mspec : ModuleOrNamespace) = ModuleOrNamespaceLP false denv Path.Empty mspec
|
|
let AssemblyL denv (mspec : ModuleOrNamespace) = ModuleOrNamespaceTypeLP true denv Path.Empty mspec.ModuleOrNamespaceType // we seem to get the *assembly* name as an outer module, this strips this off
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Nice printing of a subset of expressions, e.g. for refutations in pattern matching
|
|
//--------------------------------------------------------------------------
|
|
|
|
let rec dataExprL denv expr = dataExprWrapL denv false expr
|
|
and atomL denv expr = dataExprWrapL denv true expr (* true means bracket if needed to be atomic expr *)
|
|
|
|
and dataExprWrapL denv isAtomic expr =
|
|
let wrap = bracketIfL isAtomic in (* wrap iff require atomic expr *)
|
|
match expr with
|
|
| TExpr_const (c,m,ty) ->
|
|
if is_enum_typ denv.g ty then
|
|
wordL "enum" $$ angleL denv (typeL denv ty) $$ bracketL (constL c)
|
|
else
|
|
constL c
|
|
|
|
| TExpr_val (v,flags,m) -> wordL (v.DisplayName)
|
|
| TExpr_link rX -> dataExprWrapL denv isAtomic (!rX)
|
|
| TExpr_op(TOp_ucase(c),tyargs,args,m) ->
|
|
if denv.g.ucref_eq c denv.g.nil_ucref then wordL "[]"
|
|
elif denv.g.ucref_eq c denv.g.cons_ucref then
|
|
let rec strip = function (TExpr_op(TOp_ucase(c),tyargs,[h;t],m)) -> h::strip t | _ -> []
|
|
listL (dataExprL denv) (strip expr)
|
|
elif isNil(args) then
|
|
wordL c.CaseName
|
|
else
|
|
(wordL c.CaseName ++ bracketL (commaListL (dataExprsL denv args)))
|
|
|
|
| TExpr_op(TOp_exnconstr(c),_,args,m) -> (wordL c.MangledName ++ bracketL (commaListL (dataExprsL denv args)))
|
|
| TExpr_op(TOp_tuple,tys,xs,m) -> tupleL (dataExprsL denv xs)
|
|
| TExpr_op(TOp_recd (ctor,tc),tinst,xs,m) -> let fields = tc.TrueInstanceFieldsAsList
|
|
let lay fs x = (wordL fs.rfield_id.idText $$ sepL "=") --- (dataExprL denv x)
|
|
leftL "{" $$ semiListL (List.map2 lay fields xs) $$ rightL "}"
|
|
| TExpr_op(TOp_array,[ty],xs,m) -> leftL "[|" $$ semiListL (dataExprsL denv xs) $$ rightL "|]"
|
|
| _ -> wordL "?"
|
|
and dataExprsL denv xs = List.map (dataExprL denv) xs
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Print Signatures/Types - ouput functions - old style
|
|
//--------------------------------------------------------------------------
|
|
|
|
(* A few old-style o/p functions are used, e.g. in tc.ml *)
|
|
let output_tref denv os x = trefL denv x |> bufferL os
|
|
let output_tcref denv os x = x |> tcrefL false denv |> bufferL os
|
|
let output_val_spec denv os x = x |> valL denv |> bufferL os
|
|
let output_typ denv os x = x |> typeL denv |> bufferL os
|
|
let output_exnc denv os x = x |> exnDefnL denv |> bufferL os
|
|
let output_typar_constraints denv os x = x |> constraintsL denv SimplifyTypes.typeSimplificationInfo0 |> bufferL os
|
|
let string_of_typar_constraints denv x = x |> constraintsL denv SimplifyTypes.typeSimplificationInfo0 |> showL
|
|
let output_rfield denv os x = x |> rfspecL false denv |> bufferL os
|
|
let output_tycon denv os x = tyconL denv (wordL "type") x |> bufferL os
|
|
let output_ucase denv os x = ucaseL denv (wordL "|") x |> bufferL os
|
|
let output_typars denv nm os tps = (typarDeclsL denv (wordL nm) true tps) |> bufferL os
|
|
let output_typar_constraint denv os tpc = output_typar_constraints denv os [tpc]
|
|
let string_of_typar_constraint denv tpc = string_of_typar_constraints denv [tpc]
|
|
let string_of_typ denv typ = typeL denv typ |> showL
|
|
let pretty_string_of_typ denv typ = prettyTypeL denv typ |> showL
|
|
let pretty_string_of_unit denv unt = measureL denv unt |> showL
|
|
let string_of_rfield denv x = x |> rfspecL false denv |> showL
|
|
let string_of_tycon denv x = tyconL denv (wordL "type") x |> showL
|
|
let string_of_ucase denv x = ucaseL denv (wordL "|") x |> showL
|
|
let string_of_typars denv nm tps = (typarDeclsL denv (wordL nm) true tps) |> showL
|
|
let string_of_exnc denv x = x |> exnDefnL denv |> showL
|
|
let string_of_val_spec denv x = x |> valL denv |> showL
|
|
(* Print members with a qualification showing the type they are contained in *)
|
|
let output_qualified_val_spec denv os v = output_val_spec { denv with showMemberContainers=true; } os v
|
|
let string_of_qualified_val_spec denv v = string_of_val_spec { denv with showMemberContainers=true; } v
|
|
|
|
end
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DEBUG layout
|
|
//---------------------------------------------------------------------------
|
|
|
|
module DebugPrint = begin
|
|
open Microsoft.FSharp.Compiler.Layout
|
|
open PrettyTypes
|
|
let layout_ranges = ref false
|
|
|
|
let intL (n:int) = wordL (string n )
|
|
let int64L (n:int64) = wordL (string n )
|
|
|
|
let namemapL xL xmap = NameMap.foldRange (fun x z -> z @@ xL x) xmap emptyL
|
|
|
|
let bracketIfL x lyt = if x then bracketL lyt else lyt
|
|
|
|
let lvalopL x =
|
|
match x with
|
|
| LGetAddr -> wordL "LGetAddr"
|
|
| LByrefGet -> wordL "LByrefGet"
|
|
| LSet -> wordL "LSet"
|
|
| LByrefSet -> wordL "LByrefSet"
|
|
|
|
let angleBracketL l = leftL "<" $$ l $$ rightL ">"
|
|
let angleBracketListL l = angleBracketL (sepListL (sepL ",") l)
|
|
|
|
|
|
let memFlagsL hide memFlags =
|
|
let stat = if hide || memFlags.MemberIsInstance || (memFlags.MemberKind = MemberKindConstructor) then emptyL else wordL "static"
|
|
let stat = if not memFlags.MemberIsDispatchSlot && memFlags.MemberIsVirtual then stat ++ wordL "virtual"
|
|
elif not hide && memFlags.MemberIsDispatchSlot then stat ++ wordL "abstract"
|
|
elif memFlags.MemberIsOverrideOrExplicitImpl then stat ++ wordL "override"
|
|
else stat
|
|
(* let stat = if memFlags.MemberIsFinal then stat ++ wordL "final" else stat in *)
|
|
stat
|
|
|
|
let stampL n w = if !verboseStamps then w $$ sepL "#" $$ int64L n else w
|
|
|
|
let tcrefL (tc:TyconRef) = wordL tc.DisplayName |> stampL tc.Stamp
|
|
|
|
|
|
let rec auxTypeL env typ = auxTypeWrapL env false typ
|
|
|
|
and auxTypeAtomL env typ = auxTypeWrapL env true typ
|
|
|
|
and auxTyparsL env tcL prefix tinst =
|
|
match tinst with
|
|
| [] -> tcL
|
|
| [t] ->
|
|
let tL = auxTypeAtomL env t
|
|
if prefix then tcL $$ angleBracketL tL
|
|
else tL $$ tcL
|
|
| _ ->
|
|
let tinstL = List.map (auxTypeL env) tinst
|
|
if prefix then
|
|
tcL $$ angleBracketListL tinstL
|
|
else
|
|
tupleL tinstL $$ tcL
|
|
|
|
and auxTypeWrapL env isAtomic typ =
|
|
let wrap x = NicePrint.bracketIfL isAtomic x in (* wrap iff require atomic expr *)
|
|
match strip_tpeqns typ with
|
|
| TType_forall (typars,rty) ->
|
|
(leftL "!" $$ typarDeclsL typars --- auxTypeL env rty) |> wrap
|
|
| TType_ucase (UCRef(tcref,_),tinst)
|
|
| TType_app (tcref,tinst) ->
|
|
let prefix = tcref.IsPrefixDisplay
|
|
let tcL = tcrefL tcref
|
|
auxTyparsL env tcL prefix tinst
|
|
| TType_tuple typs -> sepListL (wordL "*") (List.map (auxTypeAtomL env) typs) |> wrap
|
|
| TType_fun (f,x) -> ((auxTypeAtomL env f $$ wordL "->") --- auxTypeL env x) |> wrap
|
|
| TType_var typar -> auxTyparWrapL env isAtomic typar
|
|
| TType_modul_bindings -> wordL "structT"
|
|
| TType_measure unt ->
|
|
#if DEBUG
|
|
leftL "{" $$
|
|
(match !global_g with
|
|
| None -> wordL "<no global g>"
|
|
| Some g ->
|
|
let sortVars (vs:(Typar * int) list) = vs |> List.sortBy (fun (v,_) -> v.DisplayName)
|
|
let sortCons (cs:(TyconRef * int) list) = cs |> List.sortBy (fun (c,_) -> c.DisplayName)
|
|
let negvs,posvs = ListMeasureVarOccsWithNonZeroExponents unt |> sortVars |> List.partition (fun (_,e) -> e<0)
|
|
let negcs,poscs = ListMeasureConOccsWithNonZeroExponents g false unt |> sortCons |> List.partition (fun (_,e) -> e<0)
|
|
let unparL (uv:Typar) = wordL ("'" ^ uv.DisplayName)
|
|
let unconL tc = tcrefL tc
|
|
let prefix = spaceListL (List.map (fun (v,e) -> if e=1 then unparL v else unparL v -- wordL (Printf.sprintf "^ %d" e)) posvs @
|
|
List.map (fun (c,e) -> if e=1 then unconL c else unconL c -- wordL (Printf.sprintf "^ %d" e)) poscs)
|
|
let postfix = spaceListL (List.map (fun (v,e) -> if e= -1 then unparL v else unparL v -- wordL (Printf.sprintf "^ %d" (-e))) negvs @
|
|
List.map (fun (c,e) -> if e= -1 then unconL c else unconL c -- wordL (Printf.sprintf "^ %d" (-e))) negcs)
|
|
match (negvs,negcs) with
|
|
| [],[] -> prefix
|
|
| _ -> prefix $$ sepL "/" $$ postfix) $$
|
|
rightL "}"
|
|
#else
|
|
wordL "<measure>"
|
|
#endif
|
|
|
|
and auxTyparWrapL env isAtomic (typar:Typar) =
|
|
let wrap x = NicePrint.bracketIfL isAtomic x in (* wrap iff require atomic expr *)
|
|
(* There are several cases for pprinting of typar.
|
|
*
|
|
* 'a - is multiple occurance.
|
|
* #Type - inplace coercion constraint and singleton
|
|
* ('a :> Type) - inplace coercion constraint not singleton
|
|
* ('a.opM : S->T) - inplace operator constraint
|
|
*)
|
|
let tpL =
|
|
wordL (prefix_of_static_req typar.StaticReq
|
|
^ prefix_of_rigid typar
|
|
^ typar.DisplayName)
|
|
let varL = tpL |> stampL typar.Stamp
|
|
|
|
match Zmap.tryfind typar env.SimplifyTypes.inplaceConstraints with
|
|
| Some (typarConstrTyp) ->
|
|
if Zset.mem typar env.SimplifyTypes.singletons then
|
|
leftL "#" $$ auxTyparConstraintTypL env typarConstrTyp
|
|
else
|
|
(varL $$ sepL ":>" $$ auxTyparConstraintTypL env typarConstrTyp) |> wrap
|
|
| _ -> varL
|
|
|
|
and auxTypar2L env typar = auxTyparWrapL env false typar
|
|
|
|
and auxTyparAtomL env typar = auxTyparWrapL env true typar
|
|
|
|
and auxTyparConstraintTypL env ty = auxTypeL env ty
|
|
|
|
and auxTraitL env (TTrait(tys,nm,memFlags,argtys,rty,_)) =
|
|
#if DEBUG
|
|
match !global_g with
|
|
| None -> wordL "<no global g>"
|
|
| Some g ->
|
|
let rty = GetFSharpViewOfReturnType g rty
|
|
let stat = memFlagsL false memFlags
|
|
let argsL = sepListL (wordL "*") (List.map (auxTypeAtomL env) argtys)
|
|
let resL = auxTypeL env rty
|
|
let methodTypeL = (argsL $$ wordL "->") ++ resL
|
|
bracketL (stat ++ bracketL (sepListL (wordL "or") (List.map (auxTypeAtomL env) tys)) ++ wordL "member" --- (wordL nm $$ wordL ":" -- methodTypeL))
|
|
#else
|
|
wordL "trait"
|
|
#endif
|
|
|
|
and auxTyparConstraintL env (tp,tpc) =
|
|
match tpc with
|
|
| TTyparCoercesToType(typarConstrTyp,m) ->
|
|
auxTypar2L env tp $$ wordL ":>" --- auxTyparConstraintTypL env typarConstrTyp
|
|
| TTyparMayResolveMemberConstraint(traitInfo,_) ->
|
|
auxTypar2L env tp $$ wordL ":" --- auxTraitL env traitInfo
|
|
| TTyparDefaultsToType(_,ty,m) ->
|
|
wordL "default" $$ auxTypar2L env tp $$ wordL ":" $$ auxTypeL env ty
|
|
| TTyparIsEnum(ty,m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ auxTyparsL env (wordL "enum") true [ty]
|
|
| TTyparIsDelegate(aty,bty,m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ auxTyparsL env (wordL "delegate") true [aty; bty]
|
|
| TTyparSupportsNull(m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ wordL "null"
|
|
| TTyparIsNotNullableValueType(m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ wordL "struct"
|
|
| TTyparIsReferenceType(m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ wordL "not struct"
|
|
| TTyparSimpleChoice(tys,m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ bracketL (sepListL (sepL "|") (List.map (auxTypeL env) tys))
|
|
| TTyparRequiresDefaultConstructor(m) ->
|
|
auxTypar2L env tp $$ wordL ":" $$ bracketL (wordL "new : unit -> " $$ (auxTypar2L env tp))
|
|
|
|
and auxTyparConstraintsL env x =
|
|
match x with
|
|
| [] -> emptyL
|
|
| cxs -> wordL "when" --- aboveListL (List.map (auxTyparConstraintL env) cxs)
|
|
|
|
and TyparL tp = auxTypar2L SimplifyTypes.typeSimplificationInfo0 tp
|
|
and typarAtomL tp = auxTyparAtomL SimplifyTypes.typeSimplificationInfo0 tp
|
|
|
|
and typeAtomL tau =
|
|
let tau,cxs = tau,[]
|
|
let env = SimplifyTypes.CollectInfo false [tau] cxs
|
|
match env.SimplifyTypes.postfixConstraints with
|
|
| [] -> auxTypeAtomL env tau
|
|
| _ -> bracketL (auxTypeL env tau --- auxTyparConstraintsL env env.SimplifyTypes.postfixConstraints)
|
|
|
|
and typeL tau =
|
|
let tau,cxs = tau,[]
|
|
let env = SimplifyTypes.CollectInfo false [tau] cxs
|
|
match env.SimplifyTypes.postfixConstraints with
|
|
| [] -> auxTypeL env tau
|
|
| _ -> (auxTypeL env tau --- auxTyparConstraintsL env env.SimplifyTypes.postfixConstraints)
|
|
|
|
and TyparDeclL tp =
|
|
let tau,cxs = mk_typar_ty tp,(List.map (fun x -> (tp,x)) tp.Constraints)
|
|
let env = SimplifyTypes.CollectInfo false [tau] cxs
|
|
match env.SimplifyTypes.postfixConstraints with
|
|
| [] -> auxTypeL env tau
|
|
| _ -> (auxTypeL env tau --- auxTyparConstraintsL env env.SimplifyTypes.postfixConstraints)
|
|
and typarDeclsL tps = angleBracketListL (List.map TyparDeclL tps)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DEBUG layout - types
|
|
//--------------------------------------------------------------------------
|
|
|
|
let rangeL m = wordL (string_of_range m)
|
|
|
|
let instL tyL tys =
|
|
match tys with
|
|
| [] -> emptyL
|
|
| tys -> sepL "@[" $$ commaListL (List.map tyL tys) $$ rightL "]"
|
|
|
|
let ValRefL (vr:ValRef) =
|
|
wordL vr.MangledName |> stampL vr.Stamp
|
|
|
|
let attribL (Attrib(_,k,args,props,m)) =
|
|
leftL "[<" $$
|
|
(match k with
|
|
| ILAttrib (ilmeth) -> wordL ilmeth.Name
|
|
| FSAttrib (vref) -> ValRefL vref) $$
|
|
rightL ">]"
|
|
|
|
let attribsL attribs = aboveListL (List.map attribL attribs)
|
|
|
|
let arityInfoL (TopValInfo (tpNames,_,_) as tvd) =
|
|
let ns = tvd.AritiesOfArgs in
|
|
leftL "arity<" $$ intL tpNames.Length $$ sepL ">[" $$ commaListL (List.map intL ns) $$ rightL "]"
|
|
|
|
|
|
let valL (vspec:Val) =
|
|
let vsL = wordL (DecompileOpName vspec.MangledName) |> stampL vspec.Stamp
|
|
let vsL = if not !verboseStamps then vsL else vsL $$ rightL (if isSome(vspec.PublicPath) then "+" else "-")
|
|
let vsL = vsL -- attribsL (vspec.Attribs)
|
|
vsL
|
|
|
|
let TypeOfvalL (v:Val) =
|
|
(valL v
|
|
$$ (if v.MustInline then wordL "inline " else emptyL)
|
|
$$ (if v.IsMutable then wordL "mutable " else emptyL)
|
|
$$ wordL ":") -- typeL v.Type
|
|
|
|
|
|
let tslotparamL(TSlotParam(nmOpt, typ, inFlag, outFlag, optFlag,attribs)) =
|
|
(optionL wordL nmOpt) $$ wordL ":" $$ typeL typ $$ (if inFlag then wordL "[in]" else emptyL) $$ (if outFlag then wordL "[out]" else emptyL) $$ (if inFlag then wordL "[opt]" else emptyL)
|
|
|
|
|
|
let SlotSigL (TSlotSig(nm,typ,tps1,tps2,pms,rty)) =
|
|
#if DEBUG
|
|
match !global_g with
|
|
| None -> wordL "<no global g>"
|
|
| Some g ->
|
|
let rty = GetFSharpViewOfReturnType g rty
|
|
(wordL "slot" --- (wordL nm) $$ wordL "@" $$ typeL typ) --
|
|
(wordL "LAM" --- spaceListL (List.map TyparL tps1) $$ rightL ".") ---
|
|
(wordL "LAM" --- spaceListL (List.map TyparL tps2) $$ rightL ".") ---
|
|
(commaListL (List.map (List.map tslotparamL >> tupleL) pms)) $$ (wordL "-> ") --- (typeL rty)
|
|
#else
|
|
wordL "slotsig"
|
|
#endif
|
|
|
|
let rec MemberL (membInfo:ValMemberInfo) =
|
|
(aboveListL [ wordL "vspr_il_name! = " $$ wordL membInfo.CompiledName ;
|
|
wordL "membInfo-slotsig! = " $$ listL SlotSigL membInfo.ImplementedSlotSigs ])
|
|
and vspecAtBindL v =
|
|
let vL = valL v in
|
|
let mutL = (if v.IsMutable then wordL "mutable" ++ vL else vL)
|
|
mutL --- (aboveListL (List.concat [[wordL ":" $$ typeL v.Type];
|
|
(match v.MemberInfo with None -> [] | Some mem_info -> [wordL "!" $$ MemberL mem_info]);
|
|
(match v.TopValInfo with None -> [] | Some arity_info -> [wordL "#" $$ arityInfoL arity_info])]))
|
|
|
|
let UnionCaseRefL (ucr:UnionCaseRef) = wordL ucr.CaseName
|
|
let recdFieldRefL (rfref:RecdFieldRef) = wordL rfref.FieldName
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DEBUG layout - bind, expr, dtree etc.
|
|
//--------------------------------------------------------------------------
|
|
|
|
let identL (id:ident) = wordL id.idText
|
|
|
|
let rec tyconL (tycon:Tycon) =
|
|
if tycon.IsModuleOrNamespace then EntityL tycon else
|
|
|
|
let lhsL = wordL (match tycon.TypeOrMeasureKind with KindMeasure -> "[<Measure>] type" | KindType -> "type") $$ wordL tycon.DisplayName $$ typarDeclsL tycon.TyparsNoRange
|
|
let lhsL = lhsL --- attribsL tycon.Attribs
|
|
let memberLs =
|
|
let tcaug = tycon.TypeContents
|
|
let adhoc = adhoc_of_tycon tycon |> List.filter (vref_is_dispatch_slot >> not)
|
|
(* Don't print individual methods forming interface implementations - these are currently never exported *)
|
|
let adhoc = adhoc |> List.filter (fun v -> isNil (the(v.MemberInfo)).ImplementedSlotSigs)
|
|
let iimpls =
|
|
match tycon.TypeReprInfo with
|
|
| Some (TFsObjModelRepr r) when r.fsobjmodel_kind = TTyconInterface -> []
|
|
| _ -> tcaug.tcaug_implements
|
|
let iimpls = iimpls |> List.filter (fun (ty,compgen,m) -> not compgen)
|
|
(* if TTyconInterface, the iimpls should be printed as inheritted interfaces *)
|
|
if (isNil adhoc && isNil iimpls)
|
|
then emptyL
|
|
else
|
|
let iimplsLs = iimpls |> List.map (fun (ty,compgen,m) -> wordL "interface" --- typeL ty)
|
|
let adhocLs = adhoc |> List.map (fun vref -> vspecAtBindL (deref_val vref))
|
|
(wordL "with" @@-- aboveListL (iimplsLs @ adhocLs)) @@ wordL "end"
|
|
|
|
let ucaseArgTypesL argtys = sepListL (wordL "*") (List.map typeL argtys)
|
|
|
|
let ucaseL prefixL ucase =
|
|
let nmL = wordL (DemangleOperatorName ucase.ucase_id.idText)
|
|
match ucase.RecdFields |> List.map (fun rfld -> rfld.FormalType) with
|
|
| [] -> (prefixL $$ nmL)
|
|
| argtys -> (prefixL $$ nmL $$ wordL "of") --- ucaseArgTypesL argtys
|
|
|
|
let ucasesL ucases =
|
|
let prefixL = if List.length ucases > 1 then wordL "|" else emptyL
|
|
List.map (ucaseL prefixL) ucases
|
|
|
|
let rfspecL (fld:RecdField) =
|
|
let lhs = wordL fld.Name
|
|
let lhs = if fld.IsMutable then wordL "mutable" --- lhs else lhs
|
|
(lhs $$ rightL ":") --- typeL fld.FormalType
|
|
|
|
let tyconReprL (repr,tycon:Tycon) =
|
|
match repr with
|
|
| TRecdRepr _ ->
|
|
tycon.TrueFieldsAsList |> List.map (fun fld -> rfspecL fld $$ rightL ";") |> aboveListL
|
|
| TFsObjModelRepr r ->
|
|
match r.fsobjmodel_kind with
|
|
| TTyconDelegate (TSlotSig(nm,typ, _,_,paraml, rty)) ->
|
|
wordL "delegate ..."
|
|
| _ ->
|
|
let start =
|
|
match r.fsobjmodel_kind with
|
|
| TTyconClass -> "class"
|
|
| TTyconInterface -> "interface"
|
|
| TTyconStruct -> "struct"
|
|
| TTyconEnum -> "enum"
|
|
| _ -> failwith "???"
|
|
let inherits =
|
|
match r.fsobjmodel_kind, tycon.TypeContents.tcaug_super with
|
|
| TTyconClass,Some super -> [wordL "inherit" $$ (typeL super)]
|
|
| TTyconInterface,_ ->
|
|
let tcaug = tycon.TypeContents
|
|
tcaug.tcaug_implements
|
|
|> List.filter (fun (ity,compgen,_) -> not compgen)
|
|
|> List.map (fun (ity,compgen,_) -> wordL "inherit" $$ (typeL ity))
|
|
| _ -> []
|
|
let vsprs = adhoc_of_tycon tycon |> List.filter vref_is_dispatch_slot |> List.map (fun vref -> vspecAtBindL (deref_val vref))
|
|
let vals = tycon.TrueFieldsAsList |> List.map (fun f -> (if f.IsStatic then wordL "static" else emptyL) $$ wordL "val" $$ rfspecL f)
|
|
let alldecls = inherits @ vsprs @ vals
|
|
let emptyMeasure = match tycon.TypeOrMeasureKind with KindMeasure -> isNil alldecls | _ -> false
|
|
if emptyMeasure then emptyL else (wordL start @@-- aboveListL alldecls) @@ wordL "end"
|
|
| TFiniteUnionRepr ucases -> tycon.UnionCasesAsList |> ucasesL |> aboveListL
|
|
| TAsmRepr s -> wordL "(# ... #)"
|
|
| TMeasureableRepr ty -> typeL ty
|
|
| TILObjModelRepr (_,_,td) -> wordL td.tdName
|
|
let reprL =
|
|
match tycon.TypeReprInfo with
|
|
| Some a -> let rhsL = tyconReprL (a,tycon) @@ memberLs
|
|
(lhsL $$ wordL "=") @@-- rhsL
|
|
| None -> match tycon.TypeAbbrev with
|
|
| None -> lhsL @@-- memberLs
|
|
| Some a -> (lhsL $$ wordL "=") --- (typeL a @@ memberLs)
|
|
reprL
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// layout - bind, expr, dtree etc.
|
|
//--------------------------------------------------------------------------
|
|
|
|
and BindingL (TBind(v,repr,_)) =
|
|
vspecAtBindL v --- (wordL "=" $$ ExprL repr)
|
|
|
|
and ExprL expr = exprWrapL false expr
|
|
and atomL expr = exprWrapL true expr (* true means bracket if needed to be atomic expr *)
|
|
|
|
and letRecL binds bodyL =
|
|
let eqnsL =
|
|
binds
|
|
|> FlatList.to_list
|
|
|> List.mapHeadTail (fun bind -> wordL "rec" $$ BindingL bind $$ wordL "in")
|
|
(fun bind -> wordL "and" $$ BindingL bind $$ wordL "in")
|
|
(aboveListL eqnsL @@ bodyL)
|
|
|
|
and letL bind bodyL =
|
|
let eqnL = wordL "let" $$ BindingL bind $$ wordL "in"
|
|
(eqnL @@ bodyL)
|
|
|
|
and exprWrapL isAtomic expr =
|
|
let wrap = bracketIfL isAtomic in (* wrap iff require atomic expr *)
|
|
let lay =
|
|
match expr with
|
|
| TExpr_const (c,m,ty) -> NicePrint.constL c
|
|
| TExpr_val (v,flags,m) -> let xL = valL (deref_val v) in
|
|
let xL =
|
|
if not !verboseStamps then xL else
|
|
let tag =
|
|
match v with
|
|
| VRef_private _ -> ""
|
|
| VRef_nonlocal _ -> "!!" in
|
|
xL $$ rightL tag in
|
|
let xL =
|
|
match flags with
|
|
| CtorValUsedAsSelfInit -> xL $$ rightL "<selfinit>"
|
|
| CtorValUsedAsSuperInit -> xL $$ rightL "<superinit>"
|
|
| VSlotDirectCall -> xL $$ rightL "<vdirect>"
|
|
| NormalValUse -> xL in
|
|
xL
|
|
| TExpr_seq (x0,x1,flag,_,m) -> (let flag =
|
|
match flag with
|
|
| NormalSeq -> "; (*Seq*)"
|
|
| ThenDoSeq -> "; (*ThenDo*)" in
|
|
((ExprL x0 $$ rightL flag) @@ ExprL x1) |> wrap)
|
|
| TExpr_lambda(lambda_id ,basevopt,argvs,body,m,rty,_) -> let formalsL = spaceListL (List.map vspecAtBindL argvs) in
|
|
let bindingL = match basevopt with
|
|
| None -> wordL "lam" $$ formalsL $$ rightL "."
|
|
| Some basev -> wordL "lam" $$ (leftL "base=" $$ vspecAtBindL basev) --- formalsL $$ rightL "." in
|
|
(bindingL ++ ExprL body) |> wrap
|
|
| TExpr_tlambda(lambda_id,argtyvs,body,m,rty,_) -> ((wordL "LAM" $$ spaceListL (List.map TyparL argtyvs) $$ rightL ".") ++ ExprL body) |> wrap
|
|
| TExpr_tchoose(argtyvs,body,m) -> ((wordL "CHOOSE" $$ spaceListL (List.map TyparL argtyvs) $$ rightL ".") ++ ExprL body) |> wrap
|
|
| TExpr_app (f,fty,tys,argtys,m) ->
|
|
let flayout = atomL f
|
|
appL flayout tys argtys |> wrap
|
|
| TExpr_letrec (binds,body,m,_) -> letRecL binds (ExprL body) |> wrap
|
|
| TExpr_let (bind,body,m,_) -> letL bind (ExprL body) |> wrap
|
|
| TExpr_link rX -> (wordL "RecLink" --- atomL (!rX)) |> wrap
|
|
| TExpr_match (spBind,exprm,dtree,targets,m,ty,_) -> leftL "[" $$ (DecisionTreeL dtree @@ aboveListL (List.mapi targetL (targets |> Array.to_list)) $$ rightL "]")
|
|
| TExpr_op(TOp_ucase (c),tyargs,args,m) -> ((UnionCaseRefL c (*$$ (instL typeL tyargs)*)) ++ spaceListL (List.map atomL args)) |> wrap
|
|
| TExpr_op(TOp_exnconstr (ecref),_,args,m) -> wordL ecref.DemangledExceptionName $$ bracketL (commaListL (List.map atomL args))
|
|
| TExpr_op(TOp_tuple,tys,xs,m) -> tupleL (List.map ExprL xs)
|
|
| TExpr_op(TOp_recd (ctor,tc),tinst,xs,m) ->
|
|
let fields = tc.TrueInstanceFieldsAsList
|
|
let lay fs x = (wordL fs.rfield_id.idText $$ sepL "=") --- (ExprL x)
|
|
let ctorL =
|
|
match ctor with
|
|
| RecdExpr -> emptyL
|
|
| RecdExprIsObjInit-> wordL "(new)"
|
|
leftL "{" $$ semiListL (List.map2 lay fields xs) $$ rightL "}" $$ ctorL
|
|
| TExpr_op(TOp_rfield_set (rf),tinst,[rx;x],m) -> (atomL rx --- wordL ".") $$ (recdFieldRefL rf $$ wordL "<-" --- ExprL x)
|
|
| TExpr_op(TOp_rfield_set (rf),tinst,[x],m) -> (recdFieldRefL rf $$ wordL "<-" --- ExprL x)
|
|
| TExpr_op(TOp_rfield_get (rf),tinst,[rx],m) -> (atomL rx $$ rightL ".#" $$ recdFieldRefL rf)
|
|
| TExpr_op(TOp_rfield_get (rf),tinst,[],m) -> (recdFieldRefL rf)
|
|
| TExpr_op(TOp_field_get_addr (rf),tinst,[rx],m) -> leftL "&" $$ bracketL (atomL rx $$ rightL ".!" $$ recdFieldRefL rf)
|
|
| TExpr_op(TOp_field_get_addr (rf),tinst,[],m) -> leftL "&" $$ (recdFieldRefL rf)
|
|
| TExpr_op(TOp_ucase_tag_get (tycr),tinst,[x],m) -> wordL ("#" ^ tycr.MangledName ^ ".tag") $$ atomL x
|
|
| TExpr_op(TOp_ucase_proof (c),tinst,[x],m) -> wordL ("#" ^ c.CaseName^ ".cast") $$ atomL x
|
|
| TExpr_op(TOp_ucase_field_get (c,i),tinst,[x],m) -> wordL ("#" ^ c.CaseName ^ "." ^ string i) --- atomL x
|
|
| TExpr_op(TOp_ucase_field_set (c,i),tinst,[x;y],m) -> ((atomL x --- (rightL ("#" ^ c.CaseName ^ "." ^ string i))) $$ wordL ":=") --- ExprL y
|
|
| TExpr_op(TOp_tuple_field_get (i),tys,[x],m) -> wordL ("#" ^ string i) --- atomL x
|
|
| TExpr_op(TOp_coerce,[typ;typ2],[x],m) -> atomL x --- (wordL ":>" $$ typeL typ) (* check: or is it typ2? *)
|
|
| TExpr_op(TOp_rethrow,[typ],[],m) -> wordL "Rethrow!"
|
|
| TExpr_op(TOp_asm (a,tys),tyargs,args,m) ->
|
|
let instrs = a |> List.map (sprintf "%+A" >> wordL) |> spaceListL // %+A has + since instrs are from an "internal" type
|
|
let instrs = leftL "(#" $$ instrs $$ rightL "#)"
|
|
(appL instrs tyargs args ---
|
|
wordL ":" $$ spaceListL (List.map typeAtomL tys)) |> wrap
|
|
| TExpr_op(TOp_lval_op (lvop,vr),_,args,m) -> (lvalopL lvop $$ ValRefL vr --- bracketL (commaListL (List.map atomL args))) |> wrap
|
|
| TExpr_op(TOp_ilcall ((virt,protect,valu,newobj,superInit,prop,isDllImport,boxthis,mref),tinst,minst,tys),tyargs,args,m) ->
|
|
let meth = mref.Name
|
|
wordL "ILCall" $$ aboveListL [wordL "meth " --- wordL meth;
|
|
wordL "tinst " --- listL typeL tinst;
|
|
wordL "minst " --- listL typeL minst;
|
|
wordL "tyargs" --- listL typeL tyargs;
|
|
wordL "args " --- listL ExprL args] |> wrap
|
|
| TExpr_op(TOp_array,[ty],xs,m) -> leftL "[|" $$ commaListL (List.map ExprL xs) $$ rightL "|]"
|
|
| TExpr_op(TOp_while _,[],[x1;x2],m) -> wordL "while" $$ ExprL x1 $$ wordL "do" $$ ExprL x2 $$ rightL "}"
|
|
| TExpr_op(TOp_for _,[],[x1;x2;x3],m) -> wordL "for" $$ aboveListL [(ExprL x1 $$ wordL "to" $$ ExprL x2 $$ wordL "do"); ExprL x3 ] $$ rightL "done"
|
|
| TExpr_op(TOp_try_catch _,[_],[x1;x2],m) -> wordL "try" $$ ExprL x1 $$ wordL "with" $$ ExprL x2 $$ rightL "}"
|
|
| TExpr_op(TOp_try_finally _,[_],[x1;x2],m) -> wordL "try" $$ ExprL x1 $$ wordL "finally" $$ ExprL x2 $$ rightL "}"
|
|
| TExpr_op(TOp_bytes _,_ ,_ ,m) -> wordL "bytes++"
|
|
| TExpr_op(TOp_uint16s _,_ ,_ ,m) -> wordL "uint16++"
|
|
| TExpr_op(TOp_get_ref_lval,tyargs,args,m) -> wordL "GetRefLVal..."
|
|
| TExpr_op(TOp_trait_call _,tyargs,args,m) -> wordL "traitcall..."
|
|
| TExpr_op(TOp_exnconstr_field_get _,tyargs,args,m) -> wordL "TOp_exnconstr_field_get..."
|
|
| TExpr_op(TOp_exnconstr_field_set _,tyargs,args,m) -> wordL "TOp_exnconstr_field_set..."
|
|
| TExpr_op(TOp_try_finally _,tyargs,args,m) -> wordL "TOp_try_finally..."
|
|
| TExpr_op(TOp_try_catch _,tyargs,args,m) -> wordL "TOp_try_catch..."
|
|
| TExpr_op(_,tys,args,m) -> wordL "TExpr_op ..." $$ bracketL (commaListL (List.map atomL args)) (* REVIEW *)
|
|
| TExpr_quote (a,_,m,_) -> leftL "<@" $$ atomL a $$ rightL "@>"
|
|
| TExpr_obj (n,typ,basev,ccall,
|
|
overrides,iimpls,_,_) ->
|
|
let ccallL (vu,mr,tinst,args) = appL (wordL "ccall") tinst args
|
|
wordL "OBJ:" $$ aboveListL [typeL typ;
|
|
ExprL ccall;
|
|
optionL vspecAtBindL basev;
|
|
aboveListL (List.map overrideL overrides);
|
|
aboveListL (List.map iimplL iimpls)]
|
|
|
|
| TExpr_static_optimization (tcs,csx,x,m) ->
|
|
let tconstraintL = function TTyconEqualsTycon (s,t) -> (typeL s $$ wordL "=") --- typeL t
|
|
(wordL "opt" @@- (ExprL x)) @@--
|
|
(wordL "|" $$ ExprL csx --- (wordL "when..." (* --- sepListL (wordL "and") (List.map tconstraintL tcs) *) ))
|
|
|
|
(* For tracking ranges through expr rewrites *)
|
|
if !layout_ranges
|
|
then leftL "{" $$ (rangeL (range_of_expr expr) $$ rightL ":") ++ lay $$ rightL "}"
|
|
else lay
|
|
|
|
and AssemblyL (TAssembly(implFiles)) =
|
|
aboveListL (List.map ImplFileL implFiles)
|
|
|
|
and appL flayout tys args =
|
|
let z = flayout
|
|
let z = z $$ instL typeL tys
|
|
let z = z --- sepL "`" --- (spaceListL (List.map atomL args))
|
|
z
|
|
|
|
and ImplFileL (TImplFile(qnm,_,e)) =
|
|
aboveListL [(wordL "top implementation ") @@-- mexprL e]
|
|
|
|
and mexprL x =
|
|
match x with
|
|
(* | TMTyped(mtyp,rest,_) -> aboveListL [wordL "CONSTRAIN" @@-- mexprL rest @@- (wordL ":" @@- EntityTypeL mtyp)] *)
|
|
| TMTyped(mtyp,defs,m) -> mdefL defs @@- (wordL ":" @@- EntityTypeL mtyp)
|
|
and mdefsL defs = wordL "Module Defs" @@-- aboveListL(List.map mdefL defs)
|
|
and mdefL x =
|
|
match x with
|
|
| TMDefRec(tycons ,binds,mbinds,m) -> aboveListL ((tycons |> List.map tyconL) @ [letRecL binds emptyL] @ List.map mbindL mbinds)
|
|
| TMDefLet(bind,m) -> letL bind emptyL
|
|
| TMDefDo(e,m) -> ExprL e
|
|
| TMDefs(defs) -> mdefsL defs;
|
|
| TMAbstract(mexpr) -> mexprL mexpr
|
|
and mbindL (TMBind(mspec, rhs)) =
|
|
(wordL (if mspec.IsNamespace then "namespace" else "module") $$ (wordL (demangled_name_of_modul mspec) |> stampL mspec.Stamp)) @@-- mdefL rhs
|
|
|
|
and EntityTypeL (mtyp:ModuleOrNamespaceType) =
|
|
aboveListL [namemapL TypeOfvalL mtyp.AllValuesAndMembers;
|
|
namemapL tyconL mtyp.AllEntities;]
|
|
|
|
and EntityL (ms:ModuleOrNamespace) =
|
|
let header = wordL "module" $$ (wordL (demangled_name_of_modul ms) |> stampL ms.Stamp) $$ wordL ":"
|
|
let footer = wordL "end"
|
|
let body = EntityTypeL ms.ModuleOrNamespaceType
|
|
(header @@-- body) @@ footer
|
|
|
|
and ccuL (ccu:ccu) = EntityL ccu.Contents
|
|
|
|
and DecisionTreeL x =
|
|
match x with
|
|
| TDBind (bind,body) -> let bind = wordL "let" $$ BindingL bind $$ wordL "in" in (bind @@ DecisionTreeL body)
|
|
| TDSuccess (args,n) -> wordL "Success" $$ leftL "T" $$ intL n $$ tupleL (args |> FlatList.to_list |> List.map ExprL)
|
|
| TDSwitch (test,dcases,dflt,r) -> (wordL "Switch" --- ExprL test) @@--
|
|
(aboveListL (List.map dcaseL dcases) @@
|
|
match dflt with
|
|
None -> emptyL
|
|
| Some dtree -> wordL "dflt:" --- DecisionTreeL dtree)
|
|
|
|
and dcaseL (TCase (test,dtree)) = (dtestL test $$ wordL "//") --- DecisionTreeL dtree
|
|
|
|
and dtestL x =
|
|
match x with
|
|
| (TTest_unionconstr (c,tinst)) -> wordL "is" $$ UnionCaseRefL c $$ instL typeL tinst
|
|
| (TTest_array_length (n,ty)) -> wordL "length" $$ intL n $$ typeL ty
|
|
| (TTest_const c ) -> wordL "is" $$ NicePrint.constL c
|
|
| (TTest_isnull ) -> wordL "isnull"
|
|
| (TTest_isinst (_,typ) ) -> wordL "isinst" $$ typeL typ
|
|
| (TTest_query (exp,_,_,idx,_)) -> wordL "query" $$ ExprL exp
|
|
|
|
and targetL i (TTarget (argvs,body,_)) = leftL "T" $$ intL i $$ tupleL (FlatValsL argvs) $$ rightL ":" --- ExprL body
|
|
and FlatValsL vs = vs |> FlatList.to_list |> List.map valL
|
|
|
|
and tmethodL (TObjExprMethod(TSlotSig(nm,_,_,_,_,_),tps,vs,e,m)) =
|
|
(wordL "TObjExprMethod" --- (wordL nm) $$ wordL "=") --
|
|
(wordL "METH-LAM" --- angleBracketListL (List.map TyparL tps) $$ rightL ".") ---
|
|
(wordL "meth-lam" --- tupleL (List.map (List.map vspecAtBindL >> tupleL) vs) $$ rightL ".") ---
|
|
(atomL e)
|
|
and overrideL tmeth = wordL "with" $$ tmethodL tmeth
|
|
and iimplL (typ,tmeths) = wordL "impl" $$ aboveListL (typeL typ :: List.map tmethodL tmeths)
|
|
|
|
let showType x = Layout.showL (typeL x)
|
|
let showExpr x = Layout.showL (ExprL x)
|
|
|
|
end
|
|
|
|
|
|
let ValRefL x = DebugPrint.ValRefL x
|
|
let UnionCaseRefL x = DebugPrint.UnionCaseRefL x
|
|
let intL x = DebugPrint.intL x
|
|
let valL x = DebugPrint.valL x
|
|
let TyparL x = DebugPrint.TyparL x
|
|
let TyparDeclL x = DebugPrint.TyparDeclL x
|
|
let TyparsL x = DebugPrint.typarDeclsL x
|
|
let typeL x = DebugPrint.typeL x
|
|
let SlotSigL x = DebugPrint.SlotSigL x
|
|
let EntityTypeL x = DebugPrint.EntityTypeL x
|
|
let EntityL x = DebugPrint.EntityL x
|
|
let TypeOfvalL x = DebugPrint.TypeOfvalL x
|
|
let MemberL x = DebugPrint.MemberL x
|
|
let BindingL x = DebugPrint. BindingL x
|
|
let ExprL x = DebugPrint.ExprL x
|
|
let DecisionTreeL x = DebugPrint.DecisionTreeL x
|
|
let tyconL x = DebugPrint.tyconL x
|
|
let ImplFileL x = DebugPrint.ImplFileL x
|
|
let AssemblyL x = DebugPrint.AssemblyL x
|
|
let vspecAtBindL x = DebugPrint.vspecAtBindL x
|
|
let recdFieldRefL x = DebugPrint.recdFieldRefL x
|
|
let traitL x = DebugPrint.auxTraitL SimplifyTypes.typeSimplificationInfo0 x
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
let mtyp_of_mexpr (TMTyped(mtyp,_,_)) = mtyp
|
|
|
|
let wrap_modul_as_mtyp_in_namespace x = NewModuleOrNamespaceType Namespace [ x ] []
|
|
|
|
let wrap_mtyp_as_mspec id cpath mtyp =
|
|
NewModuleOrNamespace (Some cpath) taccessPublic id emptyXmlDoc [] (notlazy mtyp)
|
|
let wrap_modul_in_namespace id (mspec:ModuleOrNamespace) =
|
|
wrap_mtyp_as_mspec id (parent_cpath mspec.CompilationPath) (wrap_modul_as_mtyp_in_namespace mspec)
|
|
let wrap_mbind_in_namespace (id :ident) (TMBind(mspec,defs)) =
|
|
let cpath = mspec.CompilationPath
|
|
let parentModuleSpec = NewModuleOrNamespace (Some (parent_cpath cpath)) taccessPublic id emptyXmlDoc [] (notlazy (empty_mtype Namespace))
|
|
TMBind(parentModuleSpec, TMDefRec ([],FlatList.empty,[TMBind(mspec,defs)],id.idRange))
|
|
|
|
let SigTypeOfImplFile (TImplFile(_,_,mexpr)) = mtyp_of_mexpr mexpr
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Data structures representing what gets hidden and what gets remapped (i.e. renamed or alpha-converted)
|
|
// when a module signature is applied to a module.
|
|
//--------------------------------------------------------------------------
|
|
|
|
type SignatureRepackageInfo =
|
|
{ mrpiVals : (ValRef * ValRef) list;
|
|
mrpiTycons: (TyconRef * TyconRef) list }
|
|
|
|
type SignatureHidingInfo =
|
|
{ mhiTycons : Zset.t<Tycon>;
|
|
mhiTyconReprs : Zset.t<Tycon>;
|
|
mhiVals : Zset.t<Val>;
|
|
mhiRecdFields : Zset.t<RecdFieldRef>;
|
|
mhiUnionCases : Zset.t<UnionCaseRef> }
|
|
|
|
let union_mhi x y =
|
|
{ mhiTycons = Zset.union x.mhiTycons y.mhiTycons;
|
|
mhiTyconReprs = Zset.union x.mhiTyconReprs y.mhiTyconReprs;
|
|
mhiVals = Zset.union x.mhiVals y.mhiVals;
|
|
mhiRecdFields = Zset.union x.mhiRecdFields y.mhiRecdFields;
|
|
mhiUnionCases = Zset.union x.mhiUnionCases y.mhiUnionCases; }
|
|
|
|
let empty_mhi =
|
|
{ mhiTycons = Zset.empty tycon_spec_order;
|
|
mhiTyconReprs = Zset.empty tycon_spec_order;
|
|
mhiVals = Zset.empty val_spec_order;
|
|
mhiRecdFields = Zset.empty rfref_order;
|
|
mhiUnionCases = Zset.empty ucref_order }
|
|
|
|
let empty_mrpi = { mrpiVals = []; mrpiTycons= [] }
|
|
|
|
let mk_repackage_remapping mrpi =
|
|
{ vspec_remap = vspec_map_of_list (List.map (map1'2 deref_val) mrpi.mrpiVals );
|
|
tpinst = empty_tpinst;
|
|
tcref_remap = tcref_map_of_list mrpi.mrpiTycons }
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Compute instances of the above for mty -> mty
|
|
//--------------------------------------------------------------------------
|
|
|
|
let acc_entity_remap (msigty:ModuleOrNamespaceType) (tycon:Tycon) (mrpi,mhi) =
|
|
let sigtyconOpt = (NameMap.tryfind tycon.MangledName msigty.AllEntities)
|
|
match sigtyconOpt with
|
|
| None ->
|
|
// The type constructor is not present in the signature. Hence it is hidden.
|
|
let mhi = { mhi with mhiTycons = Zset.add tycon mhi.mhiTycons }
|
|
(mrpi,mhi)
|
|
| Some sigtycon ->
|
|
// The type constructor is in the signature. Hence record the repackage entry
|
|
let sigtcref = mk_local_tcref sigtycon
|
|
let tcref = mk_local_tcref tycon
|
|
let mrpi = { mrpi with mrpiTycons = ((tcref, sigtcref) :: mrpi.mrpiTycons) }
|
|
(* OK, now look for hidden things *)
|
|
let mhi =
|
|
if isSome tycon.TypeReprInfo && isNone sigtycon.TypeReprInfo then
|
|
(* The type representation is absent in the signature, hence it is hidden *)
|
|
{ mhi with mhiTyconReprs = Zset.add tycon mhi.mhiTyconReprs }
|
|
else
|
|
(* The type representation is present in the signature. *)
|
|
(* Find the fields that have been hidden or which were non-public anyway. *)
|
|
mhi
|
|
|> Array.fold_right (fun (rfield:RecdField) mhi ->
|
|
match sigtycon.GetFieldByName(rfield.Name) with
|
|
| Some _ ->
|
|
(* The field is in the signature. Hence it is not hidden. *)
|
|
mhi
|
|
| _ ->
|
|
(* The field is not in the signature. Hence it is regarded as hidden. *)
|
|
let rfref = rfref_of_rfield tcref rfield
|
|
{ mhi with mhiRecdFields = Zset.add rfref mhi.mhiRecdFields })
|
|
tycon.AllFieldsArray
|
|
|> List.foldBack (fun (ucase:UnionCase) mhi ->
|
|
match sigtycon.GetUnionCaseByName ucase.DisplayName with
|
|
| Some _ ->
|
|
(* The constructor is in the signature. Hence it is not hidden. *)
|
|
mhi
|
|
| _ ->
|
|
(* The constructor is not in the signature. Hence it is regarded as hidden. *)
|
|
let ucref = ucref_of_ucase tcref ucase
|
|
{ mhi with mhiUnionCases = Zset.add ucref mhi.mhiUnionCases })
|
|
(tycon.UnionCasesAsList)
|
|
(mrpi,mhi)
|
|
|
|
let acc_sub_entity_remap (msigty:ModuleOrNamespaceType) (tycon:Tycon) (mrpi,mhi) =
|
|
let sigtyconOpt = (NameMap.tryfind tycon.MangledName msigty.AllEntities)
|
|
match sigtyconOpt with
|
|
| None ->
|
|
// The type constructor is not present in the signature. Hence it is hidden.
|
|
let mhi = { mhi with mhiTycons = Zset.add tycon mhi.mhiTycons }
|
|
(mrpi,mhi)
|
|
| Some sigtycon ->
|
|
// The type constructor is in the signature. Hence record the repackage entry
|
|
let sigtcref = mk_local_tcref sigtycon
|
|
let tcref = mk_local_tcref tycon
|
|
let mrpi = { mrpi with mrpiTycons = ((tcref, sigtcref) :: mrpi.mrpiTycons) }
|
|
(mrpi,mhi)
|
|
|
|
let acc_val_remap (msigty:ModuleOrNamespaceType) (vspec:Val) (mrpi,mhi) =
|
|
let sigValOpt = (NameMap.tryfind vspec.MangledName msigty.AllValuesAndMembers)
|
|
let vref = mk_local_vref vspec
|
|
match sigValOpt with
|
|
| None ->
|
|
if verbose then dprintf "acc_val_remap, hide = %s#%d\n" vspec.MangledName vspec.Stamp; (* showL(valL vspec)); *)
|
|
let mhi = { mhi with mhiVals = Zset.add vspec mhi.mhiVals }
|
|
(mrpi,mhi)
|
|
| Some sigVal ->
|
|
(* The value is in the signature. Add the repackage entry. *)
|
|
if !verboseStamps then dprintf "acc_val_remap, remap value %s#%d --> %s#%d\n" vspec.MangledName vspec.Stamp sigVal.MangledName sigVal.Stamp;
|
|
|
|
let mrpi = { mrpi with mrpiVals = (vref,mk_local_vref sigVal) :: mrpi.mrpiVals }
|
|
(mrpi,mhi)
|
|
|
|
let get_submodsigty nm (msigty:ModuleOrNamespaceType) =
|
|
match NameMap.tryfind nm msigty.AllEntities with
|
|
| None -> empty_mtype FSharpModule
|
|
| Some sigsubmodul -> sigsubmodul.ModuleOrNamespaceType
|
|
|
|
let rec acc_mty_remap (mty:ModuleOrNamespaceType) (msigty:ModuleOrNamespaceType) acc =
|
|
let acc = List.foldBack (fun (submodul:ModuleOrNamespace) acc -> acc_mty_remap submodul.ModuleOrNamespaceType (get_submodsigty submodul.MangledName msigty) acc) mty.ModuleAndNamespaceDefinitions acc
|
|
let acc = NameMap.foldRange (acc_entity_remap msigty) mty.AllEntities acc
|
|
let acc = NameMap.foldRange (acc_val_remap msigty) mty.AllValuesAndMembers acc
|
|
acc
|
|
|
|
let mk_mtyp_to_mtyp_remapping mty msigty =
|
|
(* dprintf "mk_mtyp_to_mtyp_remapping,\nmty = %s\nmmsigty=%s\n" (showL(EntityTypeL mty)) (showL(EntityTypeL msigty)); *)
|
|
acc_mty_remap mty msigty (empty_mrpi, empty_mhi)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Compute instances of the above for mexpr -> mty
|
|
//--------------------------------------------------------------------------
|
|
|
|
/// At TMDefRec nodes abstract (virtual) vslots are effectively binders, even
|
|
/// though they are tucked away inside the tycon. This helper function extracts the
|
|
/// virtual slots to aid with finding this babies.
|
|
let vslot_vals_of_tycons (tycons:Tycon list) =
|
|
tycons
|
|
|> List.collect (fun tycon -> if tycon.IsFSharpObjectModelTycon then tycon.FSharpObjectModelTypeInfo.fsobjmodel_vslots else [])
|
|
|> List.map deref_val
|
|
|
|
let rec acc_mdef_remap msigty x acc =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
(* Abstract (virtual) vslots in the tycons at TMDefRec nodes are binders. They also need to be added to the remapping. *)
|
|
let vslotvs = vslot_vals_of_tycons tycons
|
|
List.foldBack (acc_entity_remap msigty) tycons
|
|
(List.foldBack (acc_val_remap msigty) vslotvs
|
|
(FlatList.foldBack (var_of_bind >> acc_val_remap msigty) binds
|
|
(List.foldBack (acc_mbind_remap msigty) mbinds acc)))
|
|
| TMDefLet(bind,m) -> acc_val_remap msigty bind.Var acc
|
|
| TMDefDo(e,m) -> acc
|
|
| TMDefs(defs) -> acc_mdefs_remap msigty defs acc
|
|
| TMAbstract(mexpr) -> acc_mty_remap (mtyp_of_mexpr mexpr) msigty acc
|
|
and acc_mbind_remap msigty (TMBind(mspec, def)) acc =
|
|
acc_sub_entity_remap msigty mspec (acc_mdef_remap (get_submodsigty mspec.MangledName msigty) def acc)
|
|
|
|
and acc_mdefs_remap msigty mdefs acc = List.foldBack (acc_mdef_remap msigty) mdefs acc
|
|
|
|
let mk_mdef_to_mtyp_remapping mdef msigty =
|
|
if verbose then dprintf "mk_mdef_to_mtyp_remapping,\nmdefs = %s\nmsigty=%s\n" (showL(DebugPrint.mdefL mdef)) (showL(EntityTypeL msigty));
|
|
acc_mdef_remap msigty mdef (empty_mrpi, empty_mhi)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Compute instances of the above for the assembly boundary
|
|
//--------------------------------------------------------------------------
|
|
|
|
let acc_tycon_assembly_boundary_mhi (tycon:Tycon) mhi =
|
|
if not (can_access_from_everywhere tycon.Accessibility) then
|
|
// The type constructor is not public, hence hidden at the assembly boundary.
|
|
{ mhi with mhiTycons = Zset.add tycon mhi.mhiTycons }
|
|
elif not (can_access_from_everywhere tycon.TypeReprAccessibility) then
|
|
{ mhi with mhiTyconReprs = Zset.add tycon mhi.mhiTyconReprs }
|
|
else
|
|
mhi
|
|
|> Array.fold_right
|
|
(fun (rfield:RecdField) mhi ->
|
|
if not (can_access_from_everywhere rfield.Accessibility) then
|
|
let tcref = mk_local_tcref tycon
|
|
let rfref = rfref_of_rfield tcref rfield
|
|
{ mhi with mhiRecdFields = Zset.add rfref mhi.mhiRecdFields }
|
|
else mhi)
|
|
tycon.AllFieldsArray
|
|
|> List.foldBack
|
|
(fun (ucase:UnionCase) mhi ->
|
|
if not (can_access_from_everywhere ucase.Accessibility) then
|
|
let tcref = mk_local_tcref tycon
|
|
let ucref = ucref_of_ucase tcref ucase
|
|
{ mhi with mhiUnionCases = Zset.add ucref mhi.mhiUnionCases }
|
|
else mhi)
|
|
(tycon.UnionCasesAsList)
|
|
|
|
let acc_val_assembly_boundary_mhi (vspec:Val) mhi =
|
|
if not (can_access_from_everywhere vspec.Accessibility) then
|
|
// The value is not public, hence hidden at the assembly boundary.
|
|
{ mhi with mhiVals = Zset.add vspec mhi.mhiVals }
|
|
else
|
|
mhi
|
|
|
|
let rec acc_mty_assembly_boundary_mhi mty acc =
|
|
let acc = List.foldBack (fun (submodul:ModuleOrNamespace) acc -> acc_mty_assembly_boundary_mhi submodul.ModuleOrNamespaceType acc) mty.ModuleAndNamespaceDefinitions acc
|
|
let acc = NameMap.foldRange acc_tycon_assembly_boundary_mhi mty.AllEntities acc
|
|
let acc = NameMap.foldRange acc_val_assembly_boundary_mhi mty.AllValuesAndMembers acc
|
|
acc
|
|
|
|
let mk_assembly_boundary_mhi mty =
|
|
(* dprintf "mk_mtyp_to_mtyp_remapping,\nmty = %s\nmmsigty=%s\n" (showL(EntityTypeL mty)) (showL(EntityTypeL msigty)); *)
|
|
acc_mty_assembly_boundary_mhi mty empty_mhi
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Compute instances of the above for mexpr -> mty
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
let IsHidden setF accessF remapF debugF =
|
|
let rec check mrmi x =
|
|
if verbose then dprintf "IsHidden %s ??\n" (showL (debugF x));
|
|
(* Internal/private? *)
|
|
not (can_access_from_everywhere (accessF x)) ||
|
|
(match mrmi with
|
|
| [] -> false (* Ah! we escaped to freedom! *)
|
|
| (rpi,mhi) :: rest ->
|
|
(* Explicitly hidden? *)
|
|
Zset.mem x (setF mhi) or
|
|
(* Recurse... *)
|
|
check rest (remapF rpi x))
|
|
fun mrmi x ->
|
|
let res = check mrmi x
|
|
if verbose then dprintf "IsHidden, #mrmi = %d, %s = %b\n" mrmi.Length (showL (debugF x)) res;
|
|
res
|
|
|
|
let IsHiddenTycon mrmi x = IsHidden (fun mhi -> mhi.mhiTycons) (fun tc -> tc.Accessibility) (fun rpi x -> deref_tycon (remap_tcref rpi.tcref_remap (mk_local_tcref x))) tyconL mrmi x
|
|
let IsHiddenTyconRepr mrmi x = IsHidden (fun mhi -> mhi.mhiTyconReprs) (fun v -> v.TypeReprAccessibility) (fun rpi x -> deref_tycon (remap_tcref rpi.tcref_remap (mk_local_tcref x))) tyconL mrmi x
|
|
let IsHiddenVal mrmi x = IsHidden (fun mhi -> mhi.mhiVals) (fun v -> v.Accessibility) (fun rpi x -> deref_val (remap_vref rpi (mk_local_vref x))) valL mrmi x
|
|
let IsHiddenRecdField mrmi x = IsHidden (fun mhi -> mhi.mhiRecdFields) (fun rfref -> rfref.RecdField.Accessibility) (fun rpi x -> remap_rfref rpi.tcref_remap x) recdFieldRefL mrmi x
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Generic operations on module types
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
let fold_vals_and_tycons_of_mtyp ft fv =
|
|
let rec go mty acc =
|
|
let acc = NameMap.foldRange (fun (mspec:ModuleOrNamespace) acc -> go mspec.ModuleOrNamespaceType acc) mty.ModulesAndNamespacesByDemangledName acc
|
|
let acc = NameMap.foldRange ft mty.AllEntities acc
|
|
let acc = NameMap.foldRange fv mty.AllValuesAndMembers acc
|
|
acc
|
|
go
|
|
|
|
let all_vals_of_mtyp m = fold_vals_and_tycons_of_mtyp (fun ft acc -> acc) (fun v acc -> v :: acc) m []
|
|
let all_tycons_of_mtyp m = fold_vals_and_tycons_of_mtyp (fun ft acc -> ft :: acc) (fun v acc -> acc) m []
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Free variables in terms. Are all constructs public accessible?
|
|
//---------------------------------------------------------------------------
|
|
|
|
let is_public_vspec (lv:Val) = (lv.Accessibility = taccessPublic)
|
|
let is_public_ucref (ucr:UnionCaseRef) = (ucr.UnionCase.Accessibility = taccessPublic)
|
|
let is_public_rfref (rfr:RecdFieldRef) = (rfr.RecdField.Accessibility = taccessPublic)
|
|
let is_public_tycon (tcr:Tycon) = (tcr.Accessibility = taccessPublic)
|
|
|
|
let freevars_all_public fvs =
|
|
// Are any non-public items used in the expr (which corresponded to the fvs)?
|
|
// Recall, taccess occurs in:
|
|
// EntityData has entity_tycon_repr_accessibility and entity_accessiblity
|
|
// UnionCase has ucase_access
|
|
// RecdField has rfield_access
|
|
// ValData has val_access
|
|
// The freevars and FreeTyvars collect local constructs.
|
|
// Here, we test that all those constructs are public.
|
|
//
|
|
// CODEREVIEW:
|
|
// What about non-local vals. This fix assumes non-local vals must be public. OK?
|
|
Zset.for_all is_public_vspec fvs.FreeLocals &&
|
|
Zset.for_all is_public_ucref fvs.FreeUnionCases &&
|
|
Zset.for_all is_public_rfref fvs.FreeRecdFields &&
|
|
Zset.for_all is_public_tycon fvs.FreeTyvars.FreeTycons
|
|
|
|
let free_tyvars_all_public tyvars =
|
|
Zset.for_all is_public_tycon tyvars.FreeTycons
|
|
|
|
(*---------------------------------------------------------------------------
|
|
!* Free variables in terms. All binders are distinct.
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let empty_freevars =
|
|
{ UsesMethodLocalConstructs=false;
|
|
UsesUnboundRethrow=false;
|
|
FreeLocalTyconReprs=empty_free_loctycons;
|
|
FreeLocals=empty_free_locvals;
|
|
FreeTyvars=empty_free_tyvars;
|
|
FreeRecdFields = empty_free_rfields;
|
|
FreeUnionCases = empty_free_ucases}
|
|
|
|
let union_freevars fvs1 fvs2 =
|
|
if fvs1 == empty_freevars then fvs2 else
|
|
if fvs2 == empty_freevars then fvs1 else
|
|
{ FreeLocals = union_free_locvals fvs1.FreeLocals fvs2.FreeLocals;
|
|
FreeTyvars = union_free_tyvars fvs1.FreeTyvars fvs2.FreeTyvars;
|
|
UsesMethodLocalConstructs = fvs1.UsesMethodLocalConstructs || fvs2.UsesMethodLocalConstructs;
|
|
UsesUnboundRethrow = fvs1.UsesUnboundRethrow || fvs2.UsesUnboundRethrow;
|
|
FreeLocalTyconReprs = union_free_loctycons fvs1.FreeLocalTyconReprs fvs2.FreeLocalTyconReprs;
|
|
FreeRecdFields = union_free_rfields fvs1.FreeRecdFields fvs2.FreeRecdFields;
|
|
FreeUnionCases = union_free_ucases fvs1.FreeUnionCases fvs2.FreeUnionCases; }
|
|
|
|
let inline tyvars (opts:FreeVarOptions) f v acc =
|
|
if not opts.collectInTypes then acc else
|
|
let ftyvs = acc.FreeTyvars
|
|
let ftyvs' = f v ftyvs
|
|
if ftyvs == ftyvs' then acc else
|
|
{ acc with FreeTyvars = ftyvs' }
|
|
|
|
#if FREEVARS_IN_TYPES_ANALYSIS
|
|
type CheckCachability<'key,'acc>(name,f: FreeVarOptions -> 'key -> 'acc -> bool * 'acc) =
|
|
let dict = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference)
|
|
let idem = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference)
|
|
let closed = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference)
|
|
let mutable saved = 0
|
|
do System.AppDomain.CurrentDomain.ProcessExit.Add(fun _ ->
|
|
let hist = dict |> Seq.group_by (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sort_by (fun (n,_) -> n)
|
|
let total = hist |> Seq.sum_by (fun (nhits,nels) -> nels)
|
|
let totalHits = hist |> Seq.sum_by (fun (nhits,nels) -> nhits * nels)
|
|
printfn "*** %s saved %d hits (%g%%) ***" name saved (float saved / float (saved + totalHits) * 100.0)
|
|
printfn "*** %s had %d hits total, possible saving %d ***" name totalHits (totalHits - total)
|
|
//for (nhits,nels) in hist do
|
|
// printfn "%s, %g%% els for %g%% hits had %d hits" name (float nels / float total * 100.0) (float (nels * nhits) / float totalHits * 100.0) nhits
|
|
|
|
let hist = idem |> Seq.group_by (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sort_by (fun (n,_) -> n)
|
|
let total = hist |> Seq.sum_by (fun (nhits,nels) -> nels)
|
|
let totalHits = hist |> Seq.sum_by (fun (nhits,nels) -> nhits * nels)
|
|
printfn "*** %s had %d idempotent hits total, possible saving %d ***" name totalHits (totalHits - total)
|
|
//for (nhits,nels) in hist do
|
|
// printfn "%s, %g%% els for %g%% hits had %d idempotent hits" name (float nels / float total * 100.0) (float (nels * nhits) / float totalHits * 100.0) nhits
|
|
|
|
let hist = closed |> Seq.group_by (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sort_by (fun (n,_) -> n)
|
|
let total = hist |> Seq.sum_by (fun (nhits,nels) -> nels)
|
|
let totalHits = hist |> Seq.sum_by (fun (nhits,nels) -> nhits * nels)
|
|
printfn "*** %s had %d closed hits total, possible saving %d ***" name totalHits (totalHits - total)
|
|
)
|
|
|
|
member cache.Apply(opts,key,acc) =
|
|
if not opts.collectInTypes then
|
|
saved <- saved + 1
|
|
acc
|
|
else
|
|
let cls,res = f opts key acc
|
|
if opts.canCache then
|
|
if dict.ContainsKey key then
|
|
dict.[key] <- dict.[key] + 1
|
|
else
|
|
dict.[key] <- 1
|
|
if res === acc then
|
|
if idem.ContainsKey key then
|
|
idem.[key] <- idem.[key] + 1
|
|
else
|
|
idem.[key] <- 1
|
|
if cls then
|
|
if closed.ContainsKey key then
|
|
closed.[key] <- closed.[key] + 1
|
|
else
|
|
closed.[key] <- 1
|
|
res
|
|
|
|
|
|
//member cache.OnExit() =
|
|
|
|
let acc_freevars_in_type_cache = CheckCachability("acc_freevars_in_type", (fun opts ty fvs -> (free_in_type opts ty === empty_free_tyvars), tyvars opts (acc_free_in_type opts) ty fvs))
|
|
let acc_freevars_in_val_cache = CheckCachability("acc_freevars_in_val", (fun opts v fvs -> (free_in_val opts v === empty_free_tyvars), tyvars opts (acc_free_in_val opts) v fvs))
|
|
let acc_freevars_in_types_cache = CheckCachability("acc_freevars_in_types", (fun opts tys fvs -> (free_in_types opts tys === empty_free_tyvars), tyvars opts (acc_free_in_types opts) tys fvs))
|
|
let acc_freevars_in_tycon_cache = CheckCachability("acc_freevars_in_tycon", (fun opts tys fvs -> false,tyvars opts (acc_free_tycon opts) tys fvs))
|
|
|
|
let acc_freevars_in_type opts ty fvs = acc_freevars_in_type_cache.Apply(opts,ty,fvs)
|
|
let acc_freevars_in_types opts tys fvs =
|
|
if isNil tys then fvs else acc_freevars_in_types_cache.Apply(opts,tys,fvs)
|
|
let acc_freevars_in_tycon opts (tcr:TyconRef) acc =
|
|
match tcr.IsLocalRef with
|
|
| true -> acc_freevars_in_tycon_cache.Apply(opts,tcr,acc)
|
|
| _ -> acc
|
|
let acc_freevars_in_val opts v fvs = acc_freevars_in_val_cache.Apply(opts,v,fvs)
|
|
#else
|
|
|
|
let acc_freevars_in_type opts ty acc = tyvars opts (acc_free_in_type opts) ty acc
|
|
let acc_freevars_in_types opts tys acc = if isNil tys then acc else tyvars opts (acc_free_in_types opts) tys acc
|
|
let acc_freevars_in_tycon opts tcref acc = tyvars opts (acc_free_tycon opts) tcref acc
|
|
let acc_freevars_in_val opts v acc = tyvars opts (acc_free_in_val opts) v acc
|
|
#endif
|
|
|
|
let acc_freevars_in_trait_sln opts tys acc = tyvars opts (acc_free_in_trait_sln opts) tys acc
|
|
|
|
let bound_locval opts v fvs =
|
|
if not opts.includeLocals then fvs else
|
|
let fvs = acc_freevars_in_val opts v fvs
|
|
if not (Zset.mem v fvs.FreeLocals) then fvs
|
|
else {fvs with FreeLocals= Zset.remove v fvs.FreeLocals}
|
|
|
|
let bound_protect fvs =
|
|
if fvs.UsesMethodLocalConstructs then {fvs with UsesMethodLocalConstructs = false} else fvs
|
|
|
|
let acc_uses_function_local_constructs flg fvs =
|
|
if flg && not fvs.UsesMethodLocalConstructs then {fvs with UsesMethodLocalConstructs = true}
|
|
else fvs
|
|
|
|
let bound_rethrow fvs =
|
|
if fvs.UsesUnboundRethrow then {fvs with UsesUnboundRethrow = false} else fvs
|
|
|
|
let acc_uses_rethrow flg fvs =
|
|
if flg && not fvs.UsesUnboundRethrow then {fvs with UsesUnboundRethrow = true}
|
|
else fvs
|
|
|
|
let bound_locvals opts vs fvs = List.foldBack (bound_locval opts) vs fvs
|
|
|
|
let bind_lhs opts (bind:Binding) fvs = bound_locval opts bind.Var fvs
|
|
|
|
let FreeVarsCacheCompute opts cache f = if opts.canCache then cached cache f else f()
|
|
|
|
let rec acc_rhs opts (TBind(_,repr,_)) acc = acc_free_in_expr opts repr acc
|
|
|
|
and acc_free_in_switch_cases opts csl dflt (acc:FreeVars) =
|
|
Option.fold_right (acc_free_in_dtree opts) dflt (List.foldBack (acc_free_in_switch_case opts) csl acc)
|
|
|
|
and acc_free_in_switch_case opts (TCase(discrim,dtree)) acc =
|
|
acc_free_in_dtree opts dtree (acc_free_in_discrim opts discrim acc)
|
|
|
|
and acc_free_in_discrim (opts:FreeVarOptions) discrim acc =
|
|
match discrim with
|
|
| TTest_unionconstr(ucref,tinst) -> acc_free_ucref opts ucref (acc_freevars_in_types opts tinst acc)
|
|
| TTest_array_length(_,ty) -> acc_freevars_in_type opts ty acc
|
|
| TTest_const _
|
|
| TTest_isnull -> acc
|
|
| TTest_isinst (srcty,tgty) -> acc_freevars_in_type opts srcty (acc_freevars_in_type opts tgty acc)
|
|
| TTest_query (exp, tys, vref, idx, apinfo) -> acc_free_in_expr opts exp (acc_freevars_in_types opts tys (Option.fold_right (acc_free_vref opts) vref acc))
|
|
|
|
and acc_free_in_dtree opts x (acc : FreeVars) =
|
|
match x with
|
|
| TDSwitch(e1,csl,dflt,_) -> acc_free_in_expr opts e1 (acc_free_in_switch_cases opts csl dflt acc)
|
|
| TDSuccess (es,_) -> acc_free_in_FlatExprs opts es acc
|
|
| TDBind (bind,body) -> union_freevars (bind_lhs opts bind (acc_rhs opts bind (free_in_dtree opts body))) acc
|
|
|
|
and acc_free_locval opts v fvs =
|
|
if not opts.includeLocals then fvs else
|
|
if Zset.mem v fvs.FreeLocals then fvs
|
|
else
|
|
let fvs = acc_freevars_in_val opts v fvs
|
|
{fvs with FreeLocals=Zset.add v fvs.FreeLocals}
|
|
|
|
and acc_loctycon_repr opts b fvs =
|
|
if not opts.includeLocalTyconReprs then fvs else
|
|
if Zset.mem b fvs.FreeLocalTyconReprs then fvs
|
|
else { fvs with FreeLocalTyconReprs = Zset.add b fvs.FreeLocalTyconReprs }
|
|
|
|
and acc_used_tycon_repr opts (tc:Tycon) fvs =
|
|
if isSome tc.TypeReprInfo
|
|
then acc_loctycon_repr opts tc fvs
|
|
else fvs
|
|
|
|
and acc_free_ucref opts cr fvs =
|
|
if not opts.includeUnionCases then fvs else
|
|
if Zset.mem cr fvs.FreeUnionCases then fvs
|
|
else
|
|
let fvs = fvs |> acc_used_tycon_repr opts cr.Tycon
|
|
let fvs = fvs |> acc_freevars_in_tycon opts cr.TyconRef
|
|
{ fvs with FreeUnionCases = Zset.add cr fvs.FreeUnionCases }
|
|
|
|
and acc_free_rfref opts fr fvs =
|
|
if not opts.includeRecdFields then fvs else
|
|
if Zset.mem fr fvs.FreeRecdFields then fvs
|
|
else
|
|
let fvs = fvs |> acc_used_tycon_repr opts fr.Tycon
|
|
let fvs = fvs |> acc_freevars_in_tycon opts fr.TyconRef
|
|
{ fvs with FreeRecdFields = Zset.add fr fvs.FreeRecdFields }
|
|
|
|
and acc_free_ecref exnc fvs = fvs (* Note: this exnc (TyconRef) should be collected the surround types, e.g. tinst of TExpr_op *)
|
|
and acc_free_vref opts (vref:ValRef) fvs =
|
|
match vref.IsLocalRef with
|
|
| true -> acc_free_locval opts vref.PrivateTarget fvs
|
|
// non-local values do not contain free variables
|
|
| _ -> fvs
|
|
|
|
and acc_free_in_method opts (TObjExprMethod(slotsig,tps,tmvs,e,m)) acc =
|
|
acc_free_in_slotsig opts slotsig
|
|
(union_freevars (tyvars opts (bound_typars opts) tps (List.foldBack (bound_locvals opts) tmvs (free_in_expr opts e))) acc)
|
|
|
|
and acc_free_in_methods opts methods acc =
|
|
List.foldBack (acc_free_in_method opts) methods acc
|
|
|
|
and acc_free_in_iimpl opts (ty,overrides) acc =
|
|
acc_freevars_in_type opts ty (acc_free_in_methods opts overrides acc)
|
|
|
|
and acc_free_in_expr (opts:FreeVarOptions) x acc =
|
|
match x with
|
|
| TExpr_let(_) -> acc_free_in_expr_linear opts x acc (fun e -> e)
|
|
| _ -> acc_free_in_expr_nonlinear opts x acc
|
|
|
|
and acc_free_in_expr_linear (opts:FreeVarOptions) x acc contf =
|
|
(* for nested let-bindings, we need to continue after the whole let-binding is processed *)
|
|
match x with
|
|
| TExpr_let (bind,e,_,cache) ->
|
|
let contf = contf << (fun free ->
|
|
union_freevars (FreeVarsCacheCompute opts cache (fun () -> bind_lhs opts bind (acc_rhs opts bind free))) acc )
|
|
acc_free_in_expr_linear opts e empty_freevars contf
|
|
| _ ->
|
|
// No longer linear expr
|
|
acc_free_in_expr opts x acc |> contf
|
|
|
|
and acc_free_in_expr_nonlinear opts x acc =
|
|
match x with
|
|
(* BINDING CONSTRUCTS *)
|
|
| TExpr_lambda (_,basev,vs,b,_,rty,cache) ->
|
|
union_freevars (SkipCacheCompute cache (fun () -> Option.fold_right (bound_locval opts) basev (bound_locvals opts vs (acc_freevars_in_type opts rty (free_in_expr opts b))))) acc
|
|
| TExpr_tlambda (_,vs,b,_,rty, cache) ->
|
|
union_freevars (SkipCacheCompute cache (fun () -> tyvars opts (bound_typars opts) vs (acc_freevars_in_type opts rty (free_in_expr opts b)))) acc
|
|
| TExpr_tchoose (vs,b,_) ->
|
|
union_freevars (tyvars opts (bound_typars opts) vs (free_in_expr opts b)) acc
|
|
| TExpr_letrec (binds,e,_,cache) ->
|
|
union_freevars (FreeVarsCacheCompute opts cache (fun () -> FlatList.foldBack (bind_lhs opts) binds (FlatList.foldBack (acc_rhs opts) binds (free_in_expr opts e)))) acc
|
|
| TExpr_let (bind,e,_,cache) ->
|
|
failwith "unreachable - linear expr"
|
|
| TExpr_obj (_,typ,basev,basecall,overrides,iimpls,m,cache) ->
|
|
union_freevars (SkipCacheCompute cache (fun () ->
|
|
bound_protect
|
|
(Option.fold_right (bound_locval opts) basev
|
|
(acc_freevars_in_type opts typ
|
|
(acc_free_in_expr opts basecall
|
|
(acc_free_in_methods opts overrides
|
|
(List.foldBack (acc_free_in_iimpl opts) iimpls empty_freevars))))))) acc
|
|
(* NON-BINDING CONSTRUCTS *)
|
|
| TExpr_const _ -> acc
|
|
| TExpr_val (lvr,flags,_) ->
|
|
acc_uses_function_local_constructs (flags <> NormalValUse) (acc_free_vref opts lvr acc)
|
|
| TExpr_quote (ast,{contents=Some(argTypes,argExprs,data)},_,ty) ->
|
|
acc_free_in_expr opts ast
|
|
(acc_free_in_exprs opts argExprs
|
|
(acc_freevars_in_types opts argTypes
|
|
(acc_freevars_in_type opts ty acc)))
|
|
| TExpr_quote (ast,{contents=None},_,ty) ->
|
|
acc_free_in_expr opts ast (acc_freevars_in_type opts ty acc)
|
|
| TExpr_app(f0,f0ty,tyargs,args,_) ->
|
|
acc_freevars_in_type opts f0ty
|
|
(acc_free_in_expr opts f0
|
|
(acc_freevars_in_types opts tyargs
|
|
(acc_free_in_exprs opts args acc)))
|
|
| TExpr_link(eref) -> acc_free_in_expr opts !eref acc
|
|
| TExpr_seq (e1,e2,_,_,_) ->
|
|
let acc = acc_free_in_expr opts e1 acc
|
|
// tail-call - this is required because we should be able to handle (((e1; e2); e3); e4; .... ))
|
|
acc_free_in_expr opts e2 acc
|
|
|
|
| TExpr_static_optimization (_,e2,e3,m) -> acc_free_in_expr opts e2 (acc_free_in_expr opts e3 acc)
|
|
| TExpr_match (_,_,dtree,targets,_,_,cache) ->
|
|
union_freevars
|
|
(SkipCacheCompute cache (fun () -> acc_free_in_targets opts targets empty_freevars))
|
|
(acc_free_in_dtree opts dtree acc)
|
|
|
|
//| TExpr_op (TOp_try_catch,tinst,[TExpr_lambda(_,_,[_],e1,_,_,_); TExpr_lambda(_,_,[_],e2,_,_,_); TExpr_lambda(_,_,[_],e3,_,_,_)],_) ->
|
|
| TExpr_op (TOp_try_catch _,tinst,[e1;e2;e3],m) ->
|
|
union_freevars
|
|
(acc_freevars_in_types opts tinst
|
|
(acc_free_in_exprs opts [e1;e2] acc))
|
|
(bound_rethrow (acc_free_in_expr opts e3 empty_freevars))
|
|
|
|
| TExpr_op (op,tinst,args,_) ->
|
|
let acc = acc_free_in_op opts op acc
|
|
let acc = acc_freevars_in_types opts tinst acc
|
|
acc_free_in_exprs opts args acc
|
|
|
|
and acc_free_in_op opts op acc =
|
|
match op with
|
|
|
|
// Things containing no references
|
|
| TOp_bytes _
|
|
| TOp_uint16s _
|
|
| TOp_try_catch _
|
|
| TOp_try_finally _
|
|
| TOp_for _
|
|
| TOp_coerce
|
|
| TOp_get_ref_lval
|
|
| TOp_tuple
|
|
| TOp_array
|
|
| TOp_while _
|
|
| TOp_goto _ | TOp_label _ | TOp_return
|
|
| TOp_tuple_field_get _ -> acc
|
|
|
|
| TOp_ucase_tag_get tr -> acc_used_tycon_repr opts (deref_tycon tr) acc
|
|
|
|
// Things containing just a union case reference
|
|
| TOp_ucase_proof cr
|
|
| TOp_ucase cr
|
|
| TOp_ucase_field_get (cr,_)
|
|
| TOp_ucase_field_set (cr,_) -> acc_free_ucref opts cr acc
|
|
|
|
// Things containing just an exception reference
|
|
| TOp_exnconstr ecr
|
|
| TOp_exnconstr_field_get (ecr,_)
|
|
| TOp_exnconstr_field_set (ecr,_) -> acc_free_ecref ecr acc
|
|
|
|
| TOp_rfield_get fr
|
|
| TOp_field_get_addr fr
|
|
| TOp_rfield_set fr -> acc_free_rfref opts fr acc
|
|
|
|
| TOp_recd (kind,tcr) ->
|
|
let acc = acc_uses_function_local_constructs (kind = RecdExprIsObjInit) acc
|
|
(acc_used_tycon_repr opts (deref_tycon tcr) (tyvars opts (acc_free_tycon opts) tcr acc))
|
|
|
|
| TOp_asm (_,tys) -> acc_freevars_in_types opts tys acc
|
|
| TOp_rethrow -> acc_uses_rethrow true acc
|
|
|
|
| TOp_trait_call(TTrait(tys,nm,_,argtys,rty,sln)) ->
|
|
Option.fold_right (acc_freevars_in_trait_sln opts) sln.Value
|
|
(acc_freevars_in_types opts tys
|
|
(acc_freevars_in_types opts argtys
|
|
(Option.fold_right (acc_freevars_in_type opts) rty acc)))
|
|
|
|
| TOp_lval_op (_,lvr) ->
|
|
acc_free_vref opts lvr acc
|
|
|
|
| TOp_ilcall ((virt,protect,valu,newobj,superInit,prop,isDllImport,boxthis,mref),enclTypeArgs,methTypeArgs,tys) ->
|
|
acc_freevars_in_types opts enclTypeArgs
|
|
(acc_freevars_in_types opts methTypeArgs
|
|
(acc_freevars_in_types opts tys
|
|
(acc_uses_function_local_constructs protect acc)))
|
|
|
|
and acc_free_in_targets opts targets acc =
|
|
Array.fold_right (fun (TTarget(vs,e,_)) acc -> FlatList.foldBack (bound_locval opts) vs (acc_free_in_expr opts e acc)) targets acc
|
|
|
|
and acc_free_in_FlatExprs opts (es:FlatExprs) acc = FlatList.foldBack (acc_free_in_expr opts) es acc
|
|
|
|
and acc_free_in_exprs opts (es: Exprs) acc =
|
|
match es with
|
|
| [] -> acc
|
|
| h::t ->
|
|
let acc = acc_free_in_expr opts h acc
|
|
// tailcall - e.g. Cons(x,Cons(x2,.......Cons(x1000000,Nil))) and [| x1; .... ; x1000000 |]
|
|
acc_free_in_exprs opts t acc
|
|
|
|
and acc_free_in_slotsig opts (TSlotSig(_,typ,_,_,_,_)) acc = acc_freevars_in_type opts typ acc
|
|
|
|
and free_in_dtree opts e = acc_free_in_dtree opts e empty_freevars
|
|
and free_in_expr opts e = acc_free_in_expr opts e empty_freevars
|
|
|
|
(* Note: these are only an approximation - they are currently used only by the optimizer *)
|
|
let rec acc_free_in_mdef opts x acc =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) -> FlatList.foldBack (acc_rhs opts) binds (List.foldBack (acc_free_in_mbind opts) mbinds acc)
|
|
| TMDefLet(bind,m) -> acc_rhs opts bind acc
|
|
| TMDefDo(e,m) -> acc_free_in_expr opts e acc
|
|
| TMDefs(defs) -> acc_free_in_mdefs opts defs acc
|
|
| TMAbstract(TMTyped(mtyp,mdef,_)) -> acc_free_in_mdef opts mdef acc (* not really right, but sufficient for how this is used in optimization *)
|
|
and acc_free_in_mbind opts (TMBind(_, def)) acc = acc_free_in_mdef opts def acc
|
|
and acc_free_in_mdefs opts x acc =
|
|
List.foldBack (acc_free_in_mdef opts) x acc
|
|
|
|
(* NOTE: we don't yet need to ask for free variables in module expressions *)
|
|
|
|
let free_in_rhs opts bind = acc_rhs opts bind empty_freevars
|
|
let free_in_mdef opts mdef = acc_free_in_mdef opts mdef empty_freevars
|
|
|
|
(*---------------------------------------------------------------------------
|
|
!* Destruct - rarely needed
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let rec strip_lambda (e,ty) =
|
|
match e with
|
|
| TExpr_lambda (_,basevopt,v,b,_,rty,_) ->
|
|
if isSome basevopt then errorR(InternalError("skipping basevopt", range_of_expr e));
|
|
let (vs',b',rty') = strip_lambda (b,rty)
|
|
(v :: vs', b', rty')
|
|
| _ -> ([],e,ty)
|
|
|
|
let dest_top_lambda (e,ty) =
|
|
let tps,taue,tauty = match e with TExpr_tlambda (_,tps,b,_,rty,_) -> tps,b,rty | _ -> [],e,ty
|
|
let vs,body,rty = strip_lambda (taue,tauty)
|
|
tps,vs,body,rty
|
|
|
|
// This is used to infer arities of expressions
|
|
// i.e. base the chosen arity on the syntactic expression shape and type of arguments
|
|
let InferArityOfExpr g ty partialArgAttribsL retAttribs e =
|
|
let rec strip_lambda_notypes e =
|
|
match e with
|
|
| TExpr_lambda (_,_,vs,b,_,_,_) ->
|
|
let (vs',b') = strip_lambda_notypes b
|
|
(vs :: vs', b')
|
|
| TExpr_tchoose (tps,b,_) -> strip_lambda_notypes b
|
|
| _ -> ([],e)
|
|
|
|
let dest_top_lambda_notypes e =
|
|
let tps,taue = match e with TExpr_tlambda (_,tps,b,_,_,_) -> tps,b | _ -> [],e
|
|
let vs,body = strip_lambda_notypes taue
|
|
tps,vs,body
|
|
|
|
let tps,vsl,body = dest_top_lambda_notypes e
|
|
let fun_arity = vsl.Length
|
|
let dtys,rty = strip_fun_typ_upto g fun_arity (snd (try_dest_forall_typ g ty))
|
|
let partialArgAttribsL = Array.of_list partialArgAttribsL
|
|
assert (List.length vsl = List.length dtys)
|
|
|
|
let curriedArgInfos =
|
|
(List.zip vsl dtys) |> List.mapi (fun i (vs,ty) ->
|
|
let partialAttribs = if i < partialArgAttribsL.Length then partialArgAttribsL.[i] else []
|
|
let tys = if (i = 0 && is_unit_typ g ty) then [] else try_dest_tuple_typ g ty
|
|
let ids =
|
|
if vs.Length = tys.Length then vs |> List.map (fun v -> Some v.Id)
|
|
else tys |> List.map (fun _ -> None)
|
|
let attribs =
|
|
if partialAttribs.Length = tys.Length then partialAttribs
|
|
else tys |> List.map (fun _ -> [])
|
|
(ids,attribs) ||> List.map2 (fun id attribs -> TopArgInfo(attribs,id)))
|
|
let retInfo = TopArgInfo(retAttribs,None)
|
|
TopValInfo (TopValInfo.InferTyparInfo tps, curriedArgInfos, retInfo)
|
|
|
|
let InferArityOfExprBinding g (v:Val) e =
|
|
match v.TopValInfo with
|
|
| Some info -> info
|
|
| None -> InferArityOfExpr g v.Type [] [] e
|
|
|
|
let chosen_arity_of_bind (TBind(v,repr,_)) = v.TopValInfo
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Check if constraints are satisfied that allow us to use more optimized
|
|
// implementations
|
|
//-------------------------------------------------------------------------
|
|
|
|
let GetUnderlyingTypeOfEnumType g typ =
|
|
assert(is_enum_typ g typ);
|
|
let tycon = deref_tycon (tcref_of_stripped_typ g typ)
|
|
if is_il_enum_tycon tycon then
|
|
let tdef = tycon.ILTyconRawMetadata
|
|
let info = info_for_enum (tdef.tdName,tdef.tdFieldDefs)
|
|
let il_ty = typ_of_enum_info info
|
|
match il_ty.TypeSpec.Name with
|
|
| "System.Byte" -> g.byte_ty
|
|
| "System.SByte" -> g.sbyte_ty
|
|
| "System.Int16" -> g.int16_ty
|
|
| "System.Int32" -> g.int32_ty
|
|
| "System.Int64" -> g.int64_ty
|
|
| "System.UInt16" -> g.uint16_ty
|
|
| "System.UInt32" -> g.uint32_ty
|
|
| "System.UInt64" -> g.uint64_ty
|
|
| "System.Single" -> g.float32_ty
|
|
| "System.Double" -> g.float_ty
|
|
| "System.Char" -> g.char_ty
|
|
| "System.Boolean" -> g.bool_ty
|
|
| _ -> g.int32_ty
|
|
else
|
|
match tycon.GetFieldByName "value__" with
|
|
| Some rf -> rf.FormalType
|
|
| None -> error(InternalError("no 'value__' field found for enumeration type "^tycon.MangledName,tycon.Range))
|
|
|
|
|
|
(* CLEANUP NOTE: this absolutely awful. Get rid of this nonsense mutation. *)
|
|
let set_val_has_no_arity (f:Val) =
|
|
if verbose then dprintf "clearing topValInfo on %s\n" f.MangledName;
|
|
f.Data.val_top_repr_info <- None; f
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Resolve static optimization constraints
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
let norm_enum_typ g ty = (if is_enum_typ g ty then GetUnderlyingTypeOfEnumType g ty else ty)
|
|
|
|
// -1 equals "no", 0 is "unknown", 1 is "yes"
|
|
let decide_static_optimization_constraint g (TTyconEqualsTycon (a,b)) =
|
|
let a = norm_enum_typ g (strip_tpeqns_and_tcabbrevs_and_measureable g a)
|
|
let b = norm_enum_typ g (strip_tpeqns_and_tcabbrevs_and_measureable g b)
|
|
// Both types must be nominal for a definite result
|
|
match try_tcref_of_stripped_typ g a with
|
|
| Some tcref1 ->
|
|
match try_tcref_of_stripped_typ g b with
|
|
| Some tcref2 -> if tcref_eq g tcref1 tcref2 then 1 else -1
|
|
| None -> 0
|
|
| None -> 0
|
|
|
|
let rec DecideStaticOptimizations g cs =
|
|
match cs with
|
|
| [] -> 1
|
|
| h::t ->
|
|
let d = decide_static_optimization_constraint g h
|
|
if d = -1 then -1 elif d = 1 then DecideStaticOptimizations g t else 0
|
|
|
|
let mk_static_optimization_expr g (cs,e1,e2,m) =
|
|
let d = DecideStaticOptimizations g cs in
|
|
if d = -1 then e2
|
|
elif d = 1 then e1
|
|
else TExpr_static_optimization(cs,e1,e2,m)
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Copy expressions, including new names for locally bound values.
|
|
// Used to inline expressions.
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
type ValCopyFlag =
|
|
| CloneAll
|
|
| CloneAllAndMarkExprValsAsCompilerGenerated
|
|
| OnlyCloneExprVals
|
|
|
|
let mark_as_compgen compgen d =
|
|
let compgen =
|
|
match compgen with
|
|
| CloneAllAndMarkExprValsAsCompilerGenerated -> true
|
|
| _ -> false
|
|
{ d with val_flags= ValFlags.encode_compgen_of_vflags (ValFlags.is_compgen_of_vflags d.val_flags || compgen) d.val_flags }
|
|
|
|
let bind_locval (v:Val) (v':Val) tmenv =
|
|
{ tmenv with vspec_remap=vspec_map_add v (mk_local_vref v') tmenv.vspec_remap}
|
|
|
|
let bind_LocalVals vs vs' tmenv =
|
|
{ tmenv with vspec_remap=List.fold_right2 (fun v v' acc -> vspec_map_add v (mk_local_vref v') acc) vs vs' tmenv.vspec_remap}
|
|
|
|
let bind_LocalFlatVals vs vs' tmenv =
|
|
{ tmenv with vspec_remap=FlatList.foldBack2 (fun v v' acc -> vspec_map_add v (mk_local_vref v') acc) vs vs' tmenv.vspec_remap}
|
|
|
|
let bind_tycon (tc:Tycon) (tc':Tycon) tyenv =
|
|
{ tyenv with tcref_remap=tcref_map_add (mk_local_tcref tc) (mk_local_tcref tc') tyenv.tcref_remap }
|
|
|
|
let bind_tycons tcs tcs' tyenv =
|
|
{ tyenv with tcref_remap= List.fold_right2 (fun tc tc' acc -> tcref_map_add (mk_local_tcref tc) (mk_local_tcref tc') acc) tcs tcs' tyenv.tcref_remap }
|
|
|
|
let remap_attrib_kind tmenv k =
|
|
match k with
|
|
| ILAttrib _ as x -> x
|
|
| FSAttrib vref -> FSAttrib(remap_vref tmenv vref)
|
|
|
|
let tmenv_copy_remap_and_bind_typars remap_attrib tmenv tps =
|
|
let tps',tyenvinner = copy_remap_and_bind_typars_full remap_attrib tmenv tps
|
|
let tmenvinner = tyenvinner
|
|
tps',tmenvinner
|
|
|
|
let rec remap_attrib g tmenv (Attrib (tcref,kind, args, props,m)) =
|
|
Attrib(remap_tcref tmenv.tcref_remap tcref,
|
|
remap_attrib_kind tmenv kind,
|
|
args |> List.map (remap_attrib_expr g tmenv),
|
|
props |> List.map (fun (AttribNamedArg(nm,ty,flg,expr)) -> AttribNamedArg(nm,remap_type tmenv ty, flg, remap_attrib_expr g tmenv expr)),
|
|
m)
|
|
|
|
and remap_attrib_expr g tmenv (AttribExpr(e1,e2)) =
|
|
AttribExpr(remap_expr g CloneAll tmenv e1, remap_expr g CloneAll tmenv e2)
|
|
|
|
and remap_attribs g tmenv xs = List.map (remap_attrib g tmenv) xs
|
|
|
|
and remap_possible_forall_typ g tmenv ty = remap_type_full (remap_attrib g tmenv) tmenv ty
|
|
|
|
and remap_arg_data g tmenv (TopArgInfo(attribs,nm)) =
|
|
TopArgInfo(remap_attribs g tmenv attribs,nm)
|
|
|
|
and remap_top_val_info g tmenv (TopValInfo(tpNames,arginfosl,retInfo)) =
|
|
TopValInfo(tpNames,List.mapSquared (remap_arg_data g tmenv) arginfosl, remap_arg_data g tmenv retInfo)
|
|
|
|
and remap_val_data g tmenv d =
|
|
if !verboseStamps then dprintf "remap val data #%d\n" d.val_stamp;
|
|
let ty = d.val_type
|
|
let topValInfo = d.val_top_repr_info
|
|
let ty' = ty |> remap_possible_forall_typ g tmenv
|
|
{ d with
|
|
val_type = ty';
|
|
val_actual_parent = d.val_actual_parent |> remap_parent_ref tmenv;
|
|
val_top_repr_info = d.val_top_repr_info |> Option.map (remap_top_val_info g tmenv);
|
|
val_member_info = d.val_member_info |> Option.map (remap_member_info g d.val_defn_range topValInfo ty ty' tmenv);
|
|
val_attribs = d.val_attribs |> remap_attribs g tmenv }
|
|
|
|
and remap_parent_ref tyenv p =
|
|
match p with
|
|
| ParentNone -> ParentNone
|
|
| Parent x -> Parent (x |> remap_tcref tyenv.tcref_remap)
|
|
|
|
and map_immediate_vals_and_tycons_of_modtyp ft fv (x:ModuleOrNamespaceType) =
|
|
let vals = x.AllValuesAndMembers |> NameMap.map fv
|
|
let tycons = x.AllEntities |> NameMap.map ft
|
|
new ModuleOrNamespaceType(x.ModuleOrNamespaceKind, vals, tycons)
|
|
|
|
and copy_and_remap_val g compgen tmenv (v:Val) =
|
|
match compgen with
|
|
| OnlyCloneExprVals when v.IsMemberOrModuleBinding -> v
|
|
| _ -> v |> NewModifiedVal (remap_val_data g tmenv >> mark_as_compgen compgen)
|
|
|
|
and fixup_val_attribs g tmenv (v1:Val) (v2:Val) =
|
|
let attrs = v1.Attribs
|
|
if isNil attrs then () else
|
|
v2.Data.val_attribs <- attrs |> remap_attribs g tmenv
|
|
|
|
and copy_and_remap_and_bind_vals g compgen tmenv vs =
|
|
let vs' = vs |> List.map (copy_and_remap_val g compgen tmenv)
|
|
let tmenvinner = bind_LocalVals vs vs' tmenv
|
|
// Fixup attributes now we've built the full List.map of value renamings (attributes contain value references) *)
|
|
List.iter2 (fixup_val_attribs g tmenvinner) vs vs';
|
|
vs', tmenvinner
|
|
|
|
and copy_and_remap_and_bind_FlatVals g compgen tmenv vs =
|
|
let vs' = vs |> FlatList.map (copy_and_remap_val g compgen tmenv)
|
|
let tmenvinner = bind_LocalFlatVals vs vs' tmenv
|
|
(* Fixup attributes now we've built the full List.map of value renamings (attributes contain value references) *)
|
|
FlatList.iter2 (fixup_val_attribs g tmenvinner) vs vs';
|
|
vs', tmenvinner
|
|
|
|
and copy_and_remap_and_bind_val g compgen tmenv v =
|
|
let v' = v |> copy_and_remap_val g compgen tmenv
|
|
let tmenvinner = bind_locval v v' tmenv
|
|
(* Fixup attributes now we've built the full List.map of value renamings (attributes contain value references) *)
|
|
fixup_val_attribs g tmenvinner v v';
|
|
v', tmenvinner
|
|
|
|
and remap_expr g (compgen:ValCopyFlag) (tmenv:Remap) x =
|
|
match x with
|
|
// Binding constructs - see also dtrees below
|
|
| TExpr_lambda (_,basevopt,vs,b,m,rty,_) ->
|
|
let basevopt, tmenv = Option.mapfold (copy_and_remap_and_bind_val g compgen) tmenv basevopt
|
|
let vs,tmenv = copy_and_remap_and_bind_vals g compgen tmenv vs
|
|
let b = remap_expr g compgen tmenv b
|
|
let rty = remap_type tmenv rty
|
|
TExpr_lambda (new_uniq(), basevopt,vs,b,m, rty, SkipFreeVarsCache ())
|
|
| TExpr_tlambda (_,tps,b,m,rty,_) ->
|
|
let tps',tmenvinner = tmenv_copy_remap_and_bind_typars (remap_attrib g tmenv) tmenv tps
|
|
mk_tlambda m tps' (remap_expr g compgen tmenvinner b,remap_type tmenvinner rty)
|
|
| TExpr_tchoose (tps,b,m) ->
|
|
let tps',tmenvinner = tmenv_copy_remap_and_bind_typars (remap_attrib g tmenv) tmenv tps
|
|
TExpr_tchoose(tps',remap_expr g compgen tmenvinner b,m)
|
|
| TExpr_letrec (binds,e,m,_) ->
|
|
let binds',tmenvinner = copy_and_remap_and_bind_bindings g compgen tmenv binds
|
|
TExpr_letrec (binds',remap_expr g compgen tmenvinner e,m,NewFreeVarsCache())
|
|
| TExpr_let _ -> remap_linear_expr g compgen tmenv x (fun x -> x)
|
|
| TExpr_match (spBind,exprm,pt,targets,m,ty,_) ->
|
|
prim_mk_match (spBind,exprm,remap_dtree g compgen tmenv pt,
|
|
targets |> Array.map (fun (TTarget(vs,e,spTarget)) ->
|
|
let vs',tmenvinner = copy_and_remap_and_bind_FlatVals g compgen tmenv vs
|
|
TTarget(vs', remap_expr g compgen tmenvinner e,spTarget)),
|
|
m,remap_type tmenv ty)
|
|
(* Other constructs *)
|
|
| TExpr_val (vr,isSuperInit,m) ->
|
|
let vr' = remap_vref tmenv vr
|
|
if vr == vr' then x
|
|
else TExpr_val (vr',isSuperInit,m)
|
|
| TExpr_quote (a,{contents=Some(argTypes,argExprs,data)},m,ty) ->
|
|
TExpr_quote (remap_expr g compgen tmenv a,{contents=Some(remap_typesA tmenv argTypes,remap_exprs g compgen tmenv argExprs,data)},m,remap_type tmenv ty)
|
|
| TExpr_quote (a,{contents=None},m,ty) ->
|
|
TExpr_quote (remap_expr g compgen tmenv a,{contents=None},m,remap_type tmenv ty)
|
|
| TExpr_obj (_,typ,basev,basecall,overrides,iimpls,m,_) ->
|
|
let basev',tmenvinner = Option.mapfold (copy_and_remap_and_bind_val g compgen) tmenv basev
|
|
mk_obj_expr(remap_type tmenv typ,basev',
|
|
remap_expr g compgen tmenv basecall,
|
|
List.map (remap_method g compgen tmenvinner) overrides,
|
|
List.map (remap_iimpl g compgen tmenvinner) iimpls,m)
|
|
| TExpr_op(op,tinst,args,m) ->
|
|
let op' = remap_op tmenv op
|
|
let tinst' = remap_types tmenv tinst
|
|
let args' = remap_exprs g compgen tmenv args
|
|
if op == op' && tinst == tinst' && args == args' then x
|
|
else TExpr_op (op',tinst',args',m)
|
|
| TExpr_app(e1,e1ty,tyargs,args,m) ->
|
|
let e1' = remap_expr g compgen tmenv e1
|
|
let e1ty' = remap_possible_forall_typ g tmenv e1ty
|
|
let tyargs' = remap_types tmenv tyargs
|
|
let args' = remap_exprs g compgen tmenv args
|
|
if e1 == e1' && e1ty == e1ty' && tyargs == tyargs' && args == args' then x
|
|
else TExpr_app(e1',e1ty',tyargs',args',m)
|
|
| TExpr_link(eref) ->
|
|
remap_expr g compgen tmenv !eref
|
|
| TExpr_seq (e1,e2,dir,spSeq,m) ->
|
|
let e1' = remap_expr g compgen tmenv e1
|
|
let e2' = remap_expr g compgen tmenv e2
|
|
if e1 == e1' && e2 == e2' then x
|
|
else TExpr_seq (e1',e2',dir,spSeq,m)
|
|
| TExpr_static_optimization (cs,e2,e3,m) ->
|
|
(* note that type instantiation typically resolve the static constraints here *)
|
|
mk_static_optimization_expr g (List.map (remap_constraint tmenv) cs,
|
|
remap_expr g compgen tmenv e2,
|
|
remap_expr g compgen tmenv e3,m)
|
|
|
|
| TExpr_const (c,m,ty) ->
|
|
let ty' = remap_type tmenv ty
|
|
if ty == ty' then x else TExpr_const (c,m,ty')
|
|
|
|
and remap_linear_expr g compgen tmenv e contf =
|
|
match e with
|
|
| TExpr_let (bind,e,m,_) ->
|
|
let bind',tmenvinner = copy_and_remap_and_bind_binding g compgen tmenv bind
|
|
remap_linear_expr g compgen tmenvinner e (contf << mk_let_bind m bind')
|
|
| _ -> contf (remap_expr g compgen tmenv e)
|
|
and remap_constraint tyenv c =
|
|
match c with
|
|
| TTyconEqualsTycon(ty1,ty2) -> TTyconEqualsTycon(remap_type tyenv ty1, remap_type tyenv ty2)
|
|
|
|
and remap_op tmenv op =
|
|
match op with
|
|
| TOp_recd (ctor,tcr) -> TOp_recd(ctor,remap_tcref tmenv.tcref_remap tcr)
|
|
| TOp_ucase_tag_get tcr -> TOp_ucase_tag_get(remap_tcref tmenv.tcref_remap tcr)
|
|
| TOp_ucase(ucref) -> TOp_ucase(remap_ucref tmenv.tcref_remap ucref)
|
|
| TOp_ucase_proof(ucref) -> TOp_ucase_proof(remap_ucref tmenv.tcref_remap ucref)
|
|
| TOp_exnconstr(ec) -> TOp_exnconstr(remap_tcref tmenv.tcref_remap ec)
|
|
| TOp_exnconstr_field_get(ec,n) -> TOp_exnconstr_field_get(remap_tcref tmenv.tcref_remap ec,n)
|
|
| TOp_exnconstr_field_set(ec,n) -> TOp_exnconstr_field_set(remap_tcref tmenv.tcref_remap ec,n)
|
|
| TOp_rfield_set(rfref) -> TOp_rfield_set(remap_rfref tmenv.tcref_remap rfref)
|
|
| TOp_rfield_get(rfref) -> TOp_rfield_get(remap_rfref tmenv.tcref_remap rfref)
|
|
| TOp_field_get_addr(rfref) -> TOp_field_get_addr(remap_rfref tmenv.tcref_remap rfref)
|
|
| TOp_ucase_field_get(ucref,n) -> TOp_ucase_field_get(remap_ucref tmenv.tcref_remap ucref,n)
|
|
| TOp_ucase_field_set(ucref,n) -> TOp_ucase_field_set(remap_ucref tmenv.tcref_remap ucref,n)
|
|
| TOp_asm (instrs,tys) -> TOp_asm (instrs,remap_types tmenv tys)
|
|
| TOp_trait_call(traitInfo) -> TOp_trait_call(remap_traitA tmenv traitInfo)
|
|
| TOp_lval_op (kind,lvr) -> TOp_lval_op (kind,remap_vref tmenv lvr)
|
|
| TOp_ilcall ((virt,protect,valu,newobj,superInit,prop,isDllImport,boxthis,mref),enclTypeArgs,methTypeArgs,tys) ->
|
|
TOp_ilcall ((virt,protect,valu,newobj,superInit,prop,isDllImport,Option.map (fun (a,b) -> (remap_type tmenv a, remap_type tmenv b)) boxthis,mref),
|
|
remap_types tmenv enclTypeArgs,remap_types tmenv methTypeArgs,remap_types tmenv tys)
|
|
| _ -> op
|
|
|
|
|
|
and remap_exprs g compgen tmenv es = List.mapq (remap_expr g compgen tmenv) es
|
|
and remap_FlatExprs g compgen tmenv es = FlatList.mapq (remap_expr g compgen tmenv) es
|
|
|
|
and remap_dtree g compgen tmenv x =
|
|
match x with
|
|
| TDSwitch(e1,csl,dflt,m) ->
|
|
TDSwitch(remap_expr g compgen tmenv e1,
|
|
List.map (fun (TCase(test,y)) ->
|
|
let test' =
|
|
match test with
|
|
| TTest_unionconstr (uc,tinst) -> TTest_unionconstr(remap_ucref tmenv.tcref_remap uc,remap_types tmenv tinst)
|
|
| TTest_array_length (n,ty) -> TTest_array_length(n,remap_type tmenv ty)
|
|
| TTest_const c -> test
|
|
| TTest_isinst (srcty,tgty) -> TTest_isinst (remap_type tmenv srcty,remap_type tmenv tgty)
|
|
| TTest_isnull -> TTest_isnull
|
|
| TTest_query _ -> failwith "TTest_query should only be used during pattern match compilation"
|
|
TCase(test',remap_dtree g compgen tmenv y)) csl,
|
|
Option.map (remap_dtree g compgen tmenv) dflt,
|
|
m)
|
|
| TDSuccess (es,n) ->
|
|
TDSuccess (remap_FlatExprs g compgen tmenv es,n)
|
|
| TDBind (bind,rest) ->
|
|
let bind',tmenvinner = copy_and_remap_and_bind_binding g compgen tmenv bind
|
|
TDBind (bind',remap_dtree g compgen tmenvinner rest)
|
|
|
|
and copy_and_remap_and_bind_binding g compgen tmenv (bind:Binding) =
|
|
let v = bind.Var
|
|
let v', tmenv = copy_and_remap_and_bind_val g compgen tmenv v
|
|
remap_and_rename_bind g compgen tmenv bind v' , tmenv
|
|
|
|
and copy_and_remap_and_bind_bindings g compgen tmenv binds =
|
|
let vs', tmenvinner = copy_and_remap_and_bind_FlatVals g compgen tmenv (vars_of_Bindings binds)
|
|
remap_and_rename_binds g compgen tmenvinner binds vs',tmenvinner
|
|
|
|
and remap_and_rename_binds g compgen tmenvinner binds vs' = FlatList.map2 (remap_and_rename_bind g compgen tmenvinner) binds vs'
|
|
and remap_and_rename_bind g compgen tmenvinner (TBind(_,repr,letSeqPtOpt)) v' = TBind(v', remap_expr g compgen tmenvinner repr,letSeqPtOpt)
|
|
|
|
and remap_method g compgen tmenv (TObjExprMethod(slotsig,tps,vs,e,m)) =
|
|
let slotsig' = remap_slotsig (remap_attrib g tmenv) tmenv slotsig
|
|
let tps',tmenvinner = tmenv_copy_remap_and_bind_typars (remap_attrib g tmenv) tmenv tps
|
|
let vs', tmenvinner2 = List.mapfold (copy_and_remap_and_bind_vals g compgen) tmenvinner vs
|
|
let e' = remap_expr g compgen tmenvinner2 e
|
|
TObjExprMethod(slotsig',tps',vs',e',m)
|
|
|
|
and remap_iimpl g compgen tmenv (ty,overrides) =
|
|
(remap_type tmenv ty, List.map (remap_method g compgen tmenv) overrides)
|
|
|
|
and remap_rfield g tmenv x =
|
|
{ x with
|
|
rfield_type = x.rfield_type |> remap_possible_forall_typ g tmenv;
|
|
rfield_pattribs = x.rfield_pattribs |> remap_attribs g tmenv;
|
|
rfield_fattribs = x.rfield_fattribs |> remap_attribs g tmenv; }
|
|
and remap_rfields g tmenv (x:TyconRecdFields) = x.AllFieldsAsList |> List.map (remap_rfield g tmenv) |> MakeRecdFieldsTable
|
|
|
|
and remap_ucase g tmenv x =
|
|
{ x with
|
|
ucase_rfields = x.ucase_rfields |> remap_rfields g tmenv;
|
|
ucase_rty = x.ucase_rty |> remap_type tmenv;
|
|
ucase_attribs = x.ucase_attribs |> remap_attribs g tmenv; }
|
|
and remap_funion g tmenv (x:TyconUnionData) = x.UnionCasesAsList |> List.map (remap_ucase g tmenv)|> MakeUnionCases
|
|
|
|
and remap_fsobjmodel g tmenv x =
|
|
{ x with
|
|
fsobjmodel_kind =
|
|
(match x.fsobjmodel_kind with
|
|
| TTyconDelegate slotsig -> TTyconDelegate (remap_slotsig (remap_attrib g tmenv) tmenv slotsig)
|
|
| TTyconClass | TTyconInterface | TTyconStruct | TTyconEnum -> x.fsobjmodel_kind);
|
|
fsobjmodel_vslots = x.fsobjmodel_vslots |> List.map (remap_vref tmenv);
|
|
fsobjmodel_rfields = x.fsobjmodel_rfields |> remap_rfields g tmenv }
|
|
|
|
|
|
and remap_tycon_repr g tmenv repr =
|
|
match repr with
|
|
| TFsObjModelRepr x -> TFsObjModelRepr (remap_fsobjmodel g tmenv x)
|
|
| TRecdRepr x -> TRecdRepr (remap_rfields g tmenv x)
|
|
| TFiniteUnionRepr x -> TFiniteUnionRepr (remap_funion g tmenv x)
|
|
| TILObjModelRepr _ -> failwith "cannot remap IL type definitions"
|
|
| TAsmRepr x -> repr
|
|
| TMeasureableRepr x -> TMeasureableRepr (remap_type tmenv x)
|
|
|
|
and remap_tcaug tmenv x =
|
|
{ x with
|
|
tcaug_equals = x.tcaug_equals |> Option.map (pair_map (remap_vref tmenv) (remap_vref tmenv));
|
|
tcaug_compare = x.tcaug_compare |> Option.map (pair_map (remap_vref tmenv) (remap_vref tmenv));
|
|
tcaug_compare_withc = x.tcaug_compare_withc |> Option.map(remap_vref tmenv);
|
|
tcaug_hash_and_equals_withc = x.tcaug_hash_and_equals_withc |> Option.map (pair_map (remap_vref tmenv) (remap_vref tmenv));
|
|
tcaug_adhoc = x.tcaug_adhoc |> NameMap.map (List.map (remap_vref tmenv));
|
|
tcaug_super = x.tcaug_super |> Option.map (remap_type tmenv);
|
|
tcaug_implements = x.tcaug_implements |> List.map (map1'3 (remap_type tmenv)) }
|
|
|
|
and remap_tycon_exnc_info g tmenv inp =
|
|
match inp with
|
|
| TExnAbbrevRepr x -> TExnAbbrevRepr (remap_tcref tmenv.tcref_remap x)
|
|
| TExnFresh x -> TExnFresh (remap_rfields g tmenv x)
|
|
| TExnAsmRepr _ | TExnNone -> inp
|
|
|
|
and remap_member_info g m topValInfo ty ty' tmenv x =
|
|
// The slotsig in the ImplementedSlotSigs is w.r.t. the type variables in the value's type.
|
|
// REVIEW: this is a bit gross. It would be nice if the slotsig was standalone
|
|
assert (isSome topValInfo);
|
|
let tpsorig,_,_,_ = GetMemberTypeInFSharpForm g x.MemberFlags (the(topValInfo)) ty m
|
|
let tps,_,_,_ = GetMemberTypeInFSharpForm g x.MemberFlags (the(topValInfo)) ty' m
|
|
let renaming,_ = mk_typar_to_typar_renaming tpsorig tps
|
|
let tmenv = { tmenv with tpinst = tmenv.tpinst @ renaming }
|
|
{ x with
|
|
ApparentParent = x.ApparentParent |> remap_tcref tmenv.tcref_remap ;
|
|
ImplementedSlotSigs = x.ImplementedSlotSigs |> List.map (remap_slotsig (remap_attrib g tmenv) tmenv);
|
|
}
|
|
|
|
and copy_remap_and_bind_mtyp g compgen tmenv mty =
|
|
let tycons = all_tycons_of_mtyp mty
|
|
let vs = all_vals_of_mtyp mty
|
|
let _,_,tmenvinner = copy_and_remap_and_bind_tycons_and_vals g compgen tmenv tycons vs
|
|
remap_mtyp g compgen tmenvinner mty, tmenvinner
|
|
|
|
and remap_mtyp g compgen tmenv mty =
|
|
map_immediate_vals_and_tycons_of_modtyp (rename_tycon tmenv) (rename_val tmenv) mty
|
|
|
|
and rename_tycon tyenv x =
|
|
let tcref =
|
|
try
|
|
let res = tcref_map_find (mk_local_tcref x) tyenv.tcref_remap
|
|
res
|
|
with Not_found ->
|
|
errorR(InternalError("couldn't remap internal tycon "^showL(tyconL x),x.Range));
|
|
mk_local_tcref x
|
|
deref_tycon tcref
|
|
|
|
and rename_val tmenv x =
|
|
match vspec_map_tryfind x tmenv.vspec_remap with
|
|
| Some v -> deref_val v
|
|
| None -> x
|
|
|
|
and copy_tycon compgen (tycon:Tycon) =
|
|
match compgen with
|
|
| OnlyCloneExprVals -> tycon
|
|
| _ -> NewClonedTycon tycon
|
|
|
|
/// This operates over a whole nested collection of tycons and vals simultaneously *)
|
|
and copy_and_remap_and_bind_tycons_and_vals g compgen tmenv tycons vs =
|
|
let tycons' = tycons |> List.map (copy_tycon compgen)
|
|
|
|
let tmenvinner = bind_tycons tycons tycons' tmenv
|
|
|
|
(* Values need to be copied and renamed. *)
|
|
let vs',tmenvinner = copy_and_remap_and_bind_vals g compgen tmenvinner vs
|
|
if !verboseStamps then
|
|
for tycon in tycons do
|
|
dprintf "copy_and_remap_and_bind_tycons_and_vals: tycon %s#%d\n" tycon.MangledName tycon.Stamp;
|
|
for v in vs do
|
|
dprintf "copy_and_remap_and_bind_tycons_and_vals: val %s#%d\n" v.MangledName v.Stamp;
|
|
|
|
(* "if a type constructor is hidden then all its inner values and inner type constructors must also be hidden" *)
|
|
(* Hence we can just lookup the inner tycon/value mappings in the tables. *)
|
|
|
|
let lookup_val (v:Val) =
|
|
let vref =
|
|
try
|
|
let res = vspec_map_find v tmenvinner.vspec_remap
|
|
if !verboseStamps then
|
|
dprintf "remaped internal value %s#%d --> %s#%d\n" v.MangledName v.Stamp (deref_val res).MangledName (deref_val res).Stamp;
|
|
res
|
|
with Not_found ->
|
|
errorR(InternalError(sprintf "couldn't remap internal value '%s'" v.MangledName,v.Range));
|
|
mk_local_vref v
|
|
deref_val vref
|
|
|
|
let lookup_tycon tycon =
|
|
let tcref =
|
|
try
|
|
let res = tcref_map_find (mk_local_tcref tycon) tmenvinner.tcref_remap
|
|
if !verboseStamps then
|
|
dprintf "remaped internal tycon %s#%d --> %s#%d\n" tycon.MangledName tycon.Stamp (deref_tycon res).MangledName (deref_tycon res).Stamp;
|
|
res
|
|
with Not_found ->
|
|
errorR(InternalError("couldn't remap internal tycon "^showL(tyconL tycon),tycon.Range));
|
|
mk_local_tcref tycon
|
|
deref_tycon tcref
|
|
|
|
(tycons,tycons') ||> List.iter2 (fun tc tc' ->
|
|
let tcd = tc.Data
|
|
let tcd' = tc'.Data
|
|
let tps',tmenvinner2 = tmenv_copy_remap_and_bind_typars (remap_attrib g tmenvinner) tmenvinner (tcd.entity_typars.Force(tcd.entity_range))
|
|
tcd'.entity_typars <- LazyWithContext.NotLazy tps';
|
|
tcd'.entity_attribs <- tcd.entity_attribs |> remap_attribs g tmenvinner2;
|
|
tcd'.entity_tycon_repr <- tcd.entity_tycon_repr |> Option.map (remap_tycon_repr g tmenvinner2);
|
|
tcd'.entity_tycon_abbrev <- tcd.entity_tycon_abbrev |> Option.map (remap_type tmenvinner2) ;
|
|
tcd'.entity_tycon_tcaug <- tcd.entity_tycon_tcaug |> remap_tcaug tmenvinner2 ;
|
|
tcd'.entity_modul_contents <- notlazy (tcd.entity_modul_contents
|
|
|> Lazy.force
|
|
|> map_immediate_vals_and_tycons_of_modtyp lookup_tycon lookup_val);
|
|
tcd'.entity_exn_info <- tcd.entity_exn_info |> remap_tycon_exnc_info g tmenvinner2) ;
|
|
tycons',vs', tmenvinner
|
|
|
|
|
|
and all_tycons_of_mdef mdef =
|
|
match mdef with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
tycons @ List.collect (fun (TMBind(mspec, def)) -> mspec :: all_tycons_of_mdef def) mbinds
|
|
| TMDefLet _ -> []
|
|
| TMDefDo _ -> []
|
|
| TMDefs(defs) -> List.collect all_tycons_of_mdef defs
|
|
| TMAbstract(TMTyped(mty,_,_)) -> all_tycons_of_mtyp mty
|
|
and all_vals_of_mdef mdef =
|
|
match mdef with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
vslot_vals_of_tycons tycons @
|
|
(binds |> vars_of_Bindings |> FlatList.to_list) @
|
|
List.collect (fun (TMBind(mspec, def)) -> all_vals_of_mdef def) mbinds
|
|
| TMDefLet(bind,m) -> [bind.Var]
|
|
| TMDefDo _ -> []
|
|
| TMDefs(defs) -> List.collect all_vals_of_mdef defs
|
|
| TMAbstract(TMTyped(mty,_,_)) -> all_vals_of_mtyp mty
|
|
|
|
and remap_and_bind_mexpr g compgen tmenv (TMTyped(mty,mdef,m)) =
|
|
let mdef = copy_and_remap_mdef g compgen tmenv mdef
|
|
let mty,tmenv = copy_remap_and_bind_mtyp g compgen tmenv mty
|
|
TMTyped(mty,mdef,m), tmenv
|
|
|
|
and remap_mexpr g compgen tmenv (TMTyped(mty,mdef,m)) =
|
|
let mdef = copy_and_remap_mdef g compgen tmenv mdef
|
|
let mty = remap_mtyp g compgen tmenv mty
|
|
TMTyped(mty,mdef,m)
|
|
|
|
and copy_and_remap_mdef g compgen tmenv mdef =
|
|
let tycons = all_tycons_of_mdef mdef
|
|
let vs = all_vals_of_mdef mdef
|
|
let _,_,tmenvinner = copy_and_remap_and_bind_tycons_and_vals g compgen tmenv tycons vs
|
|
remap_and_rename_mdef g compgen tmenvinner mdef
|
|
|
|
and remap_and_rename_mdefs g compgen tmenv x =
|
|
List.map (remap_and_rename_mdef g compgen tmenv) x
|
|
|
|
and remap_and_rename_mdef g compgen tmenv mdef =
|
|
match mdef with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
(* Abstract (virtual) vslots in the tycons at TMDefRec nodes are binders. They also need to be copied and renamed. *)
|
|
let tycons = tycons |> List.map (rename_tycon tmenv)
|
|
let binds = remap_and_rename_binds g compgen tmenv binds (binds |> FlatList.map (var_of_bind >> rename_val tmenv))
|
|
let mbinds = mbinds |> List.map (remap_and_rename_mbind g compgen tmenv)
|
|
TMDefRec(tycons,binds,mbinds,m)
|
|
| TMDefLet(bind,m) ->
|
|
let v = bind.Var
|
|
let bind = remap_and_rename_bind g compgen tmenv bind (rename_val tmenv v)
|
|
TMDefLet(bind, m)
|
|
| TMDefDo(e,m) ->
|
|
let e = remap_expr g compgen tmenv e
|
|
TMDefDo(e, m)
|
|
| TMDefs(defs) ->
|
|
let defs = remap_and_rename_mdefs g compgen tmenv defs
|
|
TMDefs(defs)
|
|
| TMAbstract(mexpr) ->
|
|
let mexpr = remap_mexpr g compgen tmenv mexpr
|
|
TMAbstract(mexpr)
|
|
|
|
and remap_and_rename_mbind g compgen tmenv (TMBind(mspec, def)) =
|
|
let mspec = rename_tycon tmenv mspec
|
|
let def = remap_and_rename_mdef g compgen tmenv def
|
|
TMBind(mspec, def)
|
|
|
|
and remap_ImplFile g compgen tmenv mv =
|
|
map_acc_TImplFile (remap_and_bind_mexpr g compgen) tmenv mv
|
|
|
|
and remap_assembly g compgen tmenv (TAssembly(mvs)) =
|
|
let mvs,z = List.mapfold (remap_ImplFile g compgen) tmenv mvs
|
|
TAssembly(mvs),z
|
|
|
|
let empty_expr_remap = empty_remap
|
|
|
|
let copy_mtyp g compgen mtyp = copy_remap_and_bind_mtyp g compgen empty_expr_remap mtyp |> fst
|
|
let copy_val g compgen v = copy_and_remap_and_bind_val g compgen empty_expr_remap v |> fst
|
|
let copy_expr g compgen e = remap_expr g compgen empty_expr_remap e
|
|
let copy_ImplFile g compgen e = remap_ImplFile g compgen empty_expr_remap e |> fst
|
|
let copy_assembly g compgen e = remap_assembly g compgen empty_expr_remap e |> fst
|
|
|
|
let inst_expr g tpinst e = remap_expr g CloneAll (mk_inst_tyenv tpinst) e
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Replace Marks - adjust debugging marks when a lambda gets
|
|
// eliminated (i.e. an expression gets inlined)
|
|
//--------------------------------------------------------------------------
|
|
|
|
let rec RemarkExpr m x =
|
|
match x with
|
|
| TExpr_lambda (uniq,basevopt,vs,b,_,rty,fvs) -> TExpr_lambda (uniq,basevopt,vs,RemarkExpr m b,m,rty,fvs)
|
|
| TExpr_tlambda (uniq,tps,b,_,rty,fvs) -> TExpr_tlambda (uniq,tps,RemarkExpr m b,m,rty,fvs)
|
|
| TExpr_tchoose (tps,b,_) -> TExpr_tchoose (tps,RemarkExpr m b,m)
|
|
| TExpr_letrec (binds,e,_,fvs) -> TExpr_letrec (RemarkBinds m binds,RemarkExpr m e,m,fvs)
|
|
| TExpr_let (bind,e,_,fvs) -> TExpr_let (RemarkBind m bind,RemarkExpr m e,m,fvs)
|
|
| TExpr_match (_,_,pt,targets,_,ty,_) -> prim_mk_match (NoSequencePointAtInvisibleBinding,m,RemarkDecisionTree m pt, Array.map (fun (TTarget(vs,e,_)) ->TTarget(vs, RemarkExpr m e,SuppressSequencePointAtTarget)) targets,m,ty)
|
|
| TExpr_val (x,isSuperInit,_) -> TExpr_val (x,isSuperInit,m)
|
|
| TExpr_quote (a,conv,_,ty) -> TExpr_quote (RemarkExpr m a,conv,m,ty)
|
|
| TExpr_obj (n,typ,basev,basecall,overrides,iimpls,_,fvs) ->
|
|
TExpr_obj (n,typ,basev,RemarkExpr m basecall,
|
|
List.map (RemarkMethod m) overrides,
|
|
List.map (RemarkInterfaceImpl m) iimpls,m,fvs)
|
|
| TExpr_op (op,tinst,args,_) ->
|
|
let op =
|
|
match op with
|
|
| TOp_try_finally(_,_) -> TOp_try_finally(NoSequencePointAtTry,NoSequencePointAtFinally)
|
|
| TOp_try_catch(_,_) -> TOp_try_catch(NoSequencePointAtTry,NoSequencePointAtWith)
|
|
| _ -> op
|
|
|
|
TExpr_op (op,tinst,RemarkExprs m args,m)
|
|
| TExpr_link (eref) -> RemarkExpr m !eref
|
|
| TExpr_app(e1,e1ty,tyargs,args,_) -> TExpr_app(RemarkExpr m e1,e1ty,tyargs,RemarkExprs m args,m)
|
|
| TExpr_seq (e1,e2,dir,_,_) -> TExpr_seq (RemarkExpr m e1,RemarkExpr m e2,dir,SuppressSequencePointOnExprOfSequential,m)
|
|
| TExpr_static_optimization (eqns,e2,e3,_) -> TExpr_static_optimization (eqns,RemarkExpr m e2,RemarkExpr m e3,m)
|
|
| TExpr_const (c,_,ty) -> TExpr_const (c,m,ty)
|
|
|
|
and RemarkMethod m (TObjExprMethod(slotsig,tps,vs,e,_)) =
|
|
TObjExprMethod(slotsig,tps,vs,RemarkExpr m e,m)
|
|
and RemarkInterfaceImpl m (ty,overrides) =
|
|
(ty, List.map (RemarkMethod m) overrides)
|
|
and RemarkExprs m es = es |> List.map (RemarkExpr m)
|
|
and RemarkFlatExprs m es = es |> FlatList.map (RemarkExpr m)
|
|
and RemarkDecisionTree m x =
|
|
match x with
|
|
| TDSwitch(e1,csl,dflt,_) -> TDSwitch(RemarkExpr m e1, List.map (fun (TCase(test,y)) -> TCase(test,RemarkDecisionTree m y)) csl, Option.map (RemarkDecisionTree m) dflt,m)
|
|
| TDSuccess (es,n) -> TDSuccess (RemarkFlatExprs m es,n)
|
|
| TDBind (bind,rest) -> TDBind(RemarkBind m bind,RemarkDecisionTree m rest)
|
|
and RemarkBinds m binds = FlatList.map (RemarkBind m) binds
|
|
and RemarkBind m (TBind(v,repr,_)) = TBind(v, RemarkExpr m repr,NoSequencePointAtStickyBinding)
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Reference semantics?
|
|
//--------------------------------------------------------------------------
|
|
|
|
let rfield_alloc_observable (f:RecdField) = not f.IsStatic && f.IsMutable
|
|
let ucase_alloc_observable uc = uc.ucase_rfields.rfields_by_index |> Array.exists rfield_alloc_observable
|
|
let ucref_alloc_observable (uc:UnionCaseRef) = uc.UnionCase |> ucase_alloc_observable
|
|
|
|
let tycon_alloc_observable (tycon:Tycon) =
|
|
if tycon.IsRecordTycon or tycon.IsStructTycon then
|
|
tycon.AllFieldsArray |> Array.exists rfield_alloc_observable
|
|
elif tycon.IsUnionTycon then
|
|
tycon.UnionCasesArray |> Array.exists ucase_alloc_observable
|
|
else false
|
|
|
|
let tcref_alloc_observable tcr = tycon_alloc_observable (deref_tycon tcr)
|
|
|
|
// Although from the pure F# perspective exception values cannot be changed, the .NET
|
|
// implementation of exception objects attaches a whole bunch of stack information to
|
|
// each raised object. Hence we treat exception objects as if they have identity
|
|
let ecref_alloc_observable (ecref:TyconRef) = true
|
|
|
|
// Some of the implementations of library functions on lists use mutation on the tail
|
|
// of the cons cell. These cells are always private, i.e. not accessible by any other
|
|
// code until the construction of the entire return list has been completed.
|
|
// However, within the implementation code reads of the tail cell must in theory be treated
|
|
// with caution. Hence we are conservative and within fslib we don't treat list
|
|
// reads as if they were pure.
|
|
let ucref_rfield_mutable g (ucref:UnionCaseRef) n =
|
|
(g.compilingFslib && tcref_eq g ucref.TyconRef g.list_tcr_canon && n = 1) ||
|
|
(rfield_of_ucref_by_idx ucref n).IsMutable
|
|
|
|
let ecref_rfield_mutable ecref n =
|
|
if n < 0 || n >= List.length (rfields_of_ecref ecref) then errorR(InternalError(sprintf "ecref_rfield_mutable, exnc = %s, n = %d" ecref.DemangledExceptionName n,ecref.Range));
|
|
(rfield_of_ecref_by_idx ecref n).IsMutable
|
|
|
|
let use_genuine_field tycon (f:RecdField) =
|
|
isSome f.LiteralValue || is_enum_tycon tycon || f.rfield_secret || (f.rfield_mutable && not tycon.IsRecordTycon)
|
|
|
|
let gen_field_name tycon f =
|
|
if use_genuine_field tycon f then f.rfield_id.idText
|
|
else CompilerGeneratedName f.rfield_id.idText
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Helpers for building code contained in the initial environment
|
|
//-------------------------------------------------------------------------
|
|
|
|
let mk_expr_ty g ty = TType_app(g.expr_tcr,[ty])
|
|
let mk_raw_expr_ty g = TType_app(g.raw_expr_tcr,[])
|
|
|
|
// flag to specify use of System.Tuple and System.Threading.LazyInit
|
|
let mutable use_40_System_Types = true
|
|
|
|
let mk_tupled_ty g tys =
|
|
match tys with
|
|
| [] -> g.unit_ty
|
|
| [h] -> h
|
|
| _ -> mk_tuple_ty tys
|
|
|
|
let mk_tupled_vars_ty g vs =
|
|
mk_tupled_ty g (types_of_vals vs)
|
|
|
|
let mk_meth_ty g argtys rty = mk_iterated_fun_ty (List.map (mk_tupled_ty g) argtys) rty
|
|
let mk_nativeptr_typ g ty = TType_app (g.nativeptr_tcr, [ty])
|
|
let mk_array_typ g ty = TType_app (g.array_tcr, [ty])
|
|
let mk_bytearray_ty g = mk_array_typ g g.byte_ty
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// type_of_expr
|
|
//--------------------------------------------------------------------------
|
|
|
|
let rec type_of_expr g e =
|
|
match e with
|
|
| TExpr_app(f,fty,tyargs,args,m) -> apply_types g fty (tyargs,args)
|
|
| TExpr_obj (_,ty,_,_,_,_,_,_)
|
|
| TExpr_match (_,_,_,_,_,ty,_)
|
|
| TExpr_quote(_,_,_,ty)
|
|
| TExpr_const(_,_,ty) -> (ty)
|
|
| TExpr_val(vref,isSuperInit,_) -> vref.Type
|
|
| TExpr_seq(a,b,k,_,_) -> type_of_expr g (match k with NormalSeq -> b | ThenDoSeq -> a)
|
|
| TExpr_lambda(_,basevopt,vs,_,_,rty,_) -> (mk_tupled_vars_ty g vs --> rty)
|
|
| TExpr_tlambda(_,tyvs,_,_,rty,_) -> (tyvs +-> rty)
|
|
| TExpr_let(_,e,_,_)
|
|
| TExpr_tchoose(_,e,_)
|
|
| TExpr_link { contents=e}
|
|
| TExpr_static_optimization (_,_,e,_)
|
|
| TExpr_letrec(_,e,_,_) -> type_of_expr g e
|
|
| TExpr_op(op,tinst,args,m) ->
|
|
match op with
|
|
| TOp_coerce -> (match tinst with [to_ty;from_ty] -> to_ty | _ -> failwith "bad TOp_coerce node")
|
|
| (TOp_ilcall (_,_,_,rtys) | TOp_asm(_,rtys)) -> (match rtys with [h] -> h | _ -> g.unit_ty)
|
|
| TOp_ucase uc -> rty_of_uctyp uc tinst
|
|
| TOp_ucase_proof uc -> mk_proven_ucase_typ uc tinst
|
|
| TOp_recd (_,tcref) -> mk_tyapp_ty tcref tinst
|
|
| TOp_exnconstr uc -> g.exn_ty
|
|
| TOp_bytes _ -> mk_bytearray_ty g
|
|
| TOp_uint16s _ -> mk_array_typ g g.uint16_ty
|
|
| TOp_tuple_field_get(i) -> List.nth tinst i
|
|
| TOp_tuple -> mk_tuple_ty tinst
|
|
| (TOp_for _ | TOp_while _) -> g.unit_ty
|
|
| TOp_array -> (match tinst with [ty] -> mk_array_typ g ty | _ -> failwith "bad TOp_array node")
|
|
| (TOp_try_catch _ | TOp_try_finally _) -> (match tinst with [ty] -> ty | _ -> failwith "bad TOp_try node")
|
|
| TOp_field_get_addr(fref) -> mk_byref_typ g (actual_rtyp_of_rfref fref tinst)
|
|
| TOp_rfield_get(fref) -> actual_rtyp_of_rfref fref tinst
|
|
| (TOp_rfield_set _ | TOp_ucase_field_set _ | TOp_exnconstr_field_set _ | TOp_lval_op ((LSet | LByrefSet),_)) ->g.unit_ty
|
|
| TOp_ucase_tag_get(cref) -> g.int_ty
|
|
| TOp_ucase_field_get(cref,j) -> typ_of_ucref_rfield_by_idx cref tinst j
|
|
| TOp_exnconstr_field_get(ecref,j) -> typ_of_ecref_rfield ecref j
|
|
| TOp_lval_op (LByrefGet, v) -> dest_byref_typ g v.Type
|
|
| TOp_lval_op (LGetAddr, v) -> mk_byref_typ g v.Type
|
|
| TOp_get_ref_lval -> (match tinst with [ty] -> mk_byref_typ g ty | _ -> failwith "bad TOp_get_ref_lval node")
|
|
| TOp_trait_call (TTrait(_,_,_,_,ty,_)) -> GetFSharpViewOfReturnType g ty
|
|
| TOp_rethrow -> (match tinst with [rtn_ty] -> rtn_ty | _ -> failwith "bad TOp_rethrow node")
|
|
| TOp_goto _ | TOp_label _ | TOp_return ->
|
|
//assert false;
|
|
//errorR(InternalError("unexpected goto/label/return in type_of_expr",m));
|
|
// It doesn't matter what type we return here. THis is only used in free variable analysis in the code generator
|
|
g.unit_ty
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Make applications
|
|
//---------------------------------------------------------------------------
|
|
|
|
let prim_mk_app (f,fty) tyargs argsl m =
|
|
TExpr_app(f,fty,tyargs,argsl,m)
|
|
|
|
let rec mk_expr_appl_aux g f fty argsl m =
|
|
if verbose then dprintf "--- mk_expr_appl_aux, fty = %s\n" ((DebugPrint.showType fty));
|
|
match argsl with
|
|
| arg :: rest ->
|
|
match f with
|
|
(* Try to zip the term application with others *)
|
|
| TExpr_app(f',fty',tyargs,pargs,m2)
|
|
(* Only do this when the formal return type of the function type is another function type *)
|
|
when is_fun_typ g (formal_apply_types g fty' (tyargs,pargs)) ->
|
|
if verbose then dprintf "--- mk_expr_appl_aux, List.zip, fty' = %s\n" ((DebugPrint.showType fty'));
|
|
let pargs' = pargs@[arg]
|
|
let f'' = prim_mk_app (f',fty') tyargs pargs' (union_ranges m2 m)
|
|
let fty'' = apply_types g fty' (tyargs,pargs')
|
|
if verbose then dprintf "--- mk_expr_appl_aux, combined, continue, fty'' = %s\n" ((DebugPrint.showType fty''));
|
|
mk_expr_appl_aux g f'' fty'' rest m
|
|
| _ ->
|
|
if not (is_fun_typ g fty) then error(InternalError("expected a function type",m));
|
|
let _,rfty = dest_fun_typ g fty
|
|
mk_expr_appl_aux g (prim_mk_app (f,fty) [] [arg] m) rfty rest m
|
|
|
|
| [] -> (f,fty)
|
|
|
|
let rec mk_appl_aux g f fty tyargsl argsl m =
|
|
match tyargsl with
|
|
| tyargs :: rest ->
|
|
begin match tyargs with
|
|
| [] -> mk_appl_aux g f fty rest argsl m
|
|
| _ ->
|
|
let arfty = reduce_forall_typ g fty tyargs
|
|
mk_appl_aux g (prim_mk_app (f,fty) tyargs [] m) arfty rest argsl m
|
|
end
|
|
| [] -> mk_expr_appl_aux g f fty argsl m
|
|
|
|
let mk_appl g ((f,fty),tyargsl,argl,m) = fst (mk_appl_aux g f fty tyargsl argl m)
|
|
let mk_tyapp m (f,fty) tyargs = match tyargs with [] -> f | _ -> prim_mk_app (f,fty) tyargs [] m
|
|
|
|
let mk_val_set m v e = TExpr_op(TOp_lval_op (LSet, v), [], [e], m) (* localv <- e *)
|
|
let mk_lval_set m v e = TExpr_op(TOp_lval_op (LByrefSet, v), [], [e], m) (* *localv_ptr = e *)
|
|
let mk_lval_get m v = TExpr_op(TOp_lval_op (LByrefGet, v), [], [], m) (* *localv_ptr *)
|
|
let mk_val_addr m v = TExpr_op(TOp_lval_op (LGetAddr, v), [], [], m) (* &localv *)
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Decision tree reduction
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
let rec acc_targets_of_dtree tree acc =
|
|
match tree with
|
|
| TDSwitch (_,edges,dflt,_) -> List.foldBack (dest_of_case >> acc_targets_of_dtree) edges (Option.fold_right acc_targets_of_dtree dflt acc)
|
|
| TDSuccess (_,i) -> ListSet.insert (=) i acc
|
|
| TDBind (_,rest) -> acc_targets_of_dtree rest acc
|
|
|
|
let rec map_acc_tips_of_dtree f tree =
|
|
match tree with
|
|
| TDSwitch (e,edges,dflt,m) -> TDSwitch (e,List.map (map_acc_tips_of_edge f) edges,Option.map (map_acc_tips_of_dtree f) dflt,m)
|
|
| TDSuccess (es,i) -> f es i
|
|
| TDBind (bind,rest) -> TDBind(bind,map_acc_tips_of_dtree f rest)
|
|
and map_acc_tips_of_edge f (TCase(x,t)) =
|
|
TCase(x,map_acc_tips_of_dtree f t)
|
|
|
|
let map_targets_of_dtree f tree = map_acc_tips_of_dtree (fun es i -> TDSuccess(es, f i)) tree
|
|
|
|
(* Dead target elimination *)
|
|
let eliminate_dead_targets_from_match tree targets =
|
|
let used = acc_targets_of_dtree tree [] |> Array.of_list
|
|
if used.Length < Array.length targets then
|
|
Array.sortInPlace used;
|
|
let nused = Array.length used
|
|
let ntargets = Array.length targets
|
|
let tree' =
|
|
let remap = Array.create ntargets (-1)
|
|
Array.iteri (fun i tgn -> remap.[tgn] <- i) used;
|
|
map_targets_of_dtree (fun tgn -> if remap.[tgn] = -1 then failwith "eliminate_dead_targets_from_match: failure while eliminating unused targets";
|
|
remap.[tgn]) tree
|
|
let targets' = Array.map (Array.get targets) used
|
|
tree',targets'
|
|
else
|
|
tree,targets
|
|
|
|
|
|
|
|
let rec target_of_success_dtree tree =
|
|
match tree with
|
|
| TDSwitch _ -> None
|
|
| TDSuccess (_,i) -> Some i
|
|
| TDBind(b,t) -> target_of_success_dtree t
|
|
|
|
/// Check a decision tree only has bindings that immediately cover a 'Success'
|
|
let rec dtree_has_non_trivial_bindings tree =
|
|
match tree with
|
|
| TDSwitch (_,edges,dflt,_) -> List.exists (dest_of_case >> dtree_has_non_trivial_bindings) edges || (Option.exists dtree_has_non_trivial_bindings dflt)
|
|
| TDSuccess _ -> false
|
|
| TDBind (_,t) -> isNone (target_of_success_dtree t)
|
|
|
|
// If a target has assignments and can only be reached through one
|
|
// branch (i.e. is "linear"), then transfer the assignments to the r.h.s. to be a "let".
|
|
let fold_linear_binding_targets_of_match tree targets =
|
|
|
|
// Don't do this when there are any bindings in the tree except where those bindings immediately cover a success node
|
|
// since the variables would be extruded from their scope.
|
|
if dtree_has_non_trivial_bindings tree then
|
|
tree,targets
|
|
|
|
else
|
|
|
|
// Build a map showing how each target might be reached
|
|
let rec acc_tips_of_dtree accBinds tree acc =
|
|
match tree with
|
|
| TDSwitch (_,edges,dflt,_) ->
|
|
assert (isNil accBinds)
|
|
List.foldBack (acc_tips_of_edge accBinds) edges (Option.fold_right (acc_tips_of_dtree accBinds) dflt acc)
|
|
| TDSuccess (es,i) ->
|
|
Map.add i ((List.rev accBinds,es) :: Map.tryFindMulti i acc) acc
|
|
| TDBind (bind,rest) ->
|
|
acc_tips_of_dtree (bind::accBinds) rest acc
|
|
|
|
and acc_tips_of_edge accBinds (TCase(_,x)) acc = acc_tips_of_dtree accBinds x acc
|
|
|
|
// Compute the targets that can only be reached one way
|
|
let linearTips =
|
|
acc_tips_of_dtree [] tree Map.empty
|
|
|> Map.filter (fun k v -> match v with [_] -> true | _ -> false)
|
|
|
|
if linearTips.IsEmpty then
|
|
|
|
tree,targets
|
|
|
|
else
|
|
|
|
/// rebuild the decision tree, replacing 'bind-then-success' decision trees by TDSuccess nodes that just go to the target
|
|
let rec rebuildDecisionTree tree =
|
|
|
|
// Check if this is a bind-then-success tree
|
|
match target_of_success_dtree tree with
|
|
| Some i when linearTips.ContainsKey i -> TDSuccess(FlatList.empty,i)
|
|
| _ ->
|
|
match tree with
|
|
| TDSwitch (e,edges,dflt,m) -> TDSwitch (e,List.map rebuildDecisionTreeEdge edges,Option.map rebuildDecisionTree dflt,m)
|
|
| TDSuccess _ -> tree
|
|
| TDBind _ -> tree
|
|
|
|
and rebuildDecisionTreeEdge (TCase(x,t)) =
|
|
TCase(x,rebuildDecisionTree t)
|
|
|
|
let tree' = rebuildDecisionTree tree
|
|
|
|
/// rebuild the targets , replacing linear targets by ones that include all the 'let' bindings from the source
|
|
let targets' =
|
|
targets |> Array.mapi (fun i (TTarget(vs,e,spTarget) as tg) ->
|
|
match Map.tryfind i linearTips with
|
|
| Some ((binds,es) :: _) ->
|
|
let m = (range_of_expr e)
|
|
TTarget(FlatList.empty,mk_lets_bind m binds (mk_invisible_lets_from_Bindings m vs es e),spTarget)
|
|
| _ -> tg )
|
|
|
|
tree',targets'
|
|
|
|
// Simplify a little as we go, including dead target elimination
|
|
let rec simplify_trivial_match spBind exprm matchm ty tree targets =
|
|
match tree with
|
|
| TDSuccess(es,n) ->
|
|
if n >= Array.length targets then failwith "simplify_trivial_match: target out of range";
|
|
let (TTarget(vs,rhs,spTarget)) = targets.[n]
|
|
if vs.Length <> es.Length then failwith ("simplify_trivial_match: invalid argument, n = "^string n^", List.length targets = "^string (Array.length targets));
|
|
mk_invisible_lets_from_Bindings (range_of_expr rhs) vs es rhs
|
|
| _ ->
|
|
prim_mk_match (spBind,exprm,tree,targets,matchm,ty)
|
|
|
|
(* Simplify a little as we go, including dead target elimination *)
|
|
let mk_and_optimize_match spBind exprm matchm ty tree targets =
|
|
let targets = Array.of_list targets
|
|
match tree with
|
|
| TDSuccess _ ->
|
|
simplify_trivial_match spBind exprm matchm ty tree targets
|
|
| _ ->
|
|
let tree,targets = eliminate_dead_targets_from_match tree targets
|
|
let tree,targets = fold_linear_binding_targets_of_match tree targets
|
|
simplify_trivial_match spBind exprm matchm ty tree targets
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
!* mk_expra_of_expr
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
type mutates = DefinitelyMutates | PossiblyMutates | NeverMutates
|
|
exception DefensiveCopyWarning of string * range
|
|
|
|
let type_immutable g ty =
|
|
match try_tcref_of_stripped_typ g ty with
|
|
| None -> false
|
|
| Some tcref ->
|
|
not (tcref_alloc_observable tcref) ||
|
|
tcref_eq g tcref g.decimal_tcr ||
|
|
tcref_eq g tcref g.date_tcr
|
|
|
|
let MustTakeAddressOfVal (v:ValRef) = v.IsMutable
|
|
|
|
let CanTakeAddressOfVal g (v:ValRef) mut =
|
|
// We can take the address of values of struct type if all instances of the type are
|
|
// known to be immutable.
|
|
// We only do this for true locals because we can't necessarily take adddresses
|
|
// across assemblies.
|
|
// Note: type_immutable should imply PossiblyMutates or NeverMutates
|
|
not (mut = DefinitelyMutates) &&
|
|
not v.IsMemberOrModuleBinding &&
|
|
type_immutable g v.Type
|
|
|
|
let MustTakeAddressOfRecdField (rfref: RecdFieldRef) = rfref.RecdField.IsMutable
|
|
let CanTakeAddressOfRecdField g (rfref: RecdFieldRef) mut tinst =
|
|
(not (mut = DefinitelyMutates) &&
|
|
// We only do this if the field is defined in this assembly because we can't take adddresses across assemblies fro immutable fields
|
|
tcref_in_this_assembly g.compilingFslib rfref.TyconRef &&
|
|
type_immutable g (actual_rtyp_of_rfref rfref tinst))
|
|
|
|
|
|
let rec mk_expra_of_expr g valu mut e m =
|
|
if not valu then (fun x -> x),e else
|
|
match e with
|
|
(* LVALUE: "x" where "x" is byref *)
|
|
| TExpr_op(TOp_lval_op (LByrefGet, v), _,[], m) ->
|
|
(fun x -> x), expr_for_vref m v
|
|
(* LVALUE: "x" where "x" is mutable local *)
|
|
| TExpr_val(v, _,m) when MustTakeAddressOfVal v || CanTakeAddressOfVal g v mut ->
|
|
(fun x -> x), mk_val_addr m v
|
|
(* LVALUE: "x" where "e.x" is mutable record field. "e" may be an lvalue *)
|
|
| TExpr_op(TOp_rfield_get rfref, tinst,[e],m) when MustTakeAddressOfRecdField rfref || CanTakeAddressOfRecdField g rfref mut tinst ->
|
|
let exprty = type_of_expr g e
|
|
let wrap,expra = mk_expra_of_expr g (is_struct_typ g exprty) mut e m
|
|
wrap, mk_recd_field_get_addr_via_expra(expra,rfref,tinst,m)
|
|
|
|
(* LVALUE: "x" where "e.x" is a .NET static field. *)
|
|
| TExpr_op(TOp_asm ([IL.I_ldsfld(vol,fspec)],[ty2]), tinst,[],m) ->
|
|
(fun x -> x),TExpr_op(TOp_asm ([IL.I_ldsflda(fspec)],[mk_byref_typ g ty2]), tinst,[],m)
|
|
|
|
(* LVALUE: "x" where "e.x" is a .NET instance field. "e" may be an lvalue *)
|
|
| TExpr_op(TOp_asm ([IL.I_ldfld(align,vol,fspec)],[ty2]), tinst,[e],m)
|
|
->
|
|
let exprty = type_of_expr g e
|
|
let wrap,expra = mk_expra_of_expr g (is_struct_typ g exprty) mut e m
|
|
wrap,TExpr_op(TOp_asm ([IL.I_ldflda(fspec)],[mk_byref_typ g ty2]), tinst,[expra],m)
|
|
|
|
(* LVALUE: "x" where "x" is mutable static field. *)
|
|
| TExpr_op(TOp_rfield_get rfref, tinst,[],m) when MustTakeAddressOfRecdField rfref || CanTakeAddressOfRecdField g rfref mut tinst ->
|
|
(fun x -> x), mk_static_rfield_get_addr(rfref,tinst,m)
|
|
|
|
// LVALUE: "e.[n]" where e is an array of structs
|
|
| TExpr_app(TExpr_val(vf,_,_),_,[elemTy],[aexpr;nexpr],_)
|
|
when (g.vref_eq vf g.arr1_lookup_vref) ->
|
|
|
|
let shape = Rank1ArrayShape
|
|
(fun x -> x), TExpr_op(TOp_asm ([IL.I_ldelema(NormalAddress,shape,IL.mk_tyvar_ty 0us)],[mk_byref_typ g elemTy]), [elemTy],[aexpr;nexpr],m)
|
|
|
|
// LVALUE: "e.[n1,n2]", "e.[n1,n2,n3]", "e.[n1,n2,n3,n4]" where e is an array of structs
|
|
| TExpr_app(TExpr_val(vf,_,_),_,[elemTy],[aexpr;TExpr_op(TOp_tuple,_,args,_)],_)
|
|
when (g.vref_eq vf g.arr2_lookup_vref || g.vref_eq vf g.arr3_lookup_vref || g.vref_eq vf g.arr4_lookup_vref) ->
|
|
|
|
let shape = ILArrayShape(Array.to_list (Array.create args.Length (None,None)))
|
|
|
|
(fun x -> x), TExpr_op(TOp_asm ([IL.I_ldelema(NormalAddress,shape,IL.mk_tyvar_ty 0us)],[mk_byref_typ g elemTy]), [elemTy],(aexpr::args),m)
|
|
|
|
| TExpr_val(v, _,m) when mut = DefinitelyMutates
|
|
->
|
|
if is_byref_typ g v.Type then error(Error("Unexpected use of a byref-typed variable",m));
|
|
error(Error("A value must be local and mutable in order to mutate the contents of a value type, e.g. 'let mutable x = ...'",m));
|
|
|
|
| _ ->
|
|
begin match mut with
|
|
| NeverMutates -> ()
|
|
| DefinitelyMutates ->
|
|
errorR(Error("Invalid mutation of a constant expression. Consider copying the expression to a mutable local, e.g. 'let mutable x = ...'",m));
|
|
| PossiblyMutates ->
|
|
warning(DefensiveCopyWarning("The value has been copied to ensure the original is not mutated by this operation",m));
|
|
end;
|
|
let tmp,tmpe = mk_mut_compgen_local m "copyOfStruct" (type_of_expr g e)
|
|
(fun rest -> mk_compgen_let m tmp e rest), (mk_val_addr m (mk_local_vref tmp))
|
|
|
|
let mk_recd_field_get g (e,fref:RecdFieldRef,tinst,finst,m) =
|
|
let ftyp = actual_rtyp_of_rfref fref tinst
|
|
let wrap,e' = mk_expra_of_expr g fref.Tycon.IsStructTycon NeverMutates e m
|
|
wrap (mk_tyapp m (mk_recd_field_get_via_expra(e',fref,tinst,m), ftyp) finst)
|
|
|
|
let mk_recd_field_set g (e,fref:RecdFieldRef,tinst,e2,m) =
|
|
let wrap,e' = mk_expra_of_expr g fref.Tycon.IsStructTycon DefinitelyMutates e m
|
|
wrap (mk_recd_field_set_via_expra(e',fref,tinst,e2,m))
|
|
|
|
(*---------------------------------------------------------------------------
|
|
!* Compute fixups for letrec's.
|
|
*
|
|
* Generate an assignment expression that will fixup the recursion
|
|
* amongst the vals on the r.h.s. of a letrec. The returned expressions
|
|
* include disorderly constructs such as expressions/statements
|
|
* to set closure environments and non-mutable fields. These are only ever
|
|
* generated by the backend code-generator when processing a "letrec"
|
|
* construct.
|
|
*
|
|
* [self] is the top level value that is being fixed
|
|
* [expr_to_fix] is the r.h.s. expression
|
|
* [rvs] is the set of recursive vals being bound.
|
|
* [acc] accumulates the expression right-to-left.
|
|
*
|
|
* Traversal of the r.h.s. term must happen back-to-front to get the
|
|
* uniq's for the lambdas correct in the very rare case where the same lambda
|
|
* somehow appears twice on the right.
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let rec iter_letrec_fixups g (selfv : Val option) rvs ((access : expr),set) expr_to_fix =
|
|
let expr_to_fix = strip_expr expr_to_fix
|
|
match expr_to_fix with
|
|
| TExpr_const _ -> ()
|
|
| TExpr_op(TOp_tuple,argtys,args,m) ->
|
|
args |> List.iteri (fun n ->
|
|
iter_letrec_fixups g None rvs
|
|
(mk_tuple_field_get(access,argtys,n,m),
|
|
(fun e ->
|
|
(* NICE: it would be better to do this check in the type checker *)
|
|
errorR(Error("Recursively defined values may not appear directly as part of the construction of a tuple value within a recursive binding.",m));
|
|
e)))
|
|
|
|
| TExpr_op(TOp_ucase (c),tinst,args,m) ->
|
|
args |> List.iteri (fun n ->
|
|
iter_letrec_fixups g None rvs
|
|
(mk_ucase_field_get_unproven(access,c,tinst,n,m),
|
|
(fun e ->
|
|
(* NICE: it would be better to do this check in the type checker *)
|
|
let tcref = c.TyconRef
|
|
errorR(Error("Recursive values may not appear directly as a construction of the type '"^tcref.MangledName^"' within a recursive binding. This feature has been removed from the F# language. Consider using a record instead",m));
|
|
mk_ucase_field_set(access,c,tinst,n,e,m))))
|
|
|
|
| TExpr_op(TOp_recd (_,tcref),tinst,args,m) ->
|
|
(instance_rfrefs_of_tcref tcref, args) ||> List.iter2 (fun fref arg ->
|
|
let fspec = fref.RecdField
|
|
iter_letrec_fixups g None rvs
|
|
(mk_recd_field_get_via_expra(access,fref,tinst,m),
|
|
(fun e ->
|
|
(* NICE: it would be better to do this check in the type checker *)
|
|
if not fspec.IsMutable && not (tcref_in_this_assembly g.compilingFslib tcref) then
|
|
errorR(Error("Recursive values may not be directly assigned to the non-mutable field '"^fspec.rfield_id.idText^"' of the type '"^tcref.MangledName^"' within a recursive binding. Consider using a mutable field instead",m));
|
|
mk_recd_field_set g (access,fref,tinst,e,m))) arg )
|
|
| TExpr_val (_,_,m)
|
|
| TExpr_lambda (_,_,_,_,m,_,_)
|
|
| TExpr_obj (_,_,_,_,_,_,m,_)
|
|
| TExpr_tchoose (_,_,m)
|
|
| TExpr_tlambda (_,_,_,m,_,_) ->
|
|
rvs selfv access set expr_to_fix
|
|
| e -> ()
|
|
|
|
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* computations on constraints
|
|
*------------------------------------------------------------------------*)
|
|
|
|
let JoinTyparStaticReq r1 r2 =
|
|
match r1,r2 with
|
|
| NoStaticReq,r | r,NoStaticReq -> r
|
|
| HeadTypeStaticReq,r | r,HeadTypeStaticReq -> r
|
|
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
!* ExprFolder - fold steps
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
type ExprFolder<'a> = {exprIntercept : ('a -> expr -> 'a) -> 'a -> expr -> 'a option; (* intercept? *)
|
|
(* hook. this bool is 'bound in dtree' *)
|
|
valBindingSiteIntercept : 'a -> bool * Val -> 'a;
|
|
(* hook. these values are always bound to these expressions. bool is 'recursively' *)
|
|
nonRecBindingsIntercept : 'a -> Binding -> 'a;
|
|
recBindingsIntercept : 'a -> Bindings -> 'a;
|
|
dtreeAcc : 'a -> DecisionTree -> 'a; (* hook *)
|
|
targetIntercept : ('a -> expr -> 'a) -> 'a -> DecisionTreeTarget -> 'a option; (* intercept? *)
|
|
tmethodIntercept : ('a -> expr -> 'a) -> 'a -> ObjExprMethod -> 'a option; (* intercept? *)
|
|
}
|
|
|
|
let ExprFolder0 =
|
|
{ exprIntercept = (fun exprF z x -> None);
|
|
valBindingSiteIntercept = (fun z b -> z);
|
|
nonRecBindingsIntercept = (fun z bs -> z);
|
|
recBindingsIntercept = (fun z bs -> z);
|
|
dtreeAcc = (fun z dt -> z);
|
|
targetIntercept = (fun exprF z x -> None);
|
|
tmethodIntercept = (fun exprF z x -> None);
|
|
}
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
!* FoldExpr
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let mkFolders (folders : _ ExprFolder) =
|
|
(******
|
|
* Adapted from usage info folding.
|
|
* Collecting from exprs at moment.
|
|
* To collect ids etc some additional folding needed, over formals etc.
|
|
******)
|
|
let {exprIntercept = exprIntercept;
|
|
valBindingSiteIntercept = valBindingSiteIntercept;
|
|
nonRecBindingsIntercept = nonRecBindingsIntercept;
|
|
recBindingsIntercept = recBindingsIntercept;
|
|
dtreeAcc = dtreeAcc;
|
|
targetIntercept = targetIntercept;
|
|
tmethodIntercept = tmethodIntercept} = folders
|
|
let rec exprsF z xs = List.fold exprF z xs
|
|
and flatExprsF z xs = FlatList.fold exprF z xs
|
|
and exprF z x =
|
|
match exprIntercept exprF z x with (* fold this node, then recurse *)
|
|
| Some z -> z (* intercepted *)
|
|
| None -> (* structurally recurse *)
|
|
match x with
|
|
| TExpr_const (c,m,ty) -> z
|
|
| TExpr_val (v,isSuperInit,m) -> z
|
|
| TExpr_op (c,tyargs,args,m) -> exprsF z args
|
|
| TExpr_seq (x0,x1,dir,_,m) -> exprsF z [x0;x1]
|
|
| TExpr_lambda(lambda_id ,basevopt,argvs,body,m,rty,_) -> exprF z body
|
|
| TExpr_tlambda(lambda_id,argtyvs,body,m,rty,_) -> exprF z body
|
|
| TExpr_tchoose(_,body,m) -> exprF z body
|
|
| TExpr_app (f,fty,tys,argtys,m) ->
|
|
let z = exprF z f
|
|
let z = exprsF z argtys
|
|
z
|
|
| TExpr_letrec (binds,body,m,_) ->
|
|
let z = valBindsF false z binds
|
|
let z = exprF z body
|
|
z
|
|
| TExpr_let (bind,body,m,_) ->
|
|
let z = valBindF false z bind
|
|
let z = exprF z body
|
|
z
|
|
| TExpr_link rX -> exprF z (!rX)
|
|
| TExpr_match (spBind,exprm,dtree,targets,m,ty,_) ->
|
|
let z = dtreeF z dtree
|
|
let z = Array.fold_left targetF z targets
|
|
z
|
|
| TExpr_quote(e,{contents=Some(argTypes,argExprs,_)},m,_) -> exprsF z argExprs
|
|
| TExpr_quote(e,{contents=None},m,_) -> z
|
|
| TExpr_obj (n,typ,basev,basecall,overrides,iimpls,m,_) ->
|
|
let z = exprF z basecall
|
|
let z = List.fold tmethodF z overrides
|
|
let z = List.fold (foldOn snd (List.fold tmethodF)) z iimpls
|
|
z
|
|
| TExpr_static_optimization (tcs,csx,x,m) -> exprsF z [csx;x]
|
|
and valBindF dtree z bind =
|
|
let z = nonRecBindingsIntercept z bind
|
|
bindF dtree z bind
|
|
and valBindsF dtree z binds =
|
|
let z = recBindingsIntercept z binds
|
|
FlatList.fold (bindF dtree) z binds
|
|
|
|
and bindF dtree z (bind:Binding) =
|
|
let z = valBindingSiteIntercept z (dtree,bind.Var)
|
|
exprF z bind.Expr
|
|
|
|
and dtreeF z dtree =
|
|
let z = dtreeAcc z dtree
|
|
match dtree with
|
|
| TDBind (bind,rest) ->
|
|
let z = valBindF true z bind
|
|
dtreeF z rest
|
|
| TDSuccess (args,n) -> flatExprsF z args
|
|
| TDSwitch (test,dcases,dflt,r) ->
|
|
let z = exprF z test
|
|
let z = List.fold dcaseF z dcases
|
|
let z = Option.fold dtreeF z dflt
|
|
z
|
|
|
|
and dcaseF z = function
|
|
TCase (test,dtree) -> dtreeF z dtree (* not collecting from test *)
|
|
|
|
and targetF z x =
|
|
(match targetIntercept exprF z x with
|
|
Some z -> z (* intercepted *)
|
|
| None -> (* structurally recurse *)
|
|
let (TTarget (argvs,body,_)) = x
|
|
exprF z body)
|
|
|
|
and tmethodF z x =
|
|
(match tmethodIntercept exprF z x with
|
|
Some z -> z (* intercepted *)
|
|
| None -> (* structurally recurse *)
|
|
let (TObjExprMethod(_,_,_,e,_)) = x
|
|
exprF z e)
|
|
|
|
and mexprF z x =
|
|
match x with
|
|
| TMTyped(mtyp,def,m) -> mdefF z def
|
|
and mdefF z x =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) ->
|
|
(* REVIEW: also iterate the abstract slot vspecs hidden in the _vslots field in the tycons *)
|
|
let z = valBindsF false z binds
|
|
let z = List.fold mbindF z mbinds
|
|
z
|
|
| TMDefLet(bind,m) -> valBindF false z bind
|
|
| TMDefDo(e,m) -> exprF z e
|
|
| TMDefs(defs) -> List.fold mdefF z defs
|
|
| TMAbstract(x) -> mexprF z x
|
|
and mbindF z (TMBind(nm, def)) = mdefF z def
|
|
|
|
and implF z x = foldTImplFile mexprF z x
|
|
and implsF z (TAssembly(x)) = List.fold implF z x
|
|
|
|
|
|
exprF, implF,implsF
|
|
|
|
let FoldExpr folders = let exprF,implF,implsF = mkFolders folders in exprF
|
|
let FoldImplFile folders = let exprF,implF,implsF = mkFolders folders in implF
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
!* ExprStats
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
let ExprStats x =
|
|
let count = ref 0
|
|
let folders = {ExprFolder0 with exprIntercept = (fun exprF z x -> (count := !count + 1; None))}
|
|
let () = FoldExpr folders () x
|
|
string !count ^ " TExpr nodes"
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
!*
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let mk_string g m n = TExpr_const(TConst_string n,m,g.string_ty)
|
|
let mk_int64 g m n = TExpr_const(TConst_int64 n,m,g.int64_ty)
|
|
let mk_bool g m b = TExpr_const(TConst_bool b,m,g.bool_ty)
|
|
let mk_byte g m b = TExpr_const(TConst_byte b,m,g.byte_ty)
|
|
let mk_uint16 g m b = TExpr_const(TConst_uint16 b,m,g.uint16_ty)
|
|
let mk_true g m = mk_bool g m true
|
|
let mk_false g m = mk_bool g m false
|
|
let mk_unit g m = TExpr_const(TConst_unit,m,g.unit_ty)
|
|
let mk_int32 g m n = TExpr_const(TConst_int32 n,m,g.int32_ty)
|
|
let mk_int g m n = mk_int32 g m (n)
|
|
let mk_zero g m = mk_int g m 0
|
|
let mk_one g m = mk_int g m 1
|
|
let mk_two g m = mk_int g m 2
|
|
let mk_minus_one g m = mk_int g m (-1)
|
|
|
|
let dest_int32 = function TExpr_const(TConst_int32 n,m,ty) -> Some n | _ -> None
|
|
|
|
let is_fslib_IDelegateEvent_ty g ty = is_stripped_tyapp_typ g ty && tcref_eq g g.fslib_IDelegateEvent_tcr (tcref_of_stripped_typ g ty)
|
|
let dest_fslib_IDelegateEvent_ty g ty =
|
|
if is_fslib_IDelegateEvent_ty g ty then
|
|
match tinst_of_stripped_typ g ty with
|
|
| [ty1] -> ty1
|
|
| _ -> failwith "dest_fslib_IDelegateEvent_ty: internal error"
|
|
else failwith "dest_fslib_IDelegateEvent_ty: not an IDelegateEvent type"
|
|
let mk_fslib_IEvent2_ty g ty1 ty2 = TType_app (g.fslib_IEvent2_tcr, [ty1;ty2])
|
|
|
|
let mk_refcell_contents_rfref g = mk_rfref g.refcell_tcr "contents"
|
|
|
|
let typed_expr_for_val m (v:Val) = expr_for_val m v,v.Type
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* Tuples...
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let mk_tupled g m es tys =
|
|
match es with
|
|
| [] -> mk_unit g m
|
|
| [e] -> e
|
|
| _ -> TExpr_op(TOp_tuple,tys,es,m)
|
|
|
|
let mk_tupled_notypes g m args = mk_tupled g m args (List.map (type_of_expr g) args)
|
|
|
|
let mk_tupled_vars g m vs = mk_tupled g m (List.map (expr_for_val m) vs) (types_of_vals vs)
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Permutations
|
|
*------------------------------------------------------------------------*)
|
|
|
|
let inverse_perm (sigma:int array) =
|
|
let n = Array.length sigma
|
|
let inv_sigma = Array.create n (-1)
|
|
for i = 0 to n-1 do
|
|
let sigma_i = sigma.[i]
|
|
(* assert( inv_sigma.[sigma_i] = -1 ); *)
|
|
inv_sigma.[sigma_i] <- i
|
|
done;
|
|
inv_sigma
|
|
|
|
let permute (sigma:int array) (data:'a array) =
|
|
let n = Array.length sigma
|
|
let inv_sigma = inverse_perm sigma
|
|
Array.init n (fun i -> data.[inv_sigma.[i]])
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Permute expressions
|
|
*------------------------------------------------------------------------*)
|
|
|
|
let rec existsR a b pred = if a<=b then pred a || existsR (a+1) b pred else false
|
|
let mapi_acc_list f z xs =
|
|
let rec fmapi f i z = function
|
|
| [] -> z,[]
|
|
| x::xs -> let z,x = f i z x
|
|
let z,xs = fmapi f (i+1) z xs
|
|
z,x::xs
|
|
|
|
fmapi f 0 z xs
|
|
|
|
/// Given expr = xi = [| x0; ... xN |]
|
|
/// Given sigma a permutation to apply to the xi.
|
|
/// Return (bindings',expr') such that:
|
|
/// (a) xi are permutated under sigma, xi -> position sigma(i).
|
|
///------
|
|
/// Motivation:
|
|
/// opt.ml - put record field assignments in order under known effect information
|
|
/// ilxgen.ml - put record field assignments in order if necessary (no optimisations)
|
|
/// under unknown-effect information.
|
|
let permuteExpr (sigma:int array) (expr:expr array) (typ:typ array) (names:string array) =
|
|
let inv_sigma = inverse_perm sigma
|
|
let liftPosition i =
|
|
(* In english, lift out xi if
|
|
* LC2: xi goes to position that will be preceded by
|
|
* (an expr with an effect that originally followed xi).
|
|
*)
|
|
(let i' = sigma.[i]
|
|
existsR 0 (i' - 1) (fun j' ->
|
|
let j = inv_sigma.[j']
|
|
j > i))
|
|
|
|
let rewrite i rbinds xi =
|
|
if liftPosition i then
|
|
let tmpv,tmpe = mk_compgen_local (range_of_expr xi) names.[i] typ.[i]
|
|
let bind = mk_compgen_bind tmpv xi
|
|
bind :: rbinds,tmpe
|
|
else
|
|
rbinds,xi
|
|
|
|
let xis = Array.to_list expr
|
|
let rbinds,xis = mapi_acc_list rewrite [] xis
|
|
let binds = List.rev rbinds
|
|
let expr = permute sigma (Array.of_list xis)
|
|
binds,expr
|
|
|
|
let permuteExprList (sigma:int array) (expr:expr list) (typ:typ list) (names:string list) =
|
|
let binds,expr = permuteExpr sigma (Array.of_list expr) (Array.of_list typ) (Array.of_list names)
|
|
binds,Array.to_list expr
|
|
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* Build lazy expressions...
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let mk_seq spSeq m e1 e2 = TExpr_seq(e1,e2,NormalSeq,spSeq,m)
|
|
let mk_compgen_seq m e1 e2 = mk_seq SuppressSequencePointOnExprOfSequential m e1 e2
|
|
let rec mk_seqs spSeq g m es =
|
|
match es with
|
|
| [e] -> e
|
|
| e::es -> mk_seq spSeq m e (mk_seqs spSeq g m es)
|
|
| [] -> mk_unit g m
|
|
|
|
/// Evaluate the expressions in the original order, but build a record with the results in field order
|
|
/// Note some fields may be static. If this were not the case we could just use
|
|
/// let sigma = Array.map rfref_index ()
|
|
/// However the presence of static fields means rfref_index may index into a non-compact set of instance field indexes.
|
|
/// We still need to sort by index.
|
|
let mk_recd g (lnk,tcref,tinst,rfrefs,args,m) =
|
|
(* Remove any abbreviations *)
|
|
let tcref,tinst = dest_stripped_tyapp_typ g (mk_tyapp_ty tcref tinst)
|
|
|
|
let rfrefs_array = Array.of_list (List.mapi (fun i x -> (i,x)) rfrefs)
|
|
Array.sortInPlaceBy (snd >> rfref_index) rfrefs_array;
|
|
let sigma = Array.create (Array.length rfrefs_array) (-1)
|
|
Array.iteri (fun j (i,_) ->
|
|
if sigma.[i] <> -1 then error(InternalError("bad permutation",m));
|
|
sigma.[i] <- j) rfrefs_array;
|
|
|
|
let argTyps = List.map (fun rfref -> actual_rtyp_of_rfref rfref tinst) rfrefs
|
|
let names = rfrefs |> List.map (fun rfref -> rfref.FieldName)
|
|
let binds,args = permuteExprList sigma args argTyps names
|
|
mk_lets_bind m binds (TExpr_op(TOp_recd(lnk,tcref),tinst,args,m))
|
|
|
|
let mk_ldarg0 m ty = mk_asm( [ ldarg_0 ],[],[],[ty],m)
|
|
|
|
let mk_refcell g m ty e = mk_recd g (RecdExpr,g.refcell_tcr,[ty],[mk_refcell_contents_rfref g],[e],m)
|
|
let mk_refcell_get g m ty e = mk_recd_field_get g (e,mk_refcell_contents_rfref g,[ty],[],m)
|
|
let mk_refcell_set g m ty e1 e2 = mk_recd_field_set g (e1,mk_refcell_contents_rfref g,[ty],e2,m)
|
|
|
|
(*-------------------------------------------------------------------------
|
|
* List builders
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let mk_nil g m ty = mk_ucase (g.nil_ucref,[ty],[],m)
|
|
let mk_cons g ty h t = mk_ucase (g.cons_ucref,[ty],[h;t],union_ranges (range_of_expr h) (range_of_expr t))
|
|
|
|
let mk_compgen_local_and_invisible_bind g nm m e =
|
|
let locv,loce = mk_compgen_local m nm (type_of_expr g e)
|
|
locv,loce,mk_invisible_bind locv e
|
|
|
|
(*----------------------------------------------------------------------------
|
|
* Make some fragments of code
|
|
*--------------------------------------------------------------------------*)
|
|
|
|
let box = IL.I_box (IL.mk_tyvar_ty 0us)
|
|
let isinst = IL.I_isinst (IL.mk_tyvar_ty 0us)
|
|
let unbox = IL.I_unbox_any (IL.mk_tyvar_ty 0us)
|
|
let mk_unbox ty e m = mk_asm ([ unbox ], [ty],[e], [ ty ], m)
|
|
let mk_isinst ty e m = mk_asm ([ isinst ], [ty],[e], [ ty ], m)
|
|
|
|
let mspec_Object_GetHashCode ilg = IL.mk_nongeneric_instance_mspec_in_nongeneric_boxed_tref(ilg.tref_Object,"GetHashCode",[],ilg.typ_int32)
|
|
let mspec_Type_GetTypeFromHandle ilg = IL.mk_static_nongeneric_mspec_in_nongeneric_boxed_tref(ilg.tref_Type,"GetTypeFromHandle",[ilg.typ_RuntimeTypeHandle],ilg.typ_Type)
|
|
let fspec_Missing_Value ilg = IL.mk_fspec_in_nongeneric_boxed_tref(ilg.tref_Missing,"Value",ilg.typ_Missing)
|
|
|
|
|
|
let typed_expr_for_val_info m (Intrinsic(mvr,nm,ty) as i) =
|
|
let e = vref_for_val_info i
|
|
expr_for_vref m e,ty
|
|
|
|
let mk_call_get_generic_comparer g m = mk_appl g (typed_expr_for_val_info m g.get_generic_comparer_info, [], [ mk_unit g m ], m)
|
|
let mk_call_get_generic_equality_comparer g m = mk_appl g (typed_expr_for_val_info m g.get_generic_equality_comparer_info, [], [ mk_unit g m ], m)
|
|
let mk_call_unbox g m ty e1 = mk_appl g (typed_expr_for_val_info m g.unbox_info, [[ty]], [ e1 ], m)
|
|
let mk_call_unbox_fast g m ty e1 = mk_appl g (typed_expr_for_val_info m g.unbox_fast_info, [[ty]], [ e1 ], m)
|
|
let mk_call_istype g m ty e1 = mk_appl g (typed_expr_for_val_info m g.istype_info, [[ty]], [ e1 ], m)
|
|
let mk_call_istype_fast g m ty e1 = mk_appl g (typed_expr_for_val_info m g.istype_fast_info, [[ty]], [ e1 ], m)
|
|
let mk_call_typeof g m ty = mk_appl g (typed_expr_for_val_info m g.typeof_info, [[ty]], [ ], m)
|
|
|
|
let mk_call_dispose g m ty e1 = mk_appl g (typed_expr_for_val_info m g.dispose_info, [[ty]], [ e1 ], m)
|
|
let mk_call_seq g m ty e1 = mk_appl g (typed_expr_for_val_info m g.seq_info, [[ty]], [ e1 ], m)
|
|
let mk_call_create_instance g m ty = mk_appl g (typed_expr_for_val_info m g.create_instance_info, [[ty]], [ (mk_unit g m) ], m)
|
|
|
|
let mk_call_generic_comparison_outer g m ty e1 e2 = mk_appl g (typed_expr_for_val_info m g.generic_comparison_outer_info, [[ty]], [ e1;e2 ], m)
|
|
let mk_call_generic_comparison_withc_outer g m ty comp e1 e2 = mk_appl g (typed_expr_for_val_info m g.generic_comparison_withc_outer_info, [[ty]], [ comp;e1;e2 ], m)
|
|
let mk_call_generic_equality_outer g m ty e1 e2 = mk_appl g (typed_expr_for_val_info m g.generic_equality_outer_info, [[ty]], [ e1;e2 ], m)
|
|
let mk_call_generic_equality_withc_outer g m ty comp e1 e2 = mk_appl g (typed_expr_for_val_info m g.generic_equality_withc_outer_info, [[ty]], [comp;e1;e2], m)
|
|
//let mk_call_generic_hash_outer g m ty e1 = mk_appl g (typed_expr_for_val_info m g.generic_hash_outer_info, [[ty]], [e1], m)
|
|
let mk_call_generic_hash_withc_outer g m ty comp e1 = mk_appl g (typed_expr_for_val_info m g.generic_hash_withc_outer_info, [[ty]], [comp;e1], m)
|
|
|
|
let mk_call_array_get g m ty e1 e2 = mk_appl g (typed_expr_for_val_info m g.array_get_info, [[ty]], [ e1 ; e2 ], m)
|
|
let mk_call_new_decimal g m (e1,e2,e3,e4,e5) = mk_appl g (typed_expr_for_val_info m g.new_decimal_info, [], [ e1;e2;e3;e4;e5 ], m)
|
|
|
|
let mk_call_string_compare g m e1 e2 = mk_call_generic_comparison_outer g m g.string_ty e1 e2
|
|
|
|
let mk_call_new_format g m aty bty cty dty ety e1 = mk_appl g (typed_expr_for_val_info m g.new_format_info, [[aty;bty;cty;dty;ety]], [ e1 ], m)
|
|
let mk_call_raise g m aty e1 = mk_appl g (typed_expr_for_val_info m g.raise_info, [[aty]], [ e1 ], m)
|
|
|
|
let TryEliminateDesugaredConstants g m c =
|
|
match c with
|
|
|
|
| TConst_decimal d ->
|
|
match System.Decimal.GetBits(d) with
|
|
| [| lo;med;hi; signExp |] ->
|
|
let scale = (min (((signExp &&& 0xFF0000) >>> 16) &&& 0xFF) 28) |> byte
|
|
let isNegative = (signExp &&& 0x80000000) <> 0
|
|
|
|
Some(mk_call_new_decimal g m (mk_int g m lo,mk_int g m med,mk_int g m hi,mk_bool g m isNegative,mk_byte g m scale) )
|
|
| _ -> failwith "unreachable"
|
|
|
|
| _ ->
|
|
None
|
|
|
|
let mk_seq_ty g ty = mk_tyapp_ty g.seq_tcr [ty]
|
|
let mk_IEnumerator_ty g ty = mk_tyapp_ty g.tcref_System_Collections_Generic_IEnumerator [ty]
|
|
|
|
let mk_call_seq_map_concat g m alphaTy betaTy arg1 arg2 =
|
|
let enumty2 = try range_of_fun_typ g (type_of_expr g arg1) with _ -> (* defensive programming *) (mk_seq_ty g betaTy)
|
|
mk_appl g (typed_expr_for_val_info m g.seq_map_concat_info, [[alphaTy;enumty2;betaTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_using g m resourceTy elemTy arg1 arg2 =
|
|
(* We're intantiating val using : 'a -> ('a -> 'sb) -> seq<'b> when 'sb :> seq<'b> and 'a :> IDisposable *)
|
|
(* We set 'sb -> range(typeof(arg2)) *)
|
|
let enumty = try range_of_fun_typ g (type_of_expr g arg2) with _ -> (* defensive programming *) (mk_seq_ty g elemTy)
|
|
mk_appl g (typed_expr_for_val_info m g.seq_using_info, [[resourceTy;enumty;elemTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_delay g m elemTy arg1 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_delay_info, [[elemTy]], [ arg1 ], m)
|
|
|
|
let mk_call_seq_append g m elemTy arg1 arg2 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_append_info, [[elemTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_generated g m elemTy arg1 arg2 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_generated_info, [[elemTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_finally g m elemTy arg1 arg2 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_finally_info, [[elemTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_of_functions g m ty1 ty2 arg1 arg2 arg3 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_of_functions_info, [[ty1;ty2]], [ arg1; arg2; arg3 ], m)
|
|
|
|
let mk_call_seq_to_array g m elemTy arg1 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_to_array_info, [[elemTy]], [ arg1 ], m)
|
|
|
|
let mk_call_seq_to_list g m elemTy arg1 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_to_list_info, [[elemTy]], [ arg1 ], m)
|
|
|
|
let mk_call_seq_map g m inpElemTy genElemTy arg1 arg2 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_map_info, [[inpElemTy;genElemTy]], [ arg1; arg2 ], m)
|
|
|
|
let mk_call_seq_singleton g m ty1 arg1 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_singleton_info, [[ty1]], [ arg1 ], m)
|
|
|
|
let mk_call_seq_empty g m ty1 =
|
|
mk_appl g (typed_expr_for_val_info m g.seq_empty_info, [[ty1]], [ ], m)
|
|
|
|
let mk_call_unpickle_quotation g m e1 e2 e3 e4 =
|
|
let args = [ e1; e2; e3; e4 ]
|
|
mk_appl g (typed_expr_for_val_info m g.unpickle_quoted_info, [], [ mk_tupled_notypes g m args ], m)
|
|
|
|
let mk_call_cast_quotation g m ty e1 =
|
|
mk_appl g (typed_expr_for_val_info m g.cast_quotation_info, [[ty]], [ e1 ], m)
|
|
|
|
let mk_call_lift_value g m ty e1 =
|
|
mk_appl g (typed_expr_for_val_info m g.lift_value_info , [[ty]], [ e1], m)
|
|
|
|
let mk_lazy_delayed g m ty f = mk_appl g (typed_expr_for_val_info m g.lazy_create_info, [[ty]], [ f ], m)
|
|
let mk_lazy_force g m ty e = mk_appl g (typed_expr_for_val_info m g.lazy_force_info, [[ty]], [ e; mk_unit g m ], m)
|
|
|
|
|
|
let query_asm e =
|
|
match strip_expr e with
|
|
| TExpr_op(TOp_asm (instrs,_),[],args,_) ->Some(instrs,args)
|
|
| _ -> None
|
|
|
|
let dest_incr e =
|
|
match query_asm e with
|
|
| Some([ IL.I_arith IL.AI_add ],[TExpr_const(TConst_int32 1,_,_) ;arg2]) -> Some(arg2)
|
|
| _ -> None
|
|
|
|
// Note: We plan to get rid of all IL generation in the typechecker and pattern match
|
|
// compiler, or else train the quotation generator to understand the generated IL.
|
|
// Hence each of the following are marked with places where they are generated.
|
|
|
|
// Generated by the optimizer and the encoding of 'for' loops
|
|
let mk_decr g m e = mk_asm([ IL.I_arith IL.AI_sub ],[],[e; mk_one g m],[g.int_ty],m)
|
|
let mk_incr g m e = mk_asm([ IL.I_arith IL.AI_add ],[],[mk_one g m; e],[g.int_ty],m)
|
|
|
|
// Generated by the pattern match compiler and the optimizer for
|
|
// 1. array patterns
|
|
// 2. optimizations associated with getting 'for' loops into the shape expected by the JIT.
|
|
//
|
|
// NOTE: The conv.i4 assumes that int_ty is int32. Note: ldlen returns native UNSIGNED int
|
|
//
|
|
// REVIEW: quotation processing doesn't yet understand this, giving a problem with quoting
|
|
// constructors of array pattern matching
|
|
let mk_ldlen g m arre = mk_asm ([ IL.I_ldlen; IL.I_arith (IL.AI_conv IL.DT_I4) ],[],[ arre ], [ g.int_ty ], m)
|
|
|
|
// This is generated in equality/compare/hash augmentations and in the pattern match compiler.
|
|
// It is understood by the quotation processor and turned into "Equality" nodes.
|
|
let mk_ceq g m e1 e2 = mk_asm ([ IL.I_arith IL.AI_ceq ],[], [e1; e2],[g.bool_ty],m)
|
|
|
|
// This is generated in the initialization of the "ctorv" field in the typechecker's compilation of
|
|
// an implicit class construction.
|
|
let mk_null m ty = TExpr_const(TConst_zero, m,ty)
|
|
|
|
(*----------------------------------------------------------------------------
|
|
* rethrow
|
|
*--------------------------------------------------------------------------*)
|
|
|
|
(* throw, rethrow *)
|
|
let mk_throw m ty e = mk_asm ([ IL.I_throw ],[], [e],[ty],m)
|
|
let dest_throw = function
|
|
| TExpr_op(TOp_asm([IL.I_throw],[ty2]),[],[e],m) -> Some (m,ty2,e)
|
|
| _ -> None
|
|
let is_throw x = isSome (dest_throw x)
|
|
|
|
// rethrow - parsed as library call - internally represented as op form.
|
|
let mk_rethrow_library_call g ty m = let ve,vt = typed_expr_for_val_info m g.rethrow_info in TExpr_app(ve,vt,[ty],[mk_unit g m],m)
|
|
let mk_rethrow m returnTy = TExpr_op(TOp_rethrow,[returnTy],[],m) (* could suppress unitArg *)
|
|
|
|
(*----------------------------------------------------------------------------
|
|
* CompilationMappingAttribute, SourceConstructFlags
|
|
*--------------------------------------------------------------------------*)
|
|
|
|
let tref_CompilationMappingAttr() = mk_tref(Msilxlib.scoref (), lib_MFCore_name ^ ".CompilationMappingAttribute")
|
|
let tref_SourceConstructFlags ()= mk_tref(Msilxlib.scoref (), lib_MFCore_name ^ ".SourceConstructFlags")
|
|
let IsCompilationMappingAttr cattr = is_il_attrib (tref_CompilationMappingAttr ()) cattr
|
|
let mk_CompilationMappingAttrPrim g k nums =
|
|
mk_custom_attribute g.ilg (tref_CompilationMappingAttr(),
|
|
((mk_nongeneric_value_typ (tref_SourceConstructFlags())) :: (nums |> List.map (fun _ -> g.ilg.typ_Int32))),
|
|
((k :: nums) |> List.map (fun n -> CustomElem_int32(n))),
|
|
[])
|
|
let mk_CompilationMappingAttr g kind = mk_CompilationMappingAttrPrim g kind []
|
|
let mk_CompilationMappingAttrWithSeqNum g kind seqNum = mk_CompilationMappingAttrPrim g kind [seqNum]
|
|
let mk_CompilationMappingAttrWithVariantNumAndSeqNum g kind varNum seqNum = mk_CompilationMappingAttrPrim g kind [varNum;seqNum]
|
|
|
|
(*----------------------------------------------------------------------------
|
|
* FSharpInterfaceDataVersionAttribute
|
|
*--------------------------------------------------------------------------*)
|
|
|
|
let tref_SignatureDataVersionAttr () =
|
|
mk_tref(Msilxlib.scoref (), lib_MFCore_name ^ ".FSharpInterfaceDataVersionAttribute")
|
|
|
|
let mk_SignatureDataVersionAttr g ((v1,v2,v3,v4) : ILVersionInfo) =
|
|
mk_custom_attribute g.ilg
|
|
(tref_SignatureDataVersionAttr(),
|
|
[g.ilg.typ_Int32;g.ilg.typ_Int32;g.ilg.typ_Int32],
|
|
[CustomElem_int32 (int32 v1);
|
|
CustomElem_int32 (int32 v2) ;
|
|
CustomElem_int32 (int32 v3)],[])
|
|
|
|
let tref_AutoOpenAttr () =
|
|
mk_tref(Msilxlib.scoref (), lib_MFCore_name ^ ".AutoOpenAttribute")
|
|
|
|
let IsSignatureDataVersionAttr cattr = is_il_attrib (tref_SignatureDataVersionAttr ()) cattr
|
|
let TryFindAutoOpenAttr cattr =
|
|
if is_il_attrib (tref_AutoOpenAttr ()) cattr then
|
|
(* ok to use ecmaILGlobals here since we're querying metadata, not making it *)
|
|
match decode_il_attrib_data IL.ecmaILGlobals cattr with
|
|
| [CustomElem_string s],_ -> s
|
|
| [],_ -> None
|
|
| _ ->
|
|
warning(Failure("Unexpected decode of AutoOpenAttribute"));
|
|
None
|
|
else
|
|
None
|
|
|
|
let tref_InternalsVisibleToAttr () =
|
|
mk_tref (ecma_mscorlib_scoref,"System.Runtime.CompilerServices.InternalsVisibleToAttribute")
|
|
|
|
let TryFindInternalsVisibleToAttr cattr =
|
|
if is_il_attrib (tref_InternalsVisibleToAttr ()) cattr then
|
|
(* ok to use ecmaILGlobals here since we're querying metadata, not making it *)
|
|
match decode_il_attrib_data IL.ecmaILGlobals cattr with
|
|
| [CustomElem_string s],_ -> s
|
|
| [],_ -> None
|
|
| _ ->
|
|
warning(Failure("Unexpected decode of InternalsVisibleToAttribute"));
|
|
None
|
|
else
|
|
None
|
|
|
|
let IsMatchingSignatureDataVersionAttr ((v1,v2,v3,v4) : ILVersionInfo) cattr =
|
|
IsSignatureDataVersionAttr cattr &&
|
|
(* ok to use ecmaILGlobals here since we're querying metadata, not making it *)
|
|
match decode_il_attrib_data IL.ecmaILGlobals cattr with
|
|
| [CustomElem_int32 u1; CustomElem_int32 u2;CustomElem_int32 u3 ],_ ->
|
|
// add this specific case to reject the CTP format, which also shares prefix 1.9.6
|
|
let fullName = cattr.customMethod.EnclosingType.TypeSpec.FullName
|
|
if fullName.Contains("Version=1.9.6.3,") || fullName.Contains("Version=1.9.6.2,") || fullName.Contains("Version=1.9.6.0,") then false
|
|
else (v1 = uint16 u1) && (v2 = uint16 u2) && (v3 = uint16 u3)
|
|
| _ -> warning(Failure("Unexpected decode of InterfaceDataVersionAttribute")); false
|
|
|
|
let mk_CompilerGeneratedAttr g n = mk_custom_attribute g.ilg (tref_CompilationMappingAttr(), [mk_nongeneric_value_typ (tref_SourceConstructFlags())],[CustomElem_int32(n)],[])
|
|
|
|
(* match inp with :? ty as v -> e2[v] | _ -> e3 *)
|
|
let mk_isinst_cond g m tgty vinpe v e2 e3 =
|
|
// No sequence point for this simple expression form
|
|
let mbuilder = new MatchBuilder(NoSequencePointAtInvisibleBinding,m)
|
|
let tg2 = TDSuccess(FlatList.one (mk_call_unbox g m tgty vinpe), mbuilder.AddTarget(TTarget(FlatList.one v,e2,SuppressSequencePointAtTarget)))
|
|
let tg3 = mbuilder.AddResultTarget(e3,SuppressSequencePointAtTarget)
|
|
let dtree = TDSwitch(vinpe,[TCase(TTest_isinst(type_of_expr g vinpe,tgty),tg2)],Some tg3,m)
|
|
mbuilder.Close(dtree,m,type_of_expr g e2)
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* tupled lambda --> method/function with a given topValInfo specification.
|
|
*
|
|
* AdjustArityOfLambdaBody: "(vs,body)" represents a lambda "fun (vs) -> body". The
|
|
* aim is to produce a "static method" represented by a pair
|
|
* "(mvs, body)" where mvs has the List.length "arity".
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
|
|
let untupled_to_tupled vs =
|
|
let untupledTys = types_of_vals vs
|
|
let m = (List.hd vs).Range
|
|
let tupledv,tuplede = mk_compgen_local m "tupledArg" (mk_tuple_ty untupledTys)
|
|
let untupling_es = List.mapi (fun i ty -> mk_tuple_field_get(tuplede,untupledTys,i,m)) untupledTys
|
|
tupledv, mk_invisible_lets m vs untupling_es
|
|
|
|
// The required tupled-arity (arity) can either be 1
|
|
// or N, and likewise for the tuple-arity of the input lambda, i.e. either 1 or N
|
|
// where the N's will be identical.
|
|
let AdjustArityOfLambdaBody g arity (vs:Val list) body =
|
|
let nvs = vs.Length
|
|
if not (nvs = arity || nvs = 1 || arity = 1) then failwith ("lengths don't add up");
|
|
if arity = 0 then
|
|
vs,body
|
|
elif nvs = arity then
|
|
vs,body
|
|
elif nvs = 1 then
|
|
let v = vs.Head
|
|
let untupledTys = dest_tuple_typ g v.Type
|
|
if (untupledTys.Length <> arity) then failwith "length untupledTys <> arity";
|
|
let dummyvs,dummyes =
|
|
untupledTys
|
|
|> List.mapi (fun i ty -> mk_compgen_local v.Range (v.MangledName ^"_"^string i) ty)
|
|
|> List.unzip
|
|
let body = mk_invisible_let v.Range v (mk_tupled g v.Range dummyes untupledTys) body
|
|
dummyvs,body
|
|
else
|
|
let tupledv, untupler = untupled_to_tupled vs
|
|
[tupledv],untupler body
|
|
|
|
let multi_lambda_to_tupled_lambda vs body =
|
|
match vs with
|
|
| [] -> failwith "multi_lambda_to_tupled_lambda: expected some argments"
|
|
| [v] -> v,body
|
|
| vs ->
|
|
let tupledv, untupler = untupled_to_tupled vs
|
|
tupledv, untupler body
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Beta reduction via let-bindings. Reduce immediate apps. of lambdas to let bindings.
|
|
// Includes binding the immediate application of generic
|
|
// functions. Input type is the type of the function. Makes use of the invariant
|
|
// that any two expressions have distinct local variables (because we explicitly copy
|
|
// expressions).
|
|
//------------------------------------------------------------------------
|
|
|
|
let rec MakeApplicationAndBetaReduceAux g (f,fty,tyargsl,argsl,m) =
|
|
(* let verbose = true in *)
|
|
match f with
|
|
| TExpr_let(bind,body,mlet,_) ->
|
|
// Lift bindings out, i.e. (let x = e in f) y --> let x = e in f y
|
|
// This increases the scope of 'x', which I don't like as it mucks with debugging
|
|
// scopes of variables, but this is an important optimization, especially when the '|>'
|
|
// notation is used a lot.
|
|
(* REVIEW: only apply this when beta-reduction really occurs *)
|
|
if verbose then dprintf "--- MakeApplicationAndBetaReduceAux, reducing under let\n";
|
|
mk_let_bind mlet bind (MakeApplicationAndBetaReduceAux g (body,fty,tyargsl,argsl,m))
|
|
| _ ->
|
|
match tyargsl,argsl with
|
|
| [] :: rest,_ ->
|
|
MakeApplicationAndBetaReduceAux g (f,fty,rest,argsl,m)
|
|
|
|
| tyargs :: rest,_ ->
|
|
(* Bind type parameters by immediate substitution *)
|
|
match f with
|
|
| TExpr_tlambda(_, tyvs,body,_,bodyty,_) when tyvs.Length = List.length tyargs ->
|
|
let tpenv = bind_typars tyvs tyargs empty_tpenv
|
|
let body = RemarkExpr m (inst_expr g tpenv body)
|
|
let bodyty' = InstType tpenv bodyty
|
|
MakeApplicationAndBetaReduceAux g (body,bodyty', rest,argsl,m)
|
|
|
|
| _ ->
|
|
let f,fty = mk_appl_aux g f fty [tyargs] [] m
|
|
MakeApplicationAndBetaReduceAux g (f,fty, rest,argsl,m)
|
|
|
|
| [], arg :: rest ->
|
|
(* Bind term parameters by "let" explicit substitutions *)
|
|
match f with
|
|
| TExpr_lambda(_,None,argvs,body,_,bodyty,_) ->
|
|
let argv,body = multi_lambda_to_tupled_lambda argvs body
|
|
mk_compgen_let m argv arg (MakeApplicationAndBetaReduceAux g (body, bodyty, [],rest,m))
|
|
| _ ->
|
|
let f,fty = mk_expr_appl_aux g f fty [arg] m
|
|
MakeApplicationAndBetaReduceAux g (f,fty, [], rest,m)
|
|
|
|
| [],[] ->
|
|
f
|
|
|
|
let MakeApplicationAndBetaReduce g (f,fty,tyargsl,argl,m) =
|
|
MakeApplicationAndBetaReduceAux g (f,fty,tyargsl,argl,m)
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Adjust for expected usage
|
|
// Convert a use of a value to saturate to the given arity.
|
|
//---------------------------------------------------------------------------
|
|
|
|
let MakeArgsForTopArgs g m argtysl tpenv =
|
|
argtysl |> List.mapi (fun i argtys ->
|
|
let n = List.length argtys
|
|
argtys |> List.mapi (fun j (argty,TopArgInfo(_,nm)) ->
|
|
let ty = InstType tpenv argty
|
|
let nm =
|
|
match nm with
|
|
| None -> CompilerGeneratedName ("arg"^ string i^ string j)
|
|
| Some id -> id.idText
|
|
fst (mk_compgen_local m nm ty)))
|
|
|
|
let AdjustValForExpectedArity g m (vref:ValRef) flags topValInfo =
|
|
|
|
let tps,argtysl,rty,_ = GetTopValTypeInFSharpForm g topValInfo vref.Type m
|
|
let tps' = CopyTypars tps
|
|
let tyargs' = List.map mk_typar_ty tps'
|
|
let tpenv = bind_typars tps tyargs' empty_tpenv
|
|
let rty' = InstType tpenv rty
|
|
let vsl = MakeArgsForTopArgs g m argtysl tpenv
|
|
let call = MakeApplicationAndBetaReduce g (TExpr_val(vref,flags,m),vref.Type,[tyargs'],(List.map (mk_tupled_vars g m) vsl),m)
|
|
let tauexpr,tauty =
|
|
List.foldBack
|
|
(fun vs (e,ty) -> mk_multi_lambda m vs (e, ty), (mk_tupled_vars_ty g vs --> ty))
|
|
vsl
|
|
(call, rty')
|
|
// Build a type-lambda expression for the toplevel value if needed...
|
|
mk_tlambda m tps' (tauexpr,tauty),tps' +-> tauty
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
|
|
|
|
let IsSubsumptionExpr g expr =
|
|
match expr with
|
|
| TExpr_op(TOp_coerce,[inputTy;actualTy],[_],m) ->
|
|
is_fun_typ g actualTy && is_fun_typ g inputTy
|
|
| _ ->
|
|
false
|
|
|
|
let strip_tupled_fun_typ g ty =
|
|
let argTys,retTy = strip_fun_typ g ty
|
|
let curriedArgTys = argTys |> List.map (try_dest_tuple_typ g)
|
|
curriedArgTys, retTy
|
|
|
|
let (|ExprValWithPossibleTypeInst|_|) expr =
|
|
match expr with
|
|
| TExpr_app(TExpr_val(vref,flags,m),fty,tyargs,[],_) ->
|
|
Some(vref,flags,tyargs,m)
|
|
| TExpr_val(vref,flags,m) ->
|
|
Some(vref,flags,[],m)
|
|
| _ ->
|
|
None
|
|
|
|
let mk_coerce_if_needed g tgtTy srcTy expr =
|
|
//if type_definitely_subsumes_type_no_coercion 0 g cenv.amap m tgtTy srcTy then
|
|
if type_equiv g tgtTy srcTy then
|
|
expr
|
|
else
|
|
mk_coerce(expr,tgtTy,range_of_expr expr,srcTy)
|
|
|
|
let mk_compgen_let_in m nm ty e f =
|
|
let v,ve = mk_compgen_local m nm ty
|
|
mk_compgen_let m v e (f (v,ve))
|
|
|
|
/// Take a node representing a coercion from one function type to another, e.g.
|
|
/// A -> A * A -> int
|
|
/// to
|
|
/// B -> B * A -> int
|
|
/// and return an expression of the correct type that doesn't use a coercion type. For example
|
|
/// return
|
|
/// (fun b1 b2 -> E (b1 :> A) (b2 :> A))
|
|
///
|
|
/// - Use good names for the closure arguments if available
|
|
/// - Create lambda variables if needed, or use the supplied arguments if available.
|
|
///
|
|
/// Return the new expression and any unused suffix of supplied arguments
|
|
///
|
|
/// If E is a value with TopInfo then use the arity to help create a better closure.
|
|
/// In particular we can create a closure like this:
|
|
/// (fun b1 b2 -> E (b1 :> A) (b2 :> A))
|
|
/// rather than
|
|
/// (fun b1 -> let clo = E (b1 :> A) in (fun b2 -> clo (b2 :> A)))
|
|
/// The latter closures are needed to carefully preserve side effect order
|
|
///
|
|
/// Note that the results of this translation are visible to quotations
|
|
|
|
let AdjustPossibleSubsumptionExpr g (expr:expr) (suppliedArgs: expr list) : (expr* expr list) option =
|
|
|
|
match expr with
|
|
| TExpr_op(TOp_coerce,[inputTy;actualTy],[exprWithActualTy],m) when
|
|
is_fun_typ g actualTy && is_fun_typ g inputTy ->
|
|
|
|
if type_equiv g actualTy inputTy then
|
|
Some(exprWithActualTy, suppliedArgs)
|
|
else
|
|
|
|
let curriedActualArgTys,retTy = strip_tupled_fun_typ g actualTy
|
|
|
|
let curriedInputTys,_ = strip_fun_typ g inputTy
|
|
|
|
assert (curriedActualArgTys.Length = curriedInputTys.Length)
|
|
|
|
let argTys = (curriedInputTys,curriedActualArgTys) ||> List.mapi2 (fun i x y -> (i,x,y))
|
|
|
|
|
|
// Use the nice names for a function of known arity and name. Note that 'nice' here also
|
|
// carries a semantic meaning. For a function with top-info,
|
|
// let f (x:A) (y:A) (z:A) = ...
|
|
// we know there are no side effects on the application of 'f' to 1,2 args. This greatly simplifies
|
|
// the closure built for
|
|
// f b1 b2
|
|
// and indeed for
|
|
// f b1 b2 b3
|
|
// we don't build any closure at all, and just return
|
|
// f (b1 :> A) (b2 :> A) (b3 :> A)
|
|
|
|
let curriedNiceNames =
|
|
match strip_expr exprWithActualTy with
|
|
| ExprValWithPossibleTypeInst(vref,flags,tyargs,m) when vref.TopValInfo.IsSome ->
|
|
|
|
let tps,argtysl,rty,_ = GetTopValTypeInFSharpForm g vref.TopValInfo.Value vref.Type (range_of_expr expr)
|
|
argtysl |> List.mapi (fun i argtys ->
|
|
let n = List.length argtys
|
|
argtys |> List.mapi (fun j (argty,TopArgInfo(_,nm)) ->
|
|
match nm with
|
|
| None -> CompilerGeneratedName ("arg" ^ string i ^string j)
|
|
| Some id -> id.idText))
|
|
| _ ->
|
|
[]
|
|
|
|
assert (curriedActualArgTys.Length >= curriedNiceNames.Length)
|
|
|
|
let argTysWithNiceNames,argTysWithoutNiceNames =
|
|
List.chop curriedNiceNames.Length argTys
|
|
|
|
/// Only consume 'suppliedArgs' up to at most the number of nice arguments
|
|
let suppliedArgs, droppedSuppliedArgs =
|
|
List.chop (min suppliedArgs.Length curriedNiceNames.Length) suppliedArgs
|
|
|
|
/// THe relevant range for any expressions and applications includes the arguments
|
|
let appm = List.fold (fun m e -> union_ranges m (range_of_expr e)) m suppliedArgs
|
|
|
|
// See if we have 'enough' suppliedArgs. If not, we have to build some lambdas, and,
|
|
// we have to 'let' bind all arguments that we consume, e.g.
|
|
// Seq.take (effect;4) : int list -> int list
|
|
// is a classic case. Here we generate
|
|
// let tmp = (effect;4) in
|
|
// (fun v -> Seq.take tmp (v :> seq<_>))
|
|
let buildingLambdas = (suppliedArgs.Length <> curriedNiceNames.Length)
|
|
//printfn "buildingLambdas = %A" buildingLambdas
|
|
//printfn "suppliedArgs.Length = %d" suppliedArgs.Length
|
|
|
|
/// Given a tuple of argument variables that has a tuple type that satisfies the input argument types,
|
|
/// coerce it to a tuple that satisfies the matching coerced argument type(s).
|
|
let CoerceDetupled (argTys: typ list) (detupledArgs:expr list) (actualTys: typ list) =
|
|
assert (actualTys.Length = argTys.Length)
|
|
assert (actualTys.Length = detupledArgs.Length)
|
|
// Inject the coercions into the user-supplied explicit tuple
|
|
let argm = List.reduce_left union_ranges (List.map range_of_expr detupledArgs)
|
|
mk_tupled g argm (List.map3 (mk_coerce_if_needed g) actualTys argTys detupledArgs) actualTys
|
|
|
|
/// Given an argument variable of tuple type that has been evaluated and stored in the
|
|
/// given variable, where the tuple type that satisfies the input argument types,
|
|
/// coerce it to a tuple that satisfies the matching coerced argument type(s).
|
|
let CoerceBoundTuple tupleVar argTys (actualTys : typ list) =
|
|
assert (actualTys.Length > 1)
|
|
|
|
mk_tupled g appm
|
|
((actualTys,argTys) ||> List.mapi2 (fun i actualTy dummyTy ->
|
|
let argExprElement = mk_tuple_field_get(tupleVar,argTys,i,appm)
|
|
mk_coerce_if_needed g actualTy dummyTy argExprElement))
|
|
actualTys
|
|
|
|
/// Given an argument that has a tuple type that satisfies the input argument types,
|
|
/// coerce it to a tuple that satisfies the matching coerced argument type. Try to detuple the argument if possible.
|
|
let CoerceTupled niceNames (argExpr:expr) (actualTys:typ list) =
|
|
let argExprTy = (type_of_expr g argExpr)
|
|
|
|
let argTys =
|
|
match actualTys with
|
|
| [_] ->
|
|
[type_of_expr g argExpr]
|
|
| _ ->
|
|
try_dest_tuple_typ g argExprTy
|
|
|
|
assert (actualTys.Length = argTys.Length)
|
|
let nm = match niceNames with [nm] -> nm | _ -> "arg"
|
|
if buildingLambdas then
|
|
// Evaluate the user-supplied tuple-valued argument expression, inject the coercions and build an explicit tuple
|
|
// Assign the argument to make sure it is only run once
|
|
// f ~~> : B -> int
|
|
// f ~~> : (B * B) -> int
|
|
//
|
|
// for
|
|
// let f a = 1
|
|
// let f (a,a) = 1
|
|
let v,ve = mk_compgen_local appm nm argExprTy
|
|
let binderBuilder = (fun tm -> mk_compgen_let appm v argExpr tm)
|
|
let expr =
|
|
match actualTys,argTys with
|
|
| [actualTy],[argTy] -> mk_coerce_if_needed g actualTy argTy ve
|
|
| _ -> CoerceBoundTuple ve argTys actualTys
|
|
|
|
binderBuilder,expr
|
|
else
|
|
if type_equiv g (mk_tupled_ty g actualTys) argExprTy then
|
|
(fun tm -> tm), argExpr
|
|
else
|
|
|
|
let detupledArgs,argTys =
|
|
match actualTys with
|
|
| [actualType] ->
|
|
[argExpr],[type_of_expr g argExpr]
|
|
| _ ->
|
|
try_dest_tuple argExpr,try_dest_tuple_typ g argExprTy
|
|
|
|
// OK, the tuples match, or there is no de-tupling,
|
|
// f x
|
|
// f (x,y)
|
|
//
|
|
// for
|
|
// let f (x,y) = 1
|
|
// and we're not building lambdas, just coerce the arguments in place
|
|
if detupledArgs.Length = actualTys.Length then
|
|
(fun tm -> tm), CoerceDetupled argTys detupledArgs actualTys
|
|
else
|
|
// In this case there is a tuple mismatch.
|
|
// f p
|
|
//
|
|
//
|
|
// for
|
|
// let f (x,y) = 1
|
|
// Assign the argument to make sure it is only run once
|
|
let v,ve = mk_compgen_local appm nm argExprTy
|
|
let binderBuilder = (fun tm -> mk_compgen_let appm v argExpr tm)
|
|
let expr = CoerceBoundTuple ve argTys actualTys
|
|
binderBuilder,expr
|
|
|
|
|
|
// This variable is really a dummy to make the code below more regular.
|
|
// In the i = N - 1 cases we skip the introduction of the 'let' for
|
|
// this variable.
|
|
let resVar,resVarAsExpr = mk_compgen_local appm "result" retTy
|
|
let N = argTys.Length
|
|
let (cloVar,exprForOtherArgs,exprForOtherArgsTy) =
|
|
List.foldBack
|
|
(fun (i,inpArgTy,actualArgTys) (cloVar:Val,res,resTy) ->
|
|
|
|
let inpArgTys =
|
|
match actualArgTys with
|
|
| [_] -> [inpArgTy]
|
|
| _ -> dest_tuple_typ g inpArgTy
|
|
|
|
assert (inpArgTys.Length = actualArgTys.Length)
|
|
|
|
let inpsAsVars,inpsAsExprs = inpArgTys |> List.mapi (fun j ty -> mk_compgen_local appm ("arg"^string i^string j) ty) |> List.unzip
|
|
let inpsAsActualArg = CoerceDetupled inpArgTys inpsAsExprs actualArgTys
|
|
let inpCloVarType = (mk_fun_ty (mk_tupled_ty g actualArgTys) cloVar.Type)
|
|
let newResTy = mk_fun_ty inpArgTy resTy
|
|
let inpCloVar,inpCloVarAsExpr = mk_compgen_local appm ("clo"^string i) inpCloVarType
|
|
let newRes =
|
|
// For the final arg we can skip introducing the dummy variable
|
|
if i = N - 1 then
|
|
mk_multi_lambda appm inpsAsVars
|
|
(mk_appl g ((inpCloVarAsExpr,inpCloVarType),[],[inpsAsActualArg],appm),resTy)
|
|
else
|
|
mk_multi_lambda appm inpsAsVars
|
|
(mk_invisible_let appm cloVar
|
|
(mk_appl g ((inpCloVarAsExpr,inpCloVarType),[],[inpsAsActualArg],appm))
|
|
res,
|
|
resTy)
|
|
|
|
inpCloVar,newRes,newResTy)
|
|
argTysWithoutNiceNames
|
|
(resVar,resVarAsExpr,retTy)
|
|
|
|
|
|
// Mark the up as Some/None
|
|
let suppliedArgs = List.map Some suppliedArgs @ List.of_array (Array.create (curriedNiceNames.Length - suppliedArgs.Length) None)
|
|
|
|
assert (suppliedArgs.Length = curriedNiceNames.Length)
|
|
|
|
let exprForAllArgs =
|
|
|
|
if isNil argTysWithNiceNames then
|
|
mk_invisible_let appm cloVar exprWithActualTy exprForOtherArgs
|
|
else
|
|
let lambdaBuilders,binderBuilders,inpsAsArgs =
|
|
|
|
(argTysWithNiceNames,curriedNiceNames,suppliedArgs) |||> List.map3 (fun (i,inpArgTy,actualArgTys) niceNames suppliedArg ->
|
|
|
|
let inpArgTys =
|
|
match actualArgTys with
|
|
| [_] -> [inpArgTy]
|
|
| _ -> dest_tuple_typ g inpArgTy
|
|
|
|
|
|
/// Note: there might not be enough nice names, and they might not match in arity
|
|
let niceNames =
|
|
match niceNames with
|
|
| nms when nms.Length = inpArgTys.Length -> nms
|
|
| [nm] -> inpArgTys |> List.mapi (fun i _ -> (nm^string i))
|
|
| nms -> nms
|
|
match suppliedArg with
|
|
| Some arg ->
|
|
let binderBuilder,inpsAsActualArg = CoerceTupled niceNames arg actualArgTys
|
|
let lambdaBuilder = (fun tm -> tm)
|
|
lambdaBuilder, binderBuilder,inpsAsActualArg
|
|
| None ->
|
|
let inpsAsVars,inpsAsExprs = (niceNames,inpArgTys) ||> List.map2 (fun nm ty -> mk_compgen_local appm nm ty) |> List.unzip
|
|
let inpsAsActualArg = CoerceDetupled inpArgTys inpsAsExprs actualArgTys
|
|
let lambdaBuilder = (fun tm -> mk_multi_lambda appm inpsAsVars (tm, type_of_expr g tm))
|
|
let binderBuilder = (fun tm -> tm)
|
|
lambdaBuilder,binderBuilder,inpsAsActualArg)
|
|
|> List.unzip3
|
|
|
|
// If no trailing args then we can skip introducing the dummy variable
|
|
// This corresponds to
|
|
// let f (x:A) = 1
|
|
//
|
|
// f ~~> type B -> int
|
|
//
|
|
// giving
|
|
// (fun b -> f (b :> A))
|
|
// rather than
|
|
// (fun b -> let clo = f (b :> A) in clo)
|
|
let exprApp =
|
|
if argTysWithoutNiceNames.Length = 0 then
|
|
mk_appl g ((exprWithActualTy,actualTy),[],inpsAsArgs,appm)
|
|
else
|
|
mk_invisible_let appm
|
|
cloVar (mk_appl g ((exprWithActualTy,actualTy),[],inpsAsArgs,appm))
|
|
exprForOtherArgs
|
|
|
|
List.foldBack (fun f acc -> f acc) binderBuilders
|
|
(List.foldBack (fun f acc -> f acc) lambdaBuilders exprApp)
|
|
|
|
Some(exprForAllArgs,droppedSuppliedArgs)
|
|
| _ ->
|
|
None
|
|
|
|
/// Find and make all subsumption eliminations
|
|
let NormalizeAndAdjustPossibleSubsumptionExprs g inputExpr =
|
|
let expr,args =
|
|
// AdjustPossibleSubsumptionExpr can take into account an application
|
|
match strip_expr inputExpr with
|
|
| TExpr_app(f,fty,[],args,m) ->
|
|
f,args
|
|
|
|
| expr ->
|
|
inputExpr,[]
|
|
|
|
match AdjustPossibleSubsumptionExpr g expr args with
|
|
| None ->
|
|
inputExpr
|
|
| Some (expr',[]) ->
|
|
expr'
|
|
| Some (expr',args') ->
|
|
//printfn "adjusted...."
|
|
TExpr_app(expr',type_of_expr g expr',[],args',range_of_expr inputExpr)
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// LinearizeTopMatch - when only one non-failing target, make linear. The full
|
|
// complexity of this is only used for spectacularly rare bindings such as
|
|
// type ('a,'b) either = This of 'a | That of 'b
|
|
// let this_f1 = This (fun x -> x)
|
|
// let This fA | That fA = this_f1
|
|
//
|
|
// Here a polymorphic top level binding "fA" is _computed_ by a pattern match!!!
|
|
// The TAST coming out of type checking must, however, define fA as a type function,
|
|
// since it is marked with an arity that indicates it's r.h.s. is a type function]
|
|
// without side effects and so can be compiled as a generic method (for example).
|
|
|
|
// polymorphic things bound in complex matches at top level require eta expansion of the
|
|
// type function to ensure the r.h.s. of the binding is indeed a type function
|
|
let tlambda_eta g m tps (tm,ty) =
|
|
if isNil tps then tm else mk_tlambda m tps (mk_appl g ((tm,ty),[(List.map mk_typar_ty tps)],[],m),ty)
|
|
|
|
let AdjustValToTopVal (tmp:Val) parent valData =
|
|
tmp.Data.val_top_repr_info <- Some valData ;
|
|
tmp.Data.val_actual_parent <- parent;
|
|
set_is_topbind_of_vflags tmp.Data true
|
|
|
|
/// For match with only one non-failing target T0, the other targets, T1... failing (say, raise exception).
|
|
/// tree, T0(v0,..,vN) => rhs ; T1() => fail ; ...
|
|
/// Convert it to bind T0's variables, then continue with T0's rhs:
|
|
/// let tmp = switch tree, TO(fv0,...,fvN) => Tup (fv0,...,fvN) ; T1() => fail; ...
|
|
/// let v1 = #1 tmp in ...
|
|
/// and vN = #N tmp
|
|
/// rhs
|
|
/// Motivation:
|
|
/// - For top-level let bindings with possibly failing matches,
|
|
/// this makes clear that subsequent bindings (if reached) are top-level ones.
|
|
let LinearizeTopMatchAux g parent (spBind,m,tree,targets,m2,ty) =
|
|
let targetsL = Array.to_list targets
|
|
(* items* package up 0,1,more items *)
|
|
let itemsProj tys i x =
|
|
match tys with
|
|
| [] -> failwith "itemsProj: no items?"
|
|
| [t] -> x (* no projection needed *)
|
|
| tys -> TExpr_op(TOp_tuple_field_get(i),tys,[x],m)
|
|
let isThrowingTarget = function TTarget(_,x,_) -> is_throw x
|
|
if 1 + List.count isThrowingTarget targetsL = targetsL.Length then
|
|
(* Have failing targets and ONE successful one, so linearize *)
|
|
let (TTarget (vs,rhs,spTarget)) = the (List.tryfind (isThrowingTarget >> not) targetsL)
|
|
(* note - old code here used copy value to generate locals - this was not right *)
|
|
let fvs = vs |> FlatList.map (fun v -> fst(mk_local v.Range v.MangledName v.Type)) |> FlatList.to_list (* fresh *)
|
|
let vtys = vs |> List.map (fun v -> v.Type)
|
|
let tmpTy = mk_tupled_vars_ty g vs
|
|
let tmp,tmpe = mk_compgen_local m "matchResultHolder" tmpTy
|
|
|
|
AdjustValToTopVal tmp parent TopValInfo.emptyValData;
|
|
|
|
let newTg = TTarget (fvs,mk_tupled_vars g m fvs,spTarget)
|
|
let fixup (TTarget (tvs,tx,spTarget)) =
|
|
match dest_throw tx with
|
|
| Some (m,ty,e) -> let tx = mk_throw m tmpTy e
|
|
TTarget(tvs,tx,spTarget) (* Throwing targets, recast it's "return type" *)
|
|
| None -> newTg (* Non-throwing target, replaced [new/old] *)
|
|
|
|
let targets = Array.map fixup targets
|
|
let binds =
|
|
vs |> FlatList.mapi (fun i v ->
|
|
let ty = v.Type
|
|
let rhs = tlambda_eta g m v.Typars (itemsProj vtys i tmpe, ty)
|
|
(* update the arity of the value *)
|
|
v.Data.val_top_repr_info <- Some (InferArityOfExpr g ty [] [] rhs);
|
|
mk_invisible_bind v rhs) in (* vi = proj tmp *)
|
|
mk_compgen_let m
|
|
tmp (prim_mk_match (spBind,m,tree,targets,m2,tmpTy)) (* note, probably retyped match, but note, result still has same type *)
|
|
(mk_lets_from_Bindings m binds rhs)
|
|
else
|
|
(* no change *)
|
|
prim_mk_match (spBind,m,tree,targets,m2,ty)
|
|
|
|
let LinearizeTopMatch g parent = function
|
|
| TExpr_match (spBind,m,tree,targets,m2,ty,cache) -> LinearizeTopMatchAux g parent (spBind,m,tree,targets,m2,ty)
|
|
| x -> x
|
|
|
|
|
|
(*---------------------------------------------------------------------------
|
|
* XmlDoc signatures
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
|
|
let commaEncs strs = String.concat "," strs
|
|
let angleEnc str = "{" ^ str ^ "}"
|
|
let ticks_and_argcount_text_of_tcref (tcref:TyconRef) =
|
|
let nm = tcref.MangledName
|
|
text_of_path (Array.to_list (full_mangled_path_to_tcref tcref) @ [nm])
|
|
|
|
let typarEnc g gtps typar =
|
|
let idx =
|
|
try ListSet.findIndex typar_ref_eq typar gtps
|
|
with Not_found -> warning(InternalError("Typar not found during XmlDoc generation",typar.Range)); 0
|
|
"``"^string idx
|
|
|
|
let rec typeEnc g gtps ty =
|
|
if verbose then dprintf "--> typeEnc";
|
|
match (strip_tpeqns_and_tcabbrevs g ty) with
|
|
| TType_forall (typars,typ) ->
|
|
"Microsoft.FSharp.Core.TypeFunc"
|
|
| _ when is_compat_array_typ g ty ->
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
typeEnc g gtps (List.hd tinst)^ "[]"
|
|
| _ when is_il_arr_typ g ty ->
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
typeEnc g gtps (List.hd tinst)^ tcref.MangledName
|
|
| TType_ucase (UCRef(tcref,_),tinst)
|
|
| TType_app (tcref,tinst) ->
|
|
ticks_and_argcount_text_of_tcref tcref ^ tyargsEnc g gtps tinst
|
|
| TType_tuple typs ->
|
|
sprintf "Microsoft.FSharp.Core.Tuple`%d%s" typs.Length (tyargsEnc g gtps typs)
|
|
| TType_fun (f,x) ->
|
|
"Microsoft.FSharp.Core.FastFunc`2" ^ tyargsEnc g gtps [f;x]
|
|
| TType_var typar ->
|
|
typarEnc g gtps typar
|
|
| TType_modul_bindings ->
|
|
"System.Object"
|
|
| TType_measure unt -> "?"
|
|
|
|
and tyargsEnc g gtps args =
|
|
if isNil args then ""
|
|
else angleEnc (commaEncs (List.map (typeEnc g gtps) args))
|
|
|
|
let XmlDocArgsEnc g gtps argTs =
|
|
if isNil argTs then ""
|
|
else "(" ^ String.concat "," (List.map (typeEnc g gtps) argTs) ^ ")"
|
|
|
|
let XmlDocSigOfVal g path (v:Val) =
|
|
let tps,methTypars,argInfos,prefix,path,name =
|
|
|
|
// CLEANUP: this is one of several code paths that treat module values and members
|
|
// seperately when really it would be cleaner to make sure GetTopValTypeInFSharpForm, GetMemberTypeInFSharpForm etc.
|
|
// were lined up so code paths like this could be uniform
|
|
|
|
match v.MemberInfo with
|
|
| Some membInfo when not v.IsExtensionMember ->
|
|
(* Methods, Properties etc. *)
|
|
let tps,argInfos,rtnT,_ = GetMemberTypeInMemberForm g membInfo.MemberFlags (the v.TopValInfo) v.Type v.Range
|
|
let prefix,name =
|
|
match membInfo.MemberFlags.MemberKind with
|
|
| MemberKindClassConstructor
|
|
| MemberKindConstructor
|
|
| MemberKindMember -> "M:", v.CompiledName
|
|
| MemberKindPropertyGetSet
|
|
| MemberKindPropertySet
|
|
| MemberKindPropertyGet -> "P:",membInfo.PropertyName
|
|
let path =
|
|
path^"."^ v.MemberActualParent.MangledName
|
|
let methTypars =
|
|
match PartitionValTypars g v with
|
|
| Some(_,_,memberMethodTypars,_,_) -> memberMethodTypars
|
|
| None -> tps
|
|
tps,methTypars,argInfos,prefix,path,name
|
|
| _ ->
|
|
// Regular F# values and extension members
|
|
let w = arity_of_val v
|
|
let tps,argInfos,_,_ = GetTopValTypeInCompiledForm g w v.Type v.Range
|
|
let name = v.CompiledName
|
|
let prefix =
|
|
if w.NumCurriedArgs = 0 && isNil tps then "P:"
|
|
else "M:"
|
|
tps,tps,argInfos,prefix,path,name
|
|
let argTs = argInfos |> List.concat |> List.map fst
|
|
let args = XmlDocArgsEnc g tps argTs
|
|
let arity = List.length methTypars in (* C# XML doc adds ``<arity> to *generic* member names *)
|
|
let genArity = if arity=0 then "" else Printf.sprintf "``%d" arity
|
|
prefix ^ path ^ "." ^ name ^ genArity ^ args
|
|
|
|
let XmlDocSigOfTycon (g:TcGlobals) path (tc:Tycon) = "T:" ^ path ^ "." ^ tc.MangledName
|
|
let XmlDocSigOfSubModul (g:TcGlobals) path = "T:" ^ path
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Some unions have null as representations
|
|
*------------------------------------------------------------------------*)
|
|
|
|
|
|
let enum_CompilationRepresentationAttribute_Static = 0b0000000000000001
|
|
let enum_CompilationRepresentationAttribute_Instance = 0b0000000000000010
|
|
let enum_CompilationRepresentationAttribute_StaticInstanceMask = 0b0000000000000011
|
|
let enum_CompilationRepresentationAttribute_ModuleSuffix = 0b0000000000000100
|
|
let enum_CompilationRepresentationAttribute_PermitNull = 0b0000000000001000
|
|
|
|
let TyconHasUseNullAsTrueValueAttribute g (tycon:Tycon) =
|
|
match TryFindInt32Attrib g g.attrib_CompilationRepresentationAttribute tycon.Attribs with
|
|
| Some(flags) -> ((flags &&& enum_CompilationRepresentationAttribute_PermitNull) <> 0)
|
|
| _ -> false
|
|
|
|
|
|
(* WARNING: this must match optimizeAlternativeToNull in ilx/cu_erase.ml *)
|
|
(* REVIEW: make this fully attribute controlled *)
|
|
let IsUnionTypeWithNullAsTrueValue (g:TcGlobals) (tycon:Tycon) =
|
|
(tycon.IsUnionTycon &&
|
|
let ucs = tycon.UnionCasesArray
|
|
(Array.length ucs = 0 ||
|
|
(TyconHasUseNullAsTrueValueAttribute g tycon &&
|
|
ucs |> Array.existsOne (fun uc -> uc.IsNullary) &&
|
|
ucs |> Array.exists (fun uc -> not uc.IsNullary))))
|
|
|
|
let TyconCompilesInstanceMembersAsStatic g tycon = IsUnionTypeWithNullAsTrueValue g tycon
|
|
let TcrefCompilesInstanceMembersAsStatic g tcref = TyconCompilesInstanceMembersAsStatic g (deref_tycon tcref)
|
|
|
|
let TypeNullNever g ty =
|
|
let underlyingTy = strip_tpeqns_and_tcabbrevs_and_measureable g ty
|
|
(is_struct_typ g underlyingTy) ||
|
|
(is_byref_typ g underlyingTy)
|
|
|
|
let TypeNullIsExtraValue g ty =
|
|
is_il_ref_typ g ty ||
|
|
is_delegate_typ g ty //||
|
|
//(not (TypeNullNever g ty) &&
|
|
//is_stripped_tyapp_typ ty &&
|
|
//HasAttrib g g.attrib_PermitNullLiteralAttribute (deref_tycon (tcref_of_stripped_typ ty)).Attribs)
|
|
|
|
let TypeNullIsTrueValue g ty =
|
|
(is_stripped_tyapp_typ g ty && IsUnionTypeWithNullAsTrueValue g (deref_tycon (tcref_of_stripped_typ g ty))) ||
|
|
(is_unit_typ g ty)
|
|
|
|
let TypeNullNotLiked g ty =
|
|
not (TypeNullIsExtraValue g ty)
|
|
&& not (TypeNullIsTrueValue g ty)
|
|
&& not (TypeNullNever g ty)
|
|
|
|
let TypeSatisfiesNullConstraint g ty =
|
|
TypeNullIsExtraValue g ty
|
|
|
|
let rec TypeHasDefaultValue g ty =
|
|
TypeSatisfiesNullConstraint g ty
|
|
|| (is_struct_typ g ty &&
|
|
// Is it an F# struct type?
|
|
(if is_fsobjmodel_struct_typ g ty then
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
let flds =
|
|
tcref.TrueInstanceFieldsAsList
|
|
// We can ignore fields with the DefaultValue(false) attribute
|
|
|> List.filter (fun fld -> not (TryFindBoolAttrib g g.attrib_DefaultValueAttribute fld.FieldAttribs = Some(false)))
|
|
|
|
flds |> List.forall (typ_of_rfield (mk_tcref_inst tcref tinst) >> TypeHasDefaultValue g)
|
|
elif is_tuple_struct_typ g ty then
|
|
dest_tuple_typ g ty |> List.forall (TypeHasDefaultValue g)
|
|
else
|
|
// All struct types defined in other .NET languages have a DefaultValue regardless of their
|
|
// instantiation
|
|
true))
|
|
|
|
(* Can we use the fast helper for the 'LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric'? *)
|
|
let can_use_istype_fast g ty =
|
|
not (is_typar_typ g ty) &&
|
|
not (TypeNullIsTrueValue g ty) &&
|
|
not (TypeNullNever g ty)
|
|
|
|
(* Can we use the fast helper for the 'LanguagePrimitives.IntrinsicFunctions.UnboxGeneric'? *)
|
|
let can_use_unbox_fast g ty =
|
|
not (is_typar_typ g ty) &&
|
|
not (TypeNullNotLiked g ty)
|
|
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Nullness tests and pokes
|
|
*------------------------------------------------------------------------*)
|
|
|
|
// Null tests are generated by
|
|
// 1. The compilation of array patterns in the pattern match compiler
|
|
// 2. The compilation of string patterns in the pattern match compiler
|
|
|
|
let mk_nonnull_test g m e = mk_asm ([ IL.I_arith IL.AI_ldnull ; IL.I_arith IL.AI_cgt_un ],[], [e],[g.bool_ty],m)
|
|
let mk_nonnull_poke g m e = mk_asm ([ IL.I_arith IL.AI_dup ; IL.I_ldvirtftn (mspec_Object_GetHashCode g.ilg); IL.I_arith IL.AI_pop ],[], [e],[type_of_expr g e],m)
|
|
let mk_nonnull_cond g m ty e1 e2 e3 = mk_cond NoSequencePointAtStickyBinding SuppressSequencePointAtTarget m ty (mk_nonnull_test g m e1) e2 e3
|
|
|
|
|
|
let ModuleNameIsMangled g attrs =
|
|
match TryFindInt32Attrib g g.attrib_CompilationRepresentationAttribute attrs with
|
|
| Some(flags) -> ((flags &&& enum_CompilationRepresentationAttribute_ModuleSuffix) <> 0)
|
|
| _ -> false
|
|
|
|
let CompileAsEvent g attrs = HasAttrib g g.attrib_CLIEventAttribute attrs
|
|
|
|
|
|
let MemberIsCompiledAsInstance g parent isExtensionMember membInfo attrs =
|
|
// All extension members are compiled as static members
|
|
if isExtensionMember then false
|
|
// Anything implementing a dispatch slot is compiled as an instance member
|
|
elif nonNil membInfo.ImplementedSlotSigs then true
|
|
else
|
|
// Otherwise check attributes to see if there is an explicit instance or explicit static flag
|
|
let explicitInstance,explicitStatic =
|
|
match TryFindInt32Attrib g g.attrib_CompilationRepresentationAttribute attrs with
|
|
| Some(flags) ->
|
|
((flags &&& enum_CompilationRepresentationAttribute_Instance) <> 0),
|
|
((flags &&& enum_CompilationRepresentationAttribute_Static) <> 0)
|
|
| _ -> false,false
|
|
explicitInstance ||
|
|
(membInfo.MemberFlags.MemberIsInstance &&
|
|
not explicitStatic &&
|
|
not (TcrefCompilesInstanceMembersAsStatic g parent))
|
|
|
|
|
|
let is_sealed_typ g ty =
|
|
let ty = strip_tpeqns_and_tcabbrevs_and_measureable g ty
|
|
not (is_ref_typ g ty) ||
|
|
is_any_array_typ g ty ||
|
|
(if is_il_named_typ g ty then
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
let tdef = tcref.ILTyconRawMetadata
|
|
tdef.tdSealed
|
|
elif (is_fsobjmodel_interface_typ g ty || is_fsobjmodel_class_typ g ty) then
|
|
let tcref,tinst = dest_stripped_tyapp_typ g ty
|
|
(TryFindBoolAttrib g g.attrib_SealedAttribute tcref.Attribs = Some(true))
|
|
else true)
|
|
|
|
let IsComInteropType g ty =
|
|
let tcr,_ = dest_stripped_tyapp_typ g ty
|
|
TryFindBoolAttrib g g.attrib_ComImportAttribute tcr.Attribs = Some(true)
|
|
|
|
let ValSpecIsCompiledAsInstance g (v:Val) =
|
|
match v.MemberInfo with
|
|
| Some(membInfo) ->
|
|
// Note it doesn't matter if we pass 'v.MemberActualParent' or 'v.MemberApparentParent' here.
|
|
// These only differ if the value is an extension member, and in that case MemberIsCompiledAsInstance always returns
|
|
// false anyway
|
|
MemberIsCompiledAsInstance g v.MemberApparentParent v.IsExtensionMember membInfo v.Attribs
|
|
| _ -> false
|
|
|
|
let ValRefIsCompiledAsInstanceMember g vref = ValSpecIsCompiledAsInstance g (deref_val vref)
|
|
|
|
|
|
(*---------------------------------------------------------------------------
|
|
* Crack information about an F# object model call
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let GetMemberCallInfo g (vref:ValRef,vFlags) =
|
|
match vref.MemberInfo with
|
|
| Some(membInfo) when not vref.IsExtensionMember ->
|
|
let numEnclTypeArgs = vref.MemberApparentParent.TyparsNoRange.Length
|
|
let virtualCall =
|
|
(membInfo.MemberFlags.MemberIsVirtual ||
|
|
membInfo.MemberFlags.MemberIsOverrideOrExplicitImpl ||
|
|
membInfo.MemberFlags.MemberIsDispatchSlot) &&
|
|
not membInfo.MemberFlags.MemberIsFinal &&
|
|
not (vFlags = VSlotDirectCall)
|
|
let isNewObj = (membInfo.MemberFlags.MemberKind = MemberKindConstructor) && (vFlags = NormalValUse)
|
|
let isSuperInit = (membInfo.MemberFlags.MemberKind = MemberKindConstructor) && (vFlags = CtorValUsedAsSuperInit)
|
|
let isSelfInit = (membInfo.MemberFlags.MemberKind = MemberKindConstructor) && (vFlags = CtorValUsedAsSelfInit)
|
|
let isCompiledAsInstance = ValRefIsCompiledAsInstanceMember g vref
|
|
let takesInstanceArg = isCompiledAsInstance && not isNewObj
|
|
let isPropGet = (membInfo.MemberFlags.MemberKind = MemberKindPropertyGet) && (membInfo.MemberFlags.MemberIsInstance = isCompiledAsInstance)
|
|
let isPropSet = (membInfo.MemberFlags.MemberKind = MemberKindPropertySet) && (membInfo.MemberFlags.MemberIsInstance = isCompiledAsInstance)
|
|
numEnclTypeArgs, virtualCall,isNewObj,isSuperInit,isSelfInit ,takesInstanceArg,isPropGet,isPropSet
|
|
| _ ->
|
|
0,false,false,false,false,false,false,false
|
|
|
|
(*---------------------------------------------------------------------------
|
|
* Active pattern name helpers
|
|
*------------------------------------------------------------------------- *)
|
|
|
|
let core_display_name_of_vref (vref:ValRef) = vref.CoreDisplayName
|
|
|
|
let is_ap_name(nm) =
|
|
// REVIEW: This may not be correct in all the cases. For example, is "|+|" valid AP name?
|
|
let len = String.length nm
|
|
String.contains nm '|' &&
|
|
(String.index nm '|' = 0) &&
|
|
len >= 3 &&
|
|
(String.rindex nm '|' = len - 1)
|
|
|
|
let apinfo_of_vname (nm, m) =
|
|
let rec loop nm =
|
|
if String.contains nm '|' then
|
|
let n = String.index nm '|'
|
|
String.sub nm 0 n :: loop (String.sub nm (n+1) (String.length nm - n - 1))
|
|
else
|
|
[nm]
|
|
let nm = DecompileOpName nm
|
|
let len = String.length nm
|
|
if is_ap_name nm then
|
|
let res = loop (String.sub nm 1 (len - 2))
|
|
let resH,resT = List.frontAndBack res
|
|
Some(if resT = "_" then APInfo(false,resH,m) else APInfo(true,res,m))
|
|
(* dprintf "apinfo_of_vname %s, res = %s\n" nm (String.concat ";" res);*)
|
|
else None
|
|
|
|
let apinfo_of_vref (vref:ValRef) =
|
|
// This next line is an optimization to prevent calls to core_display_name_of_vref, which calls DemangleOperatorName
|
|
if not (String.hasPrefix vref.MangledName "|" ) then None else
|
|
apinfo_of_vname (vref.CoreDisplayName, vref.Range)
|
|
|
|
let name_of_apref (APElemRef(_,vref,n)) =
|
|
match apinfo_of_vref vref with
|
|
| None -> error(InternalError("name_of_apref: not an active pattern name", vref.Range))
|
|
| Some (APInfo(total,nms,_)) ->
|
|
if n < 0 || n >= List.length nms then error(InternalError("name_of_apref: index out of range for active pattern refernce", vref.Range));
|
|
List.nth nms n
|
|
|
|
let mk_choices_tcref g m n =
|
|
match n with
|
|
| 0 | 1 -> error(InternalError("mk_choices_tcref",m))
|
|
| 2 -> g.choice2_tcr
|
|
| 3 -> g.choice3_tcr
|
|
| 4 -> g.choice4_tcr
|
|
| 5 -> g.choice5_tcr
|
|
| 6 -> g.choice6_tcr
|
|
| 7 -> g.choice7_tcr
|
|
| _ -> error(Error("active patterns may not return more than 7 possibilities",m))
|
|
let mk_choices_typ g m tinst =
|
|
match List.length tinst with
|
|
| 0 -> g.unit_ty
|
|
| 1 -> List.hd tinst
|
|
| _ -> mk_tyapp_ty (mk_choices_tcref g m (List.length tinst)) tinst
|
|
|
|
let mk_choices_ucref g m n i =
|
|
mk_ucref (mk_choices_tcref g m n) ("Choice"^string (i+1)^"Of"^string n)
|
|
|
|
let names_of_apinfo (APInfo(_,nms,_)) = nms
|
|
let total_of_apinfo (APInfo(total,_,_)) = total
|
|
|
|
let mk_apinfo_result_typ g m apinfo rtys =
|
|
let choicety = mk_choices_typ g m rtys
|
|
if total_of_apinfo apinfo then choicety else mk_option_ty g choicety
|
|
|
|
let mk_apinfo_typ g m apinfo dty rtys = mk_fun_ty dty (mk_apinfo_result_typ g m apinfo rtys)
|
|
|
|
|
|
(*---------------------------------------------------------------------------
|
|
!* RewriteExpr: rewrite bottom up with interceptors
|
|
*-------------------------------------------------------------------------*)
|
|
|
|
type ExprRewritingEnv =
|
|
{ pre_intercept: ((expr -> expr) -> expr -> expr option) option;
|
|
post_transform: expr -> expr option;
|
|
under_quotations: bool }
|
|
|
|
let rec rewrite_bind env (TBind(v,e,letSeqPtOpt) as bind) = TBind(v,RewriteExpr env e,letSeqPtOpt)
|
|
|
|
and rewrite_binds env binds = FlatList.map (rewrite_bind env) binds
|
|
|
|
and RewriteExpr env expr =
|
|
match expr with
|
|
| TExpr_let _
|
|
| TExpr_seq _ ->
|
|
rewrite_linear_expr env expr (fun e -> e)
|
|
| _ ->
|
|
let expr =
|
|
match pre_rewrite_expr env expr with
|
|
| Some expr -> expr
|
|
| None -> rewrite_expr_structure env expr
|
|
post_rewrite_expr env expr
|
|
|
|
and pre_rewrite_expr env expr =
|
|
match env.pre_intercept with
|
|
| Some f -> f (RewriteExpr env) expr
|
|
| None -> None
|
|
and post_rewrite_expr env expr =
|
|
match env.post_transform expr with
|
|
| None -> expr
|
|
| Some expr -> expr
|
|
|
|
and rewrite_expr_structure env expr =
|
|
match expr with
|
|
| TExpr_const _
|
|
| TExpr_val _ -> expr
|
|
| TExpr_app(f0,f0ty,tyargs,args,m) ->
|
|
let f0' = RewriteExpr env f0
|
|
let args' = rewrite_exprs env args
|
|
if f0 == f0' && args == args' then expr
|
|
else TExpr_app(f0',f0ty,tyargs,args',m)
|
|
|
|
| TExpr_quote(ast,{contents=Some(argTypes,argExprs,data)},m,ty) ->
|
|
TExpr_quote((if env.under_quotations then RewriteExpr env ast else ast),{contents=Some(argTypes,rewrite_exprs env argExprs,data)},m,ty)
|
|
| TExpr_quote(ast,{contents=None},m,ty) ->
|
|
TExpr_quote((if env.under_quotations then RewriteExpr env ast else ast),{contents=None},m,ty)
|
|
| TExpr_obj (_,ty,basev,basecall,overrides,iimpls,m,_) ->
|
|
mk_obj_expr(ty,basev,RewriteExpr env basecall,List.map (rewrite_override env) overrides,
|
|
List.map (rewrite_iimpl env) iimpls,m)
|
|
| TExpr_link eref ->
|
|
RewriteExpr env !eref
|
|
| TExpr_op (c,tyargs,args,m) ->
|
|
let args' = rewrite_exprs env args
|
|
if args == args' then expr
|
|
else TExpr_op(c,tyargs,args',m)
|
|
| TExpr_lambda(lambda_id,basevopt,argvs,body,m,rty,_) ->
|
|
let body = RewriteExpr env body
|
|
mk_basev_multi_lambda m basevopt argvs (body,rty)
|
|
| TExpr_tlambda(lambda_id,argtyvs,body,m,rty,_) ->
|
|
let body = RewriteExpr env body
|
|
mk_tlambda m argtyvs (body,rty)
|
|
| TExpr_match(spBind,exprm,dtree,targets,m,ty,_) ->
|
|
let dtree' = rewrite_dtree env dtree
|
|
let targets' = rewrite_targets env targets
|
|
mk_and_optimize_match spBind exprm m ty dtree' targets'
|
|
| TExpr_letrec (binds,e,m,_) ->
|
|
let binds = rewrite_binds env binds
|
|
let e' = RewriteExpr env e
|
|
TExpr_letrec(binds,e',m,NewFreeVarsCache())
|
|
| TExpr_let _ -> failwith "unreachable - linear let"
|
|
| TExpr_seq _ -> failwith "unreachable - linear seq"
|
|
| TExpr_static_optimization (constraints,e2,e3,m) ->
|
|
let e2' = RewriteExpr env e2
|
|
let e3' = RewriteExpr env e3
|
|
TExpr_static_optimization(constraints,e2',e3',m)
|
|
| TExpr_tchoose (a,b,m) ->
|
|
TExpr_tchoose(a,RewriteExpr env b,m)
|
|
and rewrite_linear_expr env expr contf =
|
|
(* schedule a rewrite on the way back up by adding to the continuation *)
|
|
let contf = contf << post_rewrite_expr env
|
|
match pre_rewrite_expr env expr with
|
|
| Some expr -> contf expr (* done - intercepted! *)
|
|
| None ->
|
|
match expr with
|
|
| TExpr_let (bind,body,m,_) ->
|
|
let bind = rewrite_bind env bind
|
|
rewrite_linear_expr env body (contf << (fun body' ->
|
|
mk_let_bind m bind body'))
|
|
| TExpr_seq (e1,e2,dir,spSeq,m) ->
|
|
let e1' = RewriteExpr env e1
|
|
rewrite_linear_expr env e2 (contf << (fun e2' ->
|
|
if e1 == e1' && e2 == e2' then expr
|
|
else TExpr_seq(e1',e2',dir,spSeq,m)))
|
|
| _ ->
|
|
(* no longer linear *)
|
|
contf (RewriteExpr env expr)
|
|
|
|
and rewrite_exprs env exprs = List.mapq (RewriteExpr env) exprs
|
|
and rewrite_FlatExprs env exprs = FlatList.mapq (RewriteExpr env) exprs
|
|
|
|
and rewrite_dtree env x =
|
|
match x with
|
|
| TDSuccess (es,n) ->
|
|
let es' = rewrite_FlatExprs env es
|
|
if FlatList.physicalEquality es es' then x
|
|
else TDSuccess(es',n)
|
|
| TDSwitch (e,cases,dflt,m) ->
|
|
let e' = RewriteExpr env e
|
|
let cases' = List.map (fun (TCase(discrim,e)) -> TCase(discrim,rewrite_dtree env e)) cases
|
|
let dflt' = Option.map (rewrite_dtree env) dflt
|
|
TDSwitch (e',cases',dflt',m)
|
|
| TDBind (bind,body) ->
|
|
let bind' = rewrite_bind env bind
|
|
let body = rewrite_dtree env body
|
|
TDBind (bind',body)
|
|
and rewrite_target env (TTarget(vs,e,spTarget)) = TTarget(vs,RewriteExpr env e,spTarget)
|
|
and rewrite_targets env targets = List.map (rewrite_target env) (Array.to_list targets)
|
|
|
|
and rewrite_override env (TObjExprMethod(slotsig,tps,vs,e,m)) =
|
|
TObjExprMethod(slotsig,tps,vs,RewriteExpr env e,m)
|
|
and rewrite_iimpl env (ty,overrides) =
|
|
(ty, List.map (rewrite_override env) overrides)
|
|
|
|
and rewrite_mexpr env x =
|
|
match x with
|
|
(* | TMTyped(mty,e,m) -> TMTyped(mty,rewrite_mexpr env e,m) *)
|
|
| TMTyped(mty,def,m) -> TMTyped(mty,rewrite_mdef env def,m)
|
|
and rewrite_mdefs env x = List.map (rewrite_mdef env) x
|
|
|
|
and rewrite_mdef env x =
|
|
match x with
|
|
| TMDefRec(tycons,binds,mbinds,m) -> TMDefRec(tycons,rewrite_binds env binds,rewrite_mbinds env mbinds,m)
|
|
| TMDefLet(bind,m) -> TMDefLet(rewrite_bind env bind,m)
|
|
| TMDefDo(e,m) -> TMDefDo(RewriteExpr env e,m)
|
|
| TMDefs(defs) -> TMDefs(rewrite_mdefs env defs)
|
|
| TMAbstract(mexpr) -> TMAbstract(rewrite_mexpr env mexpr)
|
|
and rewrite_mbind env (TMBind(nm, rhs)) = TMBind(nm,rewrite_mdef env rhs)
|
|
and rewrite_mbinds env mbinds = List.map (rewrite_mbind env) mbinds
|
|
|
|
and RewriteImplFile env mv = mapTImplFile (rewrite_mexpr env) mv
|
|
|
|
|
|
let is_flag_enum_typ (g:TcGlobals) typ =
|
|
(is_enum_typ g typ (* && TyconRefHasAttrib g g.attrib_FlagsAttribute (tcref_of_stripped_typ typ) *) )
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Build a mrpi that converts all "local" references to "public" things
|
|
* to be non local references.
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
let MakeExportRemapping viewedCcu =
|
|
|
|
let acc_entity_remap (tycon:Tycon) tmenv =
|
|
match tycon.PublicPath with
|
|
| Some pubpath ->
|
|
if !verboseStamps then dprintf "adding export remapping for tycon %s#%d\n" tycon.MangledName tycon.Stamp;
|
|
let tcref = rescope_tycon_pubpath viewedCcu pubpath tycon
|
|
tmenv_add_tcref_remap (mk_local_tcref tycon) tcref tmenv
|
|
| None -> error(InternalError("Unexpected tycon without a pubpath when remapping assembly data",tycon.Range))
|
|
|
|
let acc_val_remap (vspec:Val) tmenv =
|
|
match vspec.PublicPath with
|
|
| Some pubpath ->
|
|
if !verboseStamps then dprintf "adding export remapping for value %s#%d\n" vspec.MangledName vspec.Stamp;
|
|
{tmenv with vspec_remap=vspec_map_add vspec (rescope_val_pubpath viewedCcu pubpath vspec) tmenv.vspec_remap}
|
|
| None -> error(InternalError("Unexpected value without a pubpath when remapping assembly data",vspec.Range))
|
|
|
|
fun (mspec:ModuleOrNamespace) ->
|
|
fold_vals_and_tycons_of_mtyp acc_entity_remap acc_val_remap mspec.ModuleOrNamespaceType empty_expr_remap
|
|
|
|
(*--------------------------------------------------------------------------
|
|
!* Apply a "local to nonlocal" renaming to a module type. This can't use
|
|
* remap_mspec since the remapping we want isn't to newly created nodes
|
|
* but rather to remap to the nonlocal references. This is deliberately
|
|
* "breaking" the binding structure implicit in the module type, which is
|
|
* the whole point - one things are rewritten to use non local references then
|
|
* the elements can be copied at will, e.g. when inlining during optimization.
|
|
*------------------------------------------------------------------------ *)
|
|
|
|
|
|
let rec remap_tycon_data_to_nonlocal g tmenv d =
|
|
let tps',tmenvinner = tmenv_copy_remap_and_bind_typars (remap_attrib g tmenv) tmenv (d.entity_typars.Force(d.entity_range))
|
|
|
|
{ d with
|
|
entity_typars = LazyWithContext.NotLazy tps';
|
|
entity_attribs = d.entity_attribs |> remap_attribs g tmenvinner;
|
|
entity_tycon_repr = d.entity_tycon_repr |> Option.map (remap_tycon_repr g tmenvinner);
|
|
entity_tycon_abbrev = d.entity_tycon_abbrev |> Option.map (remap_type tmenvinner) ;
|
|
entity_tycon_tcaug = d.entity_tycon_tcaug |> remap_tcaug tmenvinner ;
|
|
entity_modul_contents =
|
|
notlazy (d.entity_modul_contents
|
|
|> Lazy.force
|
|
|> map_immediate_vals_and_tycons_of_modtyp (remap_tycon_to_nonlocal g tmenv)
|
|
(remap_val_to_nonlocal g tmenv));
|
|
entity_exn_info = d.entity_exn_info |> remap_tycon_exnc_info g tmenvinner}
|
|
|
|
and remap_tycon_to_nonlocal g tmenv x =
|
|
x |> NewModifiedTycon (remap_tycon_data_to_nonlocal g tmenv)
|
|
|
|
and remap_val_to_nonlocal g tmenv inp =
|
|
inp |> NewModifiedVal (remap_val_data g tmenv)
|
|
|
|
let ApplyExportRemappingToEntity g tmenv x = remap_tycon_to_nonlocal g tmenv x
|
|
|
|
(* Which constraints actually get compiled to .NET constraints? *)
|
|
let is_compiled_constraint cx =
|
|
match cx with
|
|
| TTyparIsNotNullableValueType _
|
|
| TTyparIsReferenceType _
|
|
| TTyparRequiresDefaultConstructor _
|
|
| TTyparCoercesToType _ -> true
|
|
| _ -> false
|
|
|
|
// Is a value a first-class polymorphic value with .NET constraints?
|
|
// Used to turn off TLR and method splitting
|
|
let IsGenericValWithGenericContraints g (v:Val) =
|
|
is_forall_typ g v.Type &&
|
|
v.Type |> dest_forall_typ g |> fst |> List.exists (fun tp -> List.exists is_compiled_constraint tp.Constraints)
|
|
|
|
(* Does a type support a given interface? *)
|
|
let tcaug_has_interface g tcaug ty =
|
|
List.exists (fun (x,_,_) -> type_equiv g ty x) tcaug.tcaug_implements
|
|
|
|
(* Does a type have an override matching the given name and argument types? *)
|
|
(* Used to detet the presence of 'Equals' and 'GetHashCode' in type checking *)
|
|
let tcaug_has_override g tcaug nm argtys =
|
|
tcaug.tcaug_adhoc
|
|
|> NameMultiMap.find nm
|
|
|> List.exists (fun vref ->
|
|
match vref.MemberInfo with
|
|
| None -> false
|
|
| Some membInfo ->
|
|
let argInfos = ArgInfosOfMember g vref
|
|
argInfos.Length = 1 &&
|
|
List.lengthsEqAndForall2 (type_equiv g) (List.map fst (List.hd argInfos)) argtys &&
|
|
membInfo.MemberFlags.MemberIsOverrideOrExplicitImpl)
|
|
|
|
let mk_fast_for_loop g (spLet,m,idv:Val,start,dir,finish,body) =
|
|
let dir = if dir then FSharpForLoopUp else FSharpForLoopDown
|
|
//let startv,starte = mk_compgen_local (range_of_expr start) "loopStart" g.int_ty
|
|
//let finishv,finishe = mk_compgen_local (range_of_expr finish) "loopEnd" g.int_ty
|
|
//mk_let spLet (range_of_expr start) startv start
|
|
//(mk_compgen_let (range_of_expr finish) finishv finish
|
|
mk_for g (spLet,idv,start,dir,finish,body,m)
|
|
|
|
let rec EvalConstantExpr g x =
|
|
match x with
|
|
|
|
(* Detect standard constants *)
|
|
| TExpr_const(c,m,_) ->
|
|
match c with
|
|
| TConst_bool _
|
|
| TConst_int32 _
|
|
| TConst_sbyte _
|
|
| TConst_int16 _
|
|
| TConst_int32 _
|
|
| TConst_int64 _
|
|
| TConst_byte _
|
|
| TConst_uint16 _
|
|
| TConst_uint32 _
|
|
| TConst_uint64 _
|
|
| TConst_float _
|
|
| TConst_float32 _
|
|
| TConst_char _
|
|
| TConst_zero _
|
|
| TConst_string _ -> x
|
|
| _ ->
|
|
errorR (Error ( "This constant may not be used as a custom attribute value",m));
|
|
x
|
|
|
|
| TExpr_app(TExpr_val(vref,_,_),_,[ty],[],m) when (is_typeof_vref g vref || is_typedefof_vref g vref) ->
|
|
x
|
|
| TExpr_op(TOp_coerce,_,[arg],_) ->
|
|
EvalConstantExpr g arg
|
|
| TExpr_app(TExpr_val(vref,_,_),_,_,[arg1],_) when g.vref_eq vref g.enum_vref ->
|
|
EvalConstantExpr g arg1
|
|
(* Detect bitwise or of attribute flags: one case of constant folding (a more general treatment is needed *)
|
|
| BitwiseOr g (arg1,arg2) ->
|
|
match EvalConstantExpr g arg1, EvalConstantExpr g arg2 with
|
|
| TExpr_const(TConst_int32 x1,m,ty), TExpr_const(TConst_int32 x2,_,_) -> TExpr_const(TConst_int32 (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_sbyte x1,m,ty), TExpr_const(TConst_sbyte x2,_,_) -> TExpr_const(TConst_sbyte (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_int16 x1,m,ty), TExpr_const(TConst_int16 x2,_,_) -> TExpr_const(TConst_int16 (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_int64 x1,m,ty), TExpr_const(TConst_int64 x2,_,_) -> TExpr_const(TConst_int64 (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_byte x1,m,ty), TExpr_const(TConst_byte x2,_,_) -> TExpr_const(TConst_byte (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_uint16 x1,m,ty), TExpr_const(TConst_uint16 x2,_,_) -> TExpr_const(TConst_uint16 (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_uint32 x1,m,ty), TExpr_const(TConst_uint32 x2,_,_) -> TExpr_const(TConst_uint32 (x1 ||| x2),m,ty)
|
|
| TExpr_const(TConst_uint64 x1,m,ty), TExpr_const(TConst_uint64 x2,_,_) -> TExpr_const(TConst_uint64 (x1 ||| x2),m,ty)
|
|
| _ -> x
|
|
| _ ->
|
|
errorR (Error ( "This is not a constant expression or valid custom attribute value",range_of_expr x));
|
|
x
|
|
|
|
|
|
let EvalAttribArg g x =
|
|
match x with
|
|
| TExpr_op(TOp_array,[elemTy],args,m) ->
|
|
let args = args |> List.map (EvalConstantExpr g)
|
|
TExpr_op(TOp_array,[elemTy],args,m)
|
|
| _ ->
|
|
EvalConstantExpr g x
|
|
|
|
// Take into account the fact that some "instance" members are compiled as static
|
|
// members when usinging CompilationRepresentation.Static, or any non-virtual instance members
|
|
// in a type that supports "null" as a true value. This is all members
|
|
// where ValRefIsCompiledAsInstanceMember is false but membInfo.MemberFlags.MemberIsInstance
|
|
// is true.
|
|
//
|
|
// This is the right abstraction for viewing member types, but the implementation
|
|
// below is a little ugly.
|
|
let GetTypeOfIntrinsicMemberInCompiledForm g (vref:ValRef) =
|
|
assert (not vref.IsExtensionMember)
|
|
let membInfo,topValInfo = check_member_vref vref
|
|
let tps,argInfos,rty,retInfo = GetTypeOfMemberInMemberForm g vref
|
|
let argInfos =
|
|
// Check if the thing is really an instance member compiled as a static member
|
|
// If so, the object argument counts as a normal argument in the compiled form
|
|
if membInfo.MemberFlags.MemberIsInstance && not (ValRefIsCompiledAsInstanceMember g vref) then
|
|
let _,origArgInfos,_,_ = GetTopValTypeInFSharpForm g topValInfo vref.Type vref.Range
|
|
match origArgInfos with
|
|
| [] ->
|
|
errorR(InternalError("value does not have a valid member type",vref.Range));
|
|
argInfos
|
|
| h::t -> h ::argInfos
|
|
else argInfos
|
|
tps,argInfos,rty,retInfo
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Tuple compilation (expressions)
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
let rec compiled_mk_tuple g (argtys,args,m) =
|
|
let n = List.length argtys
|
|
if n <= 0 then failwith "compiled_mk_tuple"
|
|
elif n < maxTuple then (compiled_tuple_tcref g argtys, argtys, args, m)
|
|
else
|
|
let argtysA,argtysB = split_after goodTupleFields argtys
|
|
let argsA,argsB = split_after (goodTupleFields) args
|
|
let ty8, v8 =
|
|
match argtysB,argsB with
|
|
| [ty8],[arg8] ->
|
|
match ty8 with
|
|
// if it's already been nested or ended, pass it through
|
|
| TType_app(tn, _) when (is_tuple_tcref g tn) ->
|
|
ty8,arg8
|
|
| _ ->
|
|
let ty8enc = TType_app(g.tuple1_tcr,[ty8])
|
|
let v8enc = TExpr_op(TOp_tuple,[ty8],[arg8],m)
|
|
ty8enc,v8enc
|
|
| _ ->
|
|
let a,b,c,d = compiled_mk_tuple g (argtysB, argsB, m)
|
|
let ty8plus = TType_app(a,b)
|
|
let v8plus = TExpr_op(TOp_tuple,b,c,d)
|
|
ty8plus,v8plus
|
|
let argtysAB = argtysA @ [ty8]
|
|
(compiled_tuple_tcref g argtysAB, argtysAB,argsA @ [v8],m)
|
|
|
|
let get_rfref_of_tcref(tcref,n) =
|
|
let f = (deref_tycon tcref).GetFieldByIndex n
|
|
rfref_of_rfield tcref f
|
|
|
|
let mspec_Tuple_ItemN (g : TcGlobals) typ n = IL.mk_nongeneric_instance_mspec_in_tref((tref_of_typ typ), AsObject, (if n < goodTupleFields then "get_Item"^(n+1).ToString() else "get_Rest"), [], (mk_tyvar_ty (uint16 n)), (inst_of_typ typ))
|
|
let mk_call_Tuple_ItemN g m n typ te retty =
|
|
mk_asm([IL.mk_normal_call(mspec_Tuple_ItemN g typ n)],[],[te],[retty],m)
|
|
|
|
|