Skip to content

Fix for #83 (improve constraint error message) - #16304

Merged
vzarytovskii merged 22 commits into
dotnet:mainfrom
Martin521:issue83fix
Dec 20, 2023
Merged

Fix for #83 (improve constraint error message)#16304
vzarytovskii merged 22 commits into
dotnet:mainfrom
Martin521:issue83fix

Conversation

@Martin521

@Martin521Martin521 commented Nov 19, 2023

Copy link
Copy Markdown
Contributor

This fix for #83 includes

  • An addition of field "source" to TraitConstraintInfo / TTrait in TypedTree.fs/fsi
  • Code in CompilerImports.fs which, after import, adds the source of the constraint to all constraints
  • A new error message in FSComp.txt
  • Application of the error message, including the constraint source, in ConstraintSolver.fs
  • 4 new tests in MemberConstraints.fs
  • A few updated test expectations in AbsTests.fs, neg61.bsl and E_NoMethodsOnEnums01.fs
  • 29 adaptations in 9 files because of the new field in TTrait

This includes
- An addition of field "source" to TraitConstraintInfo / TTrait in TypedTree.fs/fsi
- Code in CompilerImports.fs which, after import, adds the sources to all constraints
- New error messages, including the constraint source, in ConstraintSolver.fs
- The error messages in FSComp.txt
- A new test in MemberConstraints.fs
- 29 adaptations b.o. the new field in TTrait in 9 files
@Martin521
Martin521 requested a review from a team as a code ownerNovember 19, 2023 14:58
@Martin521

Copy link
Copy Markdown
ContributorAuthor

/run xlf

 Co-authored-by: Martin521 <29605222+Martin521@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

@Martin521
Martin521 marked this pull request as draft November 19, 2023 19:07
@psfinaki

Copy link
Copy Markdown
Contributor

Hey @Martin521 - thanks for taking this long-standing issue! :)

I know there is a discussion in the related ticket - my opinion is that whatever we do will be better than the current situation. That said, for me even having DivideByInt notion in the error message is weird, feels like a leaking technical detail. The code in the new test can very well be written by some F# newcomer and that message would just explode their mind. I would prefer something like "Hey, the average function expects int result which cannot be guaranteed by division".

I see that the message in FSComp is generic for types and operators so for now I would appreciate just adding a few more test cases that are expected to produce the same error, so that we can see a bigger picture.

@Martin521

Copy link
Copy Markdown
ContributorAuthor

@psfinaki - thanks for your feedback!

I see that the message in FSComp is generic for types and operators so for now I would appreciate just adding a few more test cases that are expected to produce the same error, so that we can see a bigger picture.

I will do that.

I will also respond (tomorrow) to your other remarks, but in the ticket.

@smoothdeveloper

Copy link
Copy Markdown
Contributor

@psfinaki,

the average function expects int result which cannot be guaranteed by division

AFAIU, this would require infrastructure for API authors, and adjusting FSharp.Core to use it; the infrastructure would allow to give customized error message on members with constraints, given predicate on the passed / infered type arguments, to deliver a custom message.

IMO, this is out of scope in just trying to improve the constraint not matching error, which should, unless the infrastructure & special condition that it would detect in API, always be displayed, in order to give the full picture.

@vzarytovskii

Copy link
Copy Markdown
Member

That said, for me even having DivideByInt notion in the error message is weird, feels like a leaking technical detail. The code in the new test can very well be written by some F# newcomer and that message would just explode their mind. I would prefer something like "Hey, the average function expects int result which cannot be guaranteed by division".

I personally disagree with that. It doesn't have to be either/or. It should be both - a simple message as well as bunch of details.

We don't need to simplify messages in sake of simplifying them. But rather extend them.

@DedSec256 and @auduchinok may have some opinions here.

@psfinaki

Copy link
Copy Markdown
Contributor

No yeah for sure we shouldn't overhaul the compiler just in order to make this usecase clear.

We don't need to simplify messages in sake of simplifying them. But rather extend them.

... is definitely a valid approach.

@Martin521

Copy link
Copy Markdown
ContributorAuthor

Thanks @psfinaki for asking for more examples and the broader picture. This made me realize that the picture is broader indeed. I will continue with implementation (including test cases) once the discussion on the ticket is converging.

@Martin521
Martin521 marked this pull request as ready for review December 11, 2023 21:04
@Martin521

Copy link
Copy Markdown
ContributorAuthor

This is ready.
This fixes #83 for the limited scope mentioned here.

Also note that the extended error message only appears when the constraint source (the operator or method or function that has the constraint typar) is imported. Doing the same while that source is in your own project is more difficult, but also deemed to be not needed, because both the language guide and the spec say that creating member constraints is "not intended for common use".

Comment threadsrc/Compiler/Checking/PostInferenceChecks.fs
Comment threadsrc/Compiler/TypedTree/TypedTree.fs
Comment threadsrc/Compiler/TypedTree/TypedTreePickle.fs
Comment threadsrc/Compiler/Checking/ConstraintSolver.fs
Comment threadsrc/Compiler/Driver/CompilerImports.fs
@Martin521

Copy link
Copy Markdown
ContributorAuthor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Commenter does not have sufficient privileges for PR 16304 in repo dotnet/fsharp

@T-Gro

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@vzarytovskii
vzarytovskii merged commit 73970cb into dotnet:mainDec 20, 2023
@vzarytovskii

Copy link
Copy Markdown
Member

Thanks, let's see how it looks in IDEs, and get feedback from people.

auduchinok added a commit to auduchinok/fsharp that referenced this pull request Aug 10, 2026
`addConstraintSources` (added in dotnet#16304, so that a failed member constraint
names the member it came from) is applied to every imported assembly, and
recurses through `e.ModuleOrNamespaceType` for every module and namespace
entity it finds.
For an assembly imported from IL there is nothing to find: the walk only
reads `AllValsAndMembers`, and `ImportILTypeDefs` gives every namespace and
type entity an empty val list; only an F# trait constraint produces a
`TyparConstraint.MayResolveMember` to label in the first place. Meanwhile the
recursion forces each namespace entity's `ModuleOrNamespaceType`, which
imports that namespace - so referencing an assembly ends up importing every
namespace in it, and reading every type definition, whether or not the code
touches it.
Skip the CCUs that aren't F#. FSharp.Core and F# references are still walked,
so the error messages are unchanged.
Measured with FSharpChecker.ParseAndCheckProject, keeping the results alive so
the imported assembly structures stay on the heap (averages of 3 runs, one per
process): a 486-reference F# project retains 1319.2 -> 952.1 MB (-27.8%), and
a 168-reference console project 77.7 -> 69.8 MB (-10.2%). Checking
FSharp.Compiler.Service itself (124 references, 397 sources) goes 2301.7 ->
2298.0 MB, i.e. within the noise at that size - what the imports cost there is
dwarfed by the trees of the project's own code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
T-Gro pushed a commit that referenced this pull request Aug 12, 2026
…sources (#20090)
* Don't walk non-F# assemblies when labelling trait constraint sources
`addConstraintSources` (added in #16304, so that a failed member constraint
names the member it came from) is applied to every imported assembly, and
recurses through `e.ModuleOrNamespaceType` for every module and namespace
entity it finds.
For an assembly imported from IL there is nothing to find: the walk only
reads `AllValsAndMembers`, and `ImportILTypeDefs` gives every namespace and
type entity an empty val list; only an F# trait constraint produces a
`TyparConstraint.MayResolveMember` to label in the first place. Meanwhile the
recursion forces each namespace entity's `ModuleOrNamespaceType`, which
imports that namespace - so referencing an assembly ends up importing every
namespace in it, and reading every type definition, whether or not the code
touches it.
Skip the CCUs that aren't F#. FSharp.Core and F# references are still walked,
so the error messages are unchanged.
Measured with FSharpChecker.ParseAndCheckProject, keeping the results alive so
the imported assembly structures stay on the heap (averages of 3 runs, one per
process): a 486-reference F# project retains 1319.2 -> 952.1 MB (-27.8%), and
a 168-reference console project 77.7 -> 69.8 MB (-10.2%). Checking
FSharp.Compiler.Service itself (124 references, 397 sources) goes 2301.7 ->
2298.0 MB, i.e. within the noise at that size - what the imports cost there is
dwarfed by the trees of the project's own code.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants

@Martin521@psfinaki@smoothdeveloper@vzarytovskii@T-Gro