Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support empty-bodied computation expressions #17352

Merged
merged 14 commits into from
Jul 1, 2024
Merged
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* Parser: better recovery for unfinished patterns ([PR #17231](https://github.com/dotnet/fsharp/pull/17231))
* Expose inner exception information of TypeProviders to help diagnostics in IDE ([PR #17251](https://github.com/dotnet/fsharp/pull/17251))
* Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233))
* Support empty-bodied computation expressions. ([Language suggestion #1232](https://github.com/fsharp/fslang-suggestions/issues/1232), [RFC FS-1144 (PR #774)](https://github.com/fsharp/fslang-design/pull/774), [PR #17352](https://github.com/dotnet/fsharp/pull/17352))

### Changed
* Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Allow returning bool instead of unit option for partial active patterns. ([Language suggestion #1041](https://github.com/fsharp/fslang-suggestions/issues/1041), [PR #16473](https://github.com/dotnet/fsharp/pull/16473))
* Allow #nowarn to support the FS prefix on error codes to disable warnings ([Issue #17206](https://github.com/dotnet/fsharp/issues/16447), [PR #17209](https://github.com/dotnet/fsharp/pull/17209))
* Allow ParsedHashDirectives to have argument types other than strings ([Issue #17240](https://github.com/dotnet/fsharp/issues/16447), [PR #17209](https://github.com/dotnet/fsharp/pull/17209))
* Support empty-bodied computation expressions. ([Language suggestion #1232](https://github.com/fsharp/fslang-suggestions/issues/1232), [PR #17352](https://github.com/dotnet/fsharp/pull/17352))
brianrourkeboll marked this conversation as resolved.
Show resolved Hide resolved

### Fixed

Expand Down
16 changes: 15 additions & 1 deletion src/Compiler/Checking/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,8 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
&& hasMethInfo "Delay"
&& YieldFree cenv comp)

let origComp = comp

/// <summary>
/// Try translate the syntax sugar
/// </summary>
Expand Down Expand Up @@ -1633,7 +1635,19 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
(not enableImplicitYield)
&& isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy)
then
error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), m))
match origComp with
// builder { }
//
// The compiler inserts a dummy () in CheckExpressions.fs for
// empty-bodied computation expressions. In this case, the user
// has not actually written any "control construct" in the body,
// and so we use a more specific error message for clarity.
| SynExpr.Const(SynConst.Unit, mUnit) when
g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions
&& Range.equals mUnit range0
->
error (Error(FSComp.SR.tcEmptyBodyRequiresBuilderZeroMethod (), mWhole))
| _ -> error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), m))

Some(translatedCtxt (mkSynCall "Zero" m []))

Expand Down
27 changes: 24 additions & 3 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8189,6 +8189,10 @@ and Propagate (cenv: cenv) (overallTy: OverallTy) (env: TcEnv) tpenv (expr: Appl
// seq { ... }
| SynExpr.ComputationExpr _ -> ()

// async { }
// seq { }
| SynExpr.Record (None, None, [], _) when g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions -> ()

// expr[idx]
// expr[idx1, idx2]
// expr[idx1..]
Expand Down Expand Up @@ -8453,6 +8457,16 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg
let mArg = synArg.Range
let mLeftExpr = leftExpr.Range

/// Treat an application of a value to an empty record expression
/// as a computation expression with a single unit expression.
/// Insert a (), i.e., such that builder { } ≡ builder { () }.
/// This transformation is only valid for language
/// versions that support this feature.
let (|EmptyFieldListAsUnit|_|) recordFields =
match recordFields with
| [] when g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions -> Some (EmptyFieldListAsUnit (SynExpr.Const (SynConst.Unit, range0)))
| _ -> None

// If the type of 'synArg' unifies as a function type, then this is a function application, otherwise
// it is an error or a computation expression or indexer or delegate invoke
match UnifyFunctionTypeUndoIfFailed cenv denv mLeftExpr exprTy with
Expand All @@ -8474,11 +8488,15 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg
// though users don't realise that.
let synArg =
match synArg with
| SynExpr.ComputationExpr (false, comp, m) when
// seq { comp }
// seq { }
| SynExpr.ComputationExpr (false, comp, m)
| SynExpr.Record (None, None, EmptyFieldListAsUnit comp, m) when
(match leftExpr with
| ApplicableExpr(expr=Expr.Op(TOp.Coerce, _, [SeqExpr g], _)) -> true
| _ -> false) ->
SynExpr.ComputationExpr (true, comp, m)

| _ -> synArg

let arg, tpenv =
Expand Down Expand Up @@ -8519,8 +8537,11 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg
| _ -> None, delayed
TcIndexingThen cenv env overallTy mExprAndArg m tpenv setInfo synLeftExprOpt leftExpr.Expr exprTy expandedIndexArgs indexArgs delayed

// Perhaps 'leftExpr' is a computation expression builder, and 'arg' is '{ ... }'
| SynExpr.ComputationExpr (false, comp, _m) ->
// Perhaps 'leftExpr' is a computation expression builder, and 'arg' is '{ ... }' or '{ }':
// leftExpr { comp }
// leftExpr { }
| SynExpr.ComputationExpr (false, comp, _m)
| SynExpr.Record (None, None, EmptyFieldListAsUnit comp, _m) ->
let bodyOfCompExpr, tpenv = cenv.TcComputationExpression cenv env overallTy tpenv (mLeftExpr, leftExpr.Expr, exprTy, comp)
TcDelayed cenv overallTy env tpenv mExprAndArg (MakeApplicableExprNoFlex cenv bodyOfCompExpr) (tyOfExpr g bodyOfCompExpr) ExprAtomicFlag.NonAtomic delayed

Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ tcCouldNotFindIDisposable,"Couldn't find Dispose on IDisposable, or it was overl
706,tcInvalidUnitsOfMeasurePrefix,"Units-of-measure cannot be used as prefix arguments to a type. Rewrite as postfix arguments in angle brackets."
707,tcUnitsOfMeasureInvalidInTypeConstructor,"Unit-of-measure cannot be used in type constructor application"
708,tcRequireBuilderMethod,"This control construct may only be used if the computation expression builder defines a '%s' method"
708,tcEmptyBodyRequiresBuilderZeroMethod,"An empty body may only be used if the computation expression builder defines a 'Zero' method."
709,tcTypeHasNoNestedTypes,"This type has no nested types"
711,tcUnexpectedSymbolInTypeExpression,"Unexpected %s in type expression"
712,tcTypeParameterInvalidAsTypeConstructor,"Type parameter cannot be used as type constructor"
Expand Down Expand Up @@ -1755,3 +1756,4 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [<Struct>] di
featureParsedHashDirectiveArgumentNonString,"# directives with non-quoted string arguments"
3869,featureParsedHashDirectiveUnexpectedInteger,"Unexpected integer literal '%d'."
3869,featureParsedHashDirectiveUnexpectedIdentifier,"Unexpected identifier '%s'."
featureEmptyBodiedComputationExpressions,"Support for computation expressions with empty bodies: builder {{ }}"
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type LanguageFeature =
| LowerIntegralRangesToFastLoops
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
| ParsedHashDirectiveArgumentNonQuotes
| EmptyBodiedComputationExpressions

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -207,6 +208,7 @@ type LanguageVersion(versionText) =
LanguageFeature.LowerIntegralRangesToFastLoops, previewVersion
LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap, previewVersion
LanguageFeature.ParsedHashDirectiveArgumentNonQuotes, previewVersion
LanguageFeature.EmptyBodiedComputationExpressions, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -356,6 +358,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap ->
FSComp.SR.featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap ()
| LanguageFeature.ParsedHashDirectiveArgumentNonQuotes -> FSComp.SR.featureParsedHashDirectiveArgumentNonString ()
| LanguageFeature.EmptyBodiedComputationExpressions -> FSComp.SR.featureEmptyBodiedComputationExpressions ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
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 @@ -81,6 +81,7 @@ type LanguageFeature =
| LowerIntegralRangesToFastLoops
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
| ParsedHashDirectiveArgumentNonQuotes
| EmptyBodiedComputationExpressions

/// LanguageVersion management
type LanguageVersion =
Expand Down
10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading