Skip to content

Commit 8a10a4f

Browse files
committed
Allow _ in use! bindings values
1 parent 85ea990 commit 8a10a4f

22 files changed

+447
-10
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.300.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Fixed [#18433](https://github.com/dotnet/fsharp/issues/18433), a rare case of an internal error in xml comment processing. ([PR #18436](https://github.com/dotnet/fsharp/pull/18436))
2626
* Fix confusing type inference error in task expression ([Issue #13789](https://github.com/dotnet/fsharp/issues/13789), [PR #18450](https://github.com/dotnet/fsharp/pull/18450))
2727
* Fix missing `null` highlighting in tooltips ([PR #18457](https://github.com/dotnet/fsharp/pull/18457))
28+
* Allow `_` in `use!` bindings values (lift FS1228 restriction) ([PR #18189](https://github.com/dotnet/fsharp/pull/18189))
2829
* Make `[<CallerMemberName; Struct>]` combination work([PR #18444](https://github.com/dotnet/fsharp/pull/18444/))
2930

3031
### Added

docs/release-notes/.Language/preview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Deprecate places where `seq` can be omitted. ([Language suggestion #1033](https://github.com/fsharp/fslang-suggestions/issues/1033), [PR #17772](https://github.com/dotnet/fsharp/pull/17772))
55
* Added type conversions cache, only enabled for compiler runs ([PR#17668](https://github.com/dotnet/fsharp/pull/17668))
66
* Support ValueOption + Struct attribute as optional parameter for methods ([Language suggestion #1136](https://github.com/fsharp/fslang-suggestions/issues/1136), [PR #18098](https://github.com/dotnet/fsharp/pull/18098))
7+
* Allow `_` in `use!` bindings values (lift FS1228 restriction) ([PR #18189](https://github.com/dotnet/fsharp/pull/18189))
78
* Warn when `unit` is passed to an `obj`-typed argument ([PR #18330](https://github.com/dotnet/fsharp/pull/18330))
89

910
### Fixed

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/// with generalization at appropriate points.
55
module internal FSharp.Compiler.CheckComputationExpressions
66

7+
open FSharp.Compiler.TcGlobals
78
open Internal.Utilities.Library
89
open FSharp.Compiler.AccessibilityLogic
910
open FSharp.Compiler.AttributeChecking
@@ -1809,17 +1810,29 @@ let rec TryTranslateComputationExpression
18091810
translatedCtxt
18101811
)
18111812

1812-
// 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2))))
1813+
// 'use! pat = e1 in e2' --> build.Bind(e1, (function _argN -> match _argN with pat -> build.Using(x, (fun _argN -> match _argN with pat -> e2))))
1814+
// or
1815+
// 'use! _ = e1 in e2' --> build.Bind(e1, (fun _argN -> e2))
18131816
| ExprAsUseBang(spBind, isFromSource, pat, rhsExpr, andBangs, innerComp, mBind) ->
18141817
if ceenv.isQuery then
18151818
error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind))
18161819

1817-
match pat, andBangs with
1818-
| (SynPat.Named(ident = SynIdent(id, _); isThisVal = false) | SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ]))), [] ->
1820+
match andBangs with
1821+
| [] ->
18191822
// Valid pattern case - handle with Using + Bind
18201823
requireBuilderMethod "Using" mBind cenv ceenv.env ceenv.ad ceenv.builderTy mBind
18211824
requireBuilderMethod "Bind" mBind cenv ceenv.env ceenv.ad ceenv.builderTy mBind
18221825

1826+
let ident, pat =
1827+
match pat with
1828+
| SynPat.Named(ident = SynIdent(id, _); isThisVal = false) -> id, pat
1829+
| SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) -> id, pat
1830+
| SynPat.Wild(m) when ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.UseBangBindingValueDiscard ->
1831+
// Special handling for wildcard (_) patterns
1832+
let tmpIdent = mkSynId m "_"
1833+
tmpIdent, SynPat.Named(SynIdent(tmpIdent, None), false, None, m)
1834+
| _ -> error (Error(FSComp.SR.tcInvalidUseBangBinding (), pat.Range))
1835+
18231836
let bindExpr =
18241837
let consumeExpr =
18251838
SynExpr.MatchLambda(
@@ -1840,14 +1853,14 @@ let rec TryTranslateComputationExpression
18401853
)
18411854

18421855
let consumeExpr =
1843-
mkSynCall "Using" mBind [ SynExpr.Ident id; consumeExpr ] ceenv.builderValName
1856+
mkSynCall "Using" mBind [ SynExpr.Ident ident; consumeExpr ] ceenv.builderValName
18441857

18451858
let consumeExpr =
18461859
SynExpr.MatchLambda(
18471860
false,
18481861
mBind,
18491862
[
1850-
SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero)
1863+
SynMatchClause(pat, None, consumeExpr, ident.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero)
18511864
],
18521865
DebugPointAtBinding.NoneAtInvisible,
18531866
mBind
@@ -1860,8 +1873,7 @@ let rec TryTranslateComputationExpression
18601873
|> addBindDebugPoint spBind
18611874

18621875
Some(translatedCtxt bindExpr)
1863-
| _pat, [] -> error (Error(FSComp.SR.tcInvalidUseBangBinding (), mBind))
1864-
| _pat, _ands ->
1876+
| _ ->
18651877
// Has andBangs
18661878
let m =
18671879
match andBangs with

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,3 +1798,4 @@ featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase
17981798
featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted"
17991799
featureSupportValueOptionsAsOptionalParameters,"Support ValueOption as valid type for optional member parameters"
18001800
featureSupportWarnWhenUnitPassedToObjArg,"Warn when unit is passed to a member accepting `obj` argument, e.g. `Method(o:obj)` will warn if called via `Method()`."
1801+
featureUseBangBindingValueDiscard,"Use bang binding with value discard"

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type LanguageFeature =
9999
| DeprecatePlacesWhereSeqCanBeOmitted
100100
| SupportValueOptionsAsOptionalParameters
101101
| WarnWhenUnitPassedToObjArg
102+
| UseBangBindingValueDiscard
102103

103104
/// LanguageVersion management
104105
type LanguageVersion(versionText) =
@@ -229,6 +230,7 @@ type LanguageVersion(versionText) =
229230
LanguageFeature.DeprecatePlacesWhereSeqCanBeOmitted, previewVersion
230231
LanguageFeature.SupportValueOptionsAsOptionalParameters, previewVersion
231232
LanguageFeature.WarnWhenUnitPassedToObjArg, previewVersion
233+
LanguageFeature.UseBangBindingValueDiscard, previewVersion
232234
]
233235

234236
static let defaultLanguageVersion = LanguageVersion("default")
@@ -391,6 +393,7 @@ type LanguageVersion(versionText) =
391393
| LanguageFeature.DeprecatePlacesWhereSeqCanBeOmitted -> FSComp.SR.featureDeprecatePlacesWhereSeqCanBeOmitted ()
392394
| LanguageFeature.SupportValueOptionsAsOptionalParameters -> FSComp.SR.featureSupportValueOptionsAsOptionalParameters ()
393395
| LanguageFeature.WarnWhenUnitPassedToObjArg -> FSComp.SR.featureSupportWarnWhenUnitPassedToObjArg ()
396+
| LanguageFeature.UseBangBindingValueDiscard -> FSComp.SR.featureUseBangBindingValueDiscard ()
394397

395398
/// Get a version string associated with the given feature.
396399
static member GetFeatureVersionString feature =

src/Compiler/Facilities/LanguageFeatures.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type LanguageFeature =
9090
| DeprecatePlacesWhereSeqCanBeOmitted
9191
| SupportValueOptionsAsOptionalParameters
9292
| WarnWhenUnitPassedToObjArg
93+
| UseBangBindingValueDiscard
9394

9495
/// LanguageVersion management
9596
type LanguageVersion =

src/Compiler/pars.fsy

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3755,13 +3755,23 @@ atomicPattern:
37553755
{ SynPat.ArrayOrList(true, $2, lhs parseState) }
37563756

37573757
| UNDERSCORE
3758-
{ SynPat.Wild(lhs parseState) }
3758+
{ (* Underscore pattern ('_') is represented as SynPat.Wild
3759+
This wild pattern is used in all binding forms:
3760+
- let _ = ...
3761+
- use _ = ...
3762+
- let! _ = ...
3763+
- use! _ = ...
3764+
This ensures consistent representation of wildcard bindings in the AST *)
3765+
SynPat.Wild(lhs parseState) }
37593766

37603767
| QMARK ident
37613768
{ SynPat.OptionalVal($2, lhs parseState) }
37623769

37633770
| atomicPatternLongIdent %prec prec_atompat_pathop
3764-
{ let vis, lidwd = $1
3771+
{ (* This rule handles identifiers in patterns like 'use! __' *)
3772+
(* For simple identifiers (like '__'), it creates a SynPat.Named AST node *)
3773+
(* For complex paths (A.B.C) or uppercase ids, it calls mkSynPatMaybeVar *)
3774+
let vis, lidwd = $1
37653775
if not (isNilOrSingleton lidwd.LongIdent) || String.isLeadingIdentifierCharacterUpperCase (List.head lidwd.LongIdent).idText then
37663776
mkSynPatMaybeVar lidwd vis (lhs parseState)
37673777
else
@@ -4431,7 +4441,13 @@ declExpr:
44314441
SynExpr.YieldOrReturnFrom(($1, not $1), arbExpr ("yield!", mYieldAll), mYieldAll, trivia) }
44324442

44334443
| BINDER headBindingPattern EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
4434-
{ let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5)
4444+
{ (* This rule handles the 'use!' and 'let!' binding expressions in computation expressions *)
4445+
(* The BINDER token represents keywords like 'use!' or 'let!' *)
4446+
(* headBindingPattern represents patterns in the binding:
4447+
- Underscore ('_') patterns are preserved as SynPat.Wild
4448+
- Named patterns ('__') are represented as SynPat.Named
4449+
- Identifiers (like 'value') are represented as SynPat.LongIdent *)
4450+
let spBind = DebugPointAtBinding.Yes(rhs2 parseState 1 5)
44354451
let mEquals = rhs parseState 3
44364452
let m = unionRanges (rhs parseState 1) $8.Range
44374453
let trivia: SynExprLetOrUseBangTrivia = { LetOrUseBangKeyword = rhs parseState 1 ; EqualsRange = Some mEquals }

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)