Skip to content

Commit 9995898

Browse files
authored
Allow _ in use! bindings values (#18487)
1 parent 264a54e commit 9995898

28 files changed

+511
-38
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 #18487](https://github.com/dotnet/fsharp/pull/18487))
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 #18487](https://github.com/dotnet/fsharp/pull/18487))
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: 27 additions & 6 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
@@ -1783,12 +1784,33 @@ let rec TryTranslateComputationExpression
17831784
if ceenv.isQuery then
17841785
error (Error(FSComp.SR.tcBindMayNotBeUsedInQueries (), mBind))
17851786

1786-
match pat, andBangs with
1787-
| (SynPat.Named(ident = SynIdent(id, _); isThisVal = false) | SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ]))), [] ->
1787+
match andBangs with
1788+
| [] ->
17881789
// Valid pattern case - handle with Using + Bind
17891790
requireBuilderMethod "Using" mBind cenv ceenv.env ceenv.ad ceenv.builderTy mBind
17901791
requireBuilderMethod "Bind" mBind cenv ceenv.env ceenv.ad ceenv.builderTy mBind
17911792

1793+
let supportsUseBangBindingValueDiscard =
1794+
ceenv.cenv.g.langVersion.SupportsFeature LanguageFeature.UseBangBindingValueDiscard
1795+
1796+
// use! x = ...
1797+
// use! (x) = ...
1798+
// use! (__) = ...
1799+
// use! _ = ...
1800+
// use! (_) = ...
1801+
let rec extractIdentifierFromPattern pat =
1802+
match pat with
1803+
| SynPat.Named(ident = SynIdent(id, _); isThisVal = false) -> id, pat
1804+
| SynPat.LongIdent(longDotId = SynLongIdent(id = [ id ])) -> id, pat
1805+
| SynPat.Wild(m) when supportsUseBangBindingValueDiscard ->
1806+
// To properly call the Using(disposable) CE member, we need to convert the wildcard to a SynPat.Named
1807+
let tmpIdent = mkSynId m "_"
1808+
tmpIdent, SynPat.Named(SynIdent(tmpIdent, None), false, None, m)
1809+
| SynPat.Paren(pat = pat) -> extractIdentifierFromPattern pat
1810+
| _ -> error (Error(FSComp.SR.tcInvalidUseBangBinding (), pat.Range))
1811+
1812+
let ident, pat = extractIdentifierFromPattern pat
1813+
17921814
let bindExpr =
17931815
let consumeExpr =
17941816
SynExpr.MatchLambda(
@@ -1809,14 +1831,14 @@ let rec TryTranslateComputationExpression
18091831
)
18101832

18111833
let consumeExpr =
1812-
mkSynCall "Using" mBind [ SynExpr.Ident id; consumeExpr ] ceenv.builderValName
1834+
mkSynCall "Using" mBind [ SynExpr.Ident ident; consumeExpr ] ceenv.builderValName
18131835

18141836
let consumeExpr =
18151837
SynExpr.MatchLambda(
18161838
false,
18171839
mBind,
18181840
[
1819-
SynMatchClause(pat, None, consumeExpr, id.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero)
1841+
SynMatchClause(pat, None, consumeExpr, ident.idRange, DebugPointAtTarget.No, SynMatchClauseTrivia.Zero)
18201842
],
18211843
DebugPointAtBinding.NoneAtInvisible,
18221844
mBind
@@ -1829,8 +1851,7 @@ let rec TryTranslateComputationExpression
18291851
|> addBindDebugPoint spBind
18301852

18311853
Some(translatedCtxt bindExpr)
1832-
| _pat, [] -> error (Error(FSComp.SR.tcInvalidUseBangBinding (), mBind))
1833-
| _pat, _ands ->
1854+
| _ ->
18341855
// Has andBangs
18351856
let m =
18361857
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,"Allows use! _ = ... in computation expressions"

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)