Skip to content

Commit bd9807d

Browse files
committed
witness passing implementation
1 parent 04119ce commit bd9807d

35 files changed

+3782
-1243
lines changed

src/fsharp/ConstraintSolver.fs

Lines changed: 34 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ type ConstraintSolverState =
216216
/// The function used to freshen values we encounter during trait constraint solving
217217
TcVal: TcValF
218218

219+
/// Indicates if the constraint solver is being run after type checking is complete,
220+
/// e.g. during codegen to determine solutions and witnesses for trait constraints.
221+
/// Suppresses the generation of certain errors such as missing constraint warnings.
222+
codegen: bool
223+
219224
/// This table stores all unsolved, ungeneralized trait constraints, indexed by free type variable.
220225
/// That is, there will be one entry in this table for each free type variable in
221226
/// each outstanding, unsolved, ungeneralized trait constraint. Constraints are removed from the table and resolved
@@ -228,6 +233,7 @@ type ConstraintSolverState =
228233
amap = amap
229234
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
230235
InfoReader = infoReader
236+
codegen = false
231237
TcVal = tcVal }
232238

233239
type ConstraintSolverEnv =
@@ -1833,14 +1839,14 @@ and AddConstraint (csenv: ConstraintSolverEnv) ndeep m2 trace tp newConstraint
18331839
| (TyparRigidity.Rigid | TyparRigidity.WillBeRigid), TyparConstraint.DefaultsTo _ -> true
18341840
| _ -> false) then
18351841
()
1836-
elif tp.Rigidity = TyparRigidity.Rigid then
1842+
elif tp.Rigidity = TyparRigidity.Rigid && not csenv.SolverState.codegen then
18371843
return! ErrorD (ConstraintSolverMissingConstraint(denv, tp, newConstraint, m, m2))
18381844
else
18391845
// It is important that we give a warning if a constraint is missing from a
18401846
// will-be-made-rigid type variable. This is because the existence of these warnings
18411847
// is relevant to the overload resolution rules (see 'candidateWarnCount' in the overload resolution
18421848
// implementation).
1843-
if tp.Rigidity.WarnIfMissingConstraint then
1849+
if tp.Rigidity.WarnIfMissingConstraint && not csenv.SolverState.codegen then
18441850
do! WarnD (ConstraintSolverMissingConstraint(denv, tp, newConstraint, m, m2))
18451851

18461852
let newConstraints =
@@ -2846,113 +2852,36 @@ let AddCxTypeIsDelegate denv css m trace ty aty bty =
28462852
(fun res -> ErrorD (ErrorFromAddingConstraint(denv, res, m)))
28472853
|> RaiseOperationResult
28482854

2849-
let CodegenWitnessThatTypeSupportsTraitConstraint tcVal g amap m (traitInfo: TraitConstraintInfo) argExprs = trackErrors {
2850-
let css =
2851-
{ g = g
2852-
amap = amap
2853-
TcVal = tcVal
2854-
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
2855-
InfoReader = new InfoReader(g, amap) }
2856-
2855+
let CreateCodegenState tcVal g amap =
2856+
{ g = g
2857+
amap = amap
2858+
TcVal = tcVal
2859+
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
2860+
InfoReader = new InfoReader(g, amap)
2861+
codegen = true }
2862+
2863+
/// Generate a witness expression if none is otherwise available, e.g. in legacy non-witness-passing code
2864+
let CodegenWitnessForTraitConstraint tcVal g amap m (traitInfo:TraitConstraintInfo) argExprs = trackErrors {
2865+
let css = CreateCodegenState tcVal g amap
28572866
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g)
28582867
let! _res = SolveMemberConstraint csenv true true 0 m NoTrace traitInfo
2859-
let sln =
2860-
match traitInfo.Solution with
2861-
| None -> Choice5Of5()
2862-
| Some sln ->
2863-
match sln with
2864-
| ILMethSln(origTy, extOpt, mref, minst) ->
2865-
let metadataTy = convertToTypeWithMetadataIfPossible g origTy
2866-
let tcref = tcrefOfAppTy g metadataTy
2867-
let mdef = IL.resolveILMethodRef tcref.ILTyconRawMetadata mref
2868-
let ilMethInfo =
2869-
match extOpt with
2870-
| None -> MethInfo.CreateILMeth(amap, m, origTy, mdef)
2871-
| Some ilActualTypeRef ->
2872-
let actualTyconRef = Import.ImportILTypeRef amap m ilActualTypeRef
2873-
MethInfo.CreateILExtensionMeth(amap, m, origTy, actualTyconRef, None, mdef)
2874-
Choice1Of5 (ilMethInfo, minst)
2875-
| FSMethSln(ty, vref, minst) ->
2876-
Choice1Of5 (FSMeth(g, ty, vref, None), minst)
2877-
| FSRecdFieldSln(tinst, rfref, isSetProp) ->
2878-
Choice2Of5 (tinst, rfref, isSetProp)
2879-
| FSAnonRecdFieldSln(anonInfo, tinst, i) ->
2880-
Choice3Of5 (anonInfo, tinst, i)
2881-
| BuiltInSln ->
2882-
Choice5Of5 ()
2883-
| ClosedExprSln expr ->
2884-
Choice4Of5 expr
2885-
return!
2886-
match sln with
2887-
| Choice1Of5(minfo, methArgTys) ->
2888-
let argExprs =
2889-
// FIX for #421894 - typechecker assumes that coercion can be applied for the trait calls arguments but codegen doesn't emit coercion operations
2890-
// result - generation of non-verifiable code
2891-
// fix - apply coercion for the arguments (excluding 'receiver' argument in instance calls)
2892-
2893-
// flatten list of argument types (looks like trait calls with curried arguments are not supported so we can just convert argument list in straightforward way)
2894-
let argTypes =
2895-
minfo.GetParamTypes(amap, m, methArgTys)
2896-
|> List.concat
2897-
// do not apply coercion to the 'receiver' argument
2898-
let receiverArgOpt, argExprs =
2899-
if minfo.IsInstance then
2900-
match argExprs with
2901-
| h :: t -> Some h, t
2902-
| argExprs -> None, argExprs
2903-
else None, argExprs
2904-
let convertedArgs = (argExprs, argTypes) ||> List.map2 (fun expr expectedTy -> mkCoerceIfNeeded g expectedTy (tyOfExpr g expr) expr)
2905-
match receiverArgOpt with
2906-
| Some r -> r :: convertedArgs
2907-
| None -> convertedArgs
2908-
2909-
// Fix bug 1281: If we resolve to an instance method on a struct and we haven't yet taken
2910-
// the address of the object then go do that
2911-
if minfo.IsStruct && minfo.IsInstance && (match argExprs with [] -> false | h :: _ -> not (isByrefTy g (tyOfExpr g h))) then
2912-
let h, t = List.headAndTail argExprs
2913-
let wrap, h', _readonly, _writeonly = mkExprAddrOfExpr g true false PossiblyMutates h None m
2914-
ResultD (Some (wrap (Expr.Op (TOp.TraitCall (traitInfo), [], (h' :: t), m))))
2915-
else
2916-
ResultD (Some (MakeMethInfoCall amap m minfo methArgTys argExprs ))
2917-
2918-
| Choice2Of5 (tinst, rfref, isSet) ->
2919-
let res =
2920-
match isSet, rfref.RecdField.IsStatic, argExprs.Length with
2921-
| true, true, 1 ->
2922-
Some (mkStaticRecdFieldSet (rfref, tinst, argExprs.[0], m))
2923-
| true, false, 2 ->
2924-
// If we resolve to an instance field on a struct and we haven't yet taken
2925-
// the address of the object then go do that
2926-
if rfref.Tycon.IsStructOrEnumTycon && not (isByrefTy g (tyOfExpr g argExprs.[0])) then
2927-
let h = List.head argExprs
2928-
let wrap, h', _readonly, _writeonly = mkExprAddrOfExpr g true false DefinitelyMutates h None m
2929-
Some (wrap (mkRecdFieldSetViaExprAddr (h', rfref, tinst, argExprs.[1], m)))
2930-
else
2931-
Some (mkRecdFieldSetViaExprAddr (argExprs.[0], rfref, tinst, argExprs.[1], m))
2932-
| false, true, 0 ->
2933-
Some (mkStaticRecdFieldGet (rfref, tinst, m))
2934-
| false, false, 1 ->
2935-
if rfref.Tycon.IsStructOrEnumTycon && isByrefTy g (tyOfExpr g argExprs.[0]) then
2936-
Some (mkRecdFieldGetViaExprAddr (argExprs.[0], rfref, tinst, m))
2937-
else
2938-
Some (mkRecdFieldGet g (argExprs.[0], rfref, tinst, m))
2939-
| _ -> None
2940-
ResultD res
2941-
| Choice3Of5 (anonInfo, tinst, i) ->
2942-
let res =
2943-
let tupInfo = anonInfo.TupInfo
2944-
if evalTupInfoIsStruct tupInfo && isByrefTy g (tyOfExpr g argExprs.[0]) then
2945-
Some (mkAnonRecdFieldGetViaExprAddr (anonInfo, argExprs.[0], tinst, i, m))
2946-
else
2947-
Some (mkAnonRecdFieldGet g (anonInfo, argExprs.[0], tinst, i, m))
2948-
ResultD res
2949-
2950-
| Choice4Of5 expr -> ResultD (Some (MakeApplicationAndBetaReduce g (expr, tyOfExpr g expr, [], argExprs, m)))
2951-
2952-
| Choice5Of5 () -> ResultD None
2868+
let sln = GenWitnessExpr amap g m traitInfo argExprs
2869+
return sln
29532870
}
29542871

2872+
/// Generate the arguments passed for a use of a generic construct that accepts trait witnesses
2873+
let CodegenWitnessesForTyparInst tcVal g amap m typars tyargs = trackErrors {
2874+
let css = CreateCodegenState tcVal g amap
2875+
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g)
2876+
let ftps, _renaming, tinst = FreshenTypeInst m typars
2877+
let cxs = GetTraitConstraintInfosOfTypars g ftps
2878+
do! SolveTypeEqualsTypeEqns csenv 0 m NoTrace None tinst tyargs
2879+
return MethodCalls.GenWitnessArgs amap g m cxs
2880+
}
29552881

2882+
/// For some code like "let f() = ([] = [])", a free choice is made for a type parameter
2883+
/// for an interior type variable. This chooses a solution for a type parameter subject
2884+
/// to its constraints and applies that solution by using a constraint.
29562885
let ChooseTyparSolutionAndSolve css denv tp =
29572886
let g = css.g
29582887
let amap = css.amap
@@ -2984,6 +2913,7 @@ let IsApplicableMethApprox g amap m (minfo: MethInfo) availObjTy =
29842913
amap = amap
29852914
TcVal = (fun _ -> failwith "should not be called")
29862915
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
2916+
codegen = false
29872917
InfoReader = new InfoReader(g, amap) }
29882918
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g)
29892919
let minst = FreshenMethInfo m minfo

src/fsharp/ConstraintSolver.fsi

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,15 @@ val AddCxTypeIsUnmanaged : DisplayEnv -> ConstraintSolverSt
142142
val AddCxTypeIsEnum : DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> TType -> unit
143143
val AddCxTypeIsDelegate : DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> TType -> TType -> unit
144144

145-
val CodegenWitnessThatTypeSupportsTraitConstraint : TcValF -> TcGlobals -> ImportMap -> range -> TraitConstraintInfo -> Expr list -> OperationResult<Expr option>
145+
/// Generate a witness expression if none is otherwise available, e.g. in legacy non-witness-passing code
146+
val CodegenWitnessForTraitConstraint : TcValF -> TcGlobals -> ImportMap -> range -> TraitConstraintInfo -> Expr list -> OperationResult<Expr option>
146147

148+
/// Generate the arguments passed when using a generic construct that accepts traits witnesses
149+
val CodegenWitnessesForTyparInst : TcValF -> TcGlobals -> ImportMap -> range -> Typars -> TType list -> OperationResult<Choice<TraitWitnessInfo, Expr> list>
150+
151+
/// For some code like "let f() = ([] = [])", a free choice is made for a type parameter
152+
/// for an interior type variable. This chooses a solution for a type parameter subject
153+
/// to its constraints and applies that solution by using a constraint.
147154
val ChooseTyparSolutionAndSolve : ConstraintSolverState -> DisplayEnv -> Typar -> unit
148155

149156
val IsApplicableMethApprox : TcGlobals -> ImportMap -> range -> MethInfo -> TType -> bool

0 commit comments

Comments
 (0)