diff --git a/src/fsharp/ConstraintSolver.fs b/src/fsharp/ConstraintSolver.fs index 0469946c180..b234ae11b6d 100644 --- a/src/fsharp/ConstraintSolver.fs +++ b/src/fsharp/ConstraintSolver.fs @@ -379,6 +379,8 @@ let IsNumericType g ty = IsNonDecimalNumericType g ty || isDecimalTy g ty let IsRelationalType g ty = IsNumericType g ty || isStringTy g ty || isCharTy g ty || isBoolTy g ty +let IsEncodedTuple tupInfo g ty = tyconRefEq g ty (if evalTupInfoIsStruct tupInfo then g.struct_tuple8_tcr else g.ref_tuple8_tcr) + // Get measure of type, float<_> or float32<_> or decimal<_> but not float=float<1> or float32=float32<1> or decimal=decimal<1> let GetMeasureOfType g ty = match ty with @@ -1025,6 +1027,15 @@ and SolveTypeEqualsType (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTr -> SolveTypeEqualsType csenv ndeep m2 trace None ms (TType_measure Measure.One) | TType_app (tc1, l1), TType_app (tc2, l2) when tyconRefEq g tc1 tc2 -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace None l1 l2 + + // Catch System.Tuple<'T1,'T2,'T3,'T4,'T5,'T6,'T7,'TRest> arising from SRTP constructions + | TType_app (tc1, [a1;b1;c1;d1;e1;f1;g1;rest1]), TType_tuple (tupInfo, [a2;b2;c2;d2;e2;f2;g2;h2]) when IsEncodedTuple tupInfo g tc1 -> + match stripTyEqnsA csenv.g canShortcut rest1 with + | TType_app (_, [h1]) -> SolveTypeEqualsTypeEqns csenv ndeep m2 trace None [a1;b1;c1;d1;e1;f1;g1;h1] [a2;b2;c2;d2;e2;f2;g2;h2] + | _ -> localAbortD + | TType_app (tc1, [a1;b1;c1;d1;e1;f1;g1;rest1]), TType_tuple (tupInfo, a2::b2::c2::d2::e2::f2::g2::rest2) when IsEncodedTuple tupInfo g tc1 -> + SolveTypeEqualsTypeEqns csenv ndeep m2 trace None [a1;b1;c1;d1;e1;f1;g1;rest1] [a2;b2;c2;d2;e2;f2;g2;TType_tuple (tupInfo, rest2)] + | TType_app (_, _), TType_app (_, _) -> localAbortD | TType_tuple (tupInfo1, l1), TType_tuple (tupInfo2, l2) -> if evalTupInfoIsStruct tupInfo1 <> evalTupInfoIsStruct tupInfo2 then ErrorD (ConstraintSolverError(FSComp.SR.tcTupleStructMismatch(), csenv.m, m2)) else diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index 1c8e4928b0d..a05c583546f 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -1596,7 +1596,8 @@ let ExpandStructuralBindingRaw cenv expr = assert cenv.settings.ExpandStructuralValues() match expr with | Expr.Let (TBind(v, rhs, tgtSeqPtOpt), body, m, _) - when (isRefTupleExpr rhs && + when (isAnyTupleTy cenv.g v.Type && + isRefTupleExpr rhs && CanExpandStructuralBinding v) -> let args = tryDestRefTupleExpr rhs if List.forall ExprIsValue args then diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index d3f67c28ae7..54bb8a2f5c1 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -8461,6 +8461,20 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder // or // build.Bind(build.MergeSources(expr1, expr2), ...) | SynExpr.LetOrUseBang(letSpBind, false, isFromSource, letPat, letRhsExpr, andBangBindings, innerComp, letBindRange) -> + + /// gets the tuple-arity of a method assuming that methods' first parameter is a tuple + let tupleArity (meth: MethInfo) = + let ps = meth.GetParamTypes(cenv.amap, letBindRange, []) + //dprintfn "discovered %A params for %s" ps meth.DisplayName + match ps with + // this is the case when an N-member is present, we assume it works for all sizes + | [ [ TType_var _typar ] ] -> Some Int32.MaxValue + // this is a 'bind', ie a set of inputs that are tupled + a function that consumes them + | [ [TType_tuple(_info, types); TType_fun (TType_tuple(_, funDomainTypes), _range) ] ] when (List.length types) = (List.length funDomainTypes) -> Some (List.length types) + // this is a 'normal' call, like MergeSources4(a,b,c,d) that has 4 parameters applied + | [ nonTupledParams ] -> Some (List.length nonTupledParams) + | _ -> None + if cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang then if isQuery then error(Error(FSComp.SR.tcBindMayNotBeUsedInQueries(), letBindRange)) let bindRange = match letSpBind with DebugPointAtBinding m -> m | _ -> letRhsExpr.Range @@ -8468,15 +8482,30 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder let pats = letPat :: [for (_, _, _, andPat, _, _) in andBangBindings -> andPat ] let sourcesRange = sources |> List.map (fun e -> e.Range) |> List.reduce unionRanges - let numSources = sources.Length - let bindReturnNName = "Bind"+string numSources+"Return" - let bindNName = "Bind"+string numSources + let requiredArity = List.length sources + let numericBindNReturnName = sprintf "Bind%dReturn" requiredArity + let numericBindNName = sprintf "Bind%d" requiredArity + let bindNReturnName = "BindNReturn" + let bindNName = "BindN" - // Check if this is a Bind2Return etc. - let hasBindReturnN = not (isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindReturnNName builderTy)) - if hasBindReturnN && Option.isSome (convertSimpleReturnToExpr varSpace innerComp) then - let consumePat = SynPat.Tuple(false, pats, letPat.Range) + let hasNumericBindNReturn = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad numericBindNReturnName builderTy + |> isNil + |> not + + let bindNReturnArities = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env bindRange ad bindNReturnName builderTy + |> List.choose tupleArity + // dprintfn "BindNReturnArities = %A" bindNReturnArities + + let hasRequiredBindNReturnArity = + bindNReturnArities + |> List.contains requiredArity + + if (hasRequiredBindNReturnArity || hasNumericBindNReturn) && Option.isSome (convertSimpleReturnToExpr varSpace innerComp) then + let consumePat = SynPat.Tuple(false, pats, letPat.Range) + let bindNReturnTupleArg = SynExpr.Tuple(false, sources, [], sourcesRange) // Add the variables to the query variable space, on demand let varSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> @@ -8484,70 +8513,110 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (consumePat, None) vspecs, envinner) - Some (transBind q varSpace bindRange bindNName sources consumePat letSpBind innerComp translatedCtxt) + let memberName, args = if hasNumericBindNReturn then (numericBindNName, sources) else (bindNName, [bindNReturnTupleArg]) + Some (transBind q varSpace bindRange memberName args consumePat letSpBind innerComp translatedCtxt) else + let bindNArities = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env bindRange ad bindNName builderTy + |> List.choose tupleArity - // Check if this is a Bind2 etc. - let hasBindN = not (isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad bindNName builderTy)) - if hasBindN then - let consumePat = SynPat.Tuple(false, pats, letPat.Range) + // dprintfn "BindNArities = %A" bindNArities + // Check if this is a BindN etc. + let hasRequiredBindNArity = + bindNArities + |> List.contains requiredArity + + let hasRequiredNumericBindN = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env bindRange ad numericBindNName builderTy + |> isNil + |> not + + if (hasRequiredBindNArity || hasRequiredNumericBindN) then + let consumePat = SynPat.Tuple(false, pats, letPat.Range) + let bindNTupleArg = SynExpr.Tuple(false, sources, [], sourcesRange) // Add the variables to the query variable space, on demand let varSpace = addVarsToVarSpace varSpace (fun _mCustomOp env -> use _holder = TemporarilySuspendReportingTypecheckResultsToSink cenv.tcSink let _, _, vspecs, envinner, _ = TcMatchPattern cenv (NewInferenceType()) env tpenv (consumePat, None) vspecs, envinner) - - Some (transBind q varSpace bindRange bindNName sources consumePat letSpBind innerComp translatedCtxt) + let memberName, args = if hasRequiredNumericBindN then (numericBindNName, sources) else (bindNName, [bindNTupleArg]) + Some (transBind q varSpace bindRange memberName args consumePat letSpBind innerComp translatedCtxt) else + let mergeSourcesName = "MergeSources" + let mergeSourcesNName = "MergeSourcesN" + + let numericMergeSourcesName i = + if i = 2 then mergeSourcesName else "MergeSources"+(string i) + + let maxMergeSourcesNumeric = + let rec loop (n: int) = + let mergeSourcesName = numericMergeSourcesName n + if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad mergeSourcesName builderTy) then + (n-1) + else + loop (n+1) + loop 2 - // Look for the maximum supported MergeSources, MergeSources3, ... - let mkMergeSourcesName n = if n = 2 then "MergeSources" else "MergeSources"+(string n) + let mergeSourcesNArities = + TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env bindRange ad mergeSourcesNName builderTy + |> List.choose tupleArity - let maxMergeSources = - let rec loop (n: int) = - let mergeSourcesName = mkMergeSourcesName n - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad mergeSourcesName builderTy) then - (n-1) - else - loop (n+1) - loop 2 + // dprintfn "MergeSourcesNArities = %A" mergeSourcesNArities + + let hasMergeSourcesN = + not (isNil mergeSourcesNArities) - if maxMergeSources = 1 then error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), bindRange)) + let maxMergeSourcesN = if hasMergeSourcesN then List.max mergeSourcesNArities else 1 - let rec mergeSources (sourcesAndPats: (SynExpr * SynPat) list) = + if maxMergeSourcesN = 1 && maxMergeSourcesNumeric = 1 then error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(numericBindNName), bindRange)) + + let rec mergeSources (sourcesAndPats: (SynExpr * SynPat) list) = let numSourcesAndPats = sourcesAndPats.Length + // dprintfn "handling %d patterns" numSourcesAndPats assert (numSourcesAndPats <> 0) - if numSourcesAndPats = 1 then + if numSourcesAndPats = 1 then + // dprintfn "one pat remaining, returning it" sourcesAndPats.[0] - - elif numSourcesAndPats <= maxMergeSources then - - // Call MergeSources2(e1, e2), MergeSources3(e1, e2, e3) etc - let mergeSourcesName = mkMergeSourcesName numSourcesAndPats - + elif numSourcesAndPats = 2 then + // dprintfn "two pats left, calling MergeSources" + // call MergeSources if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad mergeSourcesName builderTy) then error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), bindRange)) - let source = mkSynCall mergeSourcesName sourcesRange (List.map fst sourcesAndPats) - let pat = SynPat.Tuple(false, List.map snd sourcesAndPats, letPat.Range) - source, pat - + let consumePat = SynPat.Tuple(false, List.map snd sourcesAndPats, letPat.Range) + source, consumePat + // we can handle this call with a MergeSources# call + elif numSourcesAndPats <= maxMergeSourcesNumeric then + // dprintfn "can handle all pats, calling MergeSources%d with %d pats" numSourcesAndPats numSourcesAndPats + // Call MergeSources%d + let memberName = numericMergeSourcesName numSourcesAndPats + if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad memberName builderTy) then + error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), bindRange)) + let source = mkSynCall memberName sourcesRange (List.map fst sourcesAndPats) + let consumePat = SynPat.Tuple(false, List.map snd sourcesAndPats, letPat.Range) + source, consumePat + // we can handle this call with a MergeSourcesN call + elif numSourcesAndPats <= maxMergeSourcesN then + // dprintfn "can handle all pats, calling MergeSourcesN with %d pats" numSourcesAndPats + // Call MergeSourcesN + + if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad mergeSourcesNName builderTy) then + error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), bindRange)) + let source = mkSynCall mergeSourcesNName sourcesRange (List.map fst sourcesAndPats) + let consumePat = SynPat.Tuple(false, List.map snd sourcesAndPats, letPat.Range) + source, consumePat else - + // split into multiple MergeSourcesN calls // Call MergeSourcesMax(e1, e2, e3, e4, (...)) - let nowSourcesAndPats, laterSourcesAndPats = List.splitAt (maxMergeSources - 1) sourcesAndPats - let mergeSourcesName = mkMergeSourcesName maxMergeSources + let splitPoint = if maxMergeSourcesNumeric <> 1 then maxMergeSourcesNumeric else maxMergeSourcesN + let nowSourcesAndPats, laterSourcesAndPats = List.splitAt (splitPoint - 1) sourcesAndPats + // dprintfn "cannot handle all pats, handling %d pats" (nowSourcesAndPats.Length) - if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env bindRange ad mergeSourcesName builderTy) then - error(Error(FSComp.SR.tcRequireMergeSourcesOrBindN(bindNName), bindRange)) - - let laterSource, laterPat = mergeSources laterSourcesAndPats - let source = mkSynCall mergeSourcesName sourcesRange (List.map fst nowSourcesAndPats @ [laterSource]) - let pat = SynPat.Tuple(false, List.map snd nowSourcesAndPats @ [laterPat], letPat.Range) - source, pat + let laterSourceAndPat = mergeSources laterSourcesAndPats + mergeSources (nowSourcesAndPats @ [laterSourceAndPat]) let mergedSources, consumePat = mergeSources (List.zip sources pats) @@ -8750,7 +8819,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder | _ -> // Check for 'where x > y' and other mis-applications of infix operators. If detected, give a good error message, and just ignore comp if isQuery && checkForBinaryApp comp then - trans true q varSpace (SynExpr.ImplicitZero comp.Range) translatedCtxt + trans true q varSpace (SynExpr.ImplicitZero comp.Range) translatedCtxt else if isQuery && not comp.IsArbExprAndThusAlreadyReportedError then match comp with @@ -8766,9 +8835,9 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder translatedCtxt fillExpr) and transBind q varSpace bindRange bindName bindArgs (consumePat: SynPat) spBind (innerComp: SynExpr) translatedCtxt = - - let innerRange = innerComp.Range + let innerRange = innerComp.Range + let innerCompReturn = if cenv.g.langVersion.SupportsFeature LanguageFeature.AndBang then convertSimpleReturnToExpr varSpace innerComp @@ -8784,7 +8853,9 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder // Build the `BindReturn` call let dataCompPriorToOp = let consumeExpr = SynExpr.MatchLambda(false, consumePat.Range, [Clause(consumePat, None, innerExpr, innerRange, DebugPointForTarget.Yes)], spBind, innerRange) - translatedCtxt (mkSynCall bindName bindRange (bindArgs @ [consumeExpr])) + let bindCall = mkSynCall bindName bindRange (bindArgs @ [consumeExpr]) + //dprintfn "BindReturn call:\n%A" bindCall + translatedCtxt bindCall match customOpInfo with | None -> dataCompPriorToOp @@ -8906,6 +8977,8 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder | [] -> quotedSynExpr | _ -> mkSynCall "Run" mBuilderVal [quotedSynExpr] + // dprintfn "Tranlated CE to\n%A\n" runExpr + let lambdaExpr = let mBuilderVal = mBuilderVal.MakeSynthetic() SynExpr.Lambda (false, false, SynSimplePats.SimplePats ([mkSynSimplePatVar false (mkSynId mBuilderVal builderValName)], mBuilderVal), runExpr, mBuilderVal) diff --git a/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs b/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs index 240583f704d..ddce3dbf313 100644 --- a/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs +++ b/tests/fsharp/Compiler/Conformance/DataExpressions/ComputationExpressions.fs @@ -710,3 +710,190 @@ let ceResult = check "grwerjkrwejgk42" ceResult.Value 2 """ + [] + let ``Applicative MergeSourcesN`` () = + let source = """ +open System + +let inline ivk f x = + let inline call (_: ^I, x:'TT) = ((^I or ^TT) : (static member Invoke : _-> _) x) + call ( f, x) + +let inline loop f x = + let inline call (_: ^I, x:'TT) = ((^I or ^TT) : (static member Loop : _-> _) x) + call ( f, x) + +type Uncons = Uncons with + static member inline Invoke tuple = (Unchecked.defaultof => tuple) + + static member inline (=>) (_:obj, t : 't when 't : not struct) = + let rest = (Uncons.Invoke (^t : (member Rest : _) t)) + (^t : (member Item1 : _) t) , + System.Tuple<_,_,_,_,_,_,_,_>( + (^t : (member Item2 : _) t) , + (^t : (member Item3 : _) t) , + (^t : (member Item4 : _) t) , + (^t : (member Item5 : _) t) , + (^t : (member Item6 : _) t) , + (^t : (member Item7 : _) t) , + fst rest, snd rest) + + static member inline (=>) (Uncons, t : 't when 't : not struct) = + let rest = (Uncons.Invoke (^t : (member Rest : _) t)) : _ * unit + (^t : (member Item1 : _) t) , + System.Tuple<_,_,_,_,_,_,_>( + (^t : (member Item2 : _) t) , + (^t : (member Item3 : _) t) , + (^t : (member Item4 : _) t) , + (^t : (member Item5 : _) t) , + (^t : (member Item6 : _) t) , + (^t : (member Item7 : _) t) , + fst rest) + + static member (=>) (Uncons, x1:Tuple<_>) = (x1.Item1, ()) + static member (=>) (Uncons, (a, b)) = a, System.Tuple<_>(b) + static member (=>) (Uncons, (a, b, c)) = a, (b, c) + static member (=>) (Uncons, (a, b, c, d)) = a, (b, c, d) + static member (=>) (Uncons, (a, b, c, d, e)) = a, (b, c, d, e) + static member (=>) (Uncons, (a, b, c, d, e, f)) = a, (b, c, d, e, f) + static member (=>) (Uncons, (a, b, c, d, e, f, g)) = a, (b, c, d, e, f, g) + + +type Cons = Cons with + static member inline Invoke tuple = let inline f (_: 'M, t: 'T) = ((^M or ^T) : (static member (!) : _ -> _) t) in f (Cons, tuple) + static member inline (!) (t:'t) = fun x -> + let (x1,x2,x3,x4,x5,x6,x7,xr) = + ( + (^t : (member Item1 : 't1) t), + (^t : (member Item2 : 't2) t), + (^t : (member Item3 : 't3) t), + (^t : (member Item4 : 't4) t), + (^t : (member Item5 : 't5) t), + (^t : (member Item6 : 't6) t), + (^t : (member Item7 : 't7) t), + (^t : (member Rest : 'tr) t) + ) + System.Tuple<_,_,_,_,_,_,_,_>(x, x1, x2, x3, x4, x5, x6, Cons.Invoke xr x7) + static member (!) (() ) = fun x -> Tuple x + static member (!) (x1:Tuple<_> ) = fun x -> (x,x1.Item1) + static member (!) ((x1,x2) ) = fun x -> (x,x1,x2) + static member (!) ((x1,x2,x3) ) = fun x -> (x,x1,x2,x3) + static member (!) ((x1,x2,x3,x4) ) = fun x -> (x,x1,x2,x3,x4) + static member (!) ((x1,x2,x3,x4,x5) ) = fun x -> (x,x1,x2,x3,x4,x5) + static member (!) ((x1,x2,x3,x4,x5,x6) ) = fun x -> (x,x1,x2,x3,x4,x5,x6) + static member (!) ((x1,x2,x3,x4,x5,x6,x7)) = fun x -> (x,x1,x2,x3,x4,x5,x6,x7) + +let inline (|Cons|) tuple = Uncons.Invoke tuple + +type Rev = Rev with + static member inline Invoke tuple = ($) Rev tuple () + static member inline ($) (Rev, Cons(h,t)) = fun ac -> ($) Rev t (Cons.Invoke ac h) + static member ($) (Rev, () ) = id + +type ZipOption = ZipOption with + static member inline Loop (tup: ^a when ^a : not struct) = + fun acc -> + let tHead, tRest = Uncons.Invoke tup + let nextAcc = + match acc, tHead with + | Some t, Some x -> Some (Cons.Invoke t x) + | _, _ -> None + loop ZipOption tRest nextAcc + static member inline Loop (()) = fun acc -> acc + static member inline Invoke tuple = + match loop ZipOption tuple (Some ()) with + | Some x -> Some (Rev.Invoke x) + | None -> None + +let inline zipN_Srtp tuple = ZipOption.Invoke tuple + +let zipN_Reflection tuple = + let read a = + let ty = typedefof> + if obj.ReferenceEquals(a, null) then None + else + let aty = a.GetType() + let v = aty.GetProperty("Value") + if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then + if a = null then None + else Some(v.GetValue(a, [| |])) + else None + let arrayToTuple a = + let types = a |> Array.map (fun o -> o.GetType()) + let tupleType = Microsoft.FSharp.Reflection.FSharpType.MakeTupleType types + Microsoft.FSharp.Reflection.FSharpValue.MakeTuple (a, tupleType) + + let a = Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields tuple + let res = a |> Array.choose read + if Array.length res = Array.length a then Some (arrayToTuple res) else None + |> Option.map unbox + + + +// computation expressions + +type ResultBuilder_srtp() = + member inline _.MergeSourcesN tupleOfOptions = zipN_Srtp tupleOfOptions + member _.BindReturn (x: 'T option, f) = Option.map f x + +type ResultBuilder_reflection() = + member _.MergeSourcesN tupleOfOptions = zipN_Reflection tupleOfOptions + member _.BindReturn (x: 'T option, f) = Option.map f x + +let option_srtp = ResultBuilder_srtp() +let option_reflection = ResultBuilder_reflection() + + +let testSrtp r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 = + let res1: _ option = + option_srtp { + let! a = r1 + and! b = r2 + and! c = r3 + and! d = r4 + and! e = r5 + and! f = r6 + and! g = r7 + and! h = r8 + and! i = r9 + and! j = r10 + return a + b - c + d - e - f - g - h - i + j + } + + match res1 with + | Some x -> sprintf "Result is: %d" x + | None -> "No result" + + +let testReflection r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 = + let res1: _ option = + option_reflection { + let! a = r1 + and! b = r2 + and! c = r3 + and! d = r4 + and! e = r5 + and! f = r6 + and! g = r7 + and! h = r8 + and! i = r9 + and! j = r10 + return a + b - c + d - e - f - g - h - i + j + } + + match res1 with + | Some x -> sprintf "Result is: %d" x + | None -> "No result" + +let a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 = (Some 1, Some 2, Some 3, Some 4, Some 5, Some 6, Some 7, Some 8, Some 9, Some 10) + +let res1 = testSrtp a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 +let res2 = testReflection a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 + +if res1 <> "Result is: -21" then failwithf "Unexpected result for testSrtp. Expected was -21 but actual was %s" res1 +if res2 <> "Result is: -21" then failwithf "Unexpected result for testSrtp. Expected was -21 but actual was %s" res2 + +() + + """ + CompilerAssert.CompileExeAndRunWithOptions [| "/langversion:preview" |] source \ No newline at end of file diff --git a/tests/fsharp/core/csext/test.fsx b/tests/fsharp/core/csext/test.fsx index c77a7307642..09b49414cc2 100644 --- a/tests/fsharp/core/csext/test.fsx +++ b/tests/fsharp/core/csext/test.fsx @@ -311,6 +311,9 @@ module TupleSRTP = let v1b = (^T : (member get_Item2 : unit -> _ ) (new System.Tuple(1,3))) let v2b = (^T : (member get_Item2 : unit -> _ ) (System.Tuple(1,3))) let v3b = (^T : (member get_Item2 : unit -> _ ) ((1,3))) + + let areEqualT8 = System.Tuple<_,_,_,_,_,_,_,_>(1,2,3,4,5,6,7,System.Tuple<_> (8) ) = (1,2,3,4,5,6,7,8) + let areEqualT9 = System.Tuple<_,_,_,_,_,_,_,_>(1,2,3,4,5,6,7,System.Tuple<_,_>(8,9)) = (1,2,3,4,5,6,7,8,9) (*--------------------*)