Skip to content

Commit

Permalink
witness passing implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
dsyme committed Jan 17, 2020
1 parent 04119ce commit 24857c5
Show file tree
Hide file tree
Showing 35 changed files with 3,801 additions and 1,227 deletions.
138 changes: 34 additions & 104 deletions src/fsharp/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ type ConstraintSolverState =
/// The function used to freshen values we encounter during trait constraint solving
TcVal: TcValF

/// Indicates if the constraint solver is being run after type checking is complete,
/// e.g. during codegen to determine solutions and witnesses for trait constraints.
/// Suppresses the generation of certain errors such as missing constraint warnings.
codegen: bool

/// This table stores all unsolved, ungeneralized trait constraints, indexed by free type variable.
/// That is, there will be one entry in this table for each free type variable in
/// each outstanding, unsolved, ungeneralized trait constraint. Constraints are removed from the table and resolved
Expand All @@ -228,6 +233,7 @@ type ConstraintSolverState =
amap = amap
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
InfoReader = infoReader
codegen = false
TcVal = tcVal }

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

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

let CodegenWitnessThatTypeSupportsTraitConstraint tcVal g amap m (traitInfo: TraitConstraintInfo) argExprs = trackErrors {
let css =
{ g = g
amap = amap
TcVal = tcVal
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
InfoReader = new InfoReader(g, amap) }

let CreateCodegenState tcVal g amap =
{ g = g
amap = amap
TcVal = tcVal
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
InfoReader = new InfoReader(g, amap)
codegen = true }

/// Generate a witness expression if none is otherwise available, e.g. in legacy non-witness-passing code
let CodegenWitnessForTraitConstraint tcVal g amap m (traitInfo:TraitConstraintInfo) argExprs = trackErrors {
let css = CreateCodegenState tcVal g amap
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g)
let! _res = SolveMemberConstraint csenv true true 0 m NoTrace traitInfo
let sln =
match traitInfo.Solution with
| None -> Choice5Of5()
| Some sln ->
match sln with
| ILMethSln(origTy, extOpt, mref, minst) ->
let metadataTy = convertToTypeWithMetadataIfPossible g origTy
let tcref = tcrefOfAppTy g metadataTy
let mdef = IL.resolveILMethodRef tcref.ILTyconRawMetadata mref
let ilMethInfo =
match extOpt with
| None -> MethInfo.CreateILMeth(amap, m, origTy, mdef)
| Some ilActualTypeRef ->
let actualTyconRef = Import.ImportILTypeRef amap m ilActualTypeRef
MethInfo.CreateILExtensionMeth(amap, m, origTy, actualTyconRef, None, mdef)
Choice1Of5 (ilMethInfo, minst)
| FSMethSln(ty, vref, minst) ->
Choice1Of5 (FSMeth(g, ty, vref, None), minst)
| FSRecdFieldSln(tinst, rfref, isSetProp) ->
Choice2Of5 (tinst, rfref, isSetProp)
| FSAnonRecdFieldSln(anonInfo, tinst, i) ->
Choice3Of5 (anonInfo, tinst, i)
| BuiltInSln ->
Choice5Of5 ()
| ClosedExprSln expr ->
Choice4Of5 expr
return!
match sln with
| Choice1Of5(minfo, methArgTys) ->
let argExprs =
// FIX for #421894 - typechecker assumes that coercion can be applied for the trait calls arguments but codegen doesn't emit coercion operations
// result - generation of non-verifiable code
// fix - apply coercion for the arguments (excluding 'receiver' argument in instance calls)

// 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)
let argTypes =
minfo.GetParamTypes(amap, m, methArgTys)
|> List.concat
// do not apply coercion to the 'receiver' argument
let receiverArgOpt, argExprs =
if minfo.IsInstance then
match argExprs with
| h :: t -> Some h, t
| argExprs -> None, argExprs
else None, argExprs
let convertedArgs = (argExprs, argTypes) ||> List.map2 (fun expr expectedTy -> mkCoerceIfNeeded g expectedTy (tyOfExpr g expr) expr)
match receiverArgOpt with
| Some r -> r :: convertedArgs
| None -> convertedArgs

// Fix bug 1281: If we resolve to an instance method on a struct and we haven't yet taken
// the address of the object then go do that
if minfo.IsStruct && minfo.IsInstance && (match argExprs with [] -> false | h :: _ -> not (isByrefTy g (tyOfExpr g h))) then
let h, t = List.headAndTail argExprs
let wrap, h', _readonly, _writeonly = mkExprAddrOfExpr g true false PossiblyMutates h None m
ResultD (Some (wrap (Expr.Op (TOp.TraitCall (traitInfo), [], (h' :: t), m))))
else
ResultD (Some (MakeMethInfoCall amap m minfo methArgTys argExprs ))

| Choice2Of5 (tinst, rfref, isSet) ->
let res =
match isSet, rfref.RecdField.IsStatic, argExprs.Length with
| true, true, 1 ->
Some (mkStaticRecdFieldSet (rfref, tinst, argExprs.[0], m))
| true, false, 2 ->
// If we resolve to an instance field on a struct and we haven't yet taken
// the address of the object then go do that
if rfref.Tycon.IsStructOrEnumTycon && not (isByrefTy g (tyOfExpr g argExprs.[0])) then
let h = List.head argExprs
let wrap, h', _readonly, _writeonly = mkExprAddrOfExpr g true false DefinitelyMutates h None m
Some (wrap (mkRecdFieldSetViaExprAddr (h', rfref, tinst, argExprs.[1], m)))
else
Some (mkRecdFieldSetViaExprAddr (argExprs.[0], rfref, tinst, argExprs.[1], m))
| false, true, 0 ->
Some (mkStaticRecdFieldGet (rfref, tinst, m))
| false, false, 1 ->
if rfref.Tycon.IsStructOrEnumTycon && isByrefTy g (tyOfExpr g argExprs.[0]) then
Some (mkRecdFieldGetViaExprAddr (argExprs.[0], rfref, tinst, m))
else
Some (mkRecdFieldGet g (argExprs.[0], rfref, tinst, m))
| _ -> None
ResultD res
| Choice3Of5 (anonInfo, tinst, i) ->
let res =
let tupInfo = anonInfo.TupInfo
if evalTupInfoIsStruct tupInfo && isByrefTy g (tyOfExpr g argExprs.[0]) then
Some (mkAnonRecdFieldGetViaExprAddr (anonInfo, argExprs.[0], tinst, i, m))
else
Some (mkAnonRecdFieldGet g (anonInfo, argExprs.[0], tinst, i, m))
ResultD res

| Choice4Of5 expr -> ResultD (Some (MakeApplicationAndBetaReduce g (expr, tyOfExpr g expr, [], argExprs, m)))

| Choice5Of5 () -> ResultD None
let sln = GenWitnessExpr amap g m traitInfo argExprs
return sln
}

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

/// For some code like "let f() = ([] = [])", a free choice is made for a type parameter
/// for an interior type variable. This chooses a solution for a type parameter subject
/// to its constraints and applies that solution by using a constraint.
let ChooseTyparSolutionAndSolve css denv tp =
let g = css.g
let amap = css.amap
Expand Down Expand Up @@ -2984,6 +2913,7 @@ let IsApplicableMethApprox g amap m (minfo: MethInfo) availObjTy =
amap = amap
TcVal = (fun _ -> failwith "should not be called")
ExtraCxs = HashMultiMap(10, HashIdentity.Structural)
codegen = false
InfoReader = new InfoReader(g, amap) }
let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m (DisplayEnv.Empty g)
let minst = FreshenMethInfo m minfo
Expand Down
9 changes: 8 additions & 1 deletion src/fsharp/ConstraintSolver.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,15 @@ val AddCxTypeIsUnmanaged : DisplayEnv -> ConstraintSolverSt
val AddCxTypeIsEnum : DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> TType -> unit
val AddCxTypeIsDelegate : DisplayEnv -> ConstraintSolverState -> range -> OptionalTrace -> TType -> TType -> TType -> unit

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

/// Generate the arguments passed when using a generic construct that accepts traits witnesses
val CodegenWitnessesForTyparInst : TcValF -> TcGlobals -> ImportMap -> range -> Typars -> TType list -> OperationResult<Choice<TraitWitnessInfo, Expr> list>

/// For some code like "let f() = ([] = [])", a free choice is made for a type parameter
/// for an interior type variable. This chooses a solution for a type parameter subject
/// to its constraints and applies that solution by using a constraint.
val ChooseTyparSolutionAndSolve : ConstraintSolverState -> DisplayEnv -> Typar -> unit

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

0 comments on commit 24857c5

Please sign in to comment.