Skip to content

Commit 8f64e48

Browse files
authored
Merge branch 'main' into merges/release/dev17.11-to-main
2 parents 02adf13 + 0294fe7 commit 8f64e48

32 files changed

+511
-53
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Fixed
22

3+
* Enforce `AttributeTargets` on records. ([PR #17207](https://github.com/dotnet/fsharp/pull/17207))
34
* Fix a false positive of the `[<TailCall>]` analysis in combination with async. ([Issue #17237](https://github.com/dotnet/fsharp/issues/17237), [PR #17241](https://github.com/dotnet/fsharp/pull/17241))
45
* Extended #help directive in fsi to show documentation in the REPL. ([PR #17140](https://github.com/dotnet/fsharp/pull/17140))
56
* Fix internal error when dotting into delegates with multiple type parameters. ([PR #17227](https://github.com/dotnet/fsharp/pull/17227))
@@ -27,6 +28,7 @@
2728
* Parser: better recovery for unfinished patterns ([PR #17231](https://github.com/dotnet/fsharp/pull/17231))
2829
* Expose inner exception information of TypeProviders to help diagnostics in IDE ([PR #17251](https://github.com/dotnet/fsharp/pull/17251))
2930
* Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233))
31+
* 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))
3032

3133
### Changed
3234
* Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))

docs/release-notes/.FSharp.Core/8.0.400.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
* Cache delegate in query extensions. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130))
1010
* Update `AllowNullLiteralAttribute` to also use `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))
11+
* Update `StructAttribute ` to also use `AttributeTargets.Class` ([PR #17207](https://github.com/dotnet/fsharp/pull/17207))
1112

1213
### Breaking Changes
1314

14-
* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328))
15+
* Fixed argument exception throwing inconsistency - accessing an out-of-bounds collection index will now throw `ArgumentOutOfRangeException` instead of `ArgumentException` ([#17328](https://github.com/dotnet/fsharp/pull/17328))

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* 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))
99
* 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))
1010
* 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))
11+
* 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))
1112

1213
### Fixed
1314

src/Compiler/Checking/CheckComputationExpressions.fs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,8 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
10861086
&& hasMethInfo "Delay"
10871087
&& YieldFree cenv comp)
10881088

1089+
let origComp = comp
1090+
10891091
/// <summary>
10901092
/// Try translate the syntax sugar
10911093
/// </summary>
@@ -1633,7 +1635,19 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
16331635
(not enableImplicitYield)
16341636
&& isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy)
16351637
then
1636-
error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), m))
1638+
match origComp with
1639+
// builder { }
1640+
//
1641+
// The compiler inserts a dummy () in CheckExpressions.fs for
1642+
// empty-bodied computation expressions. In this case, the user
1643+
// has not actually written any "control construct" in the body,
1644+
// and so we use a more specific error message for clarity.
1645+
| SynExpr.Const(SynConst.Unit, mUnit) when
1646+
g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions
1647+
&& Range.equals mUnit range0
1648+
->
1649+
error (Error(FSComp.SR.tcEmptyBodyRequiresBuilderZeroMethod (), mWhole))
1650+
| _ -> error (Error(FSComp.SR.tcRequireBuilderMethod ("Zero"), m))
16371651

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

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ module TcRecdUnionAndEnumDeclarations =
428428
let g = cenv.g
429429
let m = id.idRange
430430
let attrs, _ = TcAttributesWithPossibleTargets false cenv env AttributeTargets.FieldDecl synAttrs
431+
431432
let attrsForProperty, attrsForField = attrs |> List.partition (fun (attrTargets, _) -> (attrTargets &&& AttributeTargets.Property) <> enum 0)
432433
let attrsForProperty = (List.map snd attrsForProperty)
433434
let attrsForField = (List.map snd attrsForField)
@@ -2840,6 +2841,7 @@ module EstablishTypeDefinitionCores =
28402841
// Allow failure of constructor resolution because Vals for members in the same recursive group are not yet available
28412842
let attrs, getFinalAttrs = TcAttributesCanFail cenv envinner AttributeTargets.TyconDecl synAttrs
28422843
let hasMeasureAttr = HasFSharpAttribute g g.attrib_MeasureAttribute attrs
2844+
let hasStructAttr = HasFSharpAttribute g g.attrib_StructAttribute attrs
28432845

28442846
let isStructRecordOrUnionType =
28452847
match synTyconRepr with
@@ -2896,6 +2898,12 @@ module EstablishTypeDefinitionCores =
28962898

28972899
// Run InferTyconKind to raise errors on inconsistent attribute sets
28982900
InferTyconKind g (SynTypeDefnKind.Record, attrs, [], [], inSig, true, m) |> ignore
2901+
2902+
if g.langVersion.SupportsFeature(LanguageFeature.EnforceAttributeTargets) then
2903+
if hasStructAttr then
2904+
TcAttributesWithPossibleTargets false cenv envinner AttributeTargets.Struct synAttrs |> ignore
2905+
else
2906+
TcAttributesWithPossibleTargets false cenv envinner AttributeTargets.Class synAttrs |> ignore
28992907

29002908
// Note: the table of record fields is initially empty
29012909
TFSharpTyconRepr (Construct.NewEmptyFSharpTyconData TFSharpRecord)

src/Compiler/Checking/CheckExpressions.fs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8189,6 +8189,10 @@ and Propagate (cenv: cenv) (overallTy: OverallTy) (env: TcEnv) tpenv (expr: Appl
81898189
// seq { ... }
81908190
| SynExpr.ComputationExpr _ -> ()
81918191

8192+
// async { }
8193+
// seq { }
8194+
| SynExpr.Record (None, None, [], _) when g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions -> ()
8195+
81928196
// expr[idx]
81938197
// expr[idx1, idx2]
81948198
// expr[idx1..]
@@ -8453,6 +8457,16 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg
84538457
let mArg = synArg.Range
84548458
let mLeftExpr = leftExpr.Range
84558459

8460+
/// Treat an application of a value to an empty record expression
8461+
/// as a computation expression with a single unit expression.
8462+
/// Insert a (), i.e., such that builder { } ≡ builder { () }.
8463+
/// This transformation is only valid for language
8464+
/// versions that support this feature.
8465+
let (|EmptyFieldListAsUnit|_|) recordFields =
8466+
match recordFields with
8467+
| [] when g.langVersion.SupportsFeature LanguageFeature.EmptyBodiedComputationExpressions -> Some (EmptyFieldListAsUnit (SynExpr.Const (SynConst.Unit, range0)))
8468+
| _ -> None
8469+
84568470
// If the type of 'synArg' unifies as a function type, then this is a function application, otherwise
84578471
// it is an error or a computation expression or indexer or delegate invoke
84588472
match UnifyFunctionTypeUndoIfFailed cenv denv mLeftExpr exprTy with
@@ -8474,11 +8488,15 @@ and TcApplicationThen (cenv: cenv) (overallTy: OverallTy) env tpenv mExprAndArg
84748488
// though users don't realise that.
84758489
let synArg =
84768490
match synArg with
8477-
| SynExpr.ComputationExpr (false, comp, m) when
8491+
// seq { comp }
8492+
// seq { }
8493+
| SynExpr.ComputationExpr (false, comp, m)
8494+
| SynExpr.Record (None, None, EmptyFieldListAsUnit comp, m) when
84788495
(match leftExpr with
84798496
| ApplicableExpr(expr=Expr.Op(TOp.Coerce, _, [SeqExpr g], _)) -> true
84808497
| _ -> false) ->
84818498
SynExpr.ComputationExpr (true, comp, m)
8499+
84828500
| _ -> synArg
84838501

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

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

src/Compiler/FSComp.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ tcCouldNotFindIDisposable,"Couldn't find Dispose on IDisposable, or it was overl
561561
706,tcInvalidUnitsOfMeasurePrefix,"Units-of-measure cannot be used as prefix arguments to a type. Rewrite as postfix arguments in angle brackets."
562562
707,tcUnitsOfMeasureInvalidInTypeConstructor,"Unit-of-measure cannot be used in type constructor application"
563563
708,tcRequireBuilderMethod,"This control construct may only be used if the computation expression builder defines a '%s' method"
564+
708,tcEmptyBodyRequiresBuilderZeroMethod,"An empty body may only be used if the computation expression builder defines a 'Zero' method."
564565
709,tcTypeHasNoNestedTypes,"This type has no nested types"
565566
711,tcUnexpectedSymbolInTypeExpression,"Unexpected %s in type expression"
566567
712,tcTypeParameterInvalidAsTypeConstructor,"Type parameter cannot be used as type constructor"
@@ -1755,3 +1756,4 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [<Struct>] di
17551756
featureParsedHashDirectiveArgumentNonString,"# directives with non-quoted string arguments"
17561757
3869,featureParsedHashDirectiveUnexpectedInteger,"Unexpected integer literal '%d'."
17571758
3869,featureParsedHashDirectiveUnexpectedIdentifier,"Unexpected identifier '%s'."
1759+
featureEmptyBodiedComputationExpressions,"Support for computation expressions with empty bodies: builder {{ }}"

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type LanguageFeature =
9090
| LowerIntegralRangesToFastLoops
9191
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
9292
| ParsedHashDirectiveArgumentNonQuotes
93+
| EmptyBodiedComputationExpressions
9394

9495
/// LanguageVersion management
9596
type LanguageVersion(versionText) =
@@ -207,6 +208,7 @@ type LanguageVersion(versionText) =
207208
LanguageFeature.LowerIntegralRangesToFastLoops, previewVersion
208209
LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap, previewVersion
209210
LanguageFeature.ParsedHashDirectiveArgumentNonQuotes, previewVersion
211+
LanguageFeature.EmptyBodiedComputationExpressions, previewVersion
210212
]
211213

212214
static let defaultLanguageVersion = LanguageVersion("default")
@@ -356,6 +358,7 @@ type LanguageVersion(versionText) =
356358
| LanguageFeature.LowerSimpleMappingsInComprehensionsToDirectCallsToMap ->
357359
FSComp.SR.featureLowerSimpleMappingsInComprehensionsToDirectCallsToMap ()
358360
| LanguageFeature.ParsedHashDirectiveArgumentNonQuotes -> FSComp.SR.featureParsedHashDirectiveArgumentNonString ()
361+
| LanguageFeature.EmptyBodiedComputationExpressions -> FSComp.SR.featureEmptyBodiedComputationExpressions ()
359362

360363
/// Get a version string associated with the given feature.
361364
static member GetFeatureVersionString feature =

src/Compiler/Facilities/LanguageFeatures.fsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type LanguageFeature =
8181
| LowerIntegralRangesToFastLoops
8282
| LowerSimpleMappingsInComprehensionsToDirectCallsToMap
8383
| ParsedHashDirectiveArgumentNonQuotes
84+
| EmptyBodiedComputationExpressions
8485

8586
/// LanguageVersion management
8687
type LanguageVersion =

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

Lines changed: 10 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)