From d6fa56ef9fe746fd19e242831d75c3e2b6080fee Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Wed, 24 Sep 2025 04:28:02 +0300 Subject: [PATCH 1/5] wip --- src/Compiler/Driver/CompilerDiagnostics.fs | 35 ++++++----- src/Compiler/Symbols/FSharpDiagnostic.fs | 3 + .../Types/RecordTypes/RecordTypes.fs | 2 +- .../IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs | 2 +- .../ExtendedDiagnosticDataTests.fs | 60 ++++++++++++++++++- 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index d2a64521648..35bfab298dc 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -733,21 +733,15 @@ type Exception with if m.StartLine <> m2.StartLine then os.AppendString(SeeAlsoE().Format(stringOfRange m)) - | ConstraintSolverTypesNotInEqualityRelation(denv, (TType_measure _ as ty1), (TType_measure _ as ty2), m, m2, _) -> - // REVIEW: consider if we need to show _cxs (the type parameter constraints) - let ty1, ty2, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 - - os.AppendString(ConstraintSolverTypesNotInEqualityRelation1E().Format ty1 ty2) - - if m.StartLine <> m2.StartLine then - os.AppendString(SeeAlsoE().Format(stringOfRange m)) - | ConstraintSolverTypesNotInEqualityRelation(denv, ty1, ty2, m, m2, contextInfo) -> // REVIEW: consider if we need to show _cxs (the type parameter constraints) - let ty1, ty2, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 + let ty1str, ty2str, _cxs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 - OutputTypesNotInEqualityRelationContextInfo contextInfo ty1 ty2 m os (fun _ -> - os.AppendString(ConstraintSolverTypesNotInEqualityRelation2E().Format ty1 ty2)) + match ty1, ty2 with + | TType_measure _, TType_measure _ -> os.AppendString(ConstraintSolverTypesNotInEqualityRelation1E().Format ty1str ty2str) + | _ -> + OutputTypesNotInEqualityRelationContextInfo contextInfo ty1str ty2str m os (fun _ -> + os.AppendString(ConstraintSolverTypesNotInEqualityRelation2E().Format ty1str ty2str)) if m.StartLine <> m2.StartLine then os.AppendString(SeeAlsoE().Format(stringOfRange m)) @@ -816,11 +810,20 @@ type Exception with os.AppendString(SeeAlsoE().Format(stringOfRange m1)) | ErrorFromAddingTypeEquation(g, denv, ty1, ty2, e, _) -> - if not (typeEquiv g ty1 ty2) then - let ty1, ty2, tpcs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 + let e = + if not (typeEquiv g ty1 ty2) then + let ty1, ty2, tpcs = NicePrint.minimalStringsOfTwoTypes denv ty1 ty2 - if ty1 <> ty2 + tpcs then - os.AppendString(ErrorFromAddingTypeEquation2E().Format ty1 ty2 tpcs) + if ty1 <> ty2 + tpcs then + os.AppendString(ErrorFromAddingTypeEquation2E().Format ty1 ty2 tpcs) + + e + + else + match e with + | ConstraintSolverTypesNotInEqualityRelation(env, ty1b, ty2b, m, m2, contextInfo) when typeEquiv g ty2 ty2b -> + ConstraintSolverTypesNotInEqualityRelation(env, ty2b, ty1b, m, m2, contextInfo) + | _ -> e e.Output(os, suggestNames) diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fs b/src/Compiler/Symbols/FSharpDiagnostic.fs index 8854e9eea7e..666e0bd446d 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fs +++ b/src/Compiler/Symbols/FSharpDiagnostic.fs @@ -176,6 +176,7 @@ type FSharpDiagnostic(m: range, severity: FSharpDiagnosticSeverity, message: str | Some symbolEnv -> match diagnostic.Exception with + | ErrorFromAddingConstraint(displayEnv, ConstraintSolverTypesNotInEqualityRelation(_, actualType, expectedType, _, _, contextInfo), _) | ErrorFromAddingTypeEquation(_, displayEnv, expectedType, actualType, ConstraintSolverTupleDiffLengths(contextInfo = contextInfo), _) | ErrorsFromAddingSubsumptionConstraint(_, displayEnv, expectedType, actualType, _, contextInfo, _) -> let context = DiagnosticContextInfo.From(contextInfo) @@ -187,6 +188,8 @@ type FSharpDiagnostic(m: range, severity: FSharpDiagnosticSeverity, message: str ty1, ty2 elif not (typeEquiv g ty1 ty2) then ty1, ty2 + elif typeEquiv g ty1 ty2b then + ty1b, ty2b else ty2b, ty1b let context = DiagnosticContextInfo.From(contextInfo) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/RecordTypes.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/RecordTypes.fs index bebf3652605..da18bda2f33 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/RecordTypes.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/RecordTypes.fs @@ -116,7 +116,7 @@ module RecordTypes = (Warning 464, Line 15, Col 22, Line 15, Col 28, "This code is less generic than indicated by its annotations. A unit-of-measure specified using '_' has been determined to be '1', i.e. dimensionless. Consider making the code generic, or removing the use of '_'.") (Warning 464, Line 15, Col 35, Line 15, Col 42, "This code is less generic than indicated by its annotations. A unit-of-measure specified using '_' has been determined to be '1', i.e. dimensionless. Consider making the code generic, or removing the use of '_'.") (Error 5, Line 17, Col 1, Line 17, Col 5, "This field is not mutable") - (Error 1, Line 17, Col 16, Line 17, Col 22, "The type 'decimal' does not match the type 'float'") + (Error 1, Line 17, Col 16, Line 17, Col 22, "The type 'float' does not match the type 'decimal'") (Error 5, Line 18, Col 1, Line 18, Col 5, "This field is not mutable") (Error 1, Line 18, Col 16, Line 18, Col 21, "This expression was expected to have type\n 'float' \nbut here has type\n 'decimal' ") ] diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs index bfa44185a1c..32c16b46e04 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs @@ -1145,7 +1145,7 @@ module StaticAbstractBug = |> compile |> shouldFail |> withDiagnostics [ - (Error 1, Line 14, Col 41, Line 14, Col 42, "The type 'bool' does not match the type 'int'") + (Error 1, Line 14, Col 41, Line 14, Col 42, "The type 'int' does not match the type 'bool'") (Error 1, Line 16, Col 32, Line 16, Col 33, "This expression was expected to have type 'bool' but here has type diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index f3448e09440..6a90a90ee7a 100755 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -12,7 +12,7 @@ let inline checkDiagnosticData (check: 'a -> unit) (checkResults: 'b when 'b: (member Diagnostics: FSharpDiagnostic[])) = match checkResults.Diagnostics |> Array.tryFind (fun d -> d.ErrorNumber = diagnosticNumber) with - | None -> failwith "Expected diagnostic not found" + | None -> failwith $"Expected diagnostic (number {diagnosticNumber}) not found" | Some diagnostic -> Assert.Equal(message, diagnostic.Message) @@ -20,6 +20,7 @@ let inline checkDiagnosticData | Some(:? 'a as data) -> check data | _ -> failwith "Expected diagnostic extended data not found" + checkResults [] let ``TypeMismatchDiagnosticExtendedData 01`` () = @@ -187,7 +188,62 @@ let f2 (x: inref<'T>) = f1 &x Assert.Equal("outref<'T>", typeMismatch.ExpectedType.Format(displayContext)) Assert.Equal("inref<'T>", typeMismatch.ActualType.Format(displayContext))) -[] +[] +let ``TypeMismatchDiagnosticExtendedData 11`` () = + FSharp """ +type T() = + static member P1 = T.P2 + 1 + static member P2 = "" +""" + |> typecheckResults + // static member P1 = T.P2 ->+<- 1 + |> checkDiagnosticData + (43, "The type 'int' does not match the type 'string'") + (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> + let displayContext = typeMismatch.DisplayContext + Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) + Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) + Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + + // static member P2 = ->""<- + |> checkDiagnosticData + (1, "The type 'string' does not match the type 'int'") + (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> + let displayContext = typeMismatch.DisplayContext + Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) + Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) + Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + +[] +let ``TypeMismatchDiagnosticExtendedData 12`` () = + FSharp """ +let x: string = 1 + 1 +""" + |> typecheckResults + |> checkDiagnosticData + (1, "The type 'int' does not match the type 'string'") + (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> + let displayContext = typeMismatch.DisplayContext + Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) + Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) + Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + +[] +let ``TypeMismatchDiagnosticExtendedData 13`` () = + FSharp """ +let x: string -> string = id +let y: unit -> string = x +""" + |> typecheckResults + |> checkDiagnosticData + (1, "Type mismatch. Expecting a\n 'unit -> string' \nbut given a\n 'string -> string' \nThe type 'string' does not match the type 'unit'") + (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> + let displayContext = typeMismatch.DisplayContext + Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) + Assert.Equal("unit -> string", typeMismatch.ExpectedType.Format(displayContext)) + Assert.Equal("string -> string", typeMismatch.ActualType.Format(displayContext))) + +[] [] [] let ``ArgumentsInSigAndImplMismatchExtendedData 01`` useTransparentCompiler = From cb0464677e345cd02d0a040019cd61c301204a79 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Wed, 24 Sep 2025 18:52:27 +0300 Subject: [PATCH 2/5] wip --- src/Compiler/Driver/CompilerDiagnostics.fs | 6 + .../ExtendedDiagnosticDataTests.fs | 202 +++++++++--------- 2 files changed, 102 insertions(+), 106 deletions(-) diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 35bfab298dc..c5a04657a72 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -820,6 +820,12 @@ type Exception with e else + // Fix for https://github.com/dotnet/fsharp/issues/18905 + // If ty1 = ty2 after the type solving, then ty2 holds an actual type. + // The order of expected and actual types in ConstraintSolverTypesNotInEqualityRelation can be arbitrary + // due to type solving logic. + // If ty1 = ty2 = ty2b, it means ty2b is also an actual type, and it needs to be swapped with ty1b + // to be correctly used in the type mismatch error message based on ConstraintSolverTypesNotInEqualityRelation match e with | ConstraintSolverTypesNotInEqualityRelation(env, ty1b, ty2b, m, m2, contextInfo) when typeEquiv g ty2 ty2b -> ConstraintSolverTypesNotInEqualityRelation(env, ty2b, ty1b, m, m2, contextInfo) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs index 6a90a90ee7a..5d86b6883c7 100755 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ExtendedDiagnosticDataTests.fs @@ -4,9 +4,15 @@ open FSharp.Compiler.Text open FSharp.Compiler.Diagnostics open FSharp.Compiler.Diagnostics.ExtendedData +open FSharp.Test.Assert open FSharp.Test.Compiler open Xunit +let checkTypes (diagnosticData: TypeMismatchDiagnosticExtendedData) expected actual = + {| ExpectedType = expected; ActualType = actual |} + |> shouldBe {| ExpectedType = diagnosticData.ExpectedType.Format(diagnosticData.DisplayContext) + ActualType = diagnosticData.ActualType.Format(diagnosticData.DisplayContext) |} + let inline checkDiagnosticData (diagnosticNumber, message) (check: 'a -> unit) @@ -21,6 +27,28 @@ let inline checkDiagnosticData | _ -> failwith "Expected diagnostic extended data not found" checkResults + +let inline checkExpectedActualTypesInContext + diagnosticNumber + expectedType actualType + message + context + checkResults = + + checkResults + |> checkDiagnosticData (diagnosticNumber, message) + (fun (data: TypeMismatchDiagnosticExtendedData) -> + checkTypes data expectedType actualType + Assert.Equal(context, data.ContextInfo)) + +let inline checkExpectedActualTypes + diagnosticNumber + expectedType actualType + message + checkResults = + + checkResults + |> checkExpectedActualTypesInContext diagnosticNumber expectedType actualType message DiagnosticContextInfo.NoContext [] let ``TypeMismatchDiagnosticExtendedData 01`` () = @@ -28,13 +56,10 @@ let ``TypeMismatchDiagnosticExtendedData 01`` () = let x, y, z = 1, 2 """ |> typecheckResults - |> checkDiagnosticData - (1, "Type mismatch. Expecting a tuple of length 3 of type\n 'a * 'b * 'c \nbut given a tuple of length 2 of type\n int * int \n") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - let displayContext = typeMismatch.DisplayContext - Assert.Equal("obj * obj * obj", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int * int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "obj * obj * obj" + "int * int" + "Type mismatch. Expecting a tuple of length 3 of type\n 'a * 'b * 'c \nbut given a tuple of length 2 of type\n int * int \n" [] let ``TypeMismatchDiagnosticExtendedData 02`` () = @@ -42,13 +67,10 @@ let ``TypeMismatchDiagnosticExtendedData 02`` () = let x, y = 1 """ |> typecheckResults - |> checkDiagnosticData - (1, "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'int' ") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("obj * obj", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "obj * obj" + "int" + "This expression was expected to have type\n ''a * 'b' \nbut here has type\n 'int' " [] let ``TypeMismatchDiagnosticExtendedData 03`` () = @@ -56,13 +78,11 @@ let ``TypeMismatchDiagnosticExtendedData 03`` () = if true then 5 """ |> typecheckResults - |> checkDiagnosticData - (1, "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'int'.") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.OmittedElseBranch, typeMismatch.ContextInfo) - Assert.Equal("unit", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypesInContext 1 + "unit" + "int" + "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'int'." + DiagnosticContextInfo.OmittedElseBranch [] let ``TypeMismatchDiagnosticExtendedData 04`` () = @@ -70,13 +90,10 @@ let ``TypeMismatchDiagnosticExtendedData 04`` () = 1 :> string """ |> typecheckResults - |> checkDiagnosticData - (193, "Type constraint mismatch. The type \n 'int' \nis not compatible with type\n 'string' \n") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 193 + "string" + "int" + "Type constraint mismatch. The type \n 'int' \nis not compatible with type\n 'string' \n" [] let ``TypeMismatchDiagnosticExtendedData 05`` () = @@ -86,13 +103,11 @@ match 0 with | 1 -> "a" """ |> typecheckResults - |> checkDiagnosticData - (1, "All branches of a pattern match expression must return values implicitly convertible to the type of the first branch, which here is 'int'. This branch returns a value of type 'string'.") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.FollowingPatternMatchClause, typeMismatch.ContextInfo) - Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypesInContext 1 + "int" + "string" + "All branches of a pattern match expression must return values implicitly convertible to the type of the first branch, which here is 'int'. This branch returns a value of type 'string'." + DiagnosticContextInfo.FollowingPatternMatchClause //TODO: FollowingPatternMatchClause should be provided for type equation diagnostics come from a return type only [] @@ -107,13 +122,11 @@ match 0 with 1 """ |> typecheckResults - |> checkDiagnosticData - (1, "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.FollowingPatternMatchClause, typeMismatch.ContextInfo) //Should be NoContext - Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypesInContext 1 + "int" + "string" + "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " + DiagnosticContextInfo.FollowingPatternMatchClause //Should be NoContext [] let ``TypeMismatchDiagnosticExtendedData 06`` () = @@ -122,13 +135,11 @@ let _: bool = if true then "a" else "b" """ |> typecheckResults - |> checkDiagnosticData - (1, "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'.") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.IfExpression, typeMismatch.ContextInfo) - Assert.Equal("bool", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypesInContext 1 + "bool" + "string" + "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'." + DiagnosticContextInfo.IfExpression [] let ``TypeMismatchDiagnosticExtendedData 07`` () = @@ -136,13 +147,11 @@ let ``TypeMismatchDiagnosticExtendedData 07`` () = if true then 1 else "a" """ |> typecheckResults - |> checkDiagnosticData - (1, "All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is 'int'. This branch returns a value of type 'string'.") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.ElseBranchResult, typeMismatch.ContextInfo) - Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypesInContext 1 + "int" + "string" + "All branches of an 'if' expression must return values implicitly convertible to the type of the first branch, which here is 'int'. This branch returns a value of type 'string'." + DiagnosticContextInfo.ElseBranchResult [] let ``TypeMismatchDiagnosticExtendedData 08`` () = @@ -151,13 +160,10 @@ type R = { Field1: int } let f (x: R) = "" + x.Field1 """ |> typecheckResults - |> checkDiagnosticData - (1, "The type 'int' does not match the type 'string'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "string" + "int" + "The type 'int' does not match the type 'string'" [] let ``TypeMismatchDiagnosticExtendedData 09`` () = @@ -165,13 +171,10 @@ let ``TypeMismatchDiagnosticExtendedData 09`` () = let x: string = 1 """ |> typecheckResults - |> checkDiagnosticData - (1, "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "string" + "int" + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " [] let ``TypeMismatchDiagnosticExtendedData 10`` () = @@ -180,13 +183,12 @@ let f1 (x: outref<'T>) = 1 let f2 (x: inref<'T>) = f1 &x """ |> typecheckResults - |> checkDiagnosticData - (1, "Type mismatch. Expecting a\n 'outref<'T>' \nbut given a\n 'inref<'T>' \nThe type 'ByRefKinds.Out' does not match the type 'ByRefKinds.In'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("outref<'T>", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("inref<'T>", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "outref<'T>" + "inref<'T>" + // TODO: wrong additional generic parameters mismatch message, + // should be "The type 'ByRefKinds.In' does not match the type 'ByRefKinds.Out'" + "Type mismatch. Expecting a\n 'outref<'T>' \nbut given a\n 'inref<'T>' \nThe type 'ByRefKinds.Out' does not match the type 'ByRefKinds.In'" [] let ``TypeMismatchDiagnosticExtendedData 11`` () = @@ -197,22 +199,16 @@ type T() = """ |> typecheckResults // static member P1 = T.P2 ->+<- 1 - |> checkDiagnosticData - (43, "The type 'int' does not match the type 'string'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 43 + "string" + "int" + "The type 'int' does not match the type 'string'" // static member P2 = ->""<- - |> checkDiagnosticData - (1, "The type 'string' does not match the type 'int'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("int", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "int" + "string" + "The type 'string' does not match the type 'int'" [] let ``TypeMismatchDiagnosticExtendedData 12`` () = @@ -220,28 +216,22 @@ let ``TypeMismatchDiagnosticExtendedData 12`` () = let x: string = 1 + 1 """ |> typecheckResults - |> checkDiagnosticData - (1, "The type 'int' does not match the type 'string'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("int", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "string" + "int" + "The type 'int' does not match the type 'string'" [] let ``TypeMismatchDiagnosticExtendedData 13`` () = FSharp """ let x: string -> string = id -let y: unit -> string = x +let y: unit -> string = x """ |> typecheckResults - |> checkDiagnosticData - (1, "Type mismatch. Expecting a\n 'unit -> string' \nbut given a\n 'string -> string' \nThe type 'string' does not match the type 'unit'") - (fun (typeMismatch: TypeMismatchDiagnosticExtendedData) -> - let displayContext = typeMismatch.DisplayContext - Assert.Equal(DiagnosticContextInfo.NoContext, typeMismatch.ContextInfo) - Assert.Equal("unit -> string", typeMismatch.ExpectedType.Format(displayContext)) - Assert.Equal("string -> string", typeMismatch.ActualType.Format(displayContext))) + |> checkExpectedActualTypes 1 + "unit -> string" + "string -> string" + "Type mismatch. Expecting a\n 'unit -> string' \nbut given a\n 'string -> string' \nThe type 'string' does not match the type 'unit'" [] [] From e08e414ec5f56f7a43e5164a0e646b6212127ab6 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Wed, 24 Sep 2025 18:54:50 +0300 Subject: [PATCH 3/5] nit --- src/Compiler/Symbols/FSharpDiagnostic.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Symbols/FSharpDiagnostic.fs b/src/Compiler/Symbols/FSharpDiagnostic.fs index 666e0bd446d..cafaf1c788b 100644 --- a/src/Compiler/Symbols/FSharpDiagnostic.fs +++ b/src/Compiler/Symbols/FSharpDiagnostic.fs @@ -188,7 +188,7 @@ type FSharpDiagnostic(m: range, severity: FSharpDiagnosticSeverity, message: str ty1, ty2 elif not (typeEquiv g ty1 ty2) then ty1, ty2 - elif typeEquiv g ty1 ty2b then + elif typeEquiv g ty2 ty2b then ty1b, ty2b else ty2b, ty1b From 0f620be8123e352252cfd346fdf9ec935f1aa426 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Wed, 24 Sep 2025 19:13:40 +0300 Subject: [PATCH 4/5] release notes --- docs/release-notes/.FSharp.Compiler.Service/10.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index 9160a6db309..28ecd40a0b5 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -35,6 +35,7 @@ * Checker: fix declaring type for abbreviated types extensions ([PR #18909](https://github.com/dotnet/fsharp/pull/18909)) * Caches: type subsumption cache key perf regression ([Issue #18925](https://github.com/dotnet/fsharp/issues/18925) [PR #18926](https://github.com/dotnet/fsharp/pull/18926)) * Ensure that line directives are applied to source identifiers (issue [#18908](https://github.com/dotnet/fsharp/issues/18908), PR [#18918](https://github.com/dotnet/fsharp/pull/18918)) +* Fix expected and actual types in ErrorFromAddingTypeEquation messages and extended diagnostic data. ([PR #18915](https://github.com/dotnet/fsharp/pull/18915)) ### Changed * Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645)) From d338b9f06e7a19114c68babe50027e2a34398486 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Wed, 24 Sep 2025 19:20:20 +0300 Subject: [PATCH 5/5] nit --- docs/release-notes/.FSharp.Compiler.Service/10.0.100.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index 28ecd40a0b5..4bf5f4b80f1 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -35,7 +35,7 @@ * Checker: fix declaring type for abbreviated types extensions ([PR #18909](https://github.com/dotnet/fsharp/pull/18909)) * Caches: type subsumption cache key perf regression ([Issue #18925](https://github.com/dotnet/fsharp/issues/18925) [PR #18926](https://github.com/dotnet/fsharp/pull/18926)) * Ensure that line directives are applied to source identifiers (issue [#18908](https://github.com/dotnet/fsharp/issues/18908), PR [#18918](https://github.com/dotnet/fsharp/pull/18918)) -* Fix expected and actual types in ErrorFromAddingTypeEquation messages and extended diagnostic data. ([PR #18915](https://github.com/dotnet/fsharp/pull/18915)) +* Fix expected and actual types in ErrorFromAddingTypeEquation message and extended diagnostic data. ([PR #18915](https://github.com/dotnet/fsharp/pull/18915)) ### Changed * Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645))