Skip to content

Commit

Permalink
Allow parameter-less CustomOperation (#16475)
Browse files Browse the repository at this point in the history
  • Loading branch information
vzarytovskii authored Jan 3, 2024
1 parent 8447fa0 commit ec08a4c
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 30 deletions.
2 changes: 2 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.200.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
* Limit a type to 65K methods, introduce a compile-time error if any class has over approx 64K methods in generated IL. ([Issue #16398](https://github.com/dotnet/fsharp/issues/16398), [#PR 16427](https://github.com/dotnet/fsharp/pull/16427))

### Added

* Raise a new error when interfaces with auto properties are implemented on constructor-less types. ([PR #16352](https://github.com/dotnet/fsharp/pull/16352))
* Allow usage of `[<TailCall>]` with older `FSharp.Core` package versions. ([PR #16373](https://github.com/dotnet/fsharp/pull/16373))
* Parser recovers on unfinished `as` patterns. ([PR #16404](https://github.com/dotnet/fsharp/pull/16404))
* Allow type-checking of unfinished object expressions. ([PR #16413](https://github.com/dotnet/fsharp/pull/16413))
* Parser recovers on unfinished enum case declarations. ([PR #16401](https://github.com/dotnet/fsharp/pull/16401))
* Parser recovers on unfinished record declarations. ([PR #16357](https://github.com/dotnet/fsharp/pull/16357))
* `MutableKeyword` to [SynFieldTrivia](../reference/fsharp-compiler-syntaxtrivia-synfieldtrivia.html) ([PR #16357](https://github.com/dotnet/fsharp/pull/16357))
* Added support for a new parameterless constructor for `CustomOperationAttribute`, which, when applied, will use method name as keyword for custom operation in computation expression builder. ([PR #16475](https://github.com/dotnet/fsharp/pull/16475), part of implementation for [fslang-suggestions/1250](https://github.com/fsharp/fslang-suggestions/issues/1250))
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Core/8.0.200.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
### Added

* More inlines for Result module. ([PR #16106](https://github.com/dotnet/fsharp/pull/16106))
* Added a new parameterless constructor for `CustomOperationAttribute` ([PR #16475](https://github.com/dotnet/fsharp/pull/16475), part of implementation for [fslang-suggestions/1250](https://github.com/fsharp/fslang-suggestions/issues/1250))
2 changes: 1 addition & 1 deletion src/Compiler/Checking/AttributeChecking.fs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ let TryBindMethInfoAttribute g (m: range) (AttribInfo(atref, _) as attribSpec) m
(fun provAttribs ->
match provAttribs.PUntaint((fun a -> a.GetAttributeConstructorArgs(provAttribs.TypeProvider.PUntaintNoFailure(id), atref.FullName)), m) with
| Some args -> f3 args
| None -> None)
| None -> None)
#else
(fun _provAttribs -> None)
#endif
Expand Down
21 changes: 16 additions & 5 deletions src/Compiler/Checking/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,25 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol
/// Decide if the builder is an auto-quote builder
let isAutoQuote = hasMethInfo "Quote"

let customOperationMethods =
let customOperationMethods =
AllMethInfosOfTypeInScope ResultCollectionSettings.AllResults cenv.infoReader env.NameEnv None ad IgnoreOverrides mBuilderVal builderTy
|> List.choose (fun methInfo ->
|> List.choose (fun methInfo ->
if not (IsMethInfoAccessible cenv.amap mBuilderVal ad methInfo) then None else
let nameSearch =
TryBindMethInfoAttribute cenv.g mBuilderVal cenv.g.attrib_CustomOperationAttribute methInfo
let nameSearch =
TryBindMethInfoAttribute cenv.g mBuilderVal cenv.g.attrib_CustomOperationAttribute methInfo
IgnoreAttribute // We do not respect this attribute for IL methods
(function Attrib(_, _, [ AttribStringArg msg ], _, _, _, _) -> Some msg | _ -> None)
(fun attr ->
// NOTE: right now, we support of custom operations with spaces in them ([<CustomOperation("foo bar")>])
// In the parameterless CustomOperationAttribute - we use the method name, and also allow it to be ````-quoted (member _.``foo bar`` _ = ...)
match attr with
// Empty string and parameterless constructor - we use the method name
| Attrib(_, _, [ AttribStringArg "" ], _, _, _, _) // Empty string as parameter
| Attrib(_, _, [ ], _, _, _, _) -> // No parameters, same as empty string for compat reasons.
Some methInfo.LogicalName
// Use the specified name
| Attrib(_, _, [ AttribStringArg msg ], _, _, _, _) ->
Some msg
| _ -> None)
IgnoreAttribute // We do not respect this attribute for provided methods

match nameSearch with
Expand Down
1 change: 1 addition & 0 deletions src/FSharp.Core/prim-types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ namespace Microsoft.FSharp.Core
let mutable maintainsVarSpace = false
let mutable maintainsVarSpaceWithBind = false
let mutable joinOnWord = ""
new() = CustomOperationAttribute("")
member _.Name = name
member _.AllowIntoPattern with get() = allowInto and set v = allowInto <- v
member _.IsLikeZip with get() = isBinary and set v = isBinary <- v
Expand Down
4 changes: 4 additions & 0 deletions src/FSharp.Core/prim-types.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,10 @@ namespace Microsoft.FSharp.Core
/// <returns>CustomOperationAttribute</returns>
new: name:string -> CustomOperationAttribute

/// <summary>Create an instance of attribute with empty name</summary>
/// <returns>CustomOperationAttribute</returns>
new: unit -> CustomOperationAttribute

/// <summary>Get the name of the custom operation when used in a query or other computation expression</summary>
member Name: string

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace Conformance.Expressions.ComputationExpressions

open Xunit
open FSharp.Test.Compiler

module CustomOperations =

[<Fact>]
let ``[<CustomOperation>] without explicit name is allowed, uses method name as operation name`` () =
FSharp """
module CustomOperationTest
type CBuilder() =
[<CustomOperation>]
member this.Foo _ = "Foo"
[<CustomOperation>]
member this.foo _ = "foo"
[<CustomOperation("")>]
member this.bar _ = "bar"
member this.Yield _ = ()
member this.Zero _ = ()
[<EntryPoint>]
let main _ =
let cb = CBuilder()
let x = cb { Foo }
let y = cb { foo }
let z = cb { bar }
printfn $"{x}"
printfn $"{y}"
if x <> "Foo" then
failwith "not Foo"
if y <> "foo" then
failwith "not foo"
if z <> "bar" then
failwith "not bar"
0
"""
|> asExe
|> compileAndRun
|> shouldSucceed
Original file line number Diff line number Diff line change
Expand Up @@ -31,53 +31,77 @@
<Link>FsUnit.fs</Link>
</Compile>
<Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\Basic\Basic.fs" />
<Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\OnOverridesAndIFaceImpl\OnOverridesAndIFaceImpl.fs" />
<Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\OnTypeMembers\OnTypeMembers.fs" />
<Compile Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\PermittedLocations\PermittedLocations.fs" />
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeInheritance\AttributeInheritance.fs" />
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeUsage\AttributeUsage.fs" />
<Compile
Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\OnOverridesAndIFaceImpl\OnOverridesAndIFaceImpl.fs" />
<Compile
Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\OnTypeMembers\OnTypeMembers.fs" />
<Compile
Include="Conformance\BasicGrammarElements\AccessibilityAnnotations\PermittedLocations\PermittedLocations.fs" />
<Compile
Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeInheritance\AttributeInheritance.fs" />
<Compile
Include="Conformance\BasicGrammarElements\CustomAttributes\AttributeUsage\AttributeUsage.fs" />
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\Basic\Basic.fs" />
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ImportedAttributes\ImportedAttributes.fs" />
<Compile Include="Conformance\BasicGrammarElements\CustomAttributes\ArgumentsOfAllTypes\ArgumentsOfAllTypes.fs" />
<Compile
Include="Conformance\BasicGrammarElements\CustomAttributes\ImportedAttributes\ImportedAttributes.fs" />
<Compile
Include="Conformance\BasicGrammarElements\CustomAttributes\ArgumentsOfAllTypes\ArgumentsOfAllTypes.fs" />
<Compile Include="Conformance\BasicGrammarElements\DelegateTypes\DelegateDefinition.fs" />
<Compile Include="Conformance\BasicGrammarElements\EntryPoint\EntryPoint.fs" />
<Compile Include="Conformance\BasicGrammarElements\Events\Basic\Basic.fs" />
<Compile Include="Conformance\BasicGrammarElements\ExceptionDefinitions\ExceptionDefinitions.fs" />
<Compile Include="Conformance\BasicGrammarElements\ExplicitObjectConstructors\ExplicitObjectConstructors.fs" />
<Compile
Include="Conformance\BasicGrammarElements\ExplicitObjectConstructors\ExplicitObjectConstructors.fs" />
<Compile Include="Conformance\BasicGrammarElements\FieldMembers\FieldMembers.fs" />
<Compile Include="Conformance\BasicGrammarElements\ImplicitObjectConstructors\ImplicitObjectConstructors.fs" />
<Compile
Include="Conformance\BasicGrammarElements\ImplicitObjectConstructors\ImplicitObjectConstructors.fs" />
<Compile Include="Conformance\BasicGrammarElements\ImportDeclarations\ImportDeclarations.fs" />
<Compile Include="Conformance\BasicGrammarElements\InterfaceSpecificationsAndImplementations\InterfaceSpecificationsAndImplementations.fs" />
<Compile
Include="Conformance\BasicGrammarElements\InterfaceSpecificationsAndImplementations\InterfaceSpecificationsAndImplementations.fs" />
<Compile Include="Conformance\BasicGrammarElements\LetBindings\Basic\Basic.fs" />
<Compile Include="Conformance\BasicGrammarElements\LetBindings\TypeFunctions\TypeFunctions.fs" />
<Compile Include="Conformance\BasicGrammarElements\LetBindings\ActivePatternBindings\ActivePatternBindings.fs" />
<Compile Include="Conformance\BasicGrammarElements\LetBindings\ExplicitTypeParameters\ExplicitTypeParameters.fs" />
<Compile
Include="Conformance\BasicGrammarElements\LetBindings\ActivePatternBindings\ActivePatternBindings.fs" />
<Compile
Include="Conformance\BasicGrammarElements\LetBindings\ExplicitTypeParameters\ExplicitTypeParameters.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\MemberDefinitions.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\ImplementingDispatchSlots\ImplementingDispatchSlots.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\MethodsAndProperties\MethodsAndProperties.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\NamedArguments\NamedArguments.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\OptionalArguments\OptionalArguments.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\OptionalDefaultParamArgs\OptionalDefaultParamArgs.fs" />
<Compile Include="Conformance\BasicGrammarElements\MemberDefinitions\OverloadingMembers\OverloadingMembers.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\ImplementingDispatchSlots\ImplementingDispatchSlots.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\MethodsAndProperties\MethodsAndProperties.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\NamedArguments\NamedArguments.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\OptionalArguments\OptionalArguments.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\OptionalDefaultParamArgs\OptionalDefaultParamArgs.fs" />
<Compile
Include="Conformance\BasicGrammarElements\MemberDefinitions\OverloadingMembers\OverloadingMembers.fs" />
<Compile Include="Conformance\BasicGrammarElements\MethodResolution.fs" />
<Compile Include="Conformance\BasicGrammarElements\ModuleAbbreviations\ModuleAbbreviations.fs" />
<Compile Include="Conformance\BasicGrammarElements\ModuleDefinitions\ModuleDefinitions.fs" />
<Compile Include="Conformance\BasicGrammarElements\NamespaceDeclGroups\NamespaceDeclGroups.fs" />
<Compile Include="Conformance\BasicGrammarElements\NullRepresentations\NullRepresentations.fs" />
<Compile Include="Conformance\BasicGrammarElements\OperatorNames\OperatorNames.fs" />
<Compile Include="Conformance\BasicGrammarElements\PrecedenceAndOperators\PrecedenceAndOperators.fs" />
<Compile
Include="Conformance\BasicGrammarElements\PrecedenceAndOperators\PrecedenceAndOperators.fs" />
<Compile Include="Conformance\BasicGrammarElements\StaticLet\StaticLetInUnionsAndRecords.fs" />
<Compile Include="Conformance\BasicGrammarElements\TypeAbbreviations\TypeAbbreviations.fs" />
<Compile Include="Conformance\BasicGrammarElements\TypeAbbreviations\WarnForAutoOpenAttributeAlias.fs" />
<Compile
Include="Conformance\BasicGrammarElements\TypeAbbreviations\WarnForAutoOpenAttributeAlias.fs" />
<Compile Include="Conformance\BasicGrammarElements\ValueRestriction\ValueRestriction.fs" />
<Compile Include="Conformance\BasicGrammarElements\UseBindings\UseBindings.fs" />
<Compile Include="Conformance\Constraints\Unmanaged.fs" />
<Compile Include="Conformance\GeneratedEqualityHashingComparison\Attributes\Diags\Attributes_Diags.fs" />
<Compile Include="Conformance\GeneratedEqualityHashingComparison\Attributes\Legacy\Attributes_Legacy.fs" />
<Compile Include="Conformance\Constraints\Unmanaged.fs" />
<Compile
Include="Conformance\GeneratedEqualityHashingComparison\Attributes\Diags\Attributes_Diags.fs" />
<Compile
Include="Conformance\GeneratedEqualityHashingComparison\Attributes\Legacy\Attributes_Legacy.fs" />
<Compile Include="Conformance\GeneratedEqualityHashingComparison\Basic\Basic.fs" />
<Compile Include="Conformance\GeneratedEqualityHashingComparison\IComparison\IComparison.fs" />
<Compile Include="Conformance\Expressions\ApplicationExpressions\BasicApplication\BasicApplication.fs" />
<Compile
Include="Conformance\Expressions\ApplicationExpressions\BasicApplication\BasicApplication.fs" />
<Compile Include="Conformance\Expressions\BindingExpressions\BindingExpressions.fs" />
<Compile Include="Conformance\Expressions\ComputationExpressions\CustomOperations.fs" />
<Compile Include="Conformance\Expressions\ObjectExpressions\ObjectExpressions.fs" />
<Compile Include="Conformance\Expressions\ControlFlowExpressions\PatternMatching\PatternMatching.fs" />
<Compile Include="Conformance\Expressions\ControlFlowExpressions\SequenceIteration\SequenceIteration.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ Microsoft.FSharp.Core.CustomOperationAttribute: System.String JoinConditionWord
Microsoft.FSharp.Core.CustomOperationAttribute: System.String Name
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_JoinConditionWord()
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_Name()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor(System.String)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_AllowIntoPattern(Boolean)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_IsLikeGroupJoin(Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ Microsoft.FSharp.Core.CustomOperationAttribute: System.String JoinConditionWord
Microsoft.FSharp.Core.CustomOperationAttribute: System.String Name
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_JoinConditionWord()
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_Name()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor(System.String)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_AllowIntoPattern(Boolean)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_IsLikeGroupJoin(Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,12 +672,14 @@ Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Contro
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpAsync`1[T] Scan[T](Microsoft.FSharp.Core.FSharpFunc`2[TMsg,Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Control.FSharpAsync`1[T]]], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpHandler`1[System.Exception] Error
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg] Start(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg] StartImmediate(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Microsoft.FSharp.Core.FSharpOption`1[TReply] TryPostAndReply[TReply](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1[TReply],TMsg], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: TReply PostAndReply[TReply](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpAsyncReplyChannel`1[TReply],TMsg], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg],Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit]], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Dispose()
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Post(TMsg)
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void Start()
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void StartImmediate()
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void add_Error(Microsoft.FSharp.Control.FSharpHandler`1[System.Exception])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void remove_Error(Microsoft.FSharp.Control.FSharpHandler`1[System.Exception])
Microsoft.FSharp.Control.FSharpMailboxProcessor`1[TMsg]: Void set_DefaultTimeout(Int32)
Expand Down Expand Up @@ -951,6 +953,7 @@ Microsoft.FSharp.Core.CustomOperationAttribute: System.String JoinConditionWord
Microsoft.FSharp.Core.CustomOperationAttribute: System.String Name
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_JoinConditionWord()
Microsoft.FSharp.Core.CustomOperationAttribute: System.String get_Name()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor()
Microsoft.FSharp.Core.CustomOperationAttribute: Void .ctor(System.String)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_AllowIntoPattern(Boolean)
Microsoft.FSharp.Core.CustomOperationAttribute: Void set_IsLikeGroupJoin(Boolean)
Expand Down
Loading

0 comments on commit ec08a4c

Please sign in to comment.