diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 9abd05cfd5..c9bd24ce37 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7658,17 +7658,24 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder Some (nm, maintainsVarSpaceUsingBind, maintainsVarSpace, allowInto, isLikeZip, isLikeJoin, isLikeGroupJoin, joinConditionWord, methInfo)) - let customOperationMethodsIndexedByKeyword = + let customOperationMethodsIndexedByKeyword = customOperationMethods |> Seq.groupBy (fun (nm, _, _, _, _, _, _, _, _) -> nm) - |> Seq.map (fun (nm, g) -> (nm, Seq.toList g)) + |> Seq.map (fun (nm, g) -> + (nm, + g + |> Seq.distinctBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName) + |> Seq.toList)) |> 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)) + |> Seq.groupBy (fun (_, _, _, _, _, _, _, _, methInfo) -> methInfo.LogicalName) + |> Seq.map (fun (nm, g) -> + (nm, + g + |> Seq.distinctBy (fun (nm, _, _, _, _, _, _, _, _) -> nm) |> Seq.toList )) |> dict @@ -8299,6 +8306,66 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder // Now run the consumeCustomOpClauses Some (consumeCustomOpClauses q varSpace dataCompPriorToOp comp false mClause) + let dataCompAfterOp = + match opExpr with + | StripApps(SingleIdent nm, args) -> + // Check for the [] attribute on each argument position + let args = args |> List.mapi (fun i arg -> if isCustomOperationProjectionParameter (i+1) nm then SynExpr.Lambda (false, false, varSpaceSimplePat, arg, arg.Range.MakeSynthetic()) else arg) + mkSynCall methInfo.DisplayName mClause (dataCompPrior :: args) + | _ -> failwith "unreachable" + + match optionalCont with + | None -> + match optionalIntoPat with + | Some intoPat -> errorR(Error(FSComp.SR.tcIntoNeedsRestOfQuery(), intoPat.Range)) + | None -> () + dataCompAfterOp + + | Some contExpr -> + + // select a.Name into name; ... + // distinct into d; ... + // + // Rebind the into pattern and process the rest of the clauses + match optionalIntoPat with + | Some intoPat -> + if not (customOperationAllowsInto nm) then + error(Error(FSComp.SR.tcOperatorDoesntAcceptInto(nm.idText), intoPat.Range)) + + // Rebind using either for ... or let!.... + let rebind = + if maintainsVarSpaceUsingBind then + SynExpr.LetOrUseBang(NoSequencePointAtLetBinding, false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) + else + SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, intoPat, dataCompAfterOp, contExpr, intoPat.Range) + + trans true q emptyVarSpace rebind id + + // select a.Name; ... + // distinct; ... + // + // Process the rest of the clauses + | None -> + if maintainsVarSpace || maintainsVarSpaceUsingBind then + consumeClauses varSpace dataCompAfterOp contExpr maintainsVarSpaceUsingBind + else + consumeClauses emptyVarSpace dataCompAfterOp contExpr false + + // No more custom operator clauses in compClausesExpr, but there may be clauses like join, yield etc. + // Bind/iterate the dataCompPrior and use compClausesExpr as the body. + | _ -> + // Rebind using either for ... or let!.... + let rebind = + if lastUsesBind then + SynExpr.LetOrUseBang(NoSequencePointAtLetBinding, false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) + else + SynExpr.ForEach (NoSequencePointAtForLoop, SeqExprOnly false, false, varSpacePat, dataCompPrior, compClausesExpr, compClausesExpr.Range) + + trans true q varSpace rebind id + + // Now run the consumeClauses + Some (consumeClauses varSpace dataCompPriorToOp comp false) + | SynExpr.Sequential (sp, true, innerComp1, innerComp2, m) -> // Check for 'where x > y' and other mis-applications of infix operators. If detected, give a good error message, and just ignore innerComp1 diff --git a/tests/fsharp/typecheck/sigs/neg60.bsl b/tests/fsharp/typecheck/sigs/neg60.bsl index 9e05fc389f..21e5c3951b 100644 --- a/tests/fsharp/typecheck/sigs/neg60.bsl +++ b/tests/fsharp/typecheck/sigs/neg60.bsl @@ -55,8 +55,6 @@ neg60.fs(65,19,65,24): typecheck error FS3087: The custom operation 'where' refe neg60.fs(65,19,65,24): typecheck error FS3087: The custom operation 'where' refers to a method which is overloaded. The implementations of custom operations may not be overloaded. -neg60.fs(65,19,65,24): typecheck error FS3087: The custom operation 'where' refers to a method which is overloaded. The implementations of custom operations may not be overloaded. - neg60.fs(65,19,65,38): typecheck error FS0001: This expression was expected to have type 'bool' but here has type diff --git a/tests/fsharp/typecheck/sigs/neg61.bsl b/tests/fsharp/typecheck/sigs/neg61.bsl index 2811c3198e..accf054328 100644 --- a/tests/fsharp/typecheck/sigs/neg61.bsl +++ b/tests/fsharp/typecheck/sigs/neg61.bsl @@ -9,7 +9,10 @@ neg61.fs(22,13,22,16): typecheck error FS3145: This is not a known query operato neg61.fs(22,13,22,16): typecheck error FS0039: The value or constructor 'zip' is not defined. -neg61.fs(26,13,26,19): typecheck error FS3099: 'select' 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. +neg61.fs(25,13,25,28): typecheck error FS0001: This expression was expected to have type + 'Linq.QuerySource<'a,'b> * ('a -> 'c)' +but here has type + 'Linq.QuerySource' neg61.fs(30,13,30,16): typecheck error FS3145: This is not a known query operator. Query operators are identifiers such as 'select', 'where', 'sortBy', 'thenBy', 'groupBy', 'groupValBy', 'join', 'groupJoin', 'sumBy' and 'averageBy', defined using corresponding methods on the 'QueryBuilder' type. @@ -81,7 +84,10 @@ neg61.fs(148,20,148,23): typecheck error FS3147: This 'let' definition may not b neg61.fs(156,21,156,22): typecheck error FS3147: This 'let' definition may not be used in a query. Only simple value definitions may be used in queries. -neg61.fs(171,13,171,18): typecheck error FS3099: 'sumBy' 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. +neg61.fs(170,13,170,32): typecheck error FS0001: This expression was expected to have type + 'Linq.QuerySource<'a,'b> * ('a -> 'c)' +but here has type + 'Linq.QuerySource' neg61.fs(174,22,174,23): typecheck error FS0041: No overloads match for method 'Source'. diff --git a/tests/fsharp/typecheck/sigs/neg87.bsl b/tests/fsharp/typecheck/sigs/neg87.bsl index 5359647ec2..20e2b49f34 100644 --- a/tests/fsharp/typecheck/sigs/neg87.bsl +++ b/tests/fsharp/typecheck/sigs/neg87.bsl @@ -36,5 +36,3 @@ neg87.fsx(14,5,14,10): typecheck error FS3087: The custom operation 'test2' refe neg87.fsx(14,5,14,10): typecheck error FS3087: The custom operation 'test2' refers to a method which is overloaded. The implementations of custom operations may not be overloaded. neg87.fsx(14,5,14,10): typecheck error FS3087: The custom operation 'test2' refers to a method which is overloaded. The implementations of custom operations may not be overloaded. - -neg87.fsx(14,5,14,10): typecheck error FS3087: The custom operation 'test2' refers to a method which is overloaded. The implementations of custom operations may not be overloaded. diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy01.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy01.fs index 9e338f9121..9ed2865a7e 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy01.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy01.fs @@ -1,7 +1,7 @@ // #Conformance #DataExpressions #Query // DevDiv:179889, groupValBy was hard to use with poor diagnostics -//'groupValBy' is used with an incorrect number of arguments\. This is a custom operation in this query or computation expression\. Expected 2 argument\(s\), but given 0\.$ -//'groupValBy' is used with an incorrect number of arguments\. This is a custom operation in this query or computation expression\. Expected 2 argument\(s\), but given 1\.$ +//This expression was expected to have type. 'Linq\.QuerySource\<'a,'b\> \* \('a \-\> 'c\) \* \('a \-\> 'd\)' .but here has type. 'Linq\.QuerySource\' +//The member or object constructor 'GroupValBy' takes 3 argument\(s\) but is here given 2\. The required signature is 'member Linq\.QueryBuilder\.GroupValBy \: source\:Linq\.QuerySource\<'T,'Q\> \* resultSelector\:\('T \-\> 'Value\) \* keySelector\:\('T \-\> 'Key\) \-\> Linq\.QuerySource\,'Q\> when 'Key \: equality'\.$ let q1 = query { diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy02.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy02.fs index bc604c6f5a..434230874b 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy02.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_BadGroupValBy02.fs @@ -1,6 +1,6 @@ // #Conformance #DataExpressions #Query // DevDiv:210830, groupValBy with poor diagnostics -//This expression was expected to have type.+'System\.Linq\.IGrouping<'a,'b>'.+but here has type.+'unit' +//The member or object constructor 'GroupValBy' takes 3 argument\(s\) but is here given 4\. The required signature is 'member Linq\.QueryBuilder\.GroupValBy \: source\:Linq\.QuerySource\<'T,'Q\> \* resultSelector\:\('T \-\> 'Value\) \* keySelector\:\('T \-\> 'Key\) \-\> Linq\.QuerySource\,'Q\> when 'Key \: equality'\. let words = ["blueberry"; "chimpanzee"; ] let wordGroups = diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_WhitespaceErrors01.fs b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_WhitespaceErrors01.fs index efc3d64dbb..a9dd25f517 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_WhitespaceErrors01.fs +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/E_WhitespaceErrors01.fs @@ -1,6 +1,6 @@ // #Conformance #DataExpressions #Query // DevDiv:188241, just baselines errors around spacing -//'where' 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\.$ +//The member or object constructor 'Where' takes 2 argument\(s\) but is here given 4\. The required signature is 'member Linq\.QueryBuilder\.Where : source:Linq\.QuerySource<'T,'Q> \* predicate:\('T \-> bool\) \-> Linq\.QuerySource<'T,'Q>'\.$ let q1 = // no errors query { diff --git a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/env.lst b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/env.lst index 58c79d9641..63cfbf8bd7 100644 --- a/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/env.lst +++ b/tests/fsharpqa/Source/Conformance/Expressions/DataExpressions/QueryExpressions/env.lst @@ -13,7 +13,7 @@ NoMT SOURCE=ForWhereJoin01.fs SCFLAGS="--test:ErrorRange NoMT SOURCE=E_WhitespaceErrors01.fs SCFLAGS="--test:ErrorRanges" # E_WhitespaceErrors01.fs NoMT SOURCE=E_WhitespaceErrors02.fs SCFLAGS="--test:ErrorRanges" # E_WhitespaceErrors02.fs NoMT SOURCE=E_WhitespaceErrors03.fs SCFLAGS="--test:ErrorRanges" # E_WhitespaceErrors03.fs -NoMT SOURCE=E_BadGroupValBy01.fs SCFLAGS="--test:ErrorRanges" # E_BadGroupValBy01.fs +NoMT SOURCE=E_BadGroupValBy01.fs SCFLAGS="--test:ErrorRanges --flaterrors" # E_BadGroupValBy01.fs NoMT SOURCE=E_BadGroupValBy02.fs SCFLAGS="--test:ErrorRanges --flaterrors" # E_BadGroupValBy02.fs NoMT SOURCE=E_FirstOrDefaultWithNulls01.fs SCFLAGS="--test:ErrorRanges -r:Utils.dll" # E_FirstOrDefaultWithNulls01.fs