From 4a244403304925e6c4e6802281f8841bb1d6b04a Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 14 Jun 2022 22:50:08 +0100 Subject: [PATCH 01/30] First pass at a warning when obj is inferred --- src/Compiler/Checking/TypeRelations.fs | 39 +++++++++++-------- src/Compiler/FSComp.txt | 3 +- src/Compiler/xlf/FSComp.txt.de.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 +++ .../ConstraintSolver/ObjInference.fs | 16 ++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + 16 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index 6cbb7547f30..2557dc85dff 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -133,50 +133,55 @@ let rec TypeFeasiblySubsumesType ndeep g amap m ty1 canCoerce ty2 = /// Here x gets a generalized type "list<'T>". let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = let m = tp.Range - let max, m = + let (max, isRefined), m = let initial = match tp.Kind with - | TyparKind.Type -> g.obj_ty + | TyparKind.Type -> g.obj_ty | TyparKind.Measure -> TType_measure Measure.One // Loop through the constraints computing the lub - ((initial, m), tp.Constraints) ||> List.fold (fun (maxSoFar, _) tpc -> + (((initial, false), m), tp.Constraints) ||> List.fold (fun ((maxSoFar, haveRefined), _) tpc -> let join m x = - if TypeFeasiblySubsumesType 0 g amap m x CanCoerce maxSoFar then maxSoFar - elif TypeFeasiblySubsumesType 0 g amap m maxSoFar CanCoerce x then x - else errorR(Error(FSComp.SR.typrelCannotResolveImplicitGenericInstantiation((DebugPrint.showType x), (DebugPrint.showType maxSoFar)), m)); maxSoFar + if TypeFeasiblySubsumesType 0 g amap m x CanCoerce maxSoFar then maxSoFar, haveRefined + elif TypeFeasiblySubsumesType 0 g amap m maxSoFar CanCoerce x then x, true + else errorR(Error(FSComp.SR.typrelCannotResolveImplicitGenericInstantiation((DebugPrint.showType x), (DebugPrint.showType maxSoFar)), m)); maxSoFar, haveRefined // Don't continue if an error occurred and we set the value eagerly - if tp.IsSolved then maxSoFar, m else + if tp.IsSolved then (maxSoFar, haveRefined), m else match tpc with | TyparConstraint.CoercesTo(x, m) -> join m x, m | TyparConstraint.MayResolveMember(_traitInfo, m) -> - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.SimpleChoice(_, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInPrintf(), m)) - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.SupportsNull m -> - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.SupportsComparison m -> join m g.mk_IComparable_ty, m | TyparConstraint.SupportsEquality m -> - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.IsEnum(_, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInEnum(), m)) - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.IsDelegate(_, _, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInDelegate(), m)) - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.IsNonNullableStruct m -> join m g.int_ty, m | TyparConstraint.IsUnmanaged m -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInUnmanaged(), m)) - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.RequiresDefaultConstructor m -> - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.IsReferenceType m -> - maxSoFar, m + (maxSoFar, haveRefined), m | TyparConstraint.DefaultsTo(_priority, _ty, m) -> - maxSoFar, m) + (maxSoFar, haveRefined), m) + match tp.Kind with + | TyparKind.Type -> + if not isRefined then + warning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) + | _ -> () max, m let ChooseTyparSolution g amap tp = diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 513117632cc..a4141eefa0d 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1643,4 +1643,5 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form" 3520,invalidXmlDocPosition,"XML comment is not placed on a valid language element." 3521,tcInvalidMemberDeclNameMissingOrHasParen,"Invalid member declaration. The name of the member is missing or has parentheses." 3522,tcAnonRecdDuplicateFieldId,"The field '%s' appears multiple times in this record expression." -3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type." \ No newline at end of file +3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type." +3524,typrelNeverRefinedAwayFromTop,"A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations." diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 57d1e5450ea..f3847db0217 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -857,6 +857,11 @@ Sie können die Schnittstelle "{0}" mit den beiden Instanziierungen "{1}" und "{2}" nicht implementieren, weil sie möglicherweise zusammengeführt werden. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. Der Typ "{0}" definiert nicht das Feld, den Konstruktor oder den Member "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 6a59b872c1b..f7b9c41dc36 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -857,6 +857,11 @@ No se puede implementar la interfaz "{0}" con ambas creaciones de instancias, "{1}" y "{2}", porque pueden unificarse. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. El tipo "{0}" no define el campo, constructor o miembro "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 7b64cc5b780..62e4f623acf 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -857,6 +857,11 @@ Vous ne pouvez pas implémenter l'interface '{0}' avec les deux instanciations '{1}' et '{2}', car elles peuvent s'unifier. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. Le type '{0}' ne définit pas le champ, le constructeur ou le membre '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 8a36ec3b730..99d7a6cafe2 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -857,6 +857,11 @@ Non è possibile implementare l'interfaccia '{0}' con le due creazioni di istanze '{1}' e '{2}' perché possono essere unificate. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. Il tipo '{0}' non definisce il campo, il costruttore o il membro '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 2509bc7585a..d24a16a9414 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -857,6 +857,11 @@ 統合している可能性があるため、'{1}' と '{2}' の 2 つのインスタンス化を含むインターフェイス '{0}' を実装することはできません。 + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. 型 '{0}' は、フィールド、コンストラクター、またはメンバー '{1}' を定義していません。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 8f209c3ada1..aa91d500a4f 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -857,6 +857,11 @@ '{1}' 및 '{2}' 인스턴스화가 포함된 '{0}' 인터페이스를 구현할 수 없습니다. 이 두 인스턴스화가 통합될 수 있기 때문입니다. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. '{0}' 형식은 '{1}' 필드, 생성자 또는 멤버를 정의하지 않습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9539f9af34f..f26933d36d0 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -857,6 +857,11 @@ Nie możesz zaimplementować interfejsu „{0}” przy użyciu dwóch wystąpień „{1}” i „{2}”, ponieważ mogą one się ujednolicić. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. Typ „{0}” nie definiuje pola, konstruktora lub składowej „{1}”. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 29bcce01250..6d08b72d4c8 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -857,6 +857,11 @@ Não é possível implementar a interface '{0}' com as duas instanciações '{1}' e '{2}' porque talvez elas sejam unificadas. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. O tipo '{0}' não define o campo, o construtor ou o membro '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 1fb3a354f86..8bde89c6425 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -857,6 +857,11 @@ Невозможно реализовать интерфейс "{0}" с двумя созданиями экземпляра "{1}" и "{2}", так как они могут быть объединены. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. Тип "{0}" не определяет поле, конструктор или член "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index d36bd224b9d..7f110a24fbb 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -857,6 +857,11 @@ '{1}' ve '{2}' örnek oluşturmaları birleşebileceğinden '{0}' arabirimini bunlarla birlikte uygulayamazsınız. + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. '{0}' türü; alanı, oluşturucuyu veya '{1}' üyesini tanımlamıyor. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index a1b2b225415..98ec87c0c2b 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -857,6 +857,11 @@ 你无法实现具有两个实例化“{1}”和“{2}”的接口“{0}”,因为它们可能会统一。 + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. 类型“{0}”未定义字段、构造函数或成员“{1}”。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index b8854a10e66..8234e96a53e 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -857,6 +857,11 @@ 因為 '{1}' 和 '{2}' 兩者的具現化可能整合,所以無法將其用於實作介面 '{0}'。 + + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + + The type '{0}' does not define the field, constructor or member '{1}'. 類型 '{0}' 未定義欄位、建構函式或成員 '{1}'。 diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs new file mode 100644 index 00000000000..cc6801a0ca7 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -0,0 +1,16 @@ +namespace FSharp.Compiler.ComponentTests.ConstraintSolver + +open Xunit +open FSharp.Test.Compiler + +module ObjInference = + + [] + let ``Inference of obj``() = + FSharp """ +let f() = ([] = []) + """ + |> withErrorRanges + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3524, Line 2, Col 17, Line 2, Col 19, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 562fbb40a88..2152aaba48a 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -156,6 +156,7 @@ + From ef5090f539b677a58c706b2e5daddadc148ae4a1 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 14 Jun 2022 23:14:50 +0100 Subject: [PATCH 02/30] Add more tests, some of which are failing --- .../ConstraintSolver/ObjInference.fs | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index cc6801a0ca7..0b627b61e00 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -5,12 +5,56 @@ open FSharp.Test.Compiler module ObjInference = - [] - let ``Inference of obj``() = - FSharp """ -let f() = ([] = []) - """ + let failureCases = + [ + "let f() = ([] = [])", 1, 17, 1, 19 + ] + |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + + [] + [] + let ``Inference of obj``(code: string, line1: int, col1: int, line2: int, col2: int) = + FSharp code |> withErrorRanges |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3524, Line 2, Col 17, Line 2, Col 19, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + |> withSingleDiagnostic (Warning 3524, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + + let nullSuccessCases = + [ + """System.Object.ReferenceEquals("hello", null)""" + """System.Object.ReferenceEquals("hello", (null: string))""" + """System.Object.ReferenceEquals(null, "hello")""" + """System.Object.ReferenceEquals((null: string), "hello")""" + ] + |> List.map Array.singleton + + [] + [] + let ``Don't warn on an explicit null``(expr: string) = + sprintf "%s |> ignore" expr + |> FSharp + |> typecheck + |> shouldSucceed + + [] + [] + let ``Don't warn inside quotations, explicit nulls``(expr: string) = + sprintf "<@ %s @> |> ignore" expr + |> FSharp + |> typecheck + |> shouldSucceed + + let quotationSuccessCases = + [ + "<@ List.map ignore [1;2;3] @>" + ] + |> List.map Array.singleton + + [] + [] + let ``Don't warn inside quotations``(expr: string) = + sprintf "%s |> ignore" expr + |> FSharp + |> typecheck + |> shouldSucceed From 9e1c8c2839d418fd9e38daa3cb885fa0fa7f31ad Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 14 Jun 2022 23:36:44 +0100 Subject: [PATCH 03/30] A couple more tests This incorporates the motivating examples from https://github.com/fsharp/fslang-suggestions/issues/885 . --- .../ConstraintSolver/ObjInference.fs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 0b627b61e00..bd16cfca8ee 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -11,15 +11,31 @@ module ObjInference = ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + let successCases = + [ + "let add x y = x + y" // inferred as int + "let f x = string x" // inferred as generic 'a -> string + "let f() = ([] = ([] : obj list))" // obj is inferred, but is annotated + "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated + ] + |> List.map Array.singleton + [] [] - let ``Inference of obj``(code: string, line1: int, col1: int, line2: int, col2: int) = + let ``Inference of obj is warned``(code: string, line1: int, col1: int, line2: int, col2: int) = FSharp code |> withErrorRanges |> typecheck |> shouldFail |> withSingleDiagnostic (Warning 3524, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + [] + [] + let ``Negative cases: inference of non-obj``(code: string) = + FSharp code + |> typecheck + |> shouldSucceed + let nullSuccessCases = [ """System.Object.ReferenceEquals("hello", null)""" From 4f0390c047ac6ffda8a18e80f4f7859d3558af7f Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 15 Jun 2022 21:33:24 +0100 Subject: [PATCH 04/30] Add test for simpler quotation case --- .../ConstraintSolver/ObjInference.fs | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index bd16cfca8ee..6d0b5e886e2 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -11,27 +11,29 @@ module ObjInference = ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + [] + [] + let ``Warning is emitted when top type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = + FSharp code + |> withErrorRanges + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3524, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + let successCases = [ "let add x y = x + y" // inferred as int "let f x = string x" // inferred as generic 'a -> string "let f() = ([] = ([] : obj list))" // obj is inferred, but is annotated "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated + """let x<[]'m> : int<'m> = failwith "" +let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covered by this warning ] |> List.map Array.singleton - [] - [] - let ``Inference of obj is warned``(code: string, line1: int, col1: int, line2: int, col2: int) = - FSharp code - |> withErrorRanges - |> typecheck - |> shouldFail - |> withSingleDiagnostic (Warning 3524, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") - [] [] - let ``Negative cases: inference of non-obj``(code: string) = + let ``Warning does not fire unless required``(code: string) = FSharp code |> typecheck |> shouldSucceed @@ -55,7 +57,7 @@ module ObjInference = [] [] - let ``Don't warn inside quotations, explicit nulls``(expr: string) = + let ``Don't warn on an explicit null, inside quotations``(expr: string) = sprintf "<@ %s @> |> ignore" expr |> FSharp |> typecheck @@ -69,8 +71,16 @@ module ObjInference = [] [] - let ``Don't warn inside quotations``(expr: string) = + let ``Don't warn inside quotations of acceptable code``(expr: string) = sprintf "%s |> ignore" expr |> FSharp |> typecheck |> shouldSucceed + + [] + let ``Warn when the error appears inside a quotation``() = + sprintf "<@ [] = [] @> |> ignore" + |> FSharp + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3524, Line 1, Col 9, Line 1, Col 11, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") From 4d4875956f42f943fc87c3c258f099600cd77dd9 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 15 Jun 2022 21:46:05 +0100 Subject: [PATCH 05/30] Turn diagnostic off by default --- docs/diagnostics.md | 11 ++++++++++- src/Compiler/Driver/CompilerDiagnostics.fs | 3 ++- src/Compiler/FSharp.Compiler.Service.fsproj | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 026e3b256ba..ed6739f815b 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -32,7 +32,7 @@ Adding or adjusting diagnostics emitted by the compiler is usually straightforwa 4. Use another search tool or a tool like Find All References / Find Usages to see where it's used in the compiler source code. 5. Set a breakpoint at the location in source you found. If you debug the compiler with the same steps, it should trigger the breakpoint you set. This verifies that the location you found is the one that emits the error or warning you want to improve. -From here, you can either simply update the error test, or you can use some of the information at the point in the source code you identified to see if there is more information to include in the error message. For example, if the error message doesn't contain information about the identifier the user is using incorrectly, you may be able to include the name of the identifier based on data the compiler has available at that stage of compilation. +From here, you can either simply update the error text, or you can use some of the information at the point in the source code you identified to see if there is more information to include in the error message. For example, if the error message doesn't contain information about the identifier the user is using incorrectly, you may be able to include the name of the identifier based on data the compiler has available at that stage of compilation. If you're including data from user code in an error message, it's important to also write a test that verifies the exact error message for a given string of F# code. @@ -55,3 +55,12 @@ Diagnostics must often format types. * When displaying multiple types in a comparative way, for example, two types that didn't match, you will want to display the minimal amount of infomation to convey the fact that the two types are different, for example, `NicePrint.minimalStringsOfTwoTypes`. * When displaying a type, you have the option of displaying the constraints implied by any type variables mentioned in the types, appended as `when ...`. For example, `NicePrint.layoutPrettifiedTypeAndConstraints`. + +## Localization + +The file `FSComp.txt` contains the canonical listing of diagnostic messages, but there are also `.xlf` localization files for various languages. +When changing `FSComp.txt`, you can automatically put placeholder entries in all these `.xlf` files by running `dotnet msbuild /t:UpdateXlf FSharp.sln`. + +## Enabling a warning or error by default + +The file `CompilerDiagnostics.fs` contains the function `IsWarningOrInfoEnabled`, which determines whether a given diagnostic is emitted. \ No newline at end of file diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index a26d232746f..006d07a33d2 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -365,7 +365,7 @@ let GetWarningLevel diagnostic = let IsWarningOrInfoEnabled (diagnostic, severity) n level specificWarnOn = List.contains n specificWarnOn || - // Some specific warnings/informational are never on by default, i.e. unused variable warnings + // Some specific warnings/informational are never on by default, e.g. unused variable warnings match n with | 1182 -> false // chkUnusedValue - off by default | 3180 -> false // abImplicitHeapAllocation - off by default @@ -376,6 +376,7 @@ let IsWarningOrInfoEnabled (diagnostic, severity) n level specificWarnOn = | 3389 -> false // tcBuiltInImplicitConversionUsed - off by default | 3390 -> false // xmlDocBadlyFormed - off by default | 3395 -> false // tcImplicitConversionUsedForMethodArg - off by default + | 3524 -> false // typrelNeverRefinedAwayFromTop - off by default | _ -> (severity = FSharpDiagnosticSeverity.Info) || (severity = FSharpDiagnosticSeverity.Warning diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index e996b6f4eba..5e8fd0ab499 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -22,6 +22,8 @@ $(OtherFlags) --warnon:3218 $(OtherFlags) --warnon:3390 + + $(OtherFlags) --warnon:3524 true $(IntermediateOutputPath)$(TargetFramework)\ $(IntermediateOutputPath)$(TargetFramework)\ From 5794221e584d9cfb68f272689744b5e9890da041 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 15 Jun 2022 22:29:39 +0100 Subject: [PATCH 06/30] Add a test which is failing, no idea why --- .../ConstraintSolver/ObjInference.fs | 10 ++++++++++ tests/FSharp.Test.Utilities/Compiler.fs | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 6d0b5e886e2..9b3e44a6c98 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -7,6 +7,9 @@ module ObjInference = let failureCases = [ + // TODO: for this case, we're definitely emitting the warning (according to the debugger), + // but somehow it's not showing up in the output? + """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 1, 1, 1 "let f() = ([] = [])", 1, 17, 1, 19 ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) @@ -16,6 +19,7 @@ module ObjInference = let ``Warning is emitted when top type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = FSharp code |> withErrorRanges + |> withWarnOn 3524 |> typecheck |> shouldFail |> withSingleDiagnostic (Warning 3524, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") @@ -28,6 +32,7 @@ module ObjInference = "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated """let x<[]'m> : int<'m> = failwith "" let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covered by this warning + "let a = 5 |> unbox in let b = a in ()" // explicit obj annotation ] |> List.map Array.singleton @@ -35,6 +40,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [] let ``Warning does not fire unless required``(code: string) = FSharp code + |> withWarnOn 3524 |> typecheck |> shouldSucceed @@ -52,6 +58,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Don't warn on an explicit null``(expr: string) = sprintf "%s |> ignore" expr |> FSharp + |> withWarnOn 3524 |> typecheck |> shouldSucceed @@ -60,6 +67,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Don't warn on an explicit null, inside quotations``(expr: string) = sprintf "<@ %s @> |> ignore" expr |> FSharp + |> withWarnOn 3524 |> typecheck |> shouldSucceed @@ -74,6 +82,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Don't warn inside quotations of acceptable code``(expr: string) = sprintf "%s |> ignore" expr |> FSharp + |> withWarnOn 3524 |> typecheck |> shouldSucceed @@ -81,6 +90,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Warn when the error appears inside a quotation``() = sprintf "<@ [] = [] @> |> ignore" |> FSharp + |> withWarnOn 3524 |> typecheck |> shouldFail |> withSingleDiagnostic (Warning 3524, Line 1, Col 9, Line 1, Col 11, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index a374f3d4475..69b750f382a 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -391,10 +391,10 @@ module rec Compiler = let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = + let withWarnOn (warning: int) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit - let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = + let withNoWarn (warning: int) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--nowarn:{warning}" ] "withNoWarn is only supported for F#" cUnit let withNoOptimize (cUnit: CompilationUnit) : CompilationUnit = From c09ac18cc0f6a59bf4e8dcc6e5bb498a9929099f Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Thu, 14 Jul 2022 12:19:36 +0100 Subject: [PATCH 07/30] Update text --- src/Compiler/FSComp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index a4141eefa0d..97b68a58ada 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1644,4 +1644,4 @@ reprStateMachineInvalidForm,"The state machine has an unexpected form" 3521,tcInvalidMemberDeclNameMissingOrHasParen,"Invalid member declaration. The name of the member is missing or has parentheses." 3522,tcAnonRecdDuplicateFieldId,"The field '%s' appears multiple times in this record expression." 3523,tcAnonRecdTypeDuplicateFieldId,"The field '%s' appears multiple times in this anonymous record type." -3524,typrelNeverRefinedAwayFromTop,"A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations." +3524,typrelNeverRefinedAwayFromTop,"A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\"." From 55fd658c4cc1ad9b62b06a7ec8191d76100473d2 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 29 Jul 2022 22:14:04 +0100 Subject: [PATCH 08/30] Fix up messages --- src/Compiler/xlf/FSComp.txt.cs.xlf | 5 +++++ src/Compiler/xlf/FSComp.txt.de.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.es.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.fr.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.it.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ja.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ko.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.pl.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.ru.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.tr.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 4 ++-- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 4 ++-- 13 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index d0a995aa3b0..4279e2c4f58 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -857,6 +857,11 @@ Rozhraní {0} nemůžete implementovat se dvěma instancemi {1} a {2}, protože by se mohly sjednotit. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + + The type '{0}' does not define the field, constructor or member '{1}'. Typ {0} nedefinuje pole, konstruktor ani člen {1}. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index f3847db0217..90621f4e104 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index f7b9c41dc36..079e1d1d390 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 62e4f623acf..afc1371c8c4 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 99d7a6cafe2..5bdc71c38e7 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index d24a16a9414..de961bb5af6 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index aa91d500a4f..2a3e6e4de6e 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index f26933d36d0..514fd9a0c0a 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 6d08b72d4c8..ef6c13ba4f3 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 8bde89c6425..20ec224380f 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 7f110a24fbb..ddd01465bbf 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 98ec87c0c2b..1b2ef829512 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 8234e96a53e..4deb216b9cd 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -858,8 +858,8 @@ - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. - A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations. + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3524\". From b15bd8bddc2c4c8b48d1d997f430bf1976b9dde7 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Sat, 30 Jul 2022 00:14:23 +0100 Subject: [PATCH 09/30] Restructure tests --- .../ConstraintSolver/ObjInference.fs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index af3697092d8..ca1f2239b2f 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -5,26 +5,30 @@ open FSharp.Test.Compiler module ObjInference = - let failureCases = + let message = "A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\"." + + let warningCases = [ // TODO: for this case, we're definitely emitting the warning (according to the debugger), // but somehow it's not showing up in the output? """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 1, 1, 1 "let f() = ([] = [])", 1, 17, 1, 19 + """System.Object.ReferenceEquals(null, "hello") |> ignore""", 1, 31, 1, 35 + """System.Object.ReferenceEquals("hello", null) |> ignore""", 1, 40, 1, 44 ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) [] - [] - let ``Warning is emitted when top type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = + [] + let ``Warning is emitted when type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = FSharp code |> withErrorRanges |> withWarnOn 3525 |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3525, Line line1, Col col1, Line line2, Col col2, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + |> withSingleDiagnostic (Warning 3525, Line line1, Col col1, Line line2, Col col2, message) - let successCases = + let noWarningCases = [ "let add x y = x + y" // inferred as int "let f x = string x" // inferred as generic 'a -> string @@ -37,24 +41,22 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> List.map Array.singleton [] - [] + [] let ``Warning does not fire unless required``(code: string) = FSharp code |> withWarnOn 3525 |> typecheck |> shouldSucceed - let nullSuccessCases = + let nullNoWarningCases = [ - """System.Object.ReferenceEquals("hello", null)""" """System.Object.ReferenceEquals("hello", (null: string))""" - """System.Object.ReferenceEquals(null, "hello")""" """System.Object.ReferenceEquals((null: string), "hello")""" ] |> List.map Array.singleton [] - [] + [] let ``Don't warn on an explicit null``(expr: string) = sprintf "%s |> ignore" expr |> FSharp @@ -63,7 +65,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> shouldSucceed [] - [] + [] let ``Don't warn on an explicit null, inside quotations``(expr: string) = sprintf "<@ %s @> |> ignore" expr |> FSharp @@ -71,14 +73,14 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> typecheck |> shouldSucceed - let quotationSuccessCases = + let quotationNoWarningCases = [ "<@ List.map ignore [1;2;3] @>" ] |> List.map Array.singleton [] - [] + [] let ``Don't warn inside quotations of acceptable code``(expr: string) = sprintf "%s |> ignore" expr |> FSharp @@ -88,9 +90,9 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [] let ``Warn when the error appears inside a quotation``() = - sprintf "<@ [] = [] @> |> ignore" + "<@ [] = [] @> |> ignore" |> FSharp |> withWarnOn 3525 |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3525, Line 1, Col 9, Line 1, Col 11, "A type was not refined away from `obj`, which may be unintended. Consider adding explicit type annotations.") + |> withSingleDiagnostic (Warning 3525, Line 1, Col 9, Line 1, Col 11, message) From 97ffb686e4c0e445185a1b9703c88e2cd5ff23ca Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Sat, 30 Jul 2022 00:18:30 +0100 Subject: [PATCH 10/30] Speculative commit to restore range information where required --- src/Compiler/Checking/FindUnsolved.fs | 131 +++++++++--------- .../ConstraintSolver/ObjInference.fs | 21 ++- 2 files changed, 86 insertions(+), 66 deletions(-) diff --git a/src/Compiler/Checking/FindUnsolved.fs b/src/Compiler/Checking/FindUnsolved.fs index 93f452abd7d..f02e7988ce2 100644 --- a/src/Compiler/Checking/FindUnsolved.fs +++ b/src/Compiler/Checking/FindUnsolved.fs @@ -28,15 +28,20 @@ type cenv = override _.ToString() = "" -/// Walk types, collecting type variables -let accTy cenv _env ty = +/// Walk types, collecting type variables. +/// The backupRange is attached best-effort to unsolved type parameters, for better reporting. +let accTy cenv _env (backupRange: Text.range) ty = let normalizedTy = tryNormalizeMeasureInType cenv.g ty (freeInType CollectTyparsNoCaching normalizedTy).FreeTypars |> Zset.iter (fun tp -> - if (tp.Rigidity <> TyparRigidity.Rigid) then - cenv.unsolved <- tp :: cenv.unsolved) + if (tp.Rigidity <> TyparRigidity.Rigid) then + let tp = + if tp.Range = Text.Range.range0 then + { tp with typar_id = Syntax.Ident(tp.typar_id.idText, backupRange) } + else tp + cenv.unsolved <- tp :: cenv.unsolved) -let accTypeInst cenv env tyargs = - tyargs |> List.iter (accTy cenv env) +let accTypeInst cenv env (backupRange: Text.range) tyargs = + tyargs |> List.iter (accTy cenv env backupRange) /// Walk expressions, collecting type variables let rec accExpr (cenv: cenv) (env: env) expr = @@ -52,24 +57,24 @@ let rec accExpr (cenv: cenv) (env: env) expr = accBind cenv env bind accExpr cenv env body - | Expr.Const (_, _, ty) -> - accTy cenv env ty + | Expr.Const (_, m, ty) -> + accTy cenv env m ty | Expr.Val (_v, _vFlags, _m) -> () - | Expr.Quote (ast, _, _, _m, ty) -> + | Expr.Quote (ast, _, _, m, ty) -> accExpr cenv env ast - accTy cenv env ty + accTy cenv env m ty - | Expr.Obj (_, ty, basev, basecall, overrides, iimpls, _m) -> - accTy cenv env ty + | Expr.Obj (_, ty, basev, basecall, overrides, iimpls, m) -> + accTy cenv env m ty accExpr cenv env basecall accMethods cenv env basev overrides - accIntfImpls cenv env basev iimpls + accIntfImpls cenv env basev m iimpls - | LinearOpExpr (_op, tyargs, argsHead, argLast, _m) -> + | LinearOpExpr (_op, tyargs, argsHead, argLast, m) -> // Note, LinearOpExpr doesn't include any of the "special" cases for accOp - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env argsHead // tailcall accExpr cenv env argLast @@ -77,9 +82,9 @@ let rec accExpr (cenv: cenv) (env: env) expr = | Expr.Op (c, tyargs, args, m) -> accOp cenv env (c, tyargs, args, m) - | Expr.App (f, fty, tyargs, argsl, _m) -> - accTy cenv env fty - accTypeInst cenv env tyargs + | Expr.App (f, fty, tyargs, argsl, m) -> + accTy cenv env m fty + accTypeInst cenv env m tyargs accExpr cenv env f accExprs cenv env argsl @@ -88,9 +93,9 @@ let rec accExpr (cenv: cenv) (env: env) expr = let ty = mkMultiLambdaTy cenv.g m argvs bodyTy accLambdas cenv env valReprInfo expr ty - | Expr.TyLambda (_, tps, _body, _m, bodyTy) -> + | Expr.TyLambda (_, tps, _body, m, bodyTy) -> let valReprInfo = ValReprInfo (ValReprInfo.InferTyparInfo tps, [], ValReprInfo.unnamedRetVal) - accTy cenv env bodyTy + accTy cenv env m bodyTy let ty = mkForallTyIfNeeded tps bodyTy accLambdas cenv env valReprInfo expr ty @@ -98,7 +103,7 @@ let rec accExpr (cenv: cenv) (env: env) expr = accExpr cenv env e1 | Expr.Match (_, _exprm, dtree, targets, m, ty) -> - accTy cenv env ty + accTy cenv env m ty accDTree cenv env dtree accTargets cenv env m ty targets @@ -106,18 +111,18 @@ let rec accExpr (cenv: cenv) (env: env) expr = accBinds cenv env binds accExpr cenv env e - | Expr.StaticOptimization (constraints, e2, e3, _m) -> + | Expr.StaticOptimization (constraints, e2, e3, m) -> accExpr cenv env e2 accExpr cenv env e3 constraints |> List.iter (function | TTyconEqualsTycon(ty1, ty2) -> - accTy cenv env ty1 - accTy cenv env ty2 + accTy cenv env m ty1 + accTy cenv env m ty2 | TTyconIsStruct(ty1) -> - accTy cenv env ty1) + accTy cenv env m ty1) - | Expr.WitnessArg (traitInfo, _m) -> - accTraitInfo cenv env traitInfo + | Expr.WitnessArg (traitInfo, m) -> + accTraitInfo cenv env m traitInfo | Expr.Link eref -> accExpr cenv env eref.Value @@ -128,49 +133,49 @@ let rec accExpr (cenv: cenv) (env: env) expr = and accMethods cenv env baseValOpt l = List.iter (accMethod cenv env baseValOpt) l -and accMethod cenv env _baseValOpt (TObjExprMethod(_slotsig, _attribs, _tps, vs, bodyExpr, _m)) = - vs |> List.iterSquared (accVal cenv env) +and accMethod cenv env _baseValOpt (TObjExprMethod(_slotsig, _attribs, _tps, vs, bodyExpr, m)) = + vs |> List.iterSquared (accVal cenv env m) accExpr cenv env bodyExpr -and accIntfImpls cenv env baseValOpt l = - List.iter (accIntfImpl cenv env baseValOpt) l +and accIntfImpls cenv env baseValOpt (backupRange: Text.range) l = + List.iter (accIntfImpl cenv env baseValOpt backupRange) l -and accIntfImpl cenv env baseValOpt (ty, overrides) = - accTy cenv env ty +and accIntfImpl cenv env baseValOpt (backupRange: Text.range) (ty, overrides) = + accTy cenv env backupRange ty accMethods cenv env baseValOpt overrides -and accOp cenv env (op, tyargs, args, _m) = +and accOp cenv env (op, tyargs, args, m) = // Special cases - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env args match op with // Handle these as special cases since mutables are allowed inside their bodies | TOp.ILCall (_, _, _, _, _, _, _, _, enclTypeInst, methInst, retTys) -> - accTypeInst cenv env enclTypeInst - accTypeInst cenv env methInst - accTypeInst cenv env retTys + accTypeInst cenv env m enclTypeInst + accTypeInst cenv env m methInst + accTypeInst cenv env m retTys | TOp.TraitCall traitInfo -> - accTraitInfo cenv env traitInfo + accTraitInfo cenv env m traitInfo | TOp.ILAsm (_, retTys) -> - accTypeInst cenv env retTys + accTypeInst cenv env m retTys | _ -> () -and accTraitInfo cenv env (TTrait(tys, _nm, _, argTys, retTy, _sln)) = - argTys |> accTypeInst cenv env - retTy |> Option.iter (accTy cenv env) - tys |> List.iter (accTy cenv env) +and accTraitInfo cenv env (backupRange : Text.range) (TTrait(tys, _nm, _, argTys, retTy, _sln)) = + argTys |> accTypeInst cenv env backupRange + retTy |> Option.iter (accTy cenv env backupRange) + tys |> List.iter (accTy cenv env backupRange) and accLambdas cenv env valReprInfo expr exprTy = match stripDebugPoints expr with | Expr.TyChoose (_tps, bodyExpr, _m) -> accLambdas cenv env valReprInfo bodyExpr exprTy - | Expr.Lambda _ - | Expr.TyLambda _ -> + | Expr.Lambda (_, _, _, _, _, m, _) + | Expr.TyLambda (_, _, _, m, _) -> let _tps, ctorThisValOpt, baseValOpt, vsl, body, bodyTy = destTopLambda cenv.g cenv.amap valReprInfo (expr, exprTy) - accTy cenv env bodyTy - vsl |> List.iterSquared (accVal cenv env) - baseValOpt |> Option.iter (accVal cenv env) - ctorThisValOpt |> Option.iter (accVal cenv env) + accTy cenv env expr.Range bodyTy + vsl |> List.iterSquared (accVal cenv env m) + baseValOpt |> Option.iter (accVal cenv env m) + ctorThisValOpt |> Option.iter (accVal cenv env m) accExpr cenv env body | _ -> accExpr cenv env expr @@ -190,31 +195,31 @@ and accDTree cenv env dtree = | TDBind(bind, rest) -> accBind cenv env bind; accDTree cenv env rest | TDSwitch (e, cases, dflt, m) -> accSwitch cenv env (e, cases, dflt, m) -and accSwitch cenv env (e, cases, dflt, _m) = +and accSwitch cenv env (e, cases, dflt, m) = accExpr cenv env e - cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env discrim; accDTree cenv env e) + cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env m discrim; accDTree cenv env e) dflt |> Option.iter (accDTree cenv env) -and accDiscrim cenv env d = +and accDiscrim cenv env backupRange d = match d with - | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env tinst - | DecisionTreeTest.ArrayLength(_, ty) -> accTy cenv env ty + | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env backupRange tinst + | DecisionTreeTest.ArrayLength(_, ty) -> accTy cenv env backupRange ty | DecisionTreeTest.Const _ | DecisionTreeTest.IsNull -> () - | DecisionTreeTest.IsInst (srcTy, tgtTy) -> accTy cenv env srcTy; accTy cenv env tgtTy + | DecisionTreeTest.IsInst (srcTy, tgtTy) -> accTy cenv env backupRange srcTy; accTy cenv env backupRange tgtTy | DecisionTreeTest.ActivePatternCase (exp, tys, _, _, _, _) -> accExpr cenv env exp - accTypeInst cenv env tys + accTypeInst cenv env exp.Range tys | DecisionTreeTest.Error _ -> () -and accAttrib cenv env (Attrib(_, _k, args, props, _, _, _m)) = +and accAttrib cenv env (Attrib(_, _k, args, props, _, _, m)) = args |> List.iter (fun (AttribExpr(expr1, expr2)) -> accExpr cenv env expr1 accExpr cenv env expr2) props |> List.iter (fun (AttribNamedArg(_nm, ty, _flg, AttribExpr(expr, expr2))) -> accExpr cenv env expr accExpr cenv env expr2 - accTy cenv env ty) + accTy cenv env m ty) and accAttribs cenv env attribs = List.iter (accAttrib cenv env) attribs @@ -226,13 +231,13 @@ and accValReprInfo cenv env (ValReprInfo(_, args, ret)) = and accArgReprInfo cenv env (argInfo: ArgReprInfo) = accAttribs cenv env argInfo.Attribs -and accVal cenv env v = +and accVal cenv env (backupRange: Text.range) v = v.Attribs |> accAttribs cenv env v.ValReprInfo |> Option.iter (accValReprInfo cenv env) - v.Type |> accTy cenv env + v.Type |> accTy cenv env backupRange and accBind cenv env (bind: Binding) = - accVal cenv env bind.Var + accVal cenv env bind.Expr.Range bind.Var let valReprInfo = match bind.Var.ValReprInfo with Some info -> info | _ -> ValReprInfo.emptyValData accLambdas cenv env valReprInfo bind.Expr bind.Var.Type @@ -245,7 +250,7 @@ let accTyconRecdField cenv env _tycon (rfield:RecdField) = let accTycon cenv env (tycon:Tycon) = accAttribs cenv env tycon.Attribs - abstractSlotValsOfTycons [tycon] |> List.iter (accVal cenv env) + abstractSlotValsOfTycons [tycon] |> List.iter (accVal cenv env tycon.Range) tycon.AllFieldsArray |> Array.iter (accTyconRecdField cenv env tycon) if tycon.IsUnionTycon then (* This covers finite unions. *) tycon.UnionCasesArray |> Array.iter (fun uc -> diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index ca1f2239b2f..60503ca02cf 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -9,9 +9,6 @@ module ObjInference = let warningCases = [ - // TODO: for this case, we're definitely emitting the warning (according to the debugger), - // but somehow it's not showing up in the output? - """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 1, 1, 1 "let f() = ([] = [])", 1, 17, 1, 19 """System.Object.ReferenceEquals(null, "hello") |> ignore""", 1, 31, 1, 35 """System.Object.ReferenceEquals("hello", null) |> ignore""", 1, 40, 1, 44 @@ -28,8 +25,26 @@ module ObjInference = |> shouldFail |> withSingleDiagnostic (Warning 3525, Line line1, Col col1, Line line2, Col col2, message) + [] + let ``Three types refined to obj are all warned`` () = + FSharp """let f<'b> () : 'b = (let a = failwith "" in unbox a)""" + |> withErrorRanges + |> withWarnOn 3525 + |> typecheck + |> shouldFail + |> withDiagnostics + [ + // The `failwith ""` case + Warning 3525, Line 1, Col 30, Line 1, Col 41, message + // The `unbox a` case + Warning 3525, Line 1, Col 45, Line 1, Col 52, message + // The `unbox` case + Warning 3525, Line 1, Col 45, Line 1, Col 50, message + ] + let noWarningCases = [ + // TODO: this test is failing, it thinks `x` was inferred as obj even though it wasn't "let add x y = x + y" // inferred as int "let f x = string x" // inferred as generic 'a -> string "let f() = ([] = ([] : obj list))" // obj is inferred, but is annotated From ee81996c012c35bc06e1a8e85e1056b264a73387 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Sun, 22 Jan 2023 13:46:45 +0000 Subject: [PATCH 11/30] Another failing test --- src/Compiler/Checking/TypeRelations.fs | 23 ++++++++++--------- .../ConstraintSolver/ObjInference.fs | 5 ++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index 1c661f7c418..8e1e5087070 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -132,7 +132,7 @@ let rec TypeFeasiblySubsumesType ndeep g amap m ty1 canCoerce ty2 = /// variables when compiling patterns at generalized bindings. /// e.g. let ([], x) = ([], []) /// Here x gets a generalized type "list<'T>". -let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = +let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) : TType * Text.range * bool = let m = tp.Range let (maxTy, isRefined), m = let initialTy = @@ -178,18 +178,19 @@ let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = (maxTy, haveRefined), m | TyparConstraint.DefaultsTo(_priority, _ty, m) -> (maxTy, haveRefined), m) - match tp.Kind with - | TyparKind.Type -> - if not isRefined then - warning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) - | _ -> () - maxTy, m - -let ChooseTyparSolution g amap tp = - let ty, _m = ChooseTyparSolutionAndRange g amap tp + let wasRefined = + isRefined || + match tp.Kind with + | TyparKind.Type -> + true + | _ -> false + maxTy, m, wasRefined + +let ChooseTyparSolution g amap tp : TType * bool = + let ty, _m, wasRefined = ChooseTyparSolutionAndRange g amap tp if tp.Rigidity = TyparRigidity.Anon && typeEquiv g ty (TType_measure Measure.One) then warning(Error(FSComp.SR.csCodeLessGeneric(), tp.Range)) - ty + ty, wasRefined // Solutions can, in theory, refer to each other // For example diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 60503ca02cf..6582366b487 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -42,10 +42,15 @@ module ObjInference = Warning 3525, Line 1, Col 45, Line 1, Col 50, message ] + let inline add< ^T when ^T: (static member op_Add: ^T * ^T -> ^T)> (x : ^T) (y : ^T) : ^T = + //when ^T : ^T = ((^T or ^U): (static member (+) : ^T * ^U -> ^V) (x,y)) + x + y + let noWarningCases = [ // TODO: this test is failing, it thinks `x` was inferred as obj even though it wasn't "let add x y = x + y" // inferred as int + "let add< ^T when ^T : x y = x + y" // inferred as int "let f x = string x" // inferred as generic 'a -> string "let f() = ([] = ([] : obj list))" // obj is inferred, but is annotated "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated From e8d64faa9902806d731823b01056e7bc2106a729 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 23 Jan 2023 21:22:39 +0000 Subject: [PATCH 12/30] Resolve merge --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 39c5497feee..a7fd10930cf 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100-preview.5.22307.18", + "version": "7.0.101", "allowPrerelease": true, "rollForward": "latestMajor" }, From 030a5cb662c1d2ee8f3e5d468c95ea7e01c3203d Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 23 Jan 2023 22:00:21 +0000 Subject: [PATCH 13/30] Tidy --- docs/diagnostics.md | 11 ++++++++++- .../FSharp.Compiler.ComponentTests.fsproj | 6 ------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 026e3b256ba..784d1491c0b 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -32,7 +32,7 @@ Adding or adjusting diagnostics emitted by the compiler is usually straightforwa 4. Use another search tool or a tool like Find All References / Find Usages to see where it's used in the compiler source code. 5. Set a breakpoint at the location in source you found. If you debug the compiler with the same steps, it should trigger the breakpoint you set. This verifies that the location you found is the one that emits the error or warning you want to improve. -From here, you can either simply update the error test, or you can use some of the information at the point in the source code you identified to see if there is more information to include in the error message. For example, if the error message doesn't contain information about the identifier the user is using incorrectly, you may be able to include the name of the identifier based on data the compiler has available at that stage of compilation. +From here, you can either simply update the error text, or you can use some of the information at the point in the source code you identified to see if there is more information to include in the error message. For example, if the error message doesn't contain information about the identifier the user is using incorrectly, you may be able to include the name of the identifier based on data the compiler has available at that stage of compilation. If you're including data from user code in an error message, it's important to also write a test that verifies the exact error message for a given string of F# code. @@ -55,3 +55,12 @@ Diagnostics must often format types. * When displaying multiple types in a comparative way, for example, two types that didn't match, you will want to display the minimal amount of infomation to convey the fact that the two types are different, for example, `NicePrint.minimalStringsOfTwoTypes`. * When displaying a type, you have the option of displaying the constraints implied by any type variables mentioned in the types, appended as `when ...`. For example, `NicePrint.layoutPrettifiedTypeAndConstraints`. + +## Localization + +The file `FSComp.txt` contains the canonical listing of diagnostic messages, but there are also `.xlf` localization files for various languages. +See [the DEVGUIDE](../DEVGUIDE.md#updating-fscompfs-fscompresx-and-xlf) for more details. + +## Enabling a warning or error by default + +The file `CompilerDiagnostics.fs` contains the function `IsWarningOrInfoEnabled`, which determines whether a given diagnostic is emitted. diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index f3da4599115..41ab845057d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -182,12 +182,6 @@ - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - - - %(RelativeDir)\TestSource\%(Filename)%(Extension) - From 76ea258b45dbea90e2ea28069d76065f1de6c354 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 23 Jan 2023 22:59:54 +0000 Subject: [PATCH 14/30] Take 2 --- src/Compiler/Checking/ConstraintSolver.fs | 2 +- src/Compiler/Checking/TypeRelations.fs | 39 +++--- src/Compiler/Driver/CompilerDiagnostics.fs | 1 + src/Compiler/FSComp.txt | 3 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 7 +- src/Compiler/xlf/FSComp.txt.de.xlf | 5 + src/Compiler/xlf/FSComp.txt.es.xlf | 5 + src/Compiler/xlf/FSComp.txt.fr.xlf | 5 + src/Compiler/xlf/FSComp.txt.it.xlf | 5 + src/Compiler/xlf/FSComp.txt.ja.xlf | 5 + src/Compiler/xlf/FSComp.txt.ko.xlf | 5 + src/Compiler/xlf/FSComp.txt.pl.xlf | 5 + src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 + src/Compiler/xlf/FSComp.txt.ru.xlf | 5 + src/Compiler/xlf/FSComp.txt.tr.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 7 +- .../ConstraintSolver/ObjInference.fs | 122 ++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + tests/FSharp.Test.Utilities/Compiler.fs | 4 +- 20 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 6b0ef85d5e8..0e3a263543c 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -3688,7 +3688,7 @@ let CodegenWitnessArgForTraitConstraint tcVal g amap m traitInfo = trackErrors { let ChooseTyparSolutionAndSolve css denv tp = let g = css.g let amap = css.amap - let max, m = ChooseTyparSolutionAndRange g amap tp + let max, m = ChooseTyparSolutionAndRange g amap tp let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv PostponeOnFailedMemberConstraintResolution csenv NoTrace (fun csenv -> SolveTyparEqualsType csenv 0 m NoTrace (mkTyparTy tp) max) diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index 90d5bed1fcb..458ec40f46f 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -134,50 +134,57 @@ let rec TypeFeasiblySubsumesType ndeep g amap m ty1 canCoerce ty2 = /// Here x gets a generalized type "list<'T>". let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = let m = tp.Range - let maxTy, m = + let (maxTy, isRefined), m = let initialTy = match tp.Kind with | TyparKind.Type -> g.obj_ty | TyparKind.Measure -> TType_measure Measure.One // Loop through the constraints computing the lub - ((initialTy, m), tp.Constraints) ||> List.fold (fun (maxTy, _) tpc -> + (((initialTy, false), m), tp.Constraints) ||> List.fold (fun ((maxTy, isRefined), _) tpc -> let join m x = - if TypeFeasiblySubsumesType 0 g amap m x CanCoerce maxTy then maxTy - elif TypeFeasiblySubsumesType 0 g amap m maxTy CanCoerce x then x - else errorR(Error(FSComp.SR.typrelCannotResolveImplicitGenericInstantiation((DebugPrint.showType x), (DebugPrint.showType maxTy)), m)); maxTy + if TypeFeasiblySubsumesType 0 g amap m x CanCoerce maxTy then maxTy, isRefined + elif TypeFeasiblySubsumesType 0 g amap m maxTy CanCoerce x then x, true + else errorR(Error(FSComp.SR.typrelCannotResolveImplicitGenericInstantiation((DebugPrint.showType x), (DebugPrint.showType maxTy)), m)); maxTy, isRefined // Don't continue if an error occurred and we set the value eagerly - if tp.IsSolved then maxTy, m else + if tp.IsSolved then (maxTy, isRefined), m else match tpc with | TyparConstraint.CoercesTo(x, m) -> join m x, m | TyparConstraint.MayResolveMember(_traitInfo, m) -> - maxTy, m + (maxTy, isRefined), m | TyparConstraint.SimpleChoice(_, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInPrintf(), m)) - maxTy, m + (maxTy, isRefined), m | TyparConstraint.SupportsNull m -> - maxTy, m + (maxTy, isRefined), m | TyparConstraint.SupportsComparison m -> join m g.mk_IComparable_ty, m | TyparConstraint.SupportsEquality m -> - maxTy, m + (maxTy, isRefined), m | TyparConstraint.IsEnum(_, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInEnum(), m)) - maxTy, m + (maxTy, isRefined), m | TyparConstraint.IsDelegate(_, _, m) -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInDelegate(), m)) - maxTy, m + (maxTy, isRefined), m | TyparConstraint.IsNonNullableStruct m -> join m g.int_ty, m | TyparConstraint.IsUnmanaged m -> errorR(Error(FSComp.SR.typrelCannotResolveAmbiguityInUnmanaged(), m)) - maxTy, m + (maxTy, isRefined), m | TyparConstraint.RequiresDefaultConstructor m -> - maxTy, m + (maxTy, isRefined), m | TyparConstraint.IsReferenceType m -> - maxTy, m + (maxTy, isRefined), m | TyparConstraint.DefaultsTo(_priority, _ty, m) -> - maxTy, m) + (maxTy, isRefined), m) + + match tp.Kind with + | TyparKind.Type -> + if not isRefined then + warning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) + | TyparKind.Measure -> () + maxTy, m let ChooseTyparSolution g amap tp = diff --git a/src/Compiler/Driver/CompilerDiagnostics.fs b/src/Compiler/Driver/CompilerDiagnostics.fs index 4b78aa9b9f9..6bfe9f7c8dc 100644 --- a/src/Compiler/Driver/CompilerDiagnostics.fs +++ b/src/Compiler/Driver/CompilerDiagnostics.fs @@ -380,6 +380,7 @@ type PhasedDiagnostic with | 3389 -> false // tcBuiltInImplicitConversionUsed - off by default | 3390 -> false // xmlDocBadlyFormed - off by default | 3395 -> false // tcImplicitConversionUsedForMethodArg - off by default + | 3559 -> false | _ -> (severity = FSharpDiagnosticSeverity.Info) || (severity = FSharpDiagnosticSeverity.Warning && level >= x.WarningLevel) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 46379fc0eb4..391abf633fb 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1672,4 +1672,5 @@ featureEscapeBracesInFormattableString,"Escapes curly braces before calling Form 3555,chkInstanceLetBindingOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Instance let bindings are not allowed." 3556,chkImplementingInterfacesOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Implementing interfaces is not allowed." 3557,chkAbstractMembersDeclarationsOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Abstract member declarations are not allowed." -3558,chkExplicitFieldsDeclarationsOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Explicit field declarations are not allowed." \ No newline at end of file +3558,chkExplicitFieldsDeclarationsOnStaticClasses,"If a type uses both [] and [] attributes, it means it is static. Explicit field declarations are not allowed." +3559,typrelNeverRefinedAwayFromTop,"A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\"." diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index be4e61a8fe3..e32bb74d4ec 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -1137,6 +1137,11 @@ Rozhraní {0} nemůžete implementovat se dvěma instancemi {1} a {2}, protože by se mohly sjednotit. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Typ {0} nedefinuje pole, konstruktor ani člen {1}. @@ -8284,4 +8289,4 @@ - \ No newline at end of file + diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 5d662b8bf27..d46a98f303a 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -1137,6 +1137,11 @@ Sie können die Schnittstelle "{0}" mit den beiden Instanziierungen "{1}" und "{2}" nicht implementieren, weil sie möglicherweise zusammengeführt werden. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Der Typ "{0}" definiert nicht das Feld, den Konstruktor oder den Member "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 1ddc22b8475..344ba048267 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -1137,6 +1137,11 @@ No se puede implementar la interfaz "{0}" con ambas creaciones de instancias, "{1}" y "{2}", porque pueden unificarse. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. El tipo "{0}" no define el campo, constructor o miembro "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index a7b82e51172..f4f41c4bac8 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -1137,6 +1137,11 @@ Vous ne pouvez pas implémenter l'interface '{0}' avec les deux instanciations '{1}' et '{2}', car elles peuvent s'unifier. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Le type '{0}' ne définit pas le champ, le constructeur ou le membre '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index fcd590c3b39..b7ff94f4c8d 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -1137,6 +1137,11 @@ Non è possibile implementare l'interfaccia '{0}' con le due creazioni di istanze '{1}' e '{2}' perché possono essere unificate. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Il tipo '{0}' non definisce il campo, il costruttore o il membro '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index b064cc8d2b7..ffc781a6379 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -1137,6 +1137,11 @@ 統合している可能性があるため、'{1}' と '{2}' の 2 つのインスタンス化を含むインターフェイス '{0}' を実装することはできません。 + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. 型 '{0}' は、フィールド、コンストラクター、またはメンバー '{1}' を定義していません。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 41fced8e047..a42bebf2ebc 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -1137,6 +1137,11 @@ '{1}' 및 '{2}' 인스턴스화가 포함된 '{0}' 인터페이스를 구현할 수 없습니다. 이 두 인스턴스화가 통합될 수 있기 때문입니다. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. '{0}' 형식은 '{1}' 필드, 생성자 또는 멤버를 정의하지 않습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 764d818dcd4..df32fc53a65 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -1137,6 +1137,11 @@ Nie możesz zaimplementować interfejsu „{0}” przy użyciu dwóch wystąpień „{1}” i „{2}”, ponieważ mogą one się ujednolicić. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Typ „{0}” nie definiuje pola, konstruktora lub składowej „{1}”. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index da8982c32ac..478b449a50c 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -1137,6 +1137,11 @@ Não é possível implementar a interface '{0}' com as duas instanciações '{1}' e '{2}' porque talvez elas sejam unificadas. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. O tipo '{0}' não define o campo, o construtor ou o membro '{1}'. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 29827955000..e6d98d86b74 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -1137,6 +1137,11 @@ Невозможно реализовать интерфейс "{0}" с двумя созданиями экземпляра "{1}" и "{2}", так как они могут быть объединены. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. Тип "{0}" не определяет поле, конструктор или член "{1}". diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 3f3bb5dee68..919854f91f1 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -1137,6 +1137,11 @@ '{1}' ve '{2}' örnek oluşturmaları birleşebileceğinden '{0}' arabirimini bunlarla birlikte uygulayamazsınız. + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. '{0}' türü; alanı, oluşturucuyu veya '{1}' üyesini tanımlamıyor. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 3601169ca3a..4f36ee50bf6 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -1137,6 +1137,11 @@ 你无法实现具有两个实例化“{1}”和“{2}”的接口“{0}”,因为它们可能会统一。 + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. 类型“{0}”未定义字段、构造函数或成员“{1}”。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index b767cad8784..a5514caf73e 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -1137,6 +1137,11 @@ 因為 '{1}' 和 '{2}' 兩者的具現化可能整合,所以無法將其用於實作介面 '{0}'。 + + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\". + + The type '{0}' does not define the field, constructor or member '{1}'. 類型 '{0}' 未定義欄位、建構函式或成員 '{1}'。 @@ -8284,4 +8289,4 @@ - \ No newline at end of file + diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs new file mode 100644 index 00000000000..d72b0f4c669 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -0,0 +1,122 @@ +namespace FSharp.Compiler.ComponentTests.ConstraintSolver + +open Xunit +open FSharp.Test.Compiler + +module ObjInference = + + let message = "A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\"." + + let warningCases = + [ + "let f() = ([] = [])", 1, 17, 1, 19 + """System.Object.ReferenceEquals(null, "hello") |> ignore""", 1, 31, 1, 35 + """System.Object.ReferenceEquals("hello", null) |> ignore""", 1, 40, 1, 44 + ] + |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + + [] + [] + let ``Warning is emitted when type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = + FSharp code + |> withErrorRanges + |> withWarnOn 3559 + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) + + [] + let ``Three types refined to obj are all warned`` () = + FSharp """let f<'b> () : 'b = (let a = failwith "" in unbox a)""" + |> withErrorRanges + |> withWarnOn 3559 + |> typecheck + |> shouldFail + |> withDiagnostics + [ + // The `failwith ""` case + Warning 3559, Line 1, Col 30, Line 1, Col 41, message + // The `unbox a` case + Warning 3559, Line 1, Col 45, Line 1, Col 52, message + // The `unbox` case + Warning 3559, Line 1, Col 45, Line 1, Col 50, message + ] + + let noWarningCases = + [ + // TODO: this test is failing, it thinks `x` was inferred as obj even though it wasn't + "let add x y = x + y" // inferred as int + "let inline add x y = x + y" // inferred with SRTP + "let inline add< ^T when ^T : (static member (+) : ^T * ^T -> ^T)> (x : ^T) (y : ^T) : ^T = x + y" // with SRTP + "let f x = string x" // inferred as generic 'a -> string + "let f() = ([] = ([] : obj list))" // obj is inferred, but is annotated + "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated + """let x<[]'m> : int<'m> = failwith "" +let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covered by this warning + "let a = 5 |> unbox in let b = a in ()" // explicit obj annotation + ] + |> List.map Array.singleton + + [] + [] + let ``Warning does not fire unless required``(code: string) = + FSharp code + |> withWarnOn 3559 + |> typecheck + |> shouldSucceed + + let nullNoWarningCases = + [ + """System.Object.ReferenceEquals("hello", (null: string))""" + """System.Object.ReferenceEquals((null: string), "hello")""" + ] + |> List.map Array.singleton + + [] + [] + let ``Don't warn on an explicit null``(expr: string) = + sprintf "%s |> ignore" expr + |> FSharp + |> withWarnOn 3559 + |> typecheck + |> shouldSucceed + + [] + [] + let ``Don't warn on an explicit null, inside quotations``(expr: string) = + sprintf "<@ %s @> |> ignore" expr + |> FSharp + |> withWarnOn 3559 + |> typecheck + |> shouldSucceed + + let quotationNoWarningCases = + [ + "<@ List.map ignore [1;2;3] @>" + ] + |> List.map Array.singleton + + [] + [] + let ``Don't warn inside quotations of acceptable code``(expr: string) = + sprintf "%s |> ignore" expr + |> FSharp + |> withWarnOn 3559 + |> typecheck + |> shouldSucceed + + [] + let ``Warn when the error appears inside a quotation``() = + "<@ [] = [] @> |> ignore" + |> FSharp + |> withWarnOn 3559 + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3559, Line 1, Col 9, Line 1, Col 11, message) + + [] + let ``Warning is off by default``() = + "<@ [] = [] @> |> ignore" + |> FSharp + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index c244723eb72..41ab845057d 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -181,6 +181,7 @@ + diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 8ea26ea602b..d14083a1b11 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -404,10 +404,10 @@ module rec Compiler = let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = + let withWarnOn (warning: int) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit - let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = + let withNoWarn (warning: int) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--nowarn:{warning}" ] "withNoWarn is only supported for F#" cUnit let withNoOptimize (cUnit: CompilationUnit) : CompilationUnit = From 849a97f53fb1051f5757b6e042eb135172e8dd44 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 23 Jan 2023 23:15:13 +0000 Subject: [PATCH 15/30] Add another test --- .../ConstraintSolver/ObjInference.fs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index d72b0f4c669..4c3591f85a8 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -12,6 +12,10 @@ module ObjInference = "let f() = ([] = [])", 1, 17, 1, 19 """System.Object.ReferenceEquals(null, "hello") |> ignore""", 1, 31, 1, 35 """System.Object.ReferenceEquals("hello", null) |> ignore""", 1, 40, 1, 44 + "<@ [] = [] @> |> ignore", 1, 9, 1, 11 + """let f<'b> (x : 'b) : int = failwith "" +let deserialize<'v> (s : string) : 'v = failwith "" +let x = deserialize "" |> f""", 3, 1, 3, 10 // TODO - fix this range when the test works ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) @@ -44,7 +48,6 @@ module ObjInference = let noWarningCases = [ - // TODO: this test is failing, it thinks `x` was inferred as obj even though it wasn't "let add x y = x + y" // inferred as int "let inline add x y = x + y" // inferred with SRTP "let inline add< ^T when ^T : (static member (+) : ^T * ^T -> ^T)> (x : ^T) (y : ^T) : ^T = x + y" // with SRTP @@ -105,15 +108,6 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> typecheck |> shouldSucceed - [] - let ``Warn when the error appears inside a quotation``() = - "<@ [] = [] @> |> ignore" - |> FSharp - |> withWarnOn 3559 - |> typecheck - |> shouldFail - |> withSingleDiagnostic (Warning 3559, Line 1, Col 9, Line 1, Col 11, message) - [] let ``Warning is off by default``() = "<@ [] = [] @> |> ignore" From 25f3ba169e9d5a241d38ca8cde7431bdfcbcf877 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 3 Feb 2023 17:57:00 +0000 Subject: [PATCH 16/30] Add prospective test --- .../ConstraintSolver/ObjInference.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 4c3591f85a8..a42923c7a01 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -72,6 +72,9 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [ """System.Object.ReferenceEquals("hello", (null: string))""" """System.Object.ReferenceEquals((null: string), "hello")""" + // TODO: can we actually get this working? + // https://github.com/dotnet/fsharp/pull/13298#issuecomment-1414491640 + """System.Object.ReferenceEquals("hello", null)""" ] |> List.map Array.singleton From 286fe2188159ee7c6daa81f3e7992d671e8ae090 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 31 May 2023 22:18:43 +0100 Subject: [PATCH 17/30] Fix build --- tests/FSharp.Test.Utilities/Compiler.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index a4d59f296f7..298aa1a7ac4 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -449,7 +449,7 @@ module rec Compiler = let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = + let withWarnOn warning (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = From 49db33f31081347f822f3560f09a634fa49ec4cf Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 31 May 2023 22:27:40 +0100 Subject: [PATCH 18/30] Adjust message --- .../ConstraintSolver/ObjInference.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index a42923c7a01..8267553d57f 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -5,7 +5,7 @@ open FSharp.Test.Compiler module ObjInference = - let message = "A type inference variable has been implicitly inferred to have type `obj`. Consider adding explicit type annotations. This warning is off by default and has been explicitly enabled for this project. You may suppress this warning by using #nowarn \"3525\"." + let message = "A type has been implicitly inferred as 'obj', which may be unintended. Consider adding explicit type annotations. You can disable this warning by using '#nowarn \"3559\"' or '--nowarn:3559'." let warningCases = [ From 52dc05dfd7ffa29e65e9a6fd623c80df97d85f47 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 14:55:38 +0100 Subject: [PATCH 19/30] Add extra fallback typar ranges --- src/Compiler/Checking/FindUnsolved.fs | 42 +++++++++---------- .../ConstraintSolver/ObjInference.fs | 13 ++---- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/src/Compiler/Checking/FindUnsolved.fs b/src/Compiler/Checking/FindUnsolved.fs index 26b34d50b19..c46960f7022 100644 --- a/src/Compiler/Checking/FindUnsolved.fs +++ b/src/Compiler/Checking/FindUnsolved.fs @@ -39,8 +39,8 @@ let accTy cenv _env (fallbackRange: Range option) ty = | _ -> () cenv.unsolved <- tp :: cenv.unsolved) -let accTypeInst cenv env tyargs = - tyargs |> List.iter (accTy cenv env None) +let accTypeInst cenv env backup tyargs = + tyargs |> List.iter (accTy cenv env (Some backup)) /// Walk expressions, collecting type variables let rec accExpr (cenv: cenv) (env: env) expr = @@ -71,9 +71,9 @@ let rec accExpr (cenv: cenv) (env: env) expr = accMethods cenv env basev overrides accIntfImpls cenv env basev iimpls - | LinearOpExpr (_op, tyargs, argsHead, argLast, _m) -> + | LinearOpExpr (_op, tyargs, argsHead, argLast, m) -> // Note, LinearOpExpr doesn't include any of the "special" cases for accOp - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env argsHead // tailcall accExpr cenv env argLast @@ -83,7 +83,7 @@ let rec accExpr (cenv: cenv) (env: env) expr = | Expr.App (f, fty, tyargs, argsl, m) -> accTy cenv env (Some m) fty - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExpr cenv env f accExprs cenv env argsl @@ -120,8 +120,8 @@ let rec accExpr (cenv: cenv) (env: env) expr = | TTyconIsStruct(ty1) -> accTy cenv env (Some m) ty1) - | Expr.WitnessArg (traitInfo, _m) -> - accTraitInfo cenv env traitInfo + | Expr.WitnessArg (traitInfo, m) -> + accTraitInfo cenv env m traitInfo | Expr.Link eref -> accExpr cenv env eref.Value @@ -143,25 +143,25 @@ and accIntfImpl cenv env baseValOpt (ty, overrides) = accTy cenv env None ty accMethods cenv env baseValOpt overrides -and accOp cenv env (op, tyargs, args, _m) = +and accOp cenv env (op, tyargs, args, m) = // Special cases - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env args match op with // Handle these as special cases since mutables are allowed inside their bodies | TOp.ILCall (_, _, _, _, _, _, _, _, enclTypeInst, methInst, retTys) -> - accTypeInst cenv env enclTypeInst - accTypeInst cenv env methInst - accTypeInst cenv env retTys + accTypeInst cenv env m enclTypeInst + accTypeInst cenv env m methInst + accTypeInst cenv env m retTys | TOp.TraitCall traitInfo -> - accTraitInfo cenv env traitInfo + accTraitInfo cenv env m traitInfo | TOp.ILAsm (_, retTys) -> - accTypeInst cenv env retTys + accTypeInst cenv env m retTys | _ -> () -and accTraitInfo cenv env (TTrait(tys, _nm, _, argTys, retTy, _sln)) = - argTys |> accTypeInst cenv env +and accTraitInfo cenv env (backupRange : range) (TTrait(tys, _nm, _, argTys, retTy, _sln)) = + argTys |> accTypeInst cenv env backupRange retTy |> Option.iter (accTy cenv env None) tys |> List.iter (accTy cenv env None) @@ -194,21 +194,21 @@ and accDTree cenv env dtree = | TDBind(bind, rest) -> accBind cenv env bind; accDTree cenv env rest | TDSwitch (e, cases, dflt, m) -> accSwitch cenv env (e, cases, dflt, m) -and accSwitch cenv env (e, cases, dflt, _m) = +and accSwitch cenv env (e, cases, dflt, m) = accExpr cenv env e - cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env discrim; accDTree cenv env e) + cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env discrim m; accDTree cenv env e) dflt |> Option.iter (accDTree cenv env) -and accDiscrim cenv env d = +and accDiscrim cenv env d backupRange = match d with - | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env tinst + | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env backupRange tinst | DecisionTreeTest.ArrayLength(_, ty) -> accTy cenv env None ty | DecisionTreeTest.Const _ | DecisionTreeTest.IsNull -> () | DecisionTreeTest.IsInst (srcTy, tgtTy) -> accTy cenv env None srcTy; accTy cenv env None tgtTy | DecisionTreeTest.ActivePatternCase (exp, tys, _, _, _, _) -> accExpr cenv env exp - accTypeInst cenv env tys + accTypeInst cenv env backupRange tys | DecisionTreeTest.Error _ -> () and accAttrib cenv env (Attrib(_, _k, args, props, _, _, m)) = diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 8267553d57f..06edef51e72 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -15,7 +15,8 @@ module ObjInference = "<@ [] = [] @> |> ignore", 1, 9, 1, 11 """let f<'b> (x : 'b) : int = failwith "" let deserialize<'v> (s : string) : 'v = failwith "" -let x = deserialize "" |> f""", 3, 1, 3, 10 // TODO - fix this range when the test works +let x = deserialize "" |> f""", 3, 9, 3, 28 + "let f = typedefof<_>", 1, 19, 1, 20 ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) @@ -38,12 +39,9 @@ let x = deserialize "" |> f""", 3, 1, 3, 10 // TODO - fix this range when the te |> shouldFail |> withDiagnostics [ - // The `failwith ""` case + // The `failwith` case Warning 3559, Line 1, Col 30, Line 1, Col 41, message - // The `unbox a` case - Warning 3559, Line 1, Col 45, Line 1, Col 52, message - // The `unbox` case - Warning 3559, Line 1, Col 45, Line 1, Col 50, message + // Warning 3559, Line 1, Col 45, Line 1, Col 52, message ] let noWarningCases = @@ -72,9 +70,6 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [ """System.Object.ReferenceEquals("hello", (null: string))""" """System.Object.ReferenceEquals((null: string), "hello")""" - // TODO: can we actually get this working? - // https://github.com/dotnet/fsharp/pull/13298#issuecomment-1414491640 - """System.Object.ReferenceEquals("hello", null)""" ] |> List.map Array.singleton From 88b77808552fbb20617d73c84e26e344e66ebc04 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 17:14:05 +0100 Subject: [PATCH 20/30] WIP extra tests --- .../ConstraintSolver/ObjInference.fs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 06edef51e72..ebe55bab6e5 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -17,6 +17,7 @@ module ObjInference = let deserialize<'v> (s : string) : 'v = failwith "" let x = deserialize "" |> f""", 3, 9, 3, 28 "let f = typedefof<_>", 1, 19, 1, 20 + "let f = Unchecked.defaultof<_>" ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) @@ -30,6 +31,10 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 |> shouldFail |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) + let f () = + let (a : 'a) = failwith<'a> "" + unbox<'a> a + [] let ``Three types refined to obj are all warned`` () = FSharp """let f<'b> () : 'b = (let a = failwith "" in unbox a)""" @@ -55,6 +60,8 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 """let x<[]'m> : int<'m> = failwith "" let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covered by this warning "let a = 5 |> unbox in let b = a in ()" // explicit obj annotation + "let f () : int = Unchecked.defaultof<_>" + "let f () = Unchecked.defaultof" ] |> List.map Array.singleton From 848518882f727853a473ca3f48dc013f4e766f2c Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 17:15:38 +0100 Subject: [PATCH 21/30] Add fallback range --- src/Compiler/Checking/ConstraintSolver.fs | 2 +- src/Compiler/Checking/FindUnsolved.fs | 42 +++++++------- tests/FSharp.Test.Utilities/Compiler.fs | 68 +---------------------- 3 files changed, 25 insertions(+), 87 deletions(-) diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index 22ebc3ca4df..f80916c325e 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -3690,7 +3690,7 @@ let CodegenWitnessArgForTraitConstraint tcVal g amap m traitInfo = trackErrors { let ChooseTyparSolutionAndSolve css denv tp = let g = css.g let amap = css.amap - let max, m = ChooseTyparSolutionAndRange g amap tp + let max, m = ChooseTyparSolutionAndRange g amap tp let csenv = MakeConstraintSolverEnv ContextInfo.NoContext css m denv PostponeOnFailedMemberConstraintResolution csenv NoTrace (fun csenv -> SolveTyparEqualsType csenv 0 m NoTrace (mkTyparTy tp) max) diff --git a/src/Compiler/Checking/FindUnsolved.fs b/src/Compiler/Checking/FindUnsolved.fs index 26b34d50b19..c46960f7022 100644 --- a/src/Compiler/Checking/FindUnsolved.fs +++ b/src/Compiler/Checking/FindUnsolved.fs @@ -39,8 +39,8 @@ let accTy cenv _env (fallbackRange: Range option) ty = | _ -> () cenv.unsolved <- tp :: cenv.unsolved) -let accTypeInst cenv env tyargs = - tyargs |> List.iter (accTy cenv env None) +let accTypeInst cenv env backup tyargs = + tyargs |> List.iter (accTy cenv env (Some backup)) /// Walk expressions, collecting type variables let rec accExpr (cenv: cenv) (env: env) expr = @@ -71,9 +71,9 @@ let rec accExpr (cenv: cenv) (env: env) expr = accMethods cenv env basev overrides accIntfImpls cenv env basev iimpls - | LinearOpExpr (_op, tyargs, argsHead, argLast, _m) -> + | LinearOpExpr (_op, tyargs, argsHead, argLast, m) -> // Note, LinearOpExpr doesn't include any of the "special" cases for accOp - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env argsHead // tailcall accExpr cenv env argLast @@ -83,7 +83,7 @@ let rec accExpr (cenv: cenv) (env: env) expr = | Expr.App (f, fty, tyargs, argsl, m) -> accTy cenv env (Some m) fty - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExpr cenv env f accExprs cenv env argsl @@ -120,8 +120,8 @@ let rec accExpr (cenv: cenv) (env: env) expr = | TTyconIsStruct(ty1) -> accTy cenv env (Some m) ty1) - | Expr.WitnessArg (traitInfo, _m) -> - accTraitInfo cenv env traitInfo + | Expr.WitnessArg (traitInfo, m) -> + accTraitInfo cenv env m traitInfo | Expr.Link eref -> accExpr cenv env eref.Value @@ -143,25 +143,25 @@ and accIntfImpl cenv env baseValOpt (ty, overrides) = accTy cenv env None ty accMethods cenv env baseValOpt overrides -and accOp cenv env (op, tyargs, args, _m) = +and accOp cenv env (op, tyargs, args, m) = // Special cases - accTypeInst cenv env tyargs + accTypeInst cenv env m tyargs accExprs cenv env args match op with // Handle these as special cases since mutables are allowed inside their bodies | TOp.ILCall (_, _, _, _, _, _, _, _, enclTypeInst, methInst, retTys) -> - accTypeInst cenv env enclTypeInst - accTypeInst cenv env methInst - accTypeInst cenv env retTys + accTypeInst cenv env m enclTypeInst + accTypeInst cenv env m methInst + accTypeInst cenv env m retTys | TOp.TraitCall traitInfo -> - accTraitInfo cenv env traitInfo + accTraitInfo cenv env m traitInfo | TOp.ILAsm (_, retTys) -> - accTypeInst cenv env retTys + accTypeInst cenv env m retTys | _ -> () -and accTraitInfo cenv env (TTrait(tys, _nm, _, argTys, retTy, _sln)) = - argTys |> accTypeInst cenv env +and accTraitInfo cenv env (backupRange : range) (TTrait(tys, _nm, _, argTys, retTy, _sln)) = + argTys |> accTypeInst cenv env backupRange retTy |> Option.iter (accTy cenv env None) tys |> List.iter (accTy cenv env None) @@ -194,21 +194,21 @@ and accDTree cenv env dtree = | TDBind(bind, rest) -> accBind cenv env bind; accDTree cenv env rest | TDSwitch (e, cases, dflt, m) -> accSwitch cenv env (e, cases, dflt, m) -and accSwitch cenv env (e, cases, dflt, _m) = +and accSwitch cenv env (e, cases, dflt, m) = accExpr cenv env e - cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env discrim; accDTree cenv env e) + cases |> List.iter (fun (TCase(discrim, e)) -> accDiscrim cenv env discrim m; accDTree cenv env e) dflt |> Option.iter (accDTree cenv env) -and accDiscrim cenv env d = +and accDiscrim cenv env d backupRange = match d with - | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env tinst + | DecisionTreeTest.UnionCase(_ucref, tinst) -> accTypeInst cenv env backupRange tinst | DecisionTreeTest.ArrayLength(_, ty) -> accTy cenv env None ty | DecisionTreeTest.Const _ | DecisionTreeTest.IsNull -> () | DecisionTreeTest.IsInst (srcTy, tgtTy) -> accTy cenv env None srcTy; accTy cenv env None tgtTy | DecisionTreeTest.ActivePatternCase (exp, tys, _, _, _, _) -> accExpr cenv env exp - accTypeInst cenv env tys + accTypeInst cenv env backupRange tys | DecisionTreeTest.Error _ -> () and accAttrib cenv env (Attrib(_, _k, args, props, _, _, m)) = diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 59cb4faac1a..298aa1a7ac4 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -25,15 +25,7 @@ open System.Reflection.PortableExecutable open FSharp.Test.CompilerAssertHelpers open TestFramework -open System.Runtime.CompilerServices -open System.Runtime.InteropServices - - module rec Compiler = - [] - type SourceUtilities () = - static member getCurrentMethodName([] memberName: string) = memberName - type BaselineFile = { FilePath: string @@ -57,15 +49,6 @@ module rec Compiler = | CS of CSharpCompilationSource | IL of ILCompilationSource override this.ToString() = match this with | FS fs -> fs.ToString() | _ -> (sprintf "%A" this ) - member this.OutputDirectory = - let toString diOpt = - match diOpt: DirectoryInfo option with - | Some di -> di.FullName - | None -> "" - match this with - | FS fs -> fs.OutputDirectory |> toString - | CS cs -> cs.OutputDirectory |> toString - | _ -> raise (Exception "Not supported for this compilation type") member this.WithStaticLink(staticLink: bool) = match this with | FS fs -> FS { fs with StaticLink = staticLink } | cu -> cu type FSharpCompilationSource = @@ -208,47 +191,6 @@ module rec Compiler = let private defaultOptions : string list = [] - let normalizePathSeparator (text:string) = text.Replace(@"\", "/") - - let normalizeName name = - let invalidPathChars = Array.concat [Path.GetInvalidPathChars(); [| ':'; '\\'; '/'; ' '; '.' |]] - let result = invalidPathChars |> Array.fold(fun (acc:string) (c:char) -> acc.Replace(string(c), "_")) name - result - - let getTestOutputDirectory dir testCaseName extraDirectory = - // If the executing assembly has 'artifacts\bin' in it's path then we are operating normally in the CI or dev tests - // Thus the output directory will be in a subdirectory below where we are executing. - // The subdirectory will be relative to the source directory containing the test source file, - // E.g - // When the source code is in: - // $(repo-root)\tests\FSharp.Compiler.ComponentTests\Conformance\PseudoCustomAttributes - // and the test is running in the FSharp.Compiler.ComponentTeststest library - // The output directory will be: - // artifacts\bin\FSharp.Compiler.ComponentTests\$(Flavour)\$(TargetFramework)\tests\FSharp.Compiler.ComponentTests\Conformance\PseudoCustomAttributes - // - // If we can't find anything then we execute in the directory containing the source - // - try - let testlibraryLocation = normalizePathSeparator (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) - let pos = testlibraryLocation.IndexOf("artifacts/bin",StringComparison.OrdinalIgnoreCase) - if pos > 0 then - // Running under CI or dev build - let testRoot = Path.Combine(testlibraryLocation.Substring(0, pos), @"tests/") - let testSourceDirectory = - let dirInfo = normalizePathSeparator (Path.GetFullPath(dir)) - let testPaths = dirInfo.Replace(testRoot, "").Split('/') - testPaths[0] <- "tests" - Path.Combine(testPaths) - let n = Path.Combine(testlibraryLocation, testSourceDirectory.Trim('/'), normalizeName testCaseName, extraDirectory) - let outputDirectory = new DirectoryInfo(n) - Some outputDirectory - else - raise (new InvalidOperationException($"Failed to find the test output directory:\nTest Library Location: '{testlibraryLocation}'\n Pos: {pos}")) - None - - with | e -> - raise (new InvalidOperationException($" '{e.Message}'. Can't get the location of the executing assembly")) - // Not very safe version of reading stuff from file, but we want to fail fast for now if anything goes wrong. let private getSource (src: TestType) : string = match src with @@ -466,13 +408,9 @@ module rec Compiler = let withOptions (options: string list) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper options "withOptions is only supported for F#" cUnit - let withOptionsString (options: string) (cUnit: CompilationUnit) : CompilationUnit = - let options = if String.IsNullOrWhiteSpace options then [] else (options.Split([|';'|])) |> Array.toList - withOptionsHelper options "withOptionsString is only supported for F#" cUnit - - let withOutputDirectory (path: DirectoryInfo option) (cUnit: CompilationUnit) : CompilationUnit = + let withOutputDirectory (path: string) (cUnit: CompilationUnit) : CompilationUnit = match cUnit with - | FS fs -> FS { fs with OutputDirectory = path } + | FS fs -> FS { fs with OutputDirectory = Some (DirectoryInfo(path)) } | _ -> failwith "withOutputDirectory is only supported on F#" let withBufferWidth (width: int)(cUnit: CompilationUnit) : CompilationUnit = @@ -511,7 +449,7 @@ module rec Compiler = let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = + let withWarnOn warning (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = From 609fe05e55ff698b9d69e5afad36aea1a0bbc1db Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 17:16:38 +0100 Subject: [PATCH 22/30] Revert --- tests/FSharp.Test.Utilities/Compiler.fs | 68 +++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 298aa1a7ac4..59cb4faac1a 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -25,7 +25,15 @@ open System.Reflection.PortableExecutable open FSharp.Test.CompilerAssertHelpers open TestFramework +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + + module rec Compiler = + [] + type SourceUtilities () = + static member getCurrentMethodName([] memberName: string) = memberName + type BaselineFile = { FilePath: string @@ -49,6 +57,15 @@ module rec Compiler = | CS of CSharpCompilationSource | IL of ILCompilationSource override this.ToString() = match this with | FS fs -> fs.ToString() | _ -> (sprintf "%A" this ) + member this.OutputDirectory = + let toString diOpt = + match diOpt: DirectoryInfo option with + | Some di -> di.FullName + | None -> "" + match this with + | FS fs -> fs.OutputDirectory |> toString + | CS cs -> cs.OutputDirectory |> toString + | _ -> raise (Exception "Not supported for this compilation type") member this.WithStaticLink(staticLink: bool) = match this with | FS fs -> FS { fs with StaticLink = staticLink } | cu -> cu type FSharpCompilationSource = @@ -191,6 +208,47 @@ module rec Compiler = let private defaultOptions : string list = [] + let normalizePathSeparator (text:string) = text.Replace(@"\", "/") + + let normalizeName name = + let invalidPathChars = Array.concat [Path.GetInvalidPathChars(); [| ':'; '\\'; '/'; ' '; '.' |]] + let result = invalidPathChars |> Array.fold(fun (acc:string) (c:char) -> acc.Replace(string(c), "_")) name + result + + let getTestOutputDirectory dir testCaseName extraDirectory = + // If the executing assembly has 'artifacts\bin' in it's path then we are operating normally in the CI or dev tests + // Thus the output directory will be in a subdirectory below where we are executing. + // The subdirectory will be relative to the source directory containing the test source file, + // E.g + // When the source code is in: + // $(repo-root)\tests\FSharp.Compiler.ComponentTests\Conformance\PseudoCustomAttributes + // and the test is running in the FSharp.Compiler.ComponentTeststest library + // The output directory will be: + // artifacts\bin\FSharp.Compiler.ComponentTests\$(Flavour)\$(TargetFramework)\tests\FSharp.Compiler.ComponentTests\Conformance\PseudoCustomAttributes + // + // If we can't find anything then we execute in the directory containing the source + // + try + let testlibraryLocation = normalizePathSeparator (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) + let pos = testlibraryLocation.IndexOf("artifacts/bin",StringComparison.OrdinalIgnoreCase) + if pos > 0 then + // Running under CI or dev build + let testRoot = Path.Combine(testlibraryLocation.Substring(0, pos), @"tests/") + let testSourceDirectory = + let dirInfo = normalizePathSeparator (Path.GetFullPath(dir)) + let testPaths = dirInfo.Replace(testRoot, "").Split('/') + testPaths[0] <- "tests" + Path.Combine(testPaths) + let n = Path.Combine(testlibraryLocation, testSourceDirectory.Trim('/'), normalizeName testCaseName, extraDirectory) + let outputDirectory = new DirectoryInfo(n) + Some outputDirectory + else + raise (new InvalidOperationException($"Failed to find the test output directory:\nTest Library Location: '{testlibraryLocation}'\n Pos: {pos}")) + None + + with | e -> + raise (new InvalidOperationException($" '{e.Message}'. Can't get the location of the executing assembly")) + // Not very safe version of reading stuff from file, but we want to fail fast for now if anything goes wrong. let private getSource (src: TestType) : string = match src with @@ -408,9 +466,13 @@ module rec Compiler = let withOptions (options: string list) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper options "withOptions is only supported for F#" cUnit - let withOutputDirectory (path: string) (cUnit: CompilationUnit) : CompilationUnit = + let withOptionsString (options: string) (cUnit: CompilationUnit) : CompilationUnit = + let options = if String.IsNullOrWhiteSpace options then [] else (options.Split([|';'|])) |> Array.toList + withOptionsHelper options "withOptionsString is only supported for F#" cUnit + + let withOutputDirectory (path: DirectoryInfo option) (cUnit: CompilationUnit) : CompilationUnit = match cUnit with - | FS fs -> FS { fs with OutputDirectory = Some (DirectoryInfo(path)) } + | FS fs -> FS { fs with OutputDirectory = path } | _ -> failwith "withOutputDirectory is only supported on F#" let withBufferWidth (width: int)(cUnit: CompilationUnit) : CompilationUnit = @@ -449,7 +511,7 @@ module rec Compiler = let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn warning (cUnit: CompilationUnit) : CompilationUnit = + let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = From 407040b4cae84fae6dcd7dcf7a1e613c47f3286e Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 17:17:21 +0100 Subject: [PATCH 23/30] Reinstate --- tests/FSharp.Test.Utilities/Compiler.fs | 76 ++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 59cb4faac1a..d487dffb57c 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -290,12 +290,12 @@ module rec Compiler = let toErrorInfo (e: FSharpDiagnostic) : SourceCodeFileName * ErrorInfo = let errorNumber = e.ErrorNumber let severity = e.Severity - let error = + let error = match severity with | FSharpDiagnosticSeverity.Warning -> Warning errorNumber | FSharpDiagnosticSeverity.Error -> Error errorNumber | FSharpDiagnosticSeverity.Info -> Information errorNumber - | FSharpDiagnosticSeverity.Hidden -> Hidden errorNumber + | FSharpDiagnosticSeverity.Hidden -> Hidden errorNumber e.FileName |> Path.GetFileName, { Error = error @@ -415,7 +415,7 @@ module rec Compiler = | FS compilationSource -> FS { compilationSource with Source = compilationSource.Source.WithFileName(name) } | CS cSharpCompilationSource -> CS { cSharpCompilationSource with Source = cSharpCompilationSource.Source.WithFileName(name) } | IL _ -> failwith "IL Compilation cannot be named." - + let withReferenceFSharpCompilerService (cUnit: CompilationUnit) : CompilationUnit = // Compute the location of the FSharp.Compiler.Service dll that matches the target framework used to build this test assembly let compilerServiceAssemblyLocation = @@ -504,14 +504,14 @@ module rec Compiler = let withLangVersionPreview (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ "--langversion:preview" ] "withLangVersionPreview is only supported on F#" cUnit - + let withLangVersion (version: string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--langversion:{version}" ] "withLangVersion is only supported on F#" cUnit let withAssemblyVersion (version:string) (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--version:{version}" ] "withAssemblyVersion is only supported on F#" cUnit - let withWarnOn (cUnit: CompilationUnit) warning : CompilationUnit = + let withWarnOn warning (cUnit: CompilationUnit) : CompilationUnit = withOptionsHelper [ $"--warnon:{warning}" ] "withWarnOn is only supported for F#" cUnit let withNoWarn warning (cUnit: CompilationUnit) : CompilationUnit = @@ -584,14 +584,14 @@ module rec Compiler = | _ -> failwith "TODO: Implement where applicable." let asExe (cUnit: CompilationUnit) : CompilationUnit = - withOutputType CompileOutput.Exe cUnit - + withOutputType CompileOutput.Exe cUnit + let asLibrary (cUnit: CompilationUnit) : CompilationUnit = withOutputType CompileOutput.Library cUnit let asModule (cUnit: CompilationUnit) : CompilationUnit = - withOutputType CompileOutput.Module cUnit - + withOutputType CompileOutput.Module cUnit + let asNetStandard20 (cUnit: CompilationUnit) : CompilationUnit = match cUnit with | FS fs -> FS { fs with TargetFramework = TargetFramework.NetStandard20 } @@ -904,7 +904,7 @@ module rec Compiler = yield! fsSource.AdditionalSources |> List.map (fun source -> source.GetSourceFileName, source.GetSourceText) |] - + let getSourceText = let project = Map.ofArray sourceFiles fun (name: string) -> @@ -948,33 +948,33 @@ module rec Compiler = PerFileErrors = perFileDiagnostics Output = Some (EvalOutput evalResult) Compilation = FS fs } - + let evalError = match evalResult with Ok _ -> false | _ -> true - if evalError || errors.Length > 0 || (warnings.Length > 0 && not fs.IgnoreWarnings) then + if evalError || errors.Length > 0 || (warnings.Length > 0 && not fs.IgnoreWarnings) then CompilationResult.Failure result else CompilationResult.Success result - + let private evalFSharp (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = let source = fs.Source.GetSourceText |> Option.defaultValue "" - script.Eval(source) |> (processScriptResults fs) + script.Eval(source) |> (processScriptResults fs) let scriptingShim = Path.Combine(__SOURCE_DIRECTORY__,"ScriptingShims.fsx") let private evalScriptFromDisk (fs: FSharpCompilationSource) (script:FSharpScript) : CompilationResult = - - let fileNames = + + let fileNames = (fs.Source :: fs.AdditionalSources) |> List.map (fun x -> x.GetSourceFileName) |> List.insertAt 0 scriptingShim |> List.map (sprintf " @\"%s\"") |> String.Concat - script.Eval("#load " + fileNames ) |> (processScriptResults fs) + script.Eval("#load " + fileNames ) |> (processScriptResults fs) let eval (cUnit: CompilationUnit) : CompilationResult = match cUnit with - | FS fs -> + | FS fs -> let options = fs.Options |> Array.ofList use script = new FSharpScript(additionalArgs=options) evalFSharp fs script @@ -1146,7 +1146,7 @@ module rec Compiler = let result = regex.Replace(output, "") result - let stripEnvironment output = + let stripEnvironment output = let pattern = @"(---------------------------------------------------------------(\r\n|\r|\n)).*(\n---------------------------------------------------------------(\r\n|\r|\n))" let result = regexStrip output pattern (RegexOptions.Singleline ||| RegexOptions.ExplicitCapture) result @@ -1326,7 +1326,7 @@ module rec Compiler = failwith $"PDB file does not exists: {pdbPath}" | _ -> failwith "Output path is not set, please make sure compilation was successfull." match result with - | CompilationResult.Success r -> verifyPdbExists r + | CompilationResult.Success r -> verifyPdbExists r | _ -> failwith "Result should be \"Success\" in order to verify PDB." let verifyNoPdb (result: CompilationResult): unit = @@ -1338,7 +1338,7 @@ module rec Compiler = failwith $"PDB file exists: {pdbPath}" | _ -> failwith "Output path is not set, please make sure compilation was successfull." match result with - | CompilationResult.Success r -> verifyPdbNotExists r + | CompilationResult.Success r -> verifyPdbNotExists r | _ -> failwith "Result should be \"Success\" in order to verify PDB." [] @@ -1413,10 +1413,10 @@ module rec Compiler = match r.Output with | Some (ExecutionOutput output) -> sprintf "----output-----\n%s\n----error-------\n%s\n----------" output.StdOut output.StdErr - | Some (EvalOutput (Result.Error exn) ) -> + | Some (EvalOutput (Result.Error exn) ) -> sprintf "----script error-----\n%s\n----------" (exn.ToString()) - | Some (EvalOutput (Result.Ok fsiVal) ) -> - sprintf "----script output-----\n%A\n----------" (fsiVal) + | Some (EvalOutput (Result.Ok fsiVal) ) -> + sprintf "----script output-----\n%A\n----------" (fsiVal) | _ -> () ] |> String.concat "\n" failwith message @@ -1463,10 +1463,10 @@ module rec Compiler = let withError (expectedError: ErrorInfo) (result: CompilationResult) : CompilationResult = withErrors [expectedError] result - module StructuredResultsAsserts = - type SimpleErrorInfo = + module StructuredResultsAsserts = + type SimpleErrorInfo = { Error: ErrorType - Range: Range + Range: Range Message: string } let withResults (expectedResults: SimpleErrorInfo list) result : CompilationResult = @@ -1478,37 +1478,37 @@ module rec Compiler = - module TextBasedDiagnosticAsserts = + module TextBasedDiagnosticAsserts = open FSharp.Compiler.Text.Range - let private messageAndNumber errorType= + let private messageAndNumber errorType= match errorType with | ErrorType.Error n -> "error",n | ErrorType.Warning n-> "warning",n | ErrorType.Hidden n | ErrorType.Information n-> "info",n - let normalizeNewLines (s:string) = s.Replace("\r\n","\n").Replace("\n",Environment.NewLine) + let normalizeNewLines (s:string) = s.Replace("\r\n","\n").Replace("\n",Environment.NewLine) - let private renderToString (cr:CompilationResult) = + let private renderToString (cr:CompilationResult) = [ for (file,err) in cr.Output.PerFileErrors do let m = err.NativeRange let file = file.Replace("/", "\\") let severity,no = messageAndNumber err.Error let adjustedMessage = err.Message |> normalizeNewLines - let location = + let location = if (equals m range0) || (equals m rangeStartup) || (equals m rangeCmdArgs) then "" - else + else // The baseline .bsl files use 1-based notation for columns, hence the +1's sprintf "%s(%d,%d,%d,%d):" file m.StartLine (m.StartColumn+1) m.EndLine (m.EndColumn+1) Environment.NewLine + $"{location} {err.SubCategory} {severity} FS%04d{no}: {adjustedMessage}" + Environment.NewLine ] |> String.Concat - let withResultsMatchingFile (path:string) (result:CompilationResult) = + let withResultsMatchingFile (path:string) (result:CompilationResult) = let expectedContent = File.ReadAllText(path) |> normalizeNewLines - let actualErrors = renderToString result + let actualErrors = renderToString result match Environment.GetEnvironmentVariable("TEST_UPDATE_BSL") with | null -> () @@ -1516,9 +1516,9 @@ module rec Compiler = | _ -> File.WriteAllText(path, actualErrors) match Assert.shouldBeSameMultilineStringSets expectedContent actualErrors with - | None -> () + | None -> () | Some diff -> Assert.That(diff, Is.Empty, path) - + result let checkCodes (expected: int list) (selector: CompilationOutput -> ErrorInfo list) (result: CompilationResult) : CompilationResult = @@ -1671,6 +1671,6 @@ module rec Compiler = s.Replace("\r", "").Split('\n') |> Array.map (fun line -> line.TrimEnd()) |> String.concat "\n" - + let printSignatures cUnit = printSignaturesImpl None cUnit let printSignaturesWith pageWidth cUnit = printSignaturesImpl (Some pageWidth) cUnit From a2e7f1e2019a570eddb79e2c448c0485d7197e6d Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Fri, 2 Jun 2023 17:39:27 +0100 Subject: [PATCH 24/30] More tests --- .../ConstraintSolver/ObjInference.fs | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index ebe55bab6e5..59996bc7708 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -7,20 +7,30 @@ module ObjInference = let message = "A type has been implicitly inferred as 'obj', which may be unintended. Consider adding explicit type annotations. You can disable this warning by using '#nowarn \"3559\"' or '--nowarn:3559'." - let warningCases = + let quotableWarningCases = [ - "let f() = ([] = [])", 1, 17, 1, 19 """System.Object.ReferenceEquals(null, "hello") |> ignore""", 1, 31, 1, 35 """System.Object.ReferenceEquals("hello", null) |> ignore""", 1, 40, 1, 44 + "([] = []) |> ignore", 1, 7, 1, 9 "<@ [] = [] @> |> ignore", 1, 9, 1, 11 + "let _ = Unchecked.defaultof<_> in ()", 1, 29, 1, 30 + ] + |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + + let unquotableWarningCases = + [ + "let f() = ([] = [])", 1, 17, 1, 19 """let f<'b> (x : 'b) : int = failwith "" let deserialize<'v> (s : string) : 'v = failwith "" let x = deserialize "" |> f""", 3, 9, 3, 28 "let f = typedefof<_>", 1, 19, 1, 20 - "let f = Unchecked.defaultof<_>" + """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 30, 1, 41 ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) + let warningCases = + quotableWarningCases @ unquotableWarningCases + [] [] let ``Warning is emitted when type Obj is inferred``(code: string, line1: int, col1: int, line2: int, col2: int) = @@ -31,25 +41,18 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 |> shouldFail |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) - let f () = - let (a : 'a) = failwith<'a> "" - unbox<'a> a + let quotableNoWarningCases = + [ + "let a = 5 |> unbox in let b = a in ()" // explicit obj annotation + "let add x y = x + y in ()" // inferred as int + "let f() = ([] = ([] : obj list)) in ()" // obj is inferred, but is annotated + "let f() = (([] : obj list) = []) in ()" // obj is inferred, but is annotated + "let f () : int = Unchecked.defaultof<_> in ()" // explicitly int + "let f () = Unchecked.defaultof in ()" // explicitly int + ] + |> List.map Array.singleton - [] - let ``Three types refined to obj are all warned`` () = - FSharp """let f<'b> () : 'b = (let a = failwith "" in unbox a)""" - |> withErrorRanges - |> withWarnOn 3559 - |> typecheck - |> shouldFail - |> withDiagnostics - [ - // The `failwith` case - Warning 3559, Line 1, Col 30, Line 1, Col 41, message - // Warning 3559, Line 1, Col 45, Line 1, Col 52, message - ] - - let noWarningCases = + let unquotableNoWarningCases = [ "let add x y = x + y" // inferred as int "let inline add x y = x + y" // inferred with SRTP @@ -59,12 +62,14 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 "let f() = (([] : obj list) = [])" // obj is inferred, but is annotated """let x<[]'m> : int<'m> = failwith "" let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covered by this warning - "let a = 5 |> unbox in let b = a in ()" // explicit obj annotation - "let f () : int = Unchecked.defaultof<_>" - "let f () = Unchecked.defaultof" + "let f () : int = Unchecked.defaultof<_>" // explicitly int + "let f () = Unchecked.defaultof" // explicitly int + "let f () = Unchecked.defaultof<_>" // generic ] |> List.map Array.singleton + let noWarningCases = quotableNoWarningCases @ unquotableNoWarningCases + [] [] let ``Warning does not fire unless required``(code: string) = @@ -82,7 +87,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [] [] - let ``Don't warn on an explicit null``(expr: string) = + let ``Don't warn on an explicitly annotated null``(expr: string) = sprintf "%s |> ignore" expr |> FSharp |> withWarnOn 3559 @@ -91,31 +96,36 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere [] [] - let ``Don't warn on an explicit null, inside quotations``(expr: string) = + let ``Don't warn on an explicitly annotated null, inside quotations``(expr: string) = sprintf "<@ %s @> |> ignore" expr |> FSharp |> withWarnOn 3559 |> typecheck |> shouldSucceed - let quotationNoWarningCases = - [ - "<@ List.map ignore [1;2;3] @>" - ] - |> List.map Array.singleton + [] + [] + let ``Warn also inside quotations of acceptable code``(expr: string, line1: int, col1: int, line2: int, col2: int) = + sprintf "<@ %s @> |> ignore" expr + |> FSharp + |> withWarnOn 3559 + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) [] - [] + [] let ``Don't warn inside quotations of acceptable code``(expr: string) = - sprintf "%s |> ignore" expr + sprintf "<@ %s @> |> ignore" expr |> FSharp |> withWarnOn 3559 |> typecheck |> shouldSucceed - [] - let ``Warning is off by default``() = - "<@ [] = [] @> |> ignore" + [] + [] + let ``Warning is off by default``(expr: string, _: int, _: int, _: int, _: int) = + expr |> FSharp |> typecheck |> shouldSucceed From 3ee49289282f188c2e9da903100b3d9e6b71ae1e Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 5 Jun 2023 19:22:48 +0100 Subject: [PATCH 25/30] Downgrade warning --- src/Compiler/Checking/TypeRelations.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index 458ec40f46f..bda81af9729 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -182,7 +182,7 @@ let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = match tp.Kind with | TyparKind.Type -> if not isRefined then - warning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) + informationalWarning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) | TyparKind.Measure -> () maxTy, m From 3356912755f6c3934a093491efde7edd6a64f41a Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Mon, 5 Jun 2023 19:56:03 +0100 Subject: [PATCH 26/30] Fix tests after merge --- .../ConstraintSolver/ObjInference.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 59996bc7708..c0a3f5ac76b 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -24,7 +24,7 @@ module ObjInference = let deserialize<'v> (s : string) : 'v = failwith "" let x = deserialize "" |> f""", 3, 9, 3, 28 "let f = typedefof<_>", 1, 19, 1, 20 - """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 30, 1, 41 + """let f<'b> () : 'b = (let a = failwith "" in unbox a)""", 1, 26, 1, 27 ] |> List.map (fun (str, line1, col1, line2, col2) -> [| box str ; line1 ; col1 ; line2 ; col2 |]) @@ -39,7 +39,7 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 |> withWarnOn 3559 |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3559, Line line1, Col col1, Line line2, Col col2, message) + |> withSingleDiagnostic (Information 3559, Line line1, Col col1, Line line2, Col col2, message) let quotableNoWarningCases = [ @@ -111,7 +111,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere |> withWarnOn 3559 |> typecheck |> shouldFail - |> withSingleDiagnostic (Warning 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) + |> withSingleDiagnostic (Information 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) [] [] From 862c87d4f7d8312e942a5e87d6d32a3e4bbb6d5b Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 6 Jun 2023 18:36:58 +0100 Subject: [PATCH 27/30] Gate the diagnostic --- src/Compiler/Checking/TypeRelations.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index bda81af9729..d0642bdf44f 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -4,6 +4,7 @@ /// constraint solving and method overload resolution. module internal FSharp.Compiler.TypeRelations +open FSharp.Compiler.Features open Internal.Utilities.Collections open Internal.Utilities.Library open FSharp.Compiler.DiagnosticsLogger @@ -182,7 +183,8 @@ let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = match tp.Kind with | TyparKind.Type -> if not isRefined then - informationalWarning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) + let supported = g.langVersion.SupportsFeature LanguageFeature.DiagnosticForObjInference + informationalWarning(ErrorEnabledWithLanguageFeature(FSComp.SR.typrelNeverRefinedAwayFromTop(), m, supported)) | TyparKind.Measure -> () maxTy, m From 2a7d7d9584825843e461f3178b619d2ddd0c5316 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Thu, 8 Jun 2023 08:38:13 +0100 Subject: [PATCH 28/30] Fix error --- src/Compiler/Checking/TypeRelations.fs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/TypeRelations.fs b/src/Compiler/Checking/TypeRelations.fs index d0642bdf44f..49afd4825b6 100644 --- a/src/Compiler/Checking/TypeRelations.fs +++ b/src/Compiler/Checking/TypeRelations.fs @@ -180,12 +180,12 @@ let ChooseTyparSolutionAndRange (g: TcGlobals) amap (tp:Typar) = | TyparConstraint.DefaultsTo(_priority, _ty, m) -> (maxTy, isRefined), m) - match tp.Kind with - | TyparKind.Type -> - if not isRefined then - let supported = g.langVersion.SupportsFeature LanguageFeature.DiagnosticForObjInference - informationalWarning(ErrorEnabledWithLanguageFeature(FSComp.SR.typrelNeverRefinedAwayFromTop(), m, supported)) - | TyparKind.Measure -> () + if g.langVersion.SupportsFeature LanguageFeature.DiagnosticForObjInference then + match tp.Kind with + | TyparKind.Type -> + if not isRefined then + informationalWarning(Error(FSComp.SR.typrelNeverRefinedAwayFromTop(), m)) + | TyparKind.Measure -> () maxTy, m From 710bab08c6c9a4ff9c3f259534700f7dee58fdbf Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Thu, 8 Jun 2023 21:29:41 +0100 Subject: [PATCH 29/30] Enable language version in tests --- .../ConstraintSolver/ObjInference.fs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index c0a3f5ac76b..52837785e88 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -37,6 +37,7 @@ let x = deserialize "" |> f""", 3, 9, 3, 28 FSharp code |> withErrorRanges |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldFail |> withSingleDiagnostic (Information 3559, Line line1, Col col1, Line line2, Col col2, message) @@ -75,6 +76,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Warning does not fire unless required``(code: string) = FSharp code |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -91,6 +93,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere sprintf "%s |> ignore" expr |> FSharp |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -109,6 +112,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere sprintf "<@ %s @> |> ignore" expr |> FSharp |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldFail |> withSingleDiagnostic (Information 3559, Line line1, Col (col1 + 3), Line line2, Col (col2 + 3), message) @@ -119,6 +123,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere sprintf "<@ %s @> |> ignore" expr |> FSharp |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -127,5 +132,6 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere let ``Warning is off by default``(expr: string, _: int, _: int, _: int, _: int) = expr |> FSharp + |> withLangVersionPreview |> typecheck |> shouldSucceed From ac53fb65418f498015cadfd86082d0f9f0cf2cc6 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 14 Jun 2023 16:54:54 +0100 Subject: [PATCH 30/30] Markups --- .../ConstraintSolver/ObjInference.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs index 52837785e88..358e10a2fa4 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/ObjInference.fs @@ -103,6 +103,7 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere sprintf "<@ %s @> |> ignore" expr |> FSharp |> withWarnOn 3559 + |> withLangVersionPreview |> typecheck |> shouldSucceed @@ -133,5 +134,6 @@ let f () = x = x |> ignore""" // measure is inferred as 1, but that's not covere expr |> FSharp |> withLangVersionPreview + |> withOptions ["--warnaserror"] |> typecheck |> shouldSucceed