From 3effde2d9561069856fb21ad5ff58aacaa722152 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 3 Nov 2025 17:52:22 +0100 Subject: [PATCH 1/5] Type checker: recover on checking binding parameter contraints --- .../Checking/Expressions/CheckExpressions.fs | 14 ++++---- .../TypeChecker/TypeCheckerRecoveryTests.fs | 33 ++++++++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 65e6de7f094..527a09cf1d0 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11561,12 +11561,14 @@ and TcLetBinding (cenv: cenv) isUse env containerInfo declKind tpenv (synBinds, // Canonicalize constraints prior to generalization let denv = env.DisplayEnv - CanonicalizePartialInferenceProblem cenv.css denv synBindsRange - (checkedBinds |> List.collect (fun tbinfo -> - let (CheckedBindingInfo(_, _, _, _, explicitTyparInfo, _, _, _, tauTy, _, _, _, _, _)) = tbinfo - let (ExplicitTyparInfo(_, declaredTypars, _)) = explicitTyparInfo - let maxInferredTypars = (freeInTypeLeftToRight g false tauTy) - declaredTypars @ maxInferredTypars)) + try + CanonicalizePartialInferenceProblem cenv.css denv synBindsRange + (checkedBinds |> List.collect (fun tbinfo -> + let (CheckedBindingInfo(_, _, _, _, explicitTyparInfo, _, _, _, tauTy, _, _, _, _, _)) = tbinfo + let (ExplicitTyparInfo(_, declaredTypars, _)) = explicitTyparInfo + let maxInferredTypars = (freeInTypeLeftToRight g false tauTy) + declaredTypars @ maxInferredTypars)) + with RecoverableException _ -> () let lazyFreeInEnv = lazy (GeneralizationHelpers.ComputeUngeneralizableTypars env) diff --git a/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs b/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs index 07827b7d399..1f9dd815c80 100644 --- a/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs @@ -1,9 +1,20 @@ module FSharp.Compiler.Service.Tests.TypeChecker.TypeCheckerRecoveryTests open FSharp.Compiler.Service.Tests +open FSharp.Compiler.Text open FSharp.Test.Assert open Xunit +let assertHasSymbolUsageAtCaret name source = + let context, checkResults = Checker.getCheckedResolveContext source + + getSymbolUses checkResults + |> Seq.exists (fun symbolUse -> + Range.rangeContainsPos symbolUse.Range context.Pos && + symbolUse.Symbol.DisplayNameCore = name + ) + |> shouldEqual true + [] let ``Let 01`` () = let _, checkResults = getParseAndCheckResults """ @@ -49,4 +60,24 @@ Math.Max(a,b,) "(4,0--4,14)", 503 ] - assertHasSymbolUsages ["Max"] checkResults \ No newline at end of file + assertHasSymbolUsages ["Max"] checkResults + +module Constraints = + [] + let ``Type 01`` () = + assertHasSymbolUsageAtCaret "f" """ +let f (x: string) = + x + 1 + +{caret}f "" +""" + + [] + let ``Type 02`` () = + assertHasSymbolUsageAtCaret "M" """ +type T = + static member M(x: string) = + x + 1 + +T.M{caret} "" +""" From da763ea2c36970663b56594762082af48c8f5b39 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 3 Nov 2025 18:13:29 +0100 Subject: [PATCH 2/5] Release notes --- docs/release-notes/.FSharp.Compiler.Service/11.0.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md index 6a2048ef27a..2dd57ad6d7c 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md @@ -13,6 +13,7 @@ ### Added * Add FSharpCodeCompletionOptions ([PR #19030](https://github.com/dotnet/fsharp/pull/19030)) +* Type checker: recover on checking binding parameter constraints ([#19046](https://github.com/dotnet/fsharp/pull/19046)) ### Changed From fcd4ba13df85aa7f9b1881fdce82bb872c9148e4 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 3 Nov 2025 19:38:07 +0100 Subject: [PATCH 3/5] Update baselines --- tests/fsharp/typecheck/sigs/neg68.bsl | 4 +++- tests/fsharp/typecheck/sigs/neg68.fsx | 2 +- tests/fsharp/typecheck/sigs/neg68.vsbsl | 27 ++----------------------- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/tests/fsharp/typecheck/sigs/neg68.bsl b/tests/fsharp/typecheck/sigs/neg68.bsl index 6bc722f0f1b..90d9dc41e83 100644 --- a/tests/fsharp/typecheck/sigs/neg68.bsl +++ b/tests/fsharp/typecheck/sigs/neg68.bsl @@ -1,2 +1,4 @@ -neg68.fsx(71,46,71,47): parse error FS0010: Unexpected symbol ')' in binding. Expected incomplete structured construct at or before this point or other token. +neg68.fsx(138,15,138,16): typecheck info FS3370: The use of '!' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change '!cell' to 'cell.Value'. + +neg68.fsx(158,24,158,26): typecheck info FS3370: The use of ':=' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change 'cell := expr' to 'cell.Value <- expr'. diff --git a/tests/fsharp/typecheck/sigs/neg68.fsx b/tests/fsharp/typecheck/sigs/neg68.fsx index 88bec11c73a..d9362118200 100644 --- a/tests/fsharp/typecheck/sigs/neg68.fsx +++ b/tests/fsharp/typecheck/sigs/neg68.fsx @@ -68,7 +68,7 @@ type sRealTime [] type pixels -let G = 6.67e-11 +let G = 6.67e-11 let m_per_AU = 149597870691.0 let AU_per_m = 1.0/m_per_AU let Pixels_per_AU = 200.0 diff --git a/tests/fsharp/typecheck/sigs/neg68.vsbsl b/tests/fsharp/typecheck/sigs/neg68.vsbsl index 369c1e15865..aa0620845c8 100644 --- a/tests/fsharp/typecheck/sigs/neg68.vsbsl +++ b/tests/fsharp/typecheck/sigs/neg68.vsbsl @@ -1,26 +1,3 @@ +neg68.fsx(138,15,138,16): typecheck info FS3370: The use of '!' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change '!cell' to 'cell.Value'. -neg68.fsx(71,46,71,47): parse error FS0010: Unexpected symbol ')' in binding. Expected incomplete structured construct at or before this point or other token. - -neg68.fsx(71,32,71,33): typecheck error FS0062: This construct is for ML compatibility. Consider using the '+' operator instead. This may require a type annotation to indicate it acts on strings. This message can be disabled using '--nowarn:62' or '#nowarn "62"'. - -neg68.fsx(71,30,71,46): typecheck error FS0001: This expression was expected to have type - 'float' -but here has type - 'string' - -neg68.fsx(71,30,71,31): typecheck error FS1133: No constructors are available for the type 'm' - -neg68.fsx(71,43,71,44): typecheck error FS0062: This construct is for ML compatibility. Consider using the '+' operator instead. This may require a type annotation to indicate it acts on strings. This message can be disabled using '--nowarn:62' or '#nowarn "62"'. - -neg68.fsx(71,39,71,45): typecheck error FS0001: The type 'string' does not match the type 'int' - -neg68.fsx(71,39,71,41): typecheck error FS1133: No constructors are available for the type 'kg' - -neg68.fsx(71,44,71,45): typecheck error FS0001: This expression was expected to have type - 'string' -but here has type - 'int' - -neg68.fsx(123,40,123,41): typecheck error FS0001: The type 'bool' does not match the type 'float<'u>' - -neg68.fsx(123,38,123,39): typecheck error FS0043: The type 'bool' does not match the type 'float<'u>' +neg68.fsx(158,24,158,26): typecheck info FS3370: The use of ':=' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change 'cell := expr' to 'cell.Value <- expr'. From 6b91fdf49ee0535e87cdcf196ebb064d606bbac0 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Mon, 10 Nov 2025 19:25:09 +0100 Subject: [PATCH 4/5] Drop old test --- tests/fsharp/typecheck/sigs/neg68.bsl | 4 - tests/fsharp/typecheck/sigs/neg68.fsx | 253 ------------------------ tests/fsharp/typecheck/sigs/neg68.vsbsl | 3 - 3 files changed, 260 deletions(-) delete mode 100644 tests/fsharp/typecheck/sigs/neg68.bsl delete mode 100644 tests/fsharp/typecheck/sigs/neg68.fsx delete mode 100644 tests/fsharp/typecheck/sigs/neg68.vsbsl diff --git a/tests/fsharp/typecheck/sigs/neg68.bsl b/tests/fsharp/typecheck/sigs/neg68.bsl deleted file mode 100644 index 90d9dc41e83..00000000000 --- a/tests/fsharp/typecheck/sigs/neg68.bsl +++ /dev/null @@ -1,4 +0,0 @@ - -neg68.fsx(138,15,138,16): typecheck info FS3370: The use of '!' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change '!cell' to 'cell.Value'. - -neg68.fsx(158,24,158,26): typecheck info FS3370: The use of ':=' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change 'cell := expr' to 'cell.Value <- expr'. diff --git a/tests/fsharp/typecheck/sigs/neg68.fsx b/tests/fsharp/typecheck/sigs/neg68.fsx deleted file mode 100644 index d9362118200..00000000000 --- a/tests/fsharp/typecheck/sigs/neg68.fsx +++ /dev/null @@ -1,253 +0,0 @@ -module M -//---------------------------------------------------------------------------- -// A Simple Solar System Simulator, using Units of Measure -// -// This sample code is provided "as is" without warranty of any kind. -// We disclaim all warranties, either express or implied, including the -// warranties of merchantability and fitness for a particular purpose. - -open System -open System.Windows.Forms -open System.Drawing - - -//----------------------------------------------- -// Graphics System - -// Define a special type of form that doesn't flicker -type SmoothForm() as x = - inherit Form() - do x.DoubleBuffered <- true - -let form = new SmoothForm(Text="F# Solar System Simulator", Visible=true, TopMost=true, Width=500, Height=500) - -type IPaintObject = - abstract Paint : Graphics -> unit - -// Keep a list of objects to draw -let paintObjects = new ResizeArray() - -form.Paint.Add (fun args -> - - let g = args.Graphics - // Clear the form - g.Clear(color=Color.Blue) - - // Draw the paint objects - for paintObject in paintObjects do - paintObject.Paint(g) - - // Invalidate the form again in 10 milliseconds to get continuous update - async { do! Async.Sleep(10) - form.Invalidate() } |> Async.Start - ) - -// Set things going with an initial Invalidate -form.Invalidate() - -//----------------------------------------------- - -[] -type m - -[] -type s - -[] -type kg - -[] -type km - -[] -type AU - -[] -type sRealTime - -[] -type pixels - -let G = 6.67e-11 -let m_per_AU = 149597870691.0 -let AU_per_m = 1.0/m_per_AU -let Pixels_per_AU = 200.0 -let m_per_km = 1000.0 -let AU_per_km = m_per_km * AU_per_m - -// Make 5 seconds into one year -let sec_per_year = 60.0 * 60.0 * 24.0 * 365.0 - -// One second of real time is 1/40th of a year of model time -let realTimeToModelTime (x:float) = float x * sec_per_year / 80.0 - -let pixels (x:float) = int32 x - - -type Planet(ipx:float,ipy:float, - ivx:float,ivy:float, - brush:Brush,mass:float, - width,height) = - - // For this sample e store the simulation state directly in the object - let mutable px = ipx - let mutable py = ipy - let mutable vx = ivx - let mutable vy = ivy - - member p.Mass = mass - member p.X with get() = px and set(v) = (px <- v) - member p.Y with get() = py and set(v) = (py <- v) - member p.VX with get() = vx and set(v) = (vx <- v) - member p.VY with get() = vy and set(v) = (vy <- v) - - interface IPaintObject with - member obj.Paint(g) = - - let rect = Rectangle(x=pixels (px * Pixels_per_AU)-width/2, - y=pixels (py * Pixels_per_AU)-height/2, - width=width,height=height) - g.FillEllipse(brush,rect) - - -type Simulator() = - // Get the start time for the animation - let startTime = System.DateTime.Now - let lastTimeOption = ref None - - let ComputeGravitationalAcceleration (obj:Planet) (obj2:Planet) = - let dx = (obj2.X-obj.X)*m_per_AU - let dy = (obj2.Y-obj.Y)*m_per_AU - let d2 = (dx*dx) + (dy*dy) - let d = sqrt d2 - let g = obj.Mass * obj2.Mass * G / d2 - let ax = (dx / d) * g / obj.Mass - let ay = (dy / d) * g / obj.Mass - ax,ay - - /// Find all the gravitational objects in the system except the given object - let FindObjects(obj) = - [ for paintObject in paintObjects do - match paintObject with - | :? Planet as p2 when p2 <> obj -> - yield p2 - | _ -> - yield! [] ] - - member sim.Step(time:TimeSpan) = - match !lastTimeOption with - | None -> () - | Some(lastTime) -> - for paintObject in paintObjects do - match paintObject with - | :? Planet as obj -> - let timeStep = (time - lastTime).TotalSeconds * 1.0 |> realTimeToModelTime - obj.X <- obj.X + timeStep * obj.VX - obj.Y <- obj.Y + timeStep * obj.VY - - // Find all the gravitational objects in the system - let objects = FindObjects(obj) - - // For each object, apply its gravitational field to this object - for obj2 in objects do - let (ax,ay) = ComputeGravitationalAcceleration obj obj2 - obj.VX <- obj.VX + timeStep * ax * AU_per_m - obj.VY <- obj.VY + timeStep * ay * AU_per_m - | _ -> () - - lastTimeOption := Some time - - member sim.Start() = - async { while true do - let time = System.DateTime.Now - startTime - // Sleep a little to give better GUI updates - do! Async.Sleep(1) - sim.Step(time) } - |> Async.Start - -let s = Simulator().Start() - -let massOfEarth = 5.9742e24 -let massOfMoon = 7.3477e22 -let massOfMercury = 3.3022e23 -let massOfVenus = 4.8685e24 -let massOfSun = 1.98892e30 - -let mercuryDistanceFromSun = 57910000.0 * AU_per_km -let venusDistanceFromSun = 0.723332 -let distanceFromMoonToEarth =384403.0 * AU_per_km - -let orbitalSpeedOfMoon = 1.023 * AU_per_km -let orbitalSpeedOfMercury = 47.87 * AU_per_km -let orbitalSpeedOfVenus = 35.02 * AU_per_km -let orbitalSpeedOfEarth = 29.8 * AU_per_km - -let sun = new Planet(ipx=1.1, - ipy=1.1, - ivx=0.0, - ivy=0.0, - brush=Brushes.Yellow, - mass=massOfSun, - width=20, - height=20) - -let mercury = new Planet(ipx=sun.X+mercuryDistanceFromSun, - ipy=sun.Y, - ivx=0.0, - ivy=orbitalSpeedOfMercury, - brush=Brushes.Goldenrod, - mass=massOfMercury, - width=10, - height=10) - -let venus = new Planet(ipx=sun.X+venusDistanceFromSun, - ipy=sun.Y, - ivx=0.0, - ivy=orbitalSpeedOfVenus, - brush=Brushes.BlanchedAlmond, - mass=massOfVenus, - width=10, - height=10) - -let earth = new Planet(ipx=sun.X+1.0, - ipy=sun.Y, - ivx=0.0, - ivy=orbitalSpeedOfEarth, - brush=Brushes.Green, - mass=massOfEarth, - width=10, - height=10) - -let moon = new Planet(ipx=earth.X+distanceFromMoonToEarth, - ipy=earth.Y, - ivx=earth.VX, - ivy=earth.VY+orbitalSpeedOfMoon, - brush=Brushes.White, - mass=massOfMoon, - width=2, - height=2) - -paintObjects.Add(sun) -paintObjects.Add(mercury) -paintObjects.Add(venus) -paintObjects.Add(earth) -paintObjects.Add(moon) - -form.Show() - - - -#if COMPILED -[] -do Application.Run(form) -#endif - - - - - - - - - - diff --git a/tests/fsharp/typecheck/sigs/neg68.vsbsl b/tests/fsharp/typecheck/sigs/neg68.vsbsl deleted file mode 100644 index aa0620845c8..00000000000 --- a/tests/fsharp/typecheck/sigs/neg68.vsbsl +++ /dev/null @@ -1,3 +0,0 @@ -neg68.fsx(138,15,138,16): typecheck info FS3370: The use of '!' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change '!cell' to 'cell.Value'. - -neg68.fsx(158,24,158,26): typecheck info FS3370: The use of ':=' from the F# library is deprecated. See https://aka.ms/fsharp-refcell-ops. For example, please change 'cell := expr' to 'cell.Value <- expr'. From afbe53e793ca54be7bf13b853b9357bd0b171735 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 11 Nov 2025 12:44:49 +0100 Subject: [PATCH 5/5] Remove 'neg68' type check test case Removed the test case for 'neg68' from MigratedTypeCheckTests. --- .../Miscellaneous/MigratedTypeCheckTests.fs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs index eaed687d4a0..13744a2e39e 100644 --- a/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Miscellaneous/MigratedTypeCheckTests.fs @@ -230,9 +230,6 @@ let ``type check neg66`` () = singleNegTest ( "typecheck/sigs") "neg66" [] let ``type check neg67`` () = singleNegTest ( "typecheck/sigs") "neg67" -[] -let ``type check neg68`` () = singleNegTest ( "typecheck/sigs") "neg68" - [] let ``type check neg69`` () = singleNegTest ( "typecheck/sigs") "neg69" @@ -498,4 +495,4 @@ let ``type check neg_byref_21`` () = singleNegTest ( "typecheck/sigs") "neg_byre let ``type check neg_byref_22`` () = singleNegTest ( "typecheck/sigs") "neg_byref_22" [] -let ``type check neg_byref_23`` () = singleNegTest ( "typecheck/sigs") "neg_byref_23" \ No newline at end of file +let ``type check neg_byref_23`` () = singleNegTest ( "typecheck/sigs") "neg_byref_23"