diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index d406d513e96..75be5145a6a 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -13,6 +13,7 @@ * Fix NRE when accessing nullable fields of types within their equals/hash/compare methods ([PR #18296](https://github.com/dotnet/fsharp/pull/18296)) * Fix nullness warning for overrides of generic code with nullable type instance ([Issue #17988](https://github.com/dotnet/fsharp/issues/17988), [PR #18337](https://github.com/dotnet/fsharp/pull/18337)) * Unsafe downcast from `obj` to generic `T` no longer requires `not null` constraint on `T`([Issue #18275](https://github.com/dotnet/fsharp/issues/18275), [PR #18343](https://github.com/dotnet/fsharp/pull/18343)) +* Fix "type inference problem too complicated" for SRTP with T:null and T:struct dummy constraint([Issue #18288](https://github.com/dotnet/fsharp/issues/18288), [PR #18345](https://github.com/dotnet/fsharp/pull/18345)) * Fix for missing parse diagnostics in TransparentCompiler.ParseAndCheckProject ([PR #18366](https://github.com/dotnet/fsharp/pull/18366)) ### Added diff --git a/src/Compiler/Checking/AugmentWithHashCompare.fs b/src/Compiler/Checking/AugmentWithHashCompare.fs index 7efb505c9f3..b2e2eaf9be7 100644 --- a/src/Compiler/Checking/AugmentWithHashCompare.fs +++ b/src/Compiler/Checking/AugmentWithHashCompare.fs @@ -1647,13 +1647,7 @@ let rec TypeDefinitelyHasEquality g ty = match appTy with | ValueSome(tcref, _) when HasFSharpAttribute g g.attrib_NoEqualityAttribute tcref.Attribs -> false | _ -> - if - isTyparTy g ty - && (destTyparTy g ty).Constraints - |> List.exists (function - | TyparConstraint.SupportsEquality _ -> true - | _ -> false) - then + if ty |> IsTyparTyWithConstraint g _.IsSupportsEquality then true else match ty with diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index f6b405bf1d6..8422bd40833 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -2138,17 +2138,13 @@ module TyconConstraintInference = // Is the field type a type parameter? match tryDestTyparTy g ty with - | ValueSome tp -> - // Look for an explicit 'comparison' constraint - if tp.Constraints |> List.exists (function TyparConstraint.SupportsComparison _ -> true | _ -> false) then - true - + | ValueSome tp when tp |> HasConstraint _.IsSupportsComparison -> true + | ValueSome tp -> // Within structural types, type parameters can be optimistically assumed to have comparison // We record the ones for which we have made this assumption. - elif tycon.TyparsNoRange |> List.exists (fun tp2 -> typarRefEq tp tp2) then + if tycon.TyparsNoRange |> List.exists (fun tp2 -> typarRefEq tp tp2) then assumedTyparsAcc <- assumedTyparsAcc.Add(tp.Stamp) - true - + true else false | _ -> @@ -2267,14 +2263,11 @@ module TyconConstraintInference = // and type parameters. let rec checkIfFieldTypeSupportsEquality (tycon: Tycon) (ty: TType) = match tryDestTyparTy g ty with + | ValueSome tp when tp |> HasConstraint _.IsSupportsEquality -> true | ValueSome tp -> - // Look for an explicit 'equality' constraint - if tp.Constraints |> List.exists (function TyparConstraint.SupportsEquality _ -> true | _ -> false) then - true - // Within structural types, type parameters can be optimistically assumed to have equality // We record the ones for which we have made this assumption. - elif tycon.Typars(tycon.Range) |> List.exists (fun tp2 -> typarRefEq tp tp2) then + if tycon.Typars(tycon.Range) |> List.exists (fun tp2 -> typarRefEq tp tp2) then assumedTyparsAcc <- assumedTyparsAcc.Add(tp.Stamp) true else diff --git a/src/Compiler/Checking/ConstraintSolver.fs b/src/Compiler/Checking/ConstraintSolver.fs index a45bb37234f..02829f564ad 100644 --- a/src/Compiler/Checking/ConstraintSolver.fs +++ b/src/Compiler/Checking/ConstraintSolver.fs @@ -958,13 +958,6 @@ let rec SolveTyparEqualsTypePart1 (csenv: ConstraintSolverEnv) m2 (trace: Option // Record the solution before we solve the constraints, since // We may need to make use of the equation when solving the constraints. // Record a entry in the undo trace if one is provided - - //let ty1AllowsNull = r.Constraints |> List.exists (function | TyparConstraint.SupportsNull _ -> true | _ -> false ) - //let tyAllowsNull() = TypeNullIsExtraValueNew csenv.g m2 ty - //if ty1AllowsNull && not (tyAllowsNull()) then - // trace.Exec (fun () -> r.typar_solution <- Some (ty |> replaceNullnessOfTy csenv.g.knownWithNull)) (fun () -> r.typar_solution <- None) - //else - // trace.Exec (fun () -> r.typar_solution <- Some ty) (fun () -> r.typar_solution <- None) trace.Exec (fun () -> r.typar_solution <- Some ty) (fun () -> r.typar_solution <- None) } @@ -1295,8 +1288,9 @@ and SolveTypeEqualsType (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTr SolveTyparEqualsType csenv ndeep m2 trace sty1 (replaceNullnessOfTy g.knownWithoutNull sty2) | ValueSome NullnessInfo.WithoutNull, ValueSome NullnessInfo.WithoutNull when csenv.IsSupportsNullFlex && - isAppTy g sty2 && - tp1.Constraints |> List.exists (function TyparConstraint.SupportsNull _ -> true | _ -> false) -> + isAppTy g sty2 && + tp1 |> HasConstraint _.IsSupportsNull && + not(tp1 |> HasConstraint _.IsIsNonNullableStruct)-> let tpNew = NewCompGenTypar(TyparKind.Type, TyparRigidity.Flexible, TyparStaticReq.None, TyparDynamicReq.No, false) trackErrors { do! SolveTypeEqualsType csenv ndeep m2 trace cxsln (TType_var(tpNew, g.knownWithoutNull)) sty2 @@ -1614,10 +1608,10 @@ and SolveTyparSubtypeOfType (csenv: ConstraintSolverEnv) ndeep m2 trace tp ty1 = else AddConstraint csenv ndeep m2 trace tp (TyparConstraint.CoercesTo(ty1, csenv.m)) -and DepthCheck ndeep m = - if ndeep > 300 then - error(Error(FSComp.SR.csTypeInferenceMaxDepth(), m)) - else +and DepthCheck ndeep m = + if ndeep > 300 then + error(Error(FSComp.SR.csTypeInferenceMaxDepth(), m)) + else CompleteD // If this is a type that's parameterized on a unit-of-measure (expected to be numeric), unify its measure with 1 @@ -2426,7 +2420,9 @@ and EnforceConstraintConsistency (csenv: ConstraintSolverEnv) ndeep m2 trace ret return! SolveTypeEqualsTypeKeepAbbrevs csenv ndeep m2 trace retTy1 retTy2 | TyparConstraint.SupportsComparison _, TyparConstraint.IsDelegate _ - | TyparConstraint.IsDelegate _, TyparConstraint.SupportsComparison _ + | TyparConstraint.IsDelegate _, TyparConstraint.SupportsComparison _ -> + return! ErrorD (Error(FSComp.SR.csComparisonDelegateConstraintInconsistent(), m)) + | TyparConstraint.IsNonNullableStruct _, TyparConstraint.IsReferenceType _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsNonNullableStruct _ -> return! ErrorD (Error(FSComp.SR.csStructConstraintInconsistent(), m)) @@ -2434,6 +2430,11 @@ and EnforceConstraintConsistency (csenv: ConstraintSolverEnv) ndeep m2 trace ret | TyparConstraint.SupportsNull _, TyparConstraint.NotSupportsNull _ | TyparConstraint.NotSupportsNull _, TyparConstraint.SupportsNull _ -> return! ErrorD (Error(FSComp.SR.csNullNotNullConstraintInconsistent(), m)) + + | TyparConstraint.SupportsNull _, TyparConstraint.IsNonNullableStruct _ + | TyparConstraint.IsNonNullableStruct _, TyparConstraint.SupportsNull _ -> + () + //return! WarnD (Error(FSComp.SR.csNullStructConstraintInconsistent(), m)) | TyparConstraint.IsUnmanaged _, TyparConstraint.IsReferenceType _ | TyparConstraint.IsReferenceType _, TyparConstraint.IsUnmanaged _ -> @@ -2640,7 +2641,7 @@ and SolveTypeUseSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty = | ValueSome NullnessInfo.WithoutNull -> return! AddConstraint csenv ndeep m2 trace tp (TyparConstraint.SupportsNull m) | _ -> - if tp.Constraints |> List.exists (function | TyparConstraint.IsReferenceType _ -> true | _ -> false) |> not then + if not (tp |> HasConstraint _.IsIsReferenceType) then do! AddConstraint csenv ndeep m2 trace tp (TyparConstraint.IsReferenceType m) return! SolveNullnessSupportsNull csenv ndeep m2 trace ty nullness | _ -> @@ -2737,7 +2738,7 @@ and SolveTypeCanCarryNullness (csenv: ConstraintSolverEnv) ty nullness = let strippedTy = stripTyEqnsA g true ty match tryAddNullnessToTy nullness strippedTy with | Some _ -> - if isTyparTy g strippedTy && not (isReferenceTyparTy g strippedTy) then + if isTyparTy g strippedTy && not (IsReferenceTyparTy g strippedTy) then return! AddConstraint csenv 0 m NoTrace (destTyparTy g strippedTy) (TyparConstraint.IsReferenceType m) | None -> let tyString = NicePrint.minimalStringOfType csenv.DisplayEnv strippedTy @@ -2978,10 +2979,11 @@ and SolveTypeRequiresDefaultValue (csenv: ConstraintSolverEnv) ndeep m2 trace or let g = csenv.g let m = csenv.m let ty = stripTyEqnsAndMeasureEqns g origTy + if isTyparTy g ty then - if isNonNullableStructTyparTy g ty then + if IsNonNullableStructTyparTy g ty then SolveTypeRequiresDefaultConstructor csenv ndeep m2 trace ty - elif isReferenceTyparTy g ty then + elif IsReferenceTyparTy g ty then SolveTypeUseSupportsNull csenv ndeep m2 trace ty else ErrorD (ConstraintSolverError(FSComp.SR.csGenericConstructRequiresStructOrReferenceConstraint(), m, m2)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index e873ba12f17..9dba2586e0f 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -2194,7 +2194,7 @@ module GeneralizationHelpers = let relevantUniqueSubtypeConstraint (tp: Typar) = // Find a single subtype constraint - match tp.Constraints |> List.partition (function TyparConstraint.CoercesTo _ -> true | _ -> false) with + match tp.Constraints |> List.partition _.IsCoercesTo with | [TyparConstraint.CoercesTo(tgtTy, _)], others -> // Throw away null constraints if they are implied if others |> List.exists (function TyparConstraint.SupportsNull _ -> not (TypeNullIsExtraValue g m tgtTy) | _ -> true) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 1faeaddf82f..791fed38a3d 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -5676,32 +5676,14 @@ and GenGenericParam cenv eenv (tp: Typar) = |> List.map (GenTypeAux cenv tp.Range eenv.tyenv VoidNotOK PtrTypesNotOK) let refTypeConstraint = - tp.Constraints - |> List.exists (function - | TyparConstraint.IsReferenceType _ - // 'null' automatically implies 'not struct' - | TyparConstraint.SupportsNull _ -> true - | _ -> false) + tp |> HasConstraint(fun tc -> tc.IsIsReferenceType || tc.IsSupportsNull) // `null` implies not struct - let notNullableValueTypeConstraint = - tp.Constraints - |> List.exists (function - | TyparConstraint.IsNonNullableStruct _ -> true - | _ -> false) + let notNullableValueTypeConstraint = tp |> HasConstraint _.IsIsNonNullableStruct let nullnessOfTypar = if g.langFeatureNullness && g.checkNullness then - let hasNotSupportsNull = - tp.Constraints - |> List.exists (function - | TyparConstraint.NotSupportsNull _ -> true - | _ -> false) - - let hasSupportsNull () = - tp.Constraints - |> List.exists (function - | TyparConstraint.SupportsNull _ -> true - | _ -> false) + let hasNotSupportsNull = tp |> HasConstraint _.IsNotSupportsNull + let hasSupportsNull () = tp |> HasConstraint _.IsSupportsNull if hasNotSupportsNull || notNullableValueTypeConstraint then NullnessInfo.WithoutNull @@ -5714,17 +5696,11 @@ and GenGenericParam cenv eenv (tp: Typar) = None let defaultConstructorConstraint = - tp.Constraints - |> List.exists (function - | TyparConstraint.RequiresDefaultConstructor _ -> true - | _ -> false) + tp |> HasConstraint _.IsRequiresDefaultConstructor let emitUnmanagedInIlOutput = cenv.g.langVersion.SupportsFeature(LanguageFeature.UnmanagedConstraintCsharpInterop) - && tp.Constraints - |> List.exists (function - | TyparConstraint.IsUnmanaged _ -> true - | _ -> false) + && tp |> HasConstraint _.IsIsUnmanaged let tpName = // use the CompiledName if given diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index eedaecbb9be..9bfa12ce963 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -324,6 +324,8 @@ csMethodFoundButIsStatic,"The type '%s' has a method '%s' (full name '%s'), but csMethodFoundButIsNotStatic,"The type '%s' has a method '%s' (full name '%s'), but the method is not static" 472,csStructConstraintInconsistent,"The constraints 'struct' and 'not struct' are inconsistent" 473,csUnmanagedConstraintInconsistent,"The constraints 'unmanaged' and 'not struct' are inconsistent" +474,csComparisonDelegateConstraintInconsistent,"The constraints 'comparison' and 'delegate' are inconsistent" +475,csNullStructConstraintInconsistent,"The constraints 'struct' and 'null' are inconsistent" csTypeDoesNotHaveNull,"The type '%s' does not have 'null' as a proper value" csNullableTypeDoesNotHaveNull,"The type '%s' does not have 'null' as a proper value. To create a null value for a Nullable type use 'System.Nullable()'." csTypeDoesNotSupportComparison1,"The type '%s' does not support the 'comparison' constraint because it has the 'NoComparison' attribute" diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 4cabe94d952..e5e330759dc 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9150,41 +9150,34 @@ let IsUnionTypeWithNullAsTrueValue (g: TcGlobals) (tycon: Tycon) = let TyconCompilesInstanceMembersAsStatic g tycon = IsUnionTypeWithNullAsTrueValue g tycon let TcrefCompilesInstanceMembersAsStatic g (tcref: TyconRef) = TyconCompilesInstanceMembersAsStatic g tcref.Deref +let inline HasConstraint ([] predicate) (tp:Typar) = + tp.Constraints |> List.exists predicate + +let inline tryGetTyparTyWithConstraint g ([] predicate) ty = + match tryDestTyparTy g ty with + | ValueSome tp as x when HasConstraint predicate tp -> x + | _ -> ValueNone + +let inline IsTyparTyWithConstraint g ([] predicate) ty = + match tryDestTyparTy g ty with + | ValueSome tp -> HasConstraint predicate tp + | ValueNone -> false + // Note, isStructTy does not include type parameters with the ': struct' constraint // This predicate is used to detect those type parameters. -let isNonNullableStructTyparTy g ty = - match tryDestTyparTy g ty with - | ValueSome tp -> - tp.Constraints |> List.exists (function TyparConstraint.IsNonNullableStruct _ -> true | _ -> false) - | ValueNone -> - false +let IsNonNullableStructTyparTy g ty = ty |> IsTyparTyWithConstraint g _.IsIsNonNullableStruct // Note, isRefTy does not include type parameters with the ': not struct' or ': null' constraints // This predicate is used to detect those type parameters. -let isReferenceTyparTy g ty = - match tryDestTyparTy g ty with - | ValueSome tp -> - tp.Constraints |> List.exists (function - | TyparConstraint.IsReferenceType _ -> true - | TyparConstraint.SupportsNull _ -> true - | _ -> false) - | ValueNone -> - false +let IsReferenceTyparTy g ty = ty |> IsTyparTyWithConstraint g (fun tc -> tc.IsIsReferenceType || tc.IsSupportsNull) -let GetTyparTyIfSupportsNull g ty = - if isReferenceTyparTy g ty then - let tp = destTyparTy g ty - if tp.Constraints |> List.exists (function TyparConstraint.SupportsNull _ -> true | _ -> false) then - ValueSome tp - else ValueNone - else - ValueNone +let GetTyparTyIfSupportsNull g ty = ty |> tryGetTyparTyWithConstraint g _.IsSupportsNull let TypeNullNever g ty = let underlyingTy = stripTyEqnsAndMeasureEqns g ty isStructTy g underlyingTy || isByrefTy g underlyingTy || - isNonNullableStructTyparTy g ty + IsNonNullableStructTyparTy g ty /// The pre-nullness logic about whether a type admits the use of 'null' as a value. let TypeNullIsExtraValue g m ty = @@ -9244,7 +9237,7 @@ let changeWithNullReqTyToVariable g reqTy = let reqTyForArgumentNullnessInference g actualTy reqTy = // Only change reqd nullness if actualTy is an inference variable match tryDestTyparTy g actualTy with - | ValueSome t when t.IsCompilerGenerated && not(t.Constraints |> List.exists(function | TyparConstraint.SupportsNull _ -> true | _ -> false))-> + | ValueSome t when t.IsCompilerGenerated && not(t |> HasConstraint _.IsSupportsNull) -> changeWithNullReqTyToVariable g reqTy | _ -> reqTy @@ -9366,8 +9359,9 @@ let rec TypeHasDefaultValueAux isNew g m ty = true)) || // Check for type variables with the ":struct" and "(new : unit -> 'T)" constraints - (isNonNullableStructTyparTy g ty && - (destTyparTy g ty).Constraints |> List.exists (function TyparConstraint.RequiresDefaultConstructor _ -> true | _ -> false)) + ( match ty |> tryGetTyparTyWithConstraint g _.IsIsNonNullableStruct with + | ValueSome tp -> tp |> HasConstraint _.IsRequiresDefaultConstructor + | ValueNone -> false) let TypeHasDefaultValue (g: TcGlobals) m ty = TypeHasDefaultValueAux false g m ty @@ -9984,7 +9978,7 @@ let isCompiledOrWitnessPassingConstraint (g: TcGlobals) cx = // FSharpTypeFunc, but rather bake a "local type function" for each TyLambda abstraction. let IsGenericValWithGenericConstraints g (v: Val) = isForallTy g v.Type && - v.Type |> destForallTy g |> fst |> List.exists (fun tp -> List.exists (isCompiledOrWitnessPassingConstraint g) tp.Constraints) + v.Type |> destForallTy g |> fst |> List.exists (fun tp -> HasConstraint (isCompiledOrWitnessPassingConstraint g) tp) // Does a type support a given interface? type Entity with diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 7e98ce5bc78..c67ccb30800 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -1734,13 +1734,18 @@ val isStructOrEnumTyconTy: TcGlobals -> TType -> bool /// /// Note, isStructTy does not include type parameters with the ': struct' constraint /// This predicate is used to detect those type parameters. -val isNonNullableStructTyparTy: TcGlobals -> TType -> bool +val IsNonNullableStructTyparTy: TcGlobals -> TType -> bool + +val inline HasConstraint: [] predicate: (TyparConstraint -> bool) -> Typar -> bool + +val inline IsTyparTyWithConstraint: + TcGlobals -> [] predicate: (TyparConstraint -> bool) -> TType -> bool /// Determine if a type is a variable type with the ': not struct' constraint. /// /// Note, isRefTy does not include type parameters with the ': not struct' constraint /// This predicate is used to detect those type parameters. -val isReferenceTyparTy: TcGlobals -> TType -> bool +val IsReferenceTyparTy: TcGlobals -> TType -> bool /// Determine if a type is an unmanaged type val isUnmanagedTy: TcGlobals -> TType -> bool diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 74894da5d02..7d64601c303 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -172,6 +172,11 @@ Dostupná přetížení:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' {0} nepodporuje typ {1}, protože tento typ postrádá požadovaný (skutečný nebo vestavěný) člen {2} @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match Argument na indexu {0} neodpovídá diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 77409615867..67f4270377e 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -172,6 +172,11 @@ Verfügbare Überladungen:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' "{0}" unterstützt den Typ "{1}" nicht, da letzteres nicht das erforderliche (echte oder integrierte) Element "{2}" aufweist. @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match Das Argument bei Index {0} stimmt nicht überein. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 9f042384d39..9478e3d2c55 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -172,6 +172,11 @@ Sobrecargas disponibles:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}' no admite el tipo '{1}', porque a este último le falta el '{2}' de miembro necesario (real o integrado) @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match El argumento del índice {0} no coincide. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 621286a8e21..57fade5d250 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -172,6 +172,11 @@ Surcharges disponibles :\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}' ne prend pas en charge le type '{1}', car ce dernier n'a pas le membre requis (réel ou intégré) '{2}' @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match L'argument à l'index {0} ne correspond pas diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 1d7bdf56aa0..14d670e8455 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -172,6 +172,11 @@ Overload disponibili:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}' non supporta il tipo '{1}', perché in quest'ultimo manca il membro '{2}' richiesto (reale o predefinito) @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match L'argomento alla posizione di indice {0} non corrisponde diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index c928a51da3e..78acb0fd944 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -172,6 +172,11 @@ 使用可能なオーバーロード:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' 型 '{1}' には必要な (実数または組み込み) メンバー '{2}' がないため、'{0}' ではサポートされません @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match インデックス {0} の引数が一致しません diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index aaff373fcda..604ac431e4a 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -172,6 +172,11 @@ 사용 가능한 오버로드:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{1}' 형식에는 필수(실제 또는 기본 제공) 멤버 '{2}'이(가) 없기 때문에 '{0}'이(가) 이 형식을 지원하지 않습니다. @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match 인덱스 {0}의 인수가 일치하지 않습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index f886e72e8f8..fdee3d82f7d 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -172,6 +172,11 @@ Dostępne przeciążenia:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' Element „{0}” nie obsługuje typu „{1}”, ponieważ ten drugi nie ma wymaganej (rzeczywistej lub wbudowanej) składowej „{2}” @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match Argument pod indeksem {0} nie jest zgodny diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 69082574676..2f86c57d960 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -172,6 +172,11 @@ Sobrecargas disponíveis:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' "{0}" não dá suporte ao tipo "{1}", pois o último não tem o membro necessário (real ou interno) "{2}: @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match O argumento no índice {0} não corresponde diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index df0d5f9770a..fefd5255a0b 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -172,6 +172,11 @@ Доступные перегрузки:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}' не поддерживает тип '{1}', поскольку у последнего отсутствует необходимый (реальный или встроенный) член '{2}' @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match Аргумент в индексе {0} не соответствует diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 77568593137..5325f0ab09f 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -172,6 +172,11 @@ Kullanılabilir aşırı yüklemeler:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}', gerekli (gerçek veya yerleşik) '{2}' üyesine sahip olmadığından '{1}' türünü desteklemiyor @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match {0} dizinindeki bağımsız değişken eşleşmiyor diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 59532dc06d8..2e8b957d810 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -172,6 +172,11 @@ 可用重载:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' “{0}”不支持类型“{1}”,因为后者缺少所需的(实际或内置)成员“{2}” @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match 索引 {0} 处的参数不匹配 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 2274eb373c8..f0924b3d30f 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -172,6 +172,11 @@ 可用的多載:\n{0} + + The constraints 'comparison' and 'delegate' are inconsistent + The constraints 'comparison' and 'delegate' are inconsistent + + '{0}' does not support the type '{1}', because the latter lacks the required (real or built-in) member '{2}' '{0}' 不支援類型 '{1}',因為後者缺少必要的 (實際或內建) 成員 '{2}' @@ -212,6 +217,11 @@ The constraints 'null' and 'not null' are inconsistent + + The constraints 'struct' and 'null' are inconsistent + The constraints 'struct' and 'null' are inconsistent + + Argument at index {0} doesn't match 位於索引 {0} 的引數不相符 diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableRegressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableRegressionTests.fs index c549cc09aa2..486b68c7d2b 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableRegressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableRegressionTests.fs @@ -132,9 +132,34 @@ let ``DefaultValueBug when checknulls is disabled`` compilation = [] let ``With new nullness syntax nullness enabled`` compilation = compilation - |> withVersionAndCheckNulls ("preview",true) + |> withVersionAndCheckNulls ("preview",true) |> verifyBaseline +// https://github.com/dotnet/fsharp/issues/18288 +[] +let ``Inference problem limit regression previewNullness`` compilation = + compilation + |> withVersionAndCheckNulls ("preview",true) + |> withNoWarn 475 // The constraints 'struct' and 'null' are inconsistent + |> typecheck + |> shouldSucceed + +[] +let ``Inference problem limit regression previewNoNullness`` compilation = + compilation + |> withVersionAndCheckNulls ("preview",false) + |> withNoWarn 475 // The constraints 'struct' and 'null' are inconsistent + |> typecheck + |> shouldSucceed + +[] +let ``Inference problem limit regression v8`` compilation = + compilation + |> withVersionAndCheckNulls ("8.0",false) + |> withNoWarn 475 // The constraints 'struct' and 'null' are inconsistent + |> typecheck + |> shouldSucceed + [] diff --git a/tests/FSharp.Compiler.ComponentTests/Language/Nullness/inference-problem-size-explosion.fs b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/inference-problem-size-explosion.fs new file mode 100644 index 00000000000..e40f8177094 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/Nullness/inference-problem-size-explosion.fs @@ -0,0 +1,42 @@ +module M + +type ToSeq = + static member inline Invoke (source: 'FldT) : seq<'T> = + let inline call (mthd: ^M, input1: ^I) = ((^M or ^I) : (static member ToSeq : _*_ -> _) input1, mthd) + call (Unchecked.defaultof, source) + + static member inline ToSeq (x: 'Foldable , _: ToSeq) = (^Foldable: (static member ToSeq : _ -> _) x) + static member inline ToSeq (_: 'T when 'T: null and 'T: struct, _: ToSeq) = () + +type Append = + static member inline Append (x: 'AltT , y: 'AltT , _: obj ) = (^AltT : (static member Append : _*_ -> _) x, y) : 'AltT + static member inline Append (_: ^t when ^t: null and ^t: struct, _, _: obj ) = () + static member inline Append (x: Result<_,_> , y , _: Append) = match x, y with Ok _, _ -> x | Error x, Error y -> Error (x + y) | _, _ -> y + + static member inline Invoke (x: 'AltT) (y: 'AltT) : 'AltT = + let inline call (mthd: ^M, input1: ^I, input2: ^I) = ((^M or ^I) : (static member Append : _*_*_ -> _) input1, input2, mthd) + call (Unchecked.defaultof, x, y) + + static member inline Append (x: 'R -> 'AltT , y , _: Append) = fun r -> Append.Invoke (x r) (y r) + +type Choice = + static member inline Choice (x: ref<'RAltT>, _: obj) = + let t = ToSeq.Invoke x.Value + use e = t.GetEnumerator () + e.MoveNext() |> ignore + let mutable res = e.Current + while e.MoveNext() do res <- Append.Invoke res e.Current + res + + static member inline Choice (x: ref<'FAltT> , _: Choice) = (^FAltT : (static member Choice : _ -> _) x.Value) : 'AltT + static member inline Choice (_: ref< ^t> when ^t: null and ^t: struct, _: Choice) = () + + static member inline Invoke (x: 'FAltT) : 'AltT = + let inline call (mthd: ^M, input1: ^I) = ((^M or ^I) : (static member Choice : _*_ -> _) (ref input1, mthd)) + call (Unchecked.defaultof, x) + +[] +type WrappedSeqE<'s> = WrappedSeqE of 's seq with static member ToSeq (WrappedSeqE x) = x + +let v1 = [Ok 1; Error "a" ] +let v2 = Choice.Invoke (WrappedSeqE v1) \ No newline at end of file