diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.200.md index 99464a277dd..21a81956797 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.200.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.200.md @@ -1,5 +1,6 @@ ### Added +* Ranges can be mixed with values in list, array, sequence and computation expressions under `--langversion:preview`. ([RFC FS-1031](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1031-mixing-ranges-and-values-in-sequences.md), [PR #20759](https://github.com/dotnet/fsharp/pull/20759)) * F# Interactive gains a JSON-RPC server mode, `--fsi-server-jsonrpc:`, in which a host submits interactions over a named pipe and receives structured results — diagnostics with positions, escaping exceptions, the values each interaction bound, and the session's own process id — instead of recovering them by looking for a `SERVER-PROMPT>` marker in the output text. Program output continues to flow through the redirected console streams. The pipe admits only the user running the session; `--fsi-server-client-pid:` names the host process whose exit ends the session. `FsiEvaluationSession` exposes both options as `JsonRpcServerPipeName` and `JsonRpcClientProcessId`. The mode is part of the .NET fsi only. ([PR #20396](https://github.com/dotnet/fsharp/pull/20396)) ### Fixed @@ -11,6 +12,7 @@ * Fix `NativePtr.stackalloc` used inside the `with` handler, filter or `finally` block of a `try` expression producing an assembly that throws `InvalidProgramException` at method load. Direct or mandatory-inline allocations are now reported at compile time as error FS3924; calls to non-inline allocation helpers, including handler-local helpers, remain legal and retain their method boundary during inlining and let-binding elimination. ([Issue #20295](https://github.com/dotnet/fsharp/issues/20295), [PR #20491](https://github.com/dotnet/fsharp/pull/20491)) * Preserve SRTP code generation when extension constraint solutions are disabled. ([PR #20773](https://github.com/dotnet/fsharp/pull/20773)) * `FsiEvaluationSession.EvalInteraction` and `EvalInteractionNonThrowing` evaluate every `;;`-separated interaction in the text they are given, as their documentation has always said, instead of silently discarding everything after the first. ([PR #20396](https://github.com/dotnet/fsharp/pull/20396)) +* Fix the counted-loop lowering of integral ranges: a range with a single `unativeint` element is no longer treated as empty, a mutable range bound is read before the later bounds are evaluated, and a `nativeint` range with the smallest step no longer yields extra elements. ([PR #20759](https://github.com/dotnet/fsharp/pull/20759)) * Prevent `InvalidProgramException` when runtime async awaits a `Task` upcast to `Task`, including through `ConfigureAwait`. ([Issue #20686](https://github.com/dotnet/fsharp/issues/20686), [PR #20726](https://github.com/dotnet/fsharp/pull/20726)) * Struct/reference anonymous-record mismatches now name anonymous records rather than tuples. ([Issue #6110](https://github.com/dotnet/fsharp/issues/6110), [PR #20681](https://github.com/dotnet/fsharp/pull/20681)) * Preserve constraint inference order when explicit generic arguments share a caller type parameter, avoiding spurious FS0001 errors with multiply implemented generic interfaces. ([PR #20491](https://github.com/dotnet/fsharp/pull/20491)) diff --git a/docs/release-notes/.Language/preview.md b/docs/release-notes/.Language/preview.md index d90fb95fbe7..204b19c320f 100644 --- a/docs/release-notes/.Language/preview.md +++ b/docs/release-notes/.Language/preview.md @@ -4,6 +4,8 @@ Features assigned to F# 11.2 are listed in [11.2](https://fsharp.github.io/fshar ### Added +* Ranges can be mixed with values in list, array, sequence and computation expressions, as in `[ -3; 1..10; 19 ]`; the range splices its elements like `yield!`. ([Language suggestion #1031](https://github.com/fsharp/fslang-suggestions/issues/1031), [RFC FS-1031](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1031-mixing-ranges-and-values-in-sequences.md), [PR #20759](https://github.com/dotnet/fsharp/pull/20759)) + ### Fixed ### Changed diff --git a/src/Compiler/Checking/Expressions/CheckArrayOrListComputedExpressions.fs b/src/Compiler/Checking/Expressions/CheckArrayOrListComputedExpressions.fs index bfabf93f5cf..68537f7f8f1 100644 --- a/src/Compiler/Checking/Expressions/CheckArrayOrListComputedExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckArrayOrListComputedExpressions.fs @@ -16,7 +16,6 @@ let TcArrayOrListComputedExpression (cenv: TcFileState) env (overallTy: OverallT let g = cenv.g // The syntax '[ n .. m ]' and '[ n .. step .. m ]' is not really part of array or list syntax. - // It could be in the future, e.g. '[ 1; 2..30; 400 ]' // // The elaborated form of '[ n .. m ]' is 'List.ofSeq (seq (op_Range n m))' and this shouldn't change match RewriteRangeExpr comp with @@ -99,7 +98,8 @@ let TcArrayOrListComputedExpression (cenv: TcFileState) env (overallTy: OverallT | _ -> SynExpr.ArrayOrList(isArray, elems, m) TcExprUndelayed cenv overallTy env tpenv replacementExpr - | _ -> + | SimpleSemicolonSequenceWithRanges cenv comp + | comp -> let genCollElemTy = NewInferenceType g diff --git a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs index a9dd1205512..44b1d336786 100644 --- a/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckComputationExpressions.fs @@ -2391,6 +2391,8 @@ let rec TryTranslateComputationExpression Some(translatedCtxt callExpr) | SynExpr.YieldOrReturnFrom((true, _), synYieldExpr, mFull, { YieldOrReturnFromKeyword = m }) -> + let synYieldExpr = rewriteYieldFromRange cenv synYieldExpr + let yieldFromExpr = mkSourceExpr synYieldExpr ceenv.sourceMethInfo ceenv.builderValName @@ -2451,6 +2453,7 @@ let rec TryTranslateComputationExpression if ceenv.isQuery && not isYield then error (Error(FSComp.SR.tcReturnMayNotBeUsedInQueries (), m)) + let synYieldOrReturnExpr = rejectRangeOperand cenv synYieldOrReturnExpr requireBuilderMethod methName ceenv m m let yieldOrReturnCall = @@ -2464,6 +2467,8 @@ let rec TryTranslateComputationExpression Some(translatedCtxt yieldOrReturnCall) + | BareRangeExpr _ -> TryTranslateComputationExpression ceenv firstTry q varSpace (mkRangeSplice comp) translatedCtxt + | _ -> None) and ConsumeCustomOpClauses @@ -2746,6 +2751,8 @@ and TranslateComputationExpressionBind /// The inner option indicates if a custom operation is involved inside and convertSimpleReturnToExpr (ceenv: ComputationExpressionContext<'a>) comp varSpace innerComp = match innerComp with + | SynExpr.YieldOrReturn((false, _), RangeAsSingleValue ceenv.cenv, _, _) -> None + | SynExpr.YieldOrReturn((false, _), returnExpr, m, _) -> let returnExpr = SynExpr.DebugPoint(DebugPointAtLeafExpr.Yes(false, m), false, returnExpr) @@ -2868,6 +2875,7 @@ and isSimpleExpr ceenv comp = | SynExpr.YieldOrReturnFrom _ -> false | SynExpr.YieldOrReturn _ -> false | SynExpr.DoBang _ -> false + | BareRangeExpr _ -> false | _ -> true and TranslateComputationExpression (ceenv: ComputationExpressionContext<'a>) firstTry q varSpace comp translatedCtxt = diff --git a/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs b/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs index dbb0a7d6703..49d4e1abf05 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressionsOps.fs @@ -18,6 +18,7 @@ open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeBasics open FSharp.Compiler.TypedTreeOps open FSharp.Compiler.SyntaxTreeOps +open FSharp.Compiler.SyntaxTrivia let TryAllowFlexibleNullnessInControlFlow isFirst (g: TcGlobals.TcGlobals) ty = match isFirst, g.checkNullness, GetTyparTyIfSupportsNull g ty with @@ -181,6 +182,33 @@ let RewriteRangeExpr synExpr = Some otherExpr | _ -> None +[] +let (|BareRangeExpr|_|) synExpr = + RewriteRangeExpr synExpr |> ValueOption.ofOption + +let (|RangeAsSingleValue|_|) (cenv: TcFileState) synExpr = + match synExpr with + | BareRangeExpr _ -> cenv.g.langVersion.SupportsFeature LanguageFeature.AllowMixedRangesAndValuesInSeqExpressions + | _ -> false + +let mkRangeSplice (synRange: SynExpr) = + let m = synRange.Range + SynExpr.YieldOrReturnFrom((true, false), synRange, m, { YieldOrReturnFromKeyword = m }) + +let rewriteYieldFromRange (cenv: TcFileState) synExpr = + match synExpr with + | BareRangeExpr rangeCall -> + checkLanguageFeatureAndRecover cenv.g.langVersion LanguageFeature.AllowMixedRangesAndValuesInSeqExpressions synExpr.Range + rangeCall + | _ -> synExpr + +let rejectRangeOperand (cenv: TcFileState) operand = + match operand with + | RangeAsSingleValue cenv -> + errorR (Error(FSComp.SR.tcRangeIsNotASingleValue (), operand.Range)) + arbExpr ("rangeAsSingleValue", operand.Range) + | _ -> operand + /// Check if a computation or sequence expression is syntactically free of 'yield' (though not yield!) let YieldFree (_cenv: TcFileState) expr = let rec YieldFree expr = @@ -223,7 +251,8 @@ let inline IsSimpleSemicolonSequenceElement expr cenv acceptDeprecated = | SynExpr.Do _ | SynExpr.MatchBang _ | SynExpr.While _ - | SynExpr.WhileBang _ -> false + | SynExpr.WhileBang _ + | BareRangeExpr _ -> false | _ -> true [] @@ -247,6 +276,39 @@ let rec TryGetSimpleSemicolonSequenceOfComprehension expr acc cenv acceptDepreca let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr = TryGetSimpleSemicolonSequenceOfComprehension cexpr [] cenv acceptDeprecated +[] +let (|SimpleSemicolonSequenceWithRanges|_|) cenv cexpr = + let isRange = RewriteRangeExpr >> Option.isSome + + let yieldValue expr = + SynExpr.YieldOrReturn((true, false), expr, expr.Range, SynExprYieldOrReturnTrivia.Zero) + + let sequential head tail = + SynExpr.Sequential( + DebugPointAtSequential.SuppressNeither, + true, + head, + tail, + unionRanges head.Range tail.Range, + SynExprSequentialTrivia.Zero + ) + + let rec elements acc expr = + match expr with + | SynExpr.Sequential(isTrueSeq = true; expr1 = head; expr2 = tail) -> elements (head :: acc) tail + | _ -> List.rev (expr :: acc) + + match elements [] cexpr with + | elems when + List.exists isRange elems + && List.forall (fun e -> isRange e || IsSimpleSemicolonSequenceElement e cenv false) elems + -> + elems + |> List.map (fun e -> if isRange e then e else yieldValue e) + |> List.reduceBack sequential + |> ValueSome + | _ -> ValueNone + let elimFastIntegerForLoop (spFor, spTo, id, start: SynExpr, dir, finish: SynExpr, innerExpr, m: range) = let mOp = (unionRanges start.Range finish.Range).MakeSynthetic() diff --git a/src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs b/src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs index c491fd42988..a1a96d19faf 100644 --- a/src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs @@ -379,6 +379,13 @@ let TcSequenceExpression (cenv: TcFileState) env tpenv comp (overallTy: OverallT | SynExpr.YieldOrReturnFrom(flags = (isYield, _); expr = synYieldExpr; trivia = { YieldOrReturnFromKeyword = m }) -> let env = { env with eIsControlFlow = false } + + let synYieldExpr = + if isYield then + rewriteYieldFromRange cenv synYieldExpr + else + synYieldExpr + let resultExpr, genExprTy, tpenv = TcExprOfUnknownType cenv env tpenv synYieldExpr if not isYield then @@ -397,6 +404,14 @@ let TcSequenceExpression (cenv: TcFileState) env tpenv comp (overallTy: OverallT Some(resultExpr, tpenv) + | SynExpr.YieldOrReturn(flags = (isYield, _); expr = RangeAsSingleValue cenv as synRange; range = m) -> + if isYield then + rejectRangeOperand cenv synRange |> ignore + else + errorR (Error(FSComp.SR.tcSeqResultsUseYield (), m)) + + tryTcSequenceExprBody env genOuterTy tpenv (mkRangeSplice synRange) + | SynExpr.YieldOrReturn(flags = (isYield, _); expr = synYieldExpr; range = m) -> let env = { env with eIsControlFlow = false } let genResultTy = NewInferenceType g @@ -418,6 +433,8 @@ let TcSequenceExpression (cenv: TcFileState) env tpenv comp (overallTy: OverallT Some(resultExpr, tpenv) + | BareRangeExpr _ -> tryTcSequenceExprBody env genOuterTy tpenv (mkRangeSplice comp) + | _ -> None and tcSequenceExprBody env (genOuterTy: TType) tpenv comp = diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 1fa6ba59402..1e6b110c98e 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1731,6 +1731,7 @@ featureUseBangBindingValueDiscard,"Allows use! _ = ... in computation expression featureBetterAnonymousRecordParsing,"Support for better anonymous record parsing" featureScopedNowarn,"Support for scoped enabling / disabling of warnings by #warn and #nowarn directives, also inside modules" featureErrorOnInvalidDeclsInTypeDefinitions,"Error when invalid declarations are used in type definitions." +featureAllowMixedRangesAndValuesInSeqExpressions,"Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }}" featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type annotations without requiring parentheses" 3874,lexWarnDirectiveMustBeFirst,"#nowarn/#warnon directives must appear as the first non-whitespace characters on a line" 3875,lexWarnDirectiveMustHaveArgs,"Warn directives must have warning number(s) as argument(s)" @@ -1804,4 +1805,5 @@ featureTypeArgumentDependencyOrdering,"ordering explicit type arguments by their 3922,ilRuntimeAsyncSequenceNotStaticallyKnown,"The runtime-async sequence recipe could not be statically lowered. Supply a statically known delayed sequence recipe through an inline builder." 3923,tcMethodRequiresNamedArguments,"The method '%s' requires named arguments. Use named-argument syntax, e.g. 'MethodName(argumentName = value)'." 3924,chkNativePtrStackallocInHandler,"'NativePtr.stackalloc' cannot be used inside the 'with' handler, filter or 'finally' block of a 'try' expression. The 'localloc' IL instruction this emits is rejected inside an exception-handling region, producing a 'System.InvalidProgramException' when the method is loaded." +3925,tcRangeIsNotASingleValue,"A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value." featureReraiseInComputationExpressions,"calls to 'reraise' in a computation expression handler" diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 0a34624112e..57cf90ee37c 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -45,6 +45,7 @@ type LanguageFeature = | BetterAnonymousRecordParsing | ScopedNowarn | ErrorOnInvalidDeclsInTypeDefinitions + | AllowMixedRangesAndValuesInSeqExpressions | AllowTypedLetUseAndBang | ReturnFromFinal | MoreConcreteTiebreaker @@ -172,6 +173,7 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) // F# preview // Unfinished features that still need work before they can be assigned a release language version. LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work + LanguageFeature.AllowMixedRangesAndValuesInSeqExpressions, previewVersion ] static let defaultLanguageVersion = LanguageVersion("default") @@ -295,6 +297,7 @@ type LanguageVersion(versionText, ?disabledFeaturesArray: LanguageFeature array) | LanguageFeature.BetterAnonymousRecordParsing -> FSComp.SR.featureBetterAnonymousRecordParsing () | LanguageFeature.ScopedNowarn -> FSComp.SR.featureScopedNowarn () | LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions -> FSComp.SR.featureErrorOnInvalidDeclsInTypeDefinitions () + | LanguageFeature.AllowMixedRangesAndValuesInSeqExpressions -> FSComp.SR.featureAllowMixedRangesAndValuesInSeqExpressions () | LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens () | LanguageFeature.ReturnFromFinal -> FSComp.SR.featureReturnFromFinal () | LanguageFeature.MoreConcreteTiebreaker -> FSComp.SR.featureMoreConcreteTiebreaker () diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index 4afa992cde5..4232b9ad9ed 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -36,6 +36,7 @@ type LanguageFeature = | BetterAnonymousRecordParsing | ScopedNowarn | ErrorOnInvalidDeclsInTypeDefinitions + | AllowMixedRangesAndValuesInSeqExpressions | AllowTypedLetUseAndBang | ReturnFromFinal | MoreConcreteTiebreaker diff --git a/src/Compiler/Optimize/LowerComputedCollections.fs b/src/Compiler/Optimize/LowerComputedCollections.fs index 26a75fbde7b..4f5634232be 100644 --- a/src/Compiler/Optimize/LowerComputedCollections.fs +++ b/src/Compiler/Optimize/LowerComputedCollections.fs @@ -199,6 +199,16 @@ let LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr = Result.Ok (flag, exprR) | Result.Error msg -> Result.Error msg + | IntegralRange g (rangeTy, (start, step, finish)) when + not isUninteresting + && g.langVersion.SupportsFeature LanguageFeature.LowerIntegralRangesToFastLoops + && typeEquiv g (mkSeqTy g rangeTy) collectorSeqTy -> + let m = expr.Range + let exprR = + mkOptimizedRangeLoop g (m, m, m, DebugPointAtWhile.No) (rangeTy, expr) (start, step, finish) (fun _count mkLoop -> + mkLoop (fun _idx elem -> mkCallCollectorAdd tcVal g infoReader m collExpr elem)) + Result.Ok (false, exprR) + // yield! e ---> (for x in e -> x) | arbitrarySeqExpr -> diff --git a/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs b/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs index 7ee5f290b23..542572ef77f 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Transforms.fs @@ -1468,7 +1468,7 @@ module internal TupleCompilation = start <= 0xffffffffUL && step <= 0xffffffffUL && finish <= 0xffffffffUL - && finish <= start + && finish < start -> ValueSome EmptyRange @@ -1520,12 +1520,6 @@ module internal TupleCompilation = ValueSome(Const.UInt64((uint64 finish - uint64 start) / (uint64 Int64.MaxValue + 1UL) + 1UL)) | Expr.Const(value = Const.Int64 start), Expr.Const(value = Const.Int64 Int64.MinValue), Expr.Const(value = Const.Int64 finish) -> ValueSome(Const.UInt64((uint64 start - uint64 finish) / (uint64 Int64.MaxValue + 1UL) + 1UL)) - | Expr.Const(value = Const.IntPtr start), Expr.Const(value = Const.IntPtr Int64.MinValue), Expr.Const(value = Const.IntPtr finish) when - start <= finish - -> - ValueSome(Const.UIntPtr((uint64 start - uint64 finish) / (uint64 Int64.MaxValue + 1UL) + 1UL)) - | Expr.Const(value = Const.IntPtr start), Expr.Const(value = Const.IntPtr Int64.MinValue), Expr.Const(value = Const.IntPtr finish) -> - ValueSome(Const.UIntPtr((uint64 start - uint64 finish) / (uint64 Int64.MaxValue + 1UL) + 1UL)) | Expr.Const(value = Const.Int64 start), Expr.Const(value = Const.Int64 step), Expr.Const(value = Const.Int64 finish) when start <= finish @@ -1998,34 +1992,14 @@ module internal TupleCompilation = (buildLoop: Count -> ((Idx -> Elem -> Body) -> Loop) -> Expr) = let inline mkLetBindingsIfNeeded f = - match start, step, finish with - | (Expr.Const _ | Expr.Val _), (Expr.Const _ | Expr.Val _), (Expr.Const _ | Expr.Val _) -> f start step finish - - | (Expr.Const _ | Expr.Val _), (Expr.Const _ | Expr.Val _), _ -> - mkCompGenLetIn finish.Range (nameof finish) rangeTy finish (fun (_, finish) -> f start step finish) - - | _, (Expr.Const _ | Expr.Val _), (Expr.Const _ | Expr.Val _) -> - mkCompGenLetIn start.Range (nameof start) rangeTy start (fun (_, start) -> f start step finish) - - | (Expr.Const _ | Expr.Val _), _, (Expr.Const _ | Expr.Val _) -> - mkCompGenLetIn step.Range (nameof step) rangeTy step (fun (_, step) -> f start step finish) - - | _, (Expr.Const _ | Expr.Val _), _ -> - mkCompGenLetIn start.Range (nameof start) rangeTy start (fun (_, start) -> - mkCompGenLetIn finish.Range (nameof finish) rangeTy finish (fun (_, finish) -> f start step finish)) - - | (Expr.Const _ | Expr.Val _), _, _ -> - mkCompGenLetIn step.Range (nameof step) rangeTy step (fun (_, step) -> - mkCompGenLetIn finish.Range (nameof finish) rangeTy finish (fun (_, finish) -> f start step finish)) - - | _, _, (Expr.Const _ | Expr.Val _) -> - mkCompGenLetIn start.Range (nameof start) rangeTy start (fun (_, start) -> - mkCompGenLetIn step.Range (nameof step) rangeTy step (fun (_, step) -> f start step finish)) - - | _, _, _ -> - mkCompGenLetIn start.Range (nameof start) rangeTy start (fun (_, start) -> - mkCompGenLetIn step.Range (nameof step) rangeTy step (fun (_, step) -> - mkCompGenLetIn finish.Range (nameof finish) rangeTy finish (fun (_, finish) -> f start step finish))) + let bindIfNeeded name operand k = + match operand with + | Expr.Const _ -> k operand + | Expr.Val(valRef, _, _) when not valRef.IsMutable -> k operand + | _ -> mkCompGenLetIn operand.Range name rangeTy operand (fun (_, bound) -> k bound) + + bindIfNeeded (nameof start) start (fun start -> + bindIfNeeded (nameof step) step (fun step -> bindIfNeeded (nameof finish) finish (fun finish -> f start step finish))) mkLetBindingsIfNeeded (fun start step finish -> /// Start at 0 and count up through count - 1. diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 8bd83440050..9a7859968eb 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index ab6af560121..1dde1001524 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 3f7773686fd..d63c6815603 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 7a3822ab851..5593c82cf66 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 21aa6a838be..a3cc862885b 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index c49b5490212..e1378c6f9b0 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 11cebee14be..f5ab6276f97 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9aa0452706b..b38b397ac78 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 388d56c9a0c..456e6bb3d66 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index ef935e3d593..b238f104b82 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 1a917da78aa..6239a6279f1 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index f159ad4ee1b..f841dbea3ad 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index abb0041c10d..0759b5ae9dd 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -307,6 +307,11 @@ Allow let! and use! type annotations without requiring parentheses + + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + Allow mixed ranges and values in sequence expressions, e.g. seq {{ 1..10; 20 }} + + Allow object expressions without overrides Allow object expressions without overrides @@ -1602,6 +1607,11 @@ You can remove this `nonNull` assertion. + + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value. + + Explicit field '{0}' shadows a field with the same name from an earlier spread. Explicit field '{0}' shadows a field with the same name from an earlier spread. diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 2b473df5e1b..309b89256bf 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -382,6 +382,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/MixedSequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/MixedSequenceExpressionTests.fs new file mode 100644 index 00000000000..245becb8e4c --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/MixedSequenceExpressionTests.fs @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module Language.MixedSequenceExpressionTests + +open Xunit +open FSharp.Test.Compiler + +let private diagnostic kind code message line col endCol = + (kind code, Line line, Col col, Line line, Col endCol, message) + +let private rangeAsValue = + diagnostic Error 3925 + "A range expression cannot be used as a single value here. Use 'yield!' to splice the range, or wrap it in a list or sequence expression to use it as one value." + +let private featureUnavailable = + diagnostic Error 3350 + "Feature 'Allow mixed ranges and values in sequence expressions, e.g. seq { 1..10; 20 }' is not available in F# 11.0. Please use language version 'PREVIEW' or greater." + +let private indexerSyntax = + diagnostic Error 751 "Incomplete expression or invalid use of indexer syntax" + +[] +let ``Ranges mixed with values splice their elements`` () = + FSharp + """ +module Runtime + +let check name expected actual = + if actual <> expected then failwith $"{name}: {actual}" + +type ListBuilder() = + member _.Yield(x) = [ x ] + member _.YieldFrom(xs: seq<_>) = List.ofSeq xs + member _.Combine(a, b) = a @ b + member _.Delay(f: unit -> _ list) = f () + member _.Bind(x: _ option, f) = match x with Some v -> f v | None -> [] + +let list = ListBuilder() + +check "value, range, value" [ -3; 1; 2; 3; 19 ] [ -3; 1..3; 19 ] +check "two ranges" [ 1; 2; 5; 6; 10 ] [ 1..2; 5..6; 10 ] +check "stepped" [ 0; 2; 4; 15 ] [ 0; 2..2..4; 15 ] +check "negative step" [ 0; 3; 2; 1 ] [ 0; 3 .. -1 .. 1 ] +let down start = [ 0; start .. -1 .. 1 ] +check "negative step from a runtime start" [ 0; 3; 2; 1 ] (down 3) +check "empty and singleton" [ 0; 2; 4 ] [ 0; 1..0; 2..2; 3..1; 4 ] +check "separate lines" [ 0; 1; 2; 10; 11; 12 ] [ + 0 + 1..2 + 10 + 11..12 ] +check "array" [ 0; 1; 2; 9 ] (List.ofArray [| 0; 1..2; 9 |]) +check "seq" [ 0; 1; 2; 9 ] (List.ofSeq (seq { 0; 1..2; 9 })) +check "seq with a stepped tail" [ 0; 2; 4 ] (List.ofSeq (seq { 0; 2..2..4 })) +check "builder" [ 0; 1; 2; 9 ] (list { 0; 1..2; 9 }) + +check "char" [ 'a'; 'b'; 'Z' ] [ 'a'..'b'; 'Z' ] +check "bigint" [ 0I; 2I; 10I ] [ 0I..2I..2I; 10I ] +check "int64" [ 0L; 1L; 2L ] [ 0L; 1L..2L ] +check "byte at the limit" [ 0uy; 254uy; 255uy ] [ 0uy; 254uy..255uy ] +check "uint64 at the limit" [ 0UL; System.UInt64.MaxValue ] [ 0UL; System.UInt64.MaxValue..System.UInt64.MaxValue ] +check "singleton nativeint" [ 0n; 2n ] [ 0n; 2n..2n ] +check "singleton unativeint" [ 0un; 2un ] [ 0un; 2un..2un ] +check "singleton unativeint alone" [ 2un ] [ 2un..2un ] +check "singleton unativeint yield!" [ 0un; 2un ] [ yield 0un; yield! (..) 2un 2un ] +let mutable visited = 0 +for _ in 2un..2un do visited <- visited + 1 +check "singleton unativeint for loop" 1 visited +check "zero step" true (try ignore [ 0; 1..0..5 ]; false with :? System.ArgumentException -> true) +check "nativeint with the smallest step" [ 7n ] [ 7n; -1n .. -9223372036854775808n .. 0n ] +let mutable first = 1 +let bump () = first <- 100; 3 +check "operands are read in order" [ 0; 1; 2; 3 ] [ 0; first..bump () ] + +let inline mixed a b c = [ a..b; c ] +let flat c = [ 1; 2..3; c ] +let flatArray c = [| 0..1; c |] +check "annotated" [ 1; 2; 5 ] ([ 1..2; 5 ]: int list) +check "inline" [ 1.0; 2.0; 5.0 ] (mixed 1.0 2.0 5.0) +check "inline int" [ 1; 2; 5 ] (mixed 1 2 5) +check "flat list keeps the value" [ 1; 2; 3; 5 ] (flat 5) +check "flat array keeps the value" [| 0; 1; 7 |] (flatArray 7) + +let counter = ref 0 +check "if" [ 2; 3 ] [ if false then 1 else 2..3 ] +check "match" [ 1; 2 ] [ match Some 2 with Some n -> 1..n | None -> 0 ] +check "for" [ 1; 2; 2; 3 ] [ for x in [ 1; 2 ] do x..x + 1 ] +check "while" [ 0; 1; 1; 2 ] [ while counter.Value < 2 do counter.Value..counter.Value + 1; counter.Value <- counter.Value + 1 ] +check "try/with" [ 1; 2 ] [ try 1..2 with _ -> () ] +check "try/finally" [ 1; 2 ] [ try 1..2 finally () ] +check "let" [ 1; 2; 3; 10 ] [ let lo = 1 in lo..3; 10 ] +check "use" [ 1; 2 ] [ use _d = new System.IO.StringReader("") in 1..2 ] +check "let!" [ 1; 2; 3 ] (list { let! n = Some 3 in 1..n }) + +check "yield! range" [ 1; 2; 3 ] [ yield! 1..3 ] +check "yield! range in seq" [ 1; 2; 3 ] (List.ofSeq (seq { yield! 1..3 })) +check "yield! range in array" [ 1; 2; 3 ] (List.ofArray [| yield! 1..3 |]) +check "yield! range, value, yield! list" [ 1; 2; 3; 9; 20; 21 ] [ yield! 1..3; 9; yield! [ 20..21 ] ] +check "yield! in if" [ 1; 2; 3 ] [ if false then yield 1 else yield! 1..3 ] +check "yield! in match" [ 1; 2; 3 ] [ match false with true -> yield 1 | false -> yield! 1..3 ] +check "after yield!" [ 0; 2; 3; 4 ] [ yield! [ 0 ]; 2..3; 4 ] +check "after yield" [ 1; 2; 3 ] [ yield 1; 2..3 ] + +check "quotation" (string <@ [ yield 0; yield! (..) 1 2 ] @>) (string <@ [ 0; 1..2 ] @>) + +module CustomRange = + let (..) (a: int) (b: int) = seq { a; b } + check "range operator in scope" [ 0; 1; 3 ] [ 0; 1..3 ] + +module TailPosition = + let calls = ResizeArray() + + type Builder() = + member _.YieldFrom(xs: seq) = calls.Add "YieldFrom"; List.ofSeq xs + member _.YieldFromFinal(xs: seq) = calls.Add "YieldFromFinal"; List.ofSeq xs + member _.Combine(a, b) = a @ b + member _.Delay(f: unit -> _ list) = f () + + let builder = Builder() + check "tail position elements" [ 1; 2; 3; 4 ] (builder { 1..2; 3..4 }) + check "tail position calls" [ "YieldFrom"; "YieldFromFinal" ] (List.ofSeq calls) +""" + |> asExe + |> withLangVersionPreview + |> compileAndRun + |> shouldSucceed + +[] +let ``A range is not a single value and needs YieldFrom to splice`` () = + FSharp + """ +module Diagnostics + +type Builder() = + member _.Yield(x: int) = [ x ] + member _.YieldFrom(xs: seq) = List.ofSeq xs + member _.Return(x: int) = [ x ] + member _.Bind(x: int, f: int -> int list) = f x + member _.BindReturn(x: int, f: int -> int) = [ f x ] + +type WithoutYieldFrom() = + member _.Yield(x: int) = [ x ] + +let builder = Builder() +let withoutYieldFrom = WithoutYieldFrom() +let a = [ yield 1..3 ] +let b = [ for x in [ 1 ] -> 1..3 ] +let c = [ return 1..3 ] +let d = [ 1; (2..3) ] +let e = builder { yield 1..3 } +let f = builder { return 1..3 } +let g = builder { let! x = 1 in return x..3 } +let h = withoutYieldFrom { yield 1..3 } +let i = withoutYieldFrom { 1..3 } +let j = [ yield 1; 2..3; 4 ] +let k = [ 1..2; 3 then 4 ] +let l = query { return 1..3 } +""" + |> asLibrary + |> withLangVersionPreview + |> typecheck + |> shouldFail + |> withDiagnostics + [ rangeAsValue 16 17 21 + rangeAsValue 17 29 33 + (Error 635, Line 18, Col 11, Line 18, Col 22, "In sequence expressions, results are generated using 'yield'") + indexerSyntax 19 15 19 + rangeAsValue 20 25 29 + rangeAsValue 21 26 30 + rangeAsValue 22 40 44 + rangeAsValue 23 34 38 + (Error 708, Line 24, Col 28, Line 24, Col 32, "This control construct may only be used if the computation expression builder defines a 'YieldFrom' method") + (Warning 3221, Line 25, Col 26, Line 25, Col 27, "This expression returns a value of type 'int' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield'.") + (Error 743, Line 26, Col 17, Line 26, Col 25, "The expression form 'expr then expr' may only be used as part of an explicit object constructor") + (Warning 20, Line 26, Col 24, Line 26, Col 25, "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 3144, Line 27, Col 17, Line 27, Col 23, "'return' and 'return!' may not be used in queries") ] + +[] +let ``Before the feature each new form reports FS3350`` () = + FSharp + """ +module Gated + +type Builder() = + member _.Yield(x) = [ x ] + member _.YieldFrom(xs: seq<_>) = List.ofSeq xs + member _.Combine(a, b) = a @ b + member _.Delay(f: unit -> _ list) = f () + +let builder = Builder() +let a = [ 0; 1..3 ] +let b = seq { 0; 1..3 } +let c = [| yield! 1..3 |] +let d = builder { 0; 1..3 } +let e = [ yield 1..3 ] +""" + |> asLibrary + |> withLangVersion11 + |> typecheck + |> shouldFail + |> withDiagnostics + [ featureUnavailable 11 14 18 + featureUnavailable 12 18 22 + featureUnavailable 13 19 23 + featureUnavailable 14 22 26 + indexerSyntax 15 17 21 ] + +[] +let ``An integral splice in a list or array is a counted loop`` () = + FSharp + """ +module Loops + +let list n = [ 0; 1..n ] +let array n = [| 0; 1..2..n |] +""" + |> asLibrary + |> withLangVersionPreview + |> compile + |> shouldSucceed + |> verifyILNotPresent [ "GetEnumerator"; "AddMany"; "RangeInt32" ]