Skip to content

Commit 05b0569

Browse files
dsymeDon Syme
andauthored
Witnesses made visible in FCS (#9510)
Co-authored-by: Don Syme <[email protected]>
1 parent 4fed162 commit 05b0569

File tree

14 files changed

+2799
-2319
lines changed

14 files changed

+2799
-2319
lines changed

src/fsharp/AttributeChecking.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ type AttribInfo =
7979
| FSAttribInfo of TcGlobals * Attrib
8080
| ILAttribInfo of TcGlobals * Import.ImportMap * ILScopeRef * ILAttribute * range
8181

82+
member x.Range =
83+
match x with
84+
| FSAttribInfo(_, attrib) -> attrib.Range
85+
| ILAttribInfo (_, _, _, _, m) -> m
86+
8287
member x.TyconRef =
8388
match x with
8489
| FSAttribInfo(_g, Attrib(tcref, _, _, _, _, _, _)) -> tcref

src/fsharp/MethodCalls.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,13 +1864,21 @@ let GenWitnessExpr amap g m (traitInfo: TraitConstraintInfo) argExprs =
18641864
let argTypes =
18651865
minfo.GetParamTypes(amap, m, methArgTys)
18661866
|> List.concat
1867+
18671868
// do not apply coercion to the 'receiver' argument
18681869
let receiverArgOpt, argExprs =
18691870
if minfo.IsInstance then
18701871
match argExprs with
18711872
| h :: t -> Some h, t
18721873
| argExprs -> None, argExprs
18731874
else None, argExprs
1875+
1876+
// For methods taking no arguments, 'argExprs' will be a single unit expression here
1877+
let argExprs =
1878+
match argTypes, argExprs with
1879+
| [], [_] -> []
1880+
| _ -> argExprs
1881+
18741882
let convertedArgs = (argExprs, argTypes) ||> List.map2 (fun expr expectedTy -> mkCoerceIfNeeded g expectedTy (tyOfExpr g expr) expr)
18751883
match receiverArgOpt with
18761884
| Some r -> r :: convertedArgs

src/fsharp/TypedTree.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,6 +4115,8 @@ type Attrib =
41154115

41164116
member x.TyconRef = (let (Attrib(tcref, _, _, _, _, _, _)) = x in tcref)
41174117

4118+
member x.Range = (let (Attrib(_, _, _, _, _, _, m)) = x in m)
4119+
41184120
override x.ToString() = "attrib" + x.TyconRef.ToString()
41194121

41204122
/// We keep both source expression and evaluated expression around to help intellisense and signature printing

src/fsharp/TypedTreeOps.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4237,9 +4237,12 @@ let ComputeRemappingFromInferredSignatureToExplicitSignature g mty msigty =
42374237
/// At TMDefRec nodes abstract (virtual) vslots are effectively binders, even
42384238
/// though they are tucked away inside the tycon. This helper function extracts the
42394239
/// virtual slots to aid with finding this babies.
4240-
let abstractSlotValsOfTycons (tycons: Tycon list) =
4240+
let abstractSlotValRefsOfTycons (tycons: Tycon list) =
42414241
tycons
42424242
|> List.collect (fun tycon -> if tycon.IsFSharpObjectModelTycon then tycon.FSharpObjectModelTypeInfo.fsobjmodel_vslots else [])
4243+
4244+
let abstractSlotValsOfTycons (tycons: Tycon list) =
4245+
abstractSlotValRefsOfTycons tycons
42434246
|> List.map (fun v -> v.Deref)
42444247

42454248
let rec accEntityRemapFromModuleOrNamespace msigty x acc =

src/fsharp/TypedTreeOps.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,8 @@ module SimplifyTypes =
10431043

10441044
val superOfTycon : TcGlobals -> Tycon -> TType
10451045

1046+
val abstractSlotValRefsOfTycons : Tycon list -> ValRef list
1047+
10461048
val abstractSlotValsOfTycons : Tycon list -> Val list
10471049

10481050
//-------------------------------------------------------------------------

src/fsharp/symbols/Exprs.fs

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace FSharp.Compiler.SourceCodeServices
55
open FSharp.Compiler
66
open FSharp.Compiler.AbstractIL.Internal.Library
77
open FSharp.Compiler.AbstractIL.IL
8+
open FSharp.Compiler.ErrorLogger
89
open FSharp.Compiler.Lib
910
open FSharp.Compiler.Infos
1011
open FSharp.Compiler.QuotationTranslator
@@ -35,13 +36,26 @@ module ExprTranslationImpl =
3536
isinstVals: ValMap<TType * Expr>
3637

3738
substVals: ValMap<Expr>
39+
40+
/// Indicates that we disable generation of witnesses
41+
suppressWitnesses: bool
42+
43+
/// All witnesses in scope and their mapping to lambda variables.
44+
//
45+
// Note: this uses an immutable HashMap/Dictionary with an IEqualityComparer that captures TcGlobals, see
46+
// the point where the empty initial object is created.
47+
witnessesInScope: TraitWitnessInfoHashMap<int>
48+
3849
}
3950

40-
static member Empty =
51+
static member Empty g =
4152
{ vs=ValMap<_>.Empty
42-
tyvs = Map.empty ;
53+
tyvs = Map.empty
4354
isinstVals = ValMap<_>.Empty
44-
substVals = ValMap<_>.Empty }
55+
substVals = ValMap<_>.Empty
56+
suppressWitnesses = false
57+
witnessesInScope = EmptyTraitWitnessInfoHashMap g
58+
}
4559

4660
member env.BindTypar (v: Typar, gp) =
4761
{ env with tyvs = env.tyvs.Add(v.Stamp, gp ) }
@@ -81,7 +95,7 @@ type E =
8195
| IfThenElse of FSharpExpr * FSharpExpr * FSharpExpr
8296
| DecisionTree of FSharpExpr * (FSharpMemberOrFunctionOrValue list * FSharpExpr) list
8397
| DecisionTreeSuccess of int * FSharpExpr list
84-
| Call of FSharpExpr option * FSharpMemberOrFunctionOrValue * FSharpType list * FSharpType list * FSharpExpr list
98+
| Call of FSharpExpr option * FSharpMemberOrFunctionOrValue * FSharpType list * FSharpType list * FSharpExpr list * FSharpExpr list
8599
| NewObject of FSharpMemberOrFunctionOrValue * FSharpType list * FSharpExpr list
86100
| LetRec of ( FSharpMemberOrFunctionOrValue * FSharpExpr) list * FSharpExpr
87101
| Let of (FSharpMemberOrFunctionOrValue * FSharpExpr) * FSharpExpr
@@ -117,6 +131,7 @@ type E =
117131
| ILFieldGet of FSharpExpr option * FSharpType * string
118132
| ILFieldSet of FSharpExpr option * FSharpType * string * FSharpExpr
119133
| ILAsm of string * FSharpType list * FSharpExpr list
134+
| WitnessArg of int
120135

121136
/// Used to represent the information at an object expression member
122137
and [<Sealed>] FSharpObjectExprOverride(sgn: FSharpAbstractSignature, gps: FSharpGenericParameter list, args: FSharpMemberOrFunctionOrValue list list, body: FSharpExpr) =
@@ -128,10 +143,11 @@ and [<Sealed>] FSharpObjectExprOverride(sgn: FSharpAbstractSignature, gps: FSha
128143
/// The type of expressions provided through the compiler API.
129144
and [<Sealed>] FSharpExpr (cenv, f: (unit -> FSharpExpr) option, e: E, m: range, ty) =
130145

146+
let mutable e = match f with None -> e | Some _ -> Unchecked.defaultof<E>
131147
member x.Range = m
132148
member x.Type = FSharpType(cenv, ty)
133149
member x.cenv = cenv
134-
member x.E = match f with None -> e | Some f -> f().E
150+
member x.E = match box e with null -> (e <- f.Value().E); e | _ -> e
135151
override x.ToString() = sprintf "%+A" x.E
136152

137153
member x.ImmediateSubExpressions =
@@ -150,7 +166,7 @@ and [<Sealed>] FSharpExpr (cenv, f: (unit -> FSharpExpr) option, e: E, m: range,
150166
| E.NewUnionCase (_unionType, _unionCase, es) -> es
151167
| E.NewTuple (_tupleType, es) -> es
152168
| E.TupleGet (_tupleType, _itemIndex, tupleExpr) -> [tupleExpr]
153-
| E.Call (objOpt, _b, _c, _d, es) -> (match objOpt with None -> es | Some x -> x :: es)
169+
| E.Call (objOpt, _b, _c, _d, ws, es) -> (match objOpt with None -> ws @ es | Some x -> x :: ws @ es)
154170
| E.NewObject (_a, _b, c) -> c
155171
| E.FSharpFieldGet (objOpt, _b, _c) -> (match objOpt with None -> [] | Some x -> [x])
156172
| E.FSharpFieldSet (objOpt, _b, _c, d) -> (match objOpt with None -> [d] | Some x -> [x;d])
@@ -187,7 +203,7 @@ and [<Sealed>] FSharpExpr (cenv, f: (unit -> FSharpExpr) option, e: E, m: range,
187203
| E.UnionCaseSet (obj, _unionType, _unionCase, _unionField, valueExpr) -> [ yield obj; yield valueExpr ]
188204
| E.TraitCall (_sourceTypes, _traitName, _memberFlags, _paramTypes, _retTypes, args) -> args
189205
| E.Unused -> [] // unexpected
190-
206+
| E.WitnessArg _n -> []
191207

192208
/// The implementation of the conversion operation
193209
module FSharpExprConvert =
@@ -409,19 +425,19 @@ module FSharpExprConvert =
409425
let (numEnclTypeArgs, _, isNewObj, _valUseFlags, _isSelfInit, takesInstanceArg, _isPropGet, _isPropSet) =
410426
GetMemberCallInfo cenv.g (vref, vFlags)
411427

412-
let isMember, curriedArgInfos =
428+
let isMember, tps, curriedArgInfos =
413429

414430
match vref.MemberInfo with
415431
| Some _ when not vref.IsExtensionMember ->
416432
// This is an application of a member method
417433
// We only count one argument block for these.
418-
let _tps, curriedArgInfos, _, _ = GetTypeOfMemberInFSharpForm cenv.g vref
419-
true, curriedArgInfos
434+
let tps, curriedArgInfos, _, _ = GetTypeOfMemberInFSharpForm cenv.g vref
435+
true, tps, curriedArgInfos
420436
| _ ->
421437
// This is an application of a module value or extension member
422438
let arities = arityOfVal vref.Deref
423-
let _tps, curriedArgInfos, _, _ = GetTopValTypeInFSharpForm cenv.g arities vref.Type m
424-
false, curriedArgInfos
439+
let tps, curriedArgInfos, _, _ = GetTopValTypeInFSharpForm cenv.g arities vref.Type m
440+
false, tps, curriedArgInfos
425441

426442
// Compute the object arguments as they appear in a compiled call
427443
// Strip off the object argument, if any. The curriedArgInfos are already adjusted to compiled member form
@@ -467,12 +483,35 @@ module FSharpExprConvert =
467483
if isMember then
468484
let callArgs = (objArgs :: untupledCurriedArgs) |> List.concat
469485
let enclTyArgs, methTyArgs = List.splitAfter numEnclTypeArgs tyargs
486+
let witnessArgsR = GetWitnessArgs cenv env vref m tps tyargs
470487
// tailcall
471-
ConvObjectModelCallLinear cenv env (isNewObj, FSharpMemberOrFunctionOrValue(cenv, vref), enclTyArgs, methTyArgs, callArgs) contf2
488+
ConvObjectModelCallLinear cenv env (isNewObj, FSharpMemberOrFunctionOrValue(cenv, vref), enclTyArgs, methTyArgs, witnessArgsR, callArgs) contf2
472489
else
473490
let v = FSharpMemberOrFunctionOrValue(cenv, vref)
491+
let witnessArgsR = GetWitnessArgs cenv env vref m vref.Typars tyargs
474492
// tailcall
475-
ConvObjectModelCallLinear cenv env (false, v, [], tyargs, List.concat untupledCurriedArgs) contf2
493+
ConvObjectModelCallLinear cenv env (false, v, [], tyargs, witnessArgsR, List.concat untupledCurriedArgs) contf2
494+
495+
and GetWitnessArgs cenv (env: ExprTranslationEnv) (vref: ValRef) m tps tyargs : FSharpExpr list =
496+
let g = cenv.g
497+
if cenv.g.langVersion.SupportsFeature(Features.LanguageFeature.WitnessPassing) && not env.suppressWitnesses then
498+
let witnessExprs =
499+
match ConstraintSolver.CodegenWitnessesForTyparInst cenv.tcValF g cenv.amap m tps tyargs with
500+
// There is a case where optimized code makes expressions that do a shift-left on the 'char'
501+
// type. There is no witness for this case. This is due to the code
502+
// let inline HashChar (x:char) = (# "or" (# "shl" x 16 : int #) x : int #)
503+
// in FSharp.Core.
504+
| ErrorResult _ when vref.LogicalName = "op_LeftShift" && tyargs.Length = 1 -> []
505+
| res -> CommitOperationResult res
506+
let env = { env with suppressWitnesses = true }
507+
witnessExprs |> List.map (fun arg ->
508+
match arg with
509+
| Choice1Of2 traitInfo ->
510+
ConvWitnessInfo cenv env m traitInfo
511+
| Choice2Of2 arg ->
512+
ConvExpr cenv env arg)
513+
else
514+
[]
476515

477516
and ConvExprPrim (cenv: SymbolEnv) (env: ExprTranslationEnv) expr =
478517
// Eliminate integer 'for' loops
@@ -554,7 +593,7 @@ module FSharpExprConvert =
554593
let vslR = List.map (List.map (ConvVal cenv)) tmvs
555594
let sgn = FSharpAbstractSignature(cenv, slotsig)
556595
let tpsR = [ for tp in tps -> FSharpGenericParameter(cenv, tp) ]
557-
let env = ExprTranslationEnv.Empty.BindTypars (Seq.zip tps tpsR |> Seq.toList)
596+
let env = env.BindTypars (Seq.zip tps tpsR |> Seq.toList)
558597
let env = env.BindCurriedVals tmvs
559598
let bodyR = ConvExpr cenv env body
560599
FSharpObjectExprOverride(sgn, tpsR, vslR, bodyR) ]
@@ -841,9 +880,29 @@ module FSharpExprConvert =
841880
ConvExprPrim cenv env replExpr
842881

843882
| _ -> wfail (sprintf "unhandled construct in AST", m)
883+
| Expr.WitnessArg (traitInfo, _m) ->
884+
ConvWitnessInfoPrim cenv env traitInfo
844885
| _ ->
845886
wfail (sprintf "unhandled construct in AST", expr.Range)
846887

888+
and ConvWitnessInfoPrim _cenv env traitInfo : E =
889+
let witnessInfo = traitInfo.TraitKey
890+
let env = { env with suppressWitnesses = true }
891+
// First check if this is a witness in ReflectedDefinition code
892+
if env.witnessesInScope.ContainsKey witnessInfo then
893+
let witnessArgIdx = env.witnessesInScope.[witnessInfo]
894+
E.WitnessArg(witnessArgIdx)
895+
// Otherwise it is a witness in a quotation literal
896+
else
897+
//failwith "witness not found"
898+
E.WitnessArg(-1)
899+
900+
and ConvWitnessInfo cenv env m traitInfo : FSharpExpr =
901+
let g = cenv.g
902+
let witnessInfo = traitInfo.TraitKey
903+
let witnessTy = GenWitnessTy g witnessInfo
904+
let traitInfoR = ConvWitnessInfoPrim cenv env traitInfo
905+
Mk cenv m witnessTy traitInfoR
847906

848907
and ConvLetBind cenv env (bind : Binding) =
849908
match bind.Expr with
@@ -895,7 +954,7 @@ module FSharpExprConvert =
895954
let enclosingType = generalizedTyconRef tcref
896955

897956
let makeCall minfo =
898-
ConvObjectModelCallLinear cenv env (isNewObj, minfo, enclTypeArgs, methTypeArgs, callArgs) id
957+
ConvObjectModelCallLinear cenv env (isNewObj, minfo, enclTypeArgs, methTypeArgs, [], callArgs) id
899958

900959
let makeFSCall isMember (vr: ValRef) =
901960
let memOrVal =
@@ -1079,7 +1138,7 @@ module FSharpExprConvert =
10791138
with e ->
10801139
failwithf "An IL call to '%s' could not be resolved: %s" (ilMethRef.ToString()) e.Message
10811140

1082-
and ConvObjectModelCallLinear cenv env (isNewObj, v: FSharpMemberOrFunctionOrValue, enclTyArgs, methTyArgs, callArgs) contF =
1141+
and ConvObjectModelCallLinear cenv env (isNewObj, v: FSharpMemberOrFunctionOrValue, enclTyArgs, methTyArgs, witnessArgsR: FSharpExpr list, callArgs) contF =
10831142
let enclTyArgsR = ConvTypes cenv enclTyArgs
10841143
let methTyArgsR = ConvTypes cenv methTyArgs
10851144
let obj, callArgs =
@@ -1095,8 +1154,7 @@ module FSharpExprConvert =
10951154
if isNewObj then
10961155
E.NewObject(v, enclTyArgsR, callArgsR)
10971156
else
1098-
E.Call(objR, v, enclTyArgsR, methTyArgsR, callArgsR))
1099-
1157+
E.Call(objR, v, enclTyArgsR, methTyArgsR, witnessArgsR, callArgsR))
11001158

11011159
and ConvExprs cenv env args = List.map (ConvExpr cenv env) args
11021160

@@ -1255,7 +1313,7 @@ and FSharpImplementationFileContents(cenv, mimpl) =
12551313
let v = FSharpMemberOrFunctionOrValue(cenv, mkLocalValRef v)
12561314
let gps = v.GenericParameters
12571315
let vslR = List.map (List.map (FSharpExprConvert.ConvVal cenv)) vsl
1258-
let env = ExprTranslationEnv.Empty.BindTypars (Seq.zip tps gps |> Seq.toList)
1316+
let env = ExprTranslationEnv.Empty(cenv.g).BindTypars (Seq.zip tps gps |> Seq.toList)
12591317
let env = env.BindCurriedVals vsl
12601318
let e = FSharpExprConvert.ConvExprOnDemand cenv env body
12611319
FSharpImplementationFileDeclaration.MemberOrFunctionOrValue(v, vslR, e)
@@ -1277,7 +1335,7 @@ and FSharpImplementationFileContents(cenv, mimpl) =
12771335
| TMDefLet(bind, _m) ->
12781336
[ yield getBind bind ]
12791337
| TMDefDo(expr, _m) ->
1280-
[ let expr = FSharpExprConvert.ConvExprOnDemand cenv ExprTranslationEnv.Empty expr
1338+
[ let expr = FSharpExprConvert.ConvExprOnDemand cenv (ExprTranslationEnv.Empty(cenv.g)) expr
12811339
yield FSharpImplementationFileDeclaration.InitAction expr ]
12821340
| TMDefs mdefs ->
12831341
[ for mdef in mdefs do yield! getDecls mdef ]
@@ -1303,7 +1361,8 @@ module BasicPatterns =
13031361
let (|NewUnionCase|_|) (e: FSharpExpr) = match e.E with E.NewUnionCase (e, tys, es) -> Some (e, tys, es) | _ -> None
13041362
let (|NewTuple|_|) (e: FSharpExpr) = match e.E with E.NewTuple (ty, es) -> Some (ty, es) | _ -> None
13051363
let (|TupleGet|_|) (e: FSharpExpr) = match e.E with E.TupleGet (ty, n, es) -> Some (ty, n, es) | _ -> None
1306-
let (|Call|_|) (e: FSharpExpr) = match e.E with E.Call (a, b, c, d, e) -> Some (a, b, c, d, e) | _ -> None
1364+
let (|Call|_|) (e: FSharpExpr) = match e.E with E.Call (a, b, c, d, _e, f) -> Some (a, b, c, d, f) | _ -> None
1365+
let (|CallWithWitnesses|_|) (e: FSharpExpr) = match e.E with E.Call (a, b, c, d, e, f) -> Some (a, b, c, d, e, f) | _ -> None
13071366
let (|NewObject|_|) (e: FSharpExpr) = match e.E with E.NewObject (a, b, c) -> Some (a, b, c) | _ -> None
13081367
let (|FSharpFieldGet|_|) (e: FSharpExpr) = match e.E with E.FSharpFieldGet (a, b, c) -> Some (a, b, c) | _ -> None
13091368
let (|AnonRecordGet|_|) (e: FSharpExpr) = match e.E with E.AnonRecordGet (a, b, c) -> Some (a, b, c) | _ -> None
@@ -1335,4 +1394,5 @@ module BasicPatterns =
13351394
let (|DecisionTreeSuccess|_|) (e: FSharpExpr) = match e.E with E.DecisionTreeSuccess (a, b) -> Some (a, b) | _ -> None
13361395
let (|UnionCaseSet|_|) (e: FSharpExpr) = match e.E with E.UnionCaseSet (a, b, c, d, e) -> Some (a, b, c, d, e) | _ -> None
13371396
let (|TraitCall|_|) (e: FSharpExpr) = match e.E with E.TraitCall (a, b, c, d, e, f) -> Some (a, b, c, d, e, f) | _ -> None
1397+
let (|WitnessArg|_|) (e: FSharpExpr) = match e.E with E.WitnessArg n -> Some n | _ -> None
13381398

src/fsharp/symbols/Exprs.fsi

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
22

3-
namespace FSharp.Compiler.SourceCodeServices
3+
namespace rec FSharp.Compiler.SourceCodeServices
44

55
open FSharp.Compiler.CompilerImports
66
open FSharp.Compiler.Range
@@ -17,7 +17,7 @@ type public FSharpAssemblyContents =
1717
member ImplementationFiles: FSharpImplementationFileContents list
1818

1919
/// Represents the definitional contents of a single file or fragment in an assembly, as seen by the F# language
20-
and [<Class>] public FSharpImplementationFileContents =
20+
type public FSharpImplementationFileContents =
2121
internal new : cenv: SymbolEnv * mimpl: TypedImplFile -> FSharpImplementationFileContents
2222

2323
/// The qualified name acts to fully-qualify module specifications and implementations
@@ -52,7 +52,8 @@ and public FSharpImplementationFileDeclaration =
5252
///
5353
/// Pattern matching is reduced to decision trees and conditional tests. Some other
5454
/// constructs may be represented in reduced form.
55-
and [<Sealed>] public FSharpExpr =
55+
[<Sealed>]
56+
type public FSharpExpr =
5657
/// The range of the expression
5758
member Range : range
5859

@@ -108,6 +109,9 @@ module public BasicPatterns =
108109
/// arguments are collapsed to a single collection of arguments, as done in the compiled version of these.
109110
val (|Call|_|) : FSharpExpr -> (FSharpExpr option * FSharpMemberOrFunctionOrValue * FSharpType list * FSharpType list * FSharpExpr list) option
110111

112+
/// Like Call but also indicates witness arguments
113+
val (|CallWithWitnesses|_|) : FSharpExpr -> (FSharpExpr option * FSharpMemberOrFunctionOrValue * FSharpType list * FSharpType list * FSharpExpr list * FSharpExpr list) option
114+
111115
/// Matches expressions which are calls to object constructors
112116
val (|NewObject|_|) : FSharpExpr -> (FSharpMemberOrFunctionOrValue * FSharpType list * FSharpExpr list) option
113117

@@ -218,3 +222,5 @@ module public BasicPatterns =
218222
/// Matches expressions for an unresolved call to a trait
219223
val (|TraitCall|_|) : FSharpExpr -> (FSharpType list * string * MemberFlags * FSharpType list * FSharpType list * FSharpExpr list) option
220224

225+
/// Indicates a witness argument index from the witness arguments supplied to the enclosing method
226+
val (|WitnessArg|_|) : FSharpExpr -> int option

0 commit comments

Comments
 (0)