Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* Fix active pattern typechecking regression. ([Issue #18638](https://github.com/dotnet/fsharp/issues/18638), [PR #18642](https://github.com/dotnet/fsharp/pull/18642))
* Fix nullness warnings when casting non-nullable values to `IEquatable<T>` to match C# behavior. ([Issue #18759](https://github.com/dotnet/fsharp/issues/18759))
* Fix IsByRefLikeAttribute types being incorrectly suppressed in completion lists. Types like `Span<T>` and `ReadOnlySpan<T>` now appear correctly in IntelliSense.

* Fix SRTP nullness constraint resolution for types imported from older assemblies. AmbivalentToNull types now use legacy F# nullness rules instead of always satisfying `'T : null` constraints. ([Issue #18390](https://github.com/dotnet/fsharp/issues/18390), [Issue #18344](https://github.com/dotnet/fsharp/issues/18344))
* Fix Show XML doc for enum fields in external metadata ([Issue #17939](https://github.com/dotnet/fsharp/issues/17939#issuecomment-3137410105), [PR #18800](https://github.com/dotnet/fsharp/pull/18800))

### Changed
Expand Down
37 changes: 25 additions & 12 deletions src/Compiler/Checking/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,8 @@ and SolveTypMeetsTyparConstraints (csenv: ConstraintSolverEnv) ndeep m2 trace ty
| TyparConstraint.SimpleChoice(tys, m2) -> SolveTypeChoice csenv ndeep m2 trace ty tys
| TyparConstraint.CoercesTo(ty2, m2) -> SolveTypeSubsumesTypeKeepAbbrevs csenv ndeep m2 trace None ty2 ty
| TyparConstraint.MayResolveMember(traitInfo, m2) ->
SolveMemberConstraint csenv false PermitWeakResolution.No ndeep m2 trace traitInfo |> OperationResult.ignore
SolveMemberConstraint csenv false PermitWeakResolution.No ndeep m2 trace traitInfo
|> OperationResult.ignore
}

and shouldWarnUselessNullCheck (csenv:ConstraintSolverEnv) =
Expand Down Expand Up @@ -2659,18 +2660,28 @@ and SolveTypeUseSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 trace ty =
if not g.checkNullness && not (TypeNullIsExtraValue g m ty) then
return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2))
else
if TypeNullIsExtraValue g m ty then
()
elif isNullableTy g ty then
return! ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2))
else
match tryDestTyparTy g ty with
| ValueSome tp ->
do! AddConstraint csenv ndeep m2 trace tp (TyparConstraint.SupportsNull m)
| ValueNone ->
return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2))
// Use legacy F# nullness rules when langFeatureNullness is disabled
do! SolveLegacyTypeUseSupportsNullLiteral csenv ndeep m2 trace ty
}

// Common logic for legacy F# nullness rules - used for both non-langFeatureNullness path and AmbivalentToNull types
and SolveLegacyTypeUseSupportsNullLiteral (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) ty =
trackErrors {
let g = csenv.g
let m = csenv.m
let denv = csenv.DisplayEnv
if TypeNullIsExtraValue g m ty then
()
elif isNullableTy g ty then
return! ErrorD (ConstraintSolverError(FSComp.SR.csNullableTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2))
else
match tryDestTyparTy g ty with
| ValueSome tp ->
do! AddConstraint csenv ndeep m2 trace tp (TyparConstraint.SupportsNull m)
| ValueNone ->
return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeDoesNotHaveNull(NicePrint.minimalStringOfType denv ty), m, m2))
}

and SolveNullnessSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 (trace: OptionalTrace) ty nullness =
trackErrors {
let g = csenv.g
Expand All @@ -2684,7 +2695,9 @@ and SolveNullnessSupportsNull (csenv: ConstraintSolverEnv) ndeep m2 (trace: Opti
trace.Exec (fun () -> nv.Set KnownWithNull) (fun () -> nv.Unset())
| Nullness.Known n1 ->
match n1 with
| NullnessInfo.AmbivalentToNull -> ()
| NullnessInfo.AmbivalentToNull ->
// For AmbivalentToNull types (imported from older assemblies), use legacy F# nullness rules
do! SolveLegacyTypeUseSupportsNullLiteral csenv ndeep m2 trace ty
| NullnessInfo.WithNull -> ()
| NullnessInfo.WithoutNull ->
if g.checkNullness then
Expand Down
6 changes: 3 additions & 3 deletions src/Compiler/Symbols/SymbolHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,9 @@ module internal SymbolHelpers =

member x.Equals(item1, item2) =
match item1,item2 with
| null,null -> true
| null,_ | _,null -> false
| item1,item2 ->
| Null,Null -> true
| Null,_ | _,Null -> false
| NonNull item1,NonNull item2 ->
// This may explore assemblies that are not in the reference set.
// In this case just bail out and assume items are not equal
protectAssemblyExploration false (fun () ->
Expand Down
9 changes: 6 additions & 3 deletions src/Compiler/TypedTree/TypedTree.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4453,9 +4453,9 @@ type TType =
scope.QualifiedName

[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
member x.DebugText = x.ToString()
member x.DebugText = x.LimitedToString(4)

override x.ToString() =
member x.LimitedToString(maxDepth:int) =
match x with
| TType_forall (_tps, ty) -> "forall ... " + ty.ToString()
| TType_app (tcref, tinst, nullness) -> tcref.DisplayName + (match tinst with [] -> "" | tys -> "<" + String.concat "," (List.map string tys) + ">") + nullness.ToString()
Expand All @@ -4474,9 +4474,12 @@ type TType =
| TType_var (tp, _) ->
match tp.Solution with
| None -> tp.DisplayName
| Some _ -> tp.DisplayName + " (solved)"
| Some t -> tp.DisplayName + $" (solved: {if maxDepth < 0 then Boolean.TrueString else t.LimitedToString(maxDepth-1)})"
| TType_measure ms -> ms.ToString()

override x.ToString() = x.LimitedToString(4)


type TypeInst = TType list

type TTypes = TType list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@
<Compile Include="FSharpChecker\TransparentCompiler.fs" />
<Compile Include="FSharpChecker\SymbolUse.fs" />
<Compile Include="FSharpChecker\FindReferences.fs" />
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs"/>
<Compile Include="Attributes\AttributeCtorSetPropAccess.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,42 @@ printfn "{@"%A"}" result
Assert.Equal(1, (errors |> Seq.filter (fun error -> error.Message.Contains("FSharp.Really.Not.A.Package")) |> Seq.length))
Assert.Equal(1, (errors |> Seq.filter (fun error -> error.Message.Contains("FSharp.Really.Not.Another.Package")) |> Seq.length))

[<Fact>]
member _.``FsharpPlus - report errors``() =
let code = """
#i "nuget:https://api.nuget.org/v3/index.json"
#r "nuget: FSharpPlus, 1.6.1"

open FSharpPlus
open FSharpPlus.Data

let printTable x =
let lines (lst: 'Record list) =
let fields = Reflection.FSharpType.GetRecordFields typeof<'Record>
let headers = fields |> Seq.map _.Name
let asList (x:'record) = fields |> Seq.map (fun field -> string (Reflection.FSharpValue.GetRecordField(x, field)))
let rows = Seq.map asList lst
let table = seq { yield headers; yield! rows }
let maxs = table |> (Seq.traverse ZipList >> ZipList.run) |>> Seq.map length |>> maxBy id
let rowSep = String.replicate (sum maxs + length maxs - 1) "-"
let fill (i, s) = s + String.replicate (i - length s) " "
let printRow r = "|" + (r |> zip maxs |>> fill |> intercalate "|") + "|"
seq {
yield "." + rowSep + "."
yield printRow headers
yield "|" + rowSep + "|"
yield! (rows |>> printRow)
yield "'" + rowSep + "'" }
x |> lines |> iter (printfn "%s")
x |> List.length

printTable [{|Age = 15; Weight = 88; Name = "Blahboolahboogaloo"|}]
"""
use script = new FSharpScript(additionalArgs=[| |])
let opt = script.Eval(code) |> getValue
let value = opt.Value
Assert.Equal(1, downcast value.ReflectionValue)

[<Fact>]
member _.``ML - use assembly with ref dependencies``() =
let code = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5805,7 +5805,8 @@ let checkContentAsScript content =
| FSharpCheckFileAnswer.Succeeded r -> r

[<Collection(nameof NotThreadSafeResourceCollection)>]
module ScriptClosureCacheUse =
module ScriptClosureCacheUse =

[<Fact>]
let ``References from #r nuget are included in script project options`` () =
let checkResults = checkContentAsScript """
Expand Down
Loading