Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b9659f4
Remove limitations on custom operation overloads
Nhowka May 12, 2018
83ca57f
Updated baselines
Nhowka May 15, 2018
3ee5a49
New error messages
Nhowka May 22, 2018
7f7b4f7
Add failing tests based on RFC
panesofglass Jun 7, 2020
c6f4e47
Expand test cases for overloaded CE members
panesofglass Jun 7, 2020
4adc9a6
Merge branch 'master' into customoperation-overloads
panesofglass Jul 16, 2020
8bda72b
open Extensions to fix tests
panesofglass Jul 27, 2020
1564238
Merge branch 'master' into customoperation-overloads
panesofglass Jul 31, 2020
8d31f0f
Fix error with opening missing Extensions module
panesofglass Aug 2, 2020
0863fc7
Merge branch 'master' into customoperation-overloads
panesofglass Aug 2, 2020
20d9e98
Fix unit tests
panesofglass Aug 2, 2020
87c5de4
Give unique values for each check in CE overload tests
panesofglass Aug 3, 2020
67b79e8
Add regression scenarios to ensure current functionality does not bre…
panesofglass Aug 4, 2020
1f48cfd
Rename g to group per code review [FS-1056]
panesofglass Aug 4, 2020
8bd91f6
Merge branch 'master' into customoperation-overloads
panesofglass Aug 8, 2020
319818c
Hide overloads for custom operations behind feature flag [FS-1056]
panesofglass Aug 8, 2020
86bd25d
Add feature flag to GetFeatureString
panesofglass Aug 8, 2020
e450abd
Add featureOverloadsForCustomOperations to FSComps
panesofglass Aug 8, 2020
f7c115b
Update FSComp.txt [FS-1056]
panesofglass Aug 8, 2020
45214b9
Update baselines [FS-1056]
panesofglass Aug 11, 2020
602ecfe
Revert "New error messages"
panesofglass Aug 11, 2020
90dab4d
Merge branch 'master' into customoperation-overloads
panesofglass Aug 11, 2020
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 src/fsharp/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,7 @@ invalidFullNameForProvidedType,"invalid full name for provided type"
3085,tcCustomOperationMayNotBeUsedInConjunctionWithNonSimpleLetBindings,"A custom operation may not be used in conjunction with a non-value or recursive 'let' binding in another part of this computation expression"
3086,tcCustomOperationMayNotBeUsedHere,"A custom operation may not be used in conjunction with 'use', 'try/with', 'try/finally', 'if/then/else' or 'match' operators within this computation expression"
3087,tcCustomOperationMayNotBeOverloaded,"The custom operation '%s' refers to a method which is overloaded. The implementations of custom operations may not be overloaded."
featureOverloadsForCustomOperations,"overloads for custom operations"
3090,tcIfThenElseMayNotBeUsedWithinQueries,"An if/then/else expression may not be used within queries. Consider using either an if/then expression, or use a sequence expression instead."
3091,ilxgenUnexpectedArgumentToMethodHandleOfDuringCodegen,"Invalid argument to 'methodhandleof' during codegen"
3092,etProvidedTypeReferenceMissingArgument,"A reference to a provided type was missing a value for the static parameter '%s'. You may need to recompile one or more referenced assemblies."
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type LanguageFeature =
| WitnessPassing
| InterfacesWithMultipleGenericInstantiation
| StringInterpolation
| OverloadsForCustomOperations

/// LanguageVersion management
type LanguageVersion (specifiedVersionAsString) =
Expand Down Expand Up @@ -73,6 +74,7 @@ type LanguageVersion (specifiedVersionAsString) =
LanguageFeature.InterfacesWithMultipleGenericInstantiation, previewVersion
LanguageFeature.NameOf, previewVersion
LanguageFeature.StringInterpolation, previewVersion
LanguageFeature.OverloadsForCustomOperations, previewVersion
]

let specified =
Expand Down Expand Up @@ -144,6 +146,7 @@ type LanguageVersion (specifiedVersionAsString) =
| LanguageFeature.WitnessPassing -> FSComp.SR.featureWitnessPassing()
| LanguageFeature.InterfacesWithMultipleGenericInstantiation -> FSComp.SR.featureInterfacesWithMultipleGenericInstantiation()
| LanguageFeature.StringInterpolation -> FSComp.SR.featureStringInterpolation()
| LanguageFeature.OverloadsForCustomOperations -> FSComp.SR.featureOverloadsForCustomOperations()

/// Get a version string associated with the given feature.
member _.GetFeatureVersionString feature =
Expand Down
1 change: 1 addition & 0 deletions src/fsharp/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type LanguageFeature =
| WitnessPassing
| InterfacesWithMultipleGenericInstantiation
| StringInterpolation
| OverloadsForCustomOperations

/// LanguageVersion management
type LanguageVersion =
Expand Down
39 changes: 28 additions & 11 deletions src/fsharp/TypeChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7905,19 +7905,36 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
Some (nm, maintainsVarSpaceUsingBind, maintainsVarSpace, allowInto, isLikeZip, isLikeJoin, isLikeGroupJoin, joinConditionWord, methInfo))

let customOperationMethodsIndexedByKeyword =
customOperationMethods
|> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm)
|> Seq.map (fun (nm, g) -> (nm, Seq.toList g))
if cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then
customOperationMethods
|> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm)
|> Seq.map (fun (nm, group) ->
(nm,
group
|> Seq.distinctBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName)
|> Seq.toList))
else
customOperationMethods
|> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm)
|> Seq.map (fun (nm, g) -> (nm, Seq.toList g))
|> dict

// Check for duplicates by method name (keywords and method names must be 1:1)
let customOperationMethodsIndexedByMethodName =
customOperationMethods
|> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName)
|> Seq.map (fun (nm, g) -> (nm, Seq.toList g))
if cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then
customOperationMethods
|> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName)
|> Seq.map (fun (nm, group) ->
(nm,
group
|> Seq.distinctBy (fun (nm, _, _, _, _, _, _, _, _) -> nm)
|> Seq.toList))
else
customOperationMethods
|> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName)
|> Seq.map (fun (nm, g) -> (nm, Seq.toList g))
|> dict


/// Decide if the identifier represents a use of a custom query operator
let tryGetDataForCustomOperation (nm: Ident) =
match customOperationMethodsIndexedByKeyword.TryGetValue nm.idText with
Expand Down Expand Up @@ -8899,12 +8916,12 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
let maintainsVarSpace = customOperationMaintainsVarSpace nm
let maintainsVarSpaceUsingBind = customOperationMaintainsVarSpaceUsingBind nm

let expectedArgCount = expectedArgCountForCustomOperator nm
let expectedArgCount = expectedArgCountForCustomOperator nm

let dataCompAfterOp =
match opExpr with
| StripApps(SingleIdent nm, args) ->
if args.Length = expectedArgCount then
| StripApps(SingleIdent nm, args) ->
if args.Length = expectedArgCount || cenv.g.langVersion.SupportsFeature LanguageFeature.OverloadsForCustomOperations then
// Check for the [<ProjectionParameter>] attribute on each argument position
let args = args |> List.mapi (fun i arg ->
if isCustomOperationProjectionParameter (i+1) nm then
Expand All @@ -8913,7 +8930,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
mkSynCall methInfo.DisplayName mClause (dataCompPrior :: args)
else
errorR(Error(FSComp.SR.tcCustomOperationHasIncorrectArgCount(nm.idText, expectedArgCount, args.Length), nm.idRange))
mkSynCall methInfo.DisplayName mClause ([ dataCompPrior ] @ List.init expectedArgCount (fun i -> arbExpr("_arg" + string i, mClause)))
mkSynCall methInfo.DisplayName mClause ([ dataCompPrior ] @ List.init expectedArgCount (fun i -> arbExpr("_arg" + string i, mClause)))
| _ -> failwith "unreachable"

match optionalCont with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -710,3 +710,129 @@ let ceResult =
check "grwerjkrwejgk42" ceResult.Value 2
"""

let overloadLib includeInternalExtensions includeExternalExtensions =
"""
open System

type Content = ArraySegment<byte> list

type ContentBuilder() =
member this.Run(c: Content) =
let crlf = "\r\n"B
[|for part in List.rev c do
yield! part.Array.[part.Offset..(part.Count+part.Offset-1)]
yield! crlf |]

member this.Yield(_) = []

[<CustomOperation("body")>]
member this.Body(c: Content, segment: ArraySegment<byte>) =
segment::c
""" + (if includeInternalExtensions then """

type ContentBuilder with
// unattributed internal type extension with same arity
member this.Body(c: Content, bytes: byte[]) =
ArraySegment<byte>(bytes, 0, bytes.Length)::c

// internal type extension with different arity
[<CustomOperation("body")>]
member this.Body(c: Content, bytes: byte[], offset, count) =
ArraySegment<byte>(bytes, offset, count)::c
""" else """

// unattributed type member with same arity
member this.Body(c: Content, bytes: byte[]) =
ArraySegment<byte>(bytes, 0, bytes.Length)::c

// type member with different arity
[<CustomOperation("body")>]
member this.Body(c: Content, bytes: byte[], offset, count) =
ArraySegment<byte>(bytes, offset, count)::c
""") + (if includeExternalExtensions then """

module Extensions =
type ContentBuilder with
// unattributed external type extension with same arity
member this.Body(c: Content, content: System.IO.Stream) =
let mem = new System.IO.MemoryStream()
content.CopyTo(mem)
let bytes = mem.ToArray()
ArraySegment<byte>(bytes, 0, bytes.Length)::c

// external type extensions as ParamArray
[<CustomOperation("body")>]
member this.Body(c: Content, [<ParamArray>] contents: string[]) =
List.rev [for c in contents -> let b = Text.Encoding.ASCII.GetBytes c in ArraySegment<_>(b,0,b.Length)] @ c
open Extensions
""" else """

// unattributed type member with same arity
member this.Body(c: Content, content: System.IO.Stream) =
let mem = new System.IO.MemoryStream()
content.CopyTo(mem)
let bytes = mem.ToArray()
ArraySegment<byte>(bytes, 0, bytes.Length)::c

// type members
[<CustomOperation("body")>]
member this.Body(c: Content, [<ParamArray>] contents: string[]) =
List.rev [for c in contents -> let b = Text.Encoding.ASCII.GetBytes c in ArraySegment<_>(b,0,b.Length)] @ c
""") + """

let check msg actual expected = if actual <> expected then failwithf "FAILED %s, expected %A, got %A" msg expected actual
"""

let OverloadLibTest inclInternalExt inclExternalExt source =
CompilerAssert.CompileExeAndRunWithOptions [| "/langversion:preview" |] (overloadLib inclInternalExt inclExternalExt + source)

[<Test>]
let ``OverloadLib accepts overloaded methods`` () =
OverloadLibTest false false """
let mem = new System.IO.MemoryStream("Stream"B)
let content = ContentBuilder()
let ceResult =
content {
body "Name"
body (ArraySegment<_>("Email"B, 0, 5))
body "Password"B 2 4
body "BYTES"B
body mem
body "Description" "of" "content"
}
check "TmFtZVxyXG5FbWF1" ceResult "Name\r\nEmail\r\nsswo\r\nBYTES\r\nStream\r\nDescription\r\nof\r\ncontent\r\n"B
"""

[<Test>]
let ``OverloadLib accepts overloaded internal extension methods`` () =
OverloadLibTest true false """
let mem = new System.IO.MemoryStream("Stream"B)
let content = ContentBuilder()
let ceResult =
content {
body "Name"
body (ArraySegment<_>("Email"B, 0, 5))
body "Password"B 2 4
body "BYTES"B
body mem
body "Description" "of" "content"
}
check "TmFtZVxyXG5FbWF2" ceResult "Name\r\nEmail\r\nsswo\r\nBYTES\r\nStream\r\nDescription\r\nof\r\ncontent\r\n"B
"""

[<Test>]
let ``OverloadLib accepts overloaded internal and external extensions`` () =
OverloadLibTest true true """
let mem = new System.IO.MemoryStream("Stream"B)
let content = ContentBuilder()
let ceResult =
content {
body "Name"
body (ArraySegment<_>("Email"B, 0, 5))
body "Password"B 2 4
body "BYTES"B
body mem
body "Description" "of" "content"
}
check "TmFtZVxyXG5FbWF3" ceResult "Name\r\nEmail\r\nsswo\r\nBYTES\r\nStream\r\nDescription\r\nof\r\ncontent\r\n"B
"""
80 changes: 80 additions & 0 deletions tests/fsharp/typecheck/sigs/neg60.bsl
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,83 @@ neg60.fs(80,19,80,20): typecheck error FS0043: Expecting a type supporting the o
neg60.fs(81,22,81,34): typecheck error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

neg60.fs(87,10,87,13): typecheck error FS0043: The type 'System.Nullable<int>' does not support the operator '?=?'. Consider opening the module 'Microsoft.FSharp.Linq.NullableOperators'.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(128,9,128,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(129,9,129,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(130,9,130,13): typecheck error FS3099: 'body' is used with an incorrect number of arguments. This is a custom operation in this query or computation expression. Expected 1 argument(s), but given 3.

neg60.fs(131,9,131,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(131,9,131,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(131,9,131,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(131,9,131,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(131,9,131,13): typecheck error FS3087: The custom operation 'body' refers to a method which is overloaded. The implementations of custom operations may not be overloaded.

neg60.fs(131,9,131,13): typecheck error FS3099: 'body' is used with an incorrect number of arguments. This is a custom operation in this query or computation expression. Expected 1 argument(s), but given 3.

neg60.fs(177,9,177,13): typecheck error FS3099: 'text' is used with an incorrect number of arguments. This is a custom operation in this query or computation expression. Expected 1 argument(s), but given 0.

neg60.fs(186,9,186,24): typecheck error FS3099: 'with_validators' is used with an incorrect number of arguments. This is a custom operation in this query or computation expression. Expected 1 argument(s), but given 2.

neg60.fs(195,9,195,24): typecheck error FS3099: 'with_validators' is used with an incorrect number of arguments. This is a custom operation in this query or computation expression. Expected 1 argument(s), but given 3.
Loading