Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.200.md
Original file line number Diff line number Diff line change
@@ -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:<pipe name>`, 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:<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
Expand All @@ -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<T>` 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))
Expand Down
2 changes: 2 additions & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 =
Expand All @@ -2464,6 +2467,8 @@ let rec TryTranslateComputationExpression

Some(translatedCtxt yieldOrReturnCall)

| BareRangeExpr _ -> TryTranslateComputationExpression ceenv firstTry q varSpace (mkRangeSplice comp) translatedCtxt

| _ -> None)

and ConsumeCustomOpClauses
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 =
Expand Down
64 changes: 63 additions & 1 deletion src/Compiler/Checking/Expressions/CheckExpressionsOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -181,6 +182,33 @@ let RewriteRangeExpr synExpr =
Some otherExpr
| _ -> None

[<return: Struct>]
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 =
Expand Down Expand Up @@ -223,7 +251,8 @@ let inline IsSimpleSemicolonSequenceElement expr cenv acceptDeprecated =
| SynExpr.Do _
| SynExpr.MatchBang _
| SynExpr.While _
| SynExpr.WhileBang _ -> false
| SynExpr.WhileBang _
| BareRangeExpr _ -> false
| _ -> true

[<TailCall>]
Expand All @@ -247,6 +276,39 @@ let rec TryGetSimpleSemicolonSequenceOfComprehension expr acc cenv acceptDepreca
let (|SimpleSemicolonSequence|_|) cenv acceptDeprecated cexpr =
TryGetSimpleSemicolonSequenceOfComprehension cexpr [] cenv acceptDeprecated

[<return: Struct>]
let (|SimpleSemicolonSequenceWithRanges|_|) cenv cexpr =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖🕵️⏱️🔥

[<Params(1_000, 2_500, 5_000)>]
member val Count = 0 with get, set

[<Benchmark>]
member this.CheckMixedRange() =
    check $"[ 0; 1..{this.Count}; 9 ]"

Add mixed-range, explicit-yield!, and flat-value compiler benchmarks before merge. The bounded 5k computed forms exceeded 180s while flat values stayed ~0.8s; track the scaling instead of relying on correctness-only tests.

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()

Expand Down
17 changes: 17 additions & 0 deletions src/Compiler/Checking/Expressions/CheckSequenceExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -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"
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type LanguageFeature =
| BetterAnonymousRecordParsing
| ScopedNowarn
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowMixedRangesAndValuesInSeqExpressions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| MoreConcreteTiebreaker
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 ()
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type LanguageFeature =
| BetterAnonymousRecordParsing
| ScopedNowarn
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowMixedRangesAndValuesInSeqExpressions
| AllowTypedLetUseAndBang
| ReturnFromFinal
| MoreConcreteTiebreaker
Expand Down
10 changes: 10 additions & 0 deletions src/Compiler/Optimize/LowerComputedCollections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Comment thread
xperiandri marked this conversation as resolved.
mkLoop (fun _idx elem -> mkCallCollectorAdd tcVal g infoReader m collExpr elem))
Result.Ok (false, exprR)

// yield! e ---> (for x in e -> x)

| arbitrarySeqExpr ->
Expand Down
Loading
Loading