Uh oh!
There was an error while loading. Please reload this page.
Skip accessibility checks for compiler-generated pattern inputs when used within the same module scope. - #18426
Skip accessibility checks for compiler-generated pattern inputs when used within the same module scope.#18426edgarfgp wants to merge 24 commits into
Conversation
types when the pattern is used within the same module scope.
❗ Release notes required
|
types when the pattern is used within the same module scope.
# Conflicts: # src/Compiler/Checking/PostInferenceChecks.fs # tests/FSharp.Compiler.ComponentTests/Conformance/PatternMatching/Tuple/tuples02.fs
4fa52dc to
e0424feCompareUh oh!
There was an error while loading. Please reload this page.
vzarytovskii
commented
Mar 31, 2025
Does it interact with realsig somehow? Also, probably should have tests which check runtime as well, to make sure we're not breaking any IL rules. |
edgarfgp
commented
Mar 31, 2025
No idea. I tried
Sure. |
Uh oh!
There was an error while loading. Please reload this page.
majocha
commented
Apr 3, 2025
Test failure unrelated. I can repro it on |
A couple more tests
brianrourkeboll
commented
Apr 5, 2025
OK, now I'm wondering why the compiler emits a whole separate property and backing field for the pattern input here at all instead of eliding it. namespaceNmoduleinternalM =typeT()=classendlett,t' = T (), T ()usingSystem;usingSystem.Diagnostics;usingSystem.Reflection;usingSystem.Runtime.CompilerServices;using<StartupCode$_>;usingMicrosoft.FSharp.Core;usingN;[assembly:FSharpInterfaceDataVersion(2,0,0)][assembly:AssemblyVersion("0.0.0.0")]namespaceN{[CompilationMapping(SourceConstructFlags.Module)]internalstaticclassM{[Serializable][CompilationMapping(SourceConstructFlags.ObjectType)]internalclassT{}[CompilationMapping(SourceConstructFlags.Value)]internalstaticTuple<T,T>patternInput@6{get{return $_.patternInput@6;}}[CompilationMapping(SourceConstructFlags.Value)]
internal staticT t'
{get{return $_.t'@6;}}[CompilationMapping(SourceConstructFlags.Value)]internalstaticTt{get{return $_.t@6;}}}}namespace<StartupCode$_>{internalstaticclass $_
{[DebuggerBrowsable(DebuggerBrowsableState.Never)]internalstaticTuple<M.T,M.T>patternInput@6;[DebuggerBrowsable(DebuggerBrowsableState.Never)]internalstaticM.Tt'@6;[DebuggerBrowsable(DebuggerBrowsableState.Never)]internalstaticM.Tt@6;[DebuggerBrowsable(DebuggerBrowsableState.Never)][CompilerGenerated][DebuggerNonUserCode]internalstaticintinit@;publicstaticvoidmain@(){
patternInput@6=newTuple<M.T,M.T>(newM.T(),newM.T());
t'@6=M.patternInput@6.Item2;
t@6=M.patternInput@6.Item1;}}}At first I considered whether it might be to handle something like active patterns with side effects... But even there, the compiler emits a property and backing field to store and retrieve the active pattern result only to immediately store it in the backing field emitted for moduleM =let(|A|)x = printfn "lol"; A x
let(A x)=1I wonder if getting rid of the redundant "pattern input" field/property here would make the problem this PR is addressing not exist to begin with. (Of course, there may be other scenarios where this behavior is required that I'm not thinking of... But it seems definitely unnecessary in these examples.) |
edgarfgp
commented
Apr 7, 2025
@brianrourkeboll That sounds interesting. Let's see what the team says. |
edgarfgp
commented
Apr 7, 2025
This is ready. |
psfinaki
commented
Apr 7, 2025
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
KevinRansom
commented
Apr 8, 2025
Okay ... this is not the right fix. I don't know what the correct fix is yet, but it is a bit more fundamental than skipping visibility checks for generated code. At the end of the day it boils down to the way that F# handles class initialization. C# initializes classes in the order that they are encountered executing the process, which is always the most performant choice. I.e. a class isn't initialized until some other class touches it (reads a value, invokes a static method, constructs an instance ...) F#, however, initializes types in the order the fields are encountered in the source file. Effectively every source file is a bit like a script that is processed from beginning to end. In order to achieve this F# creates a source file initialization class, that invokes a property on the class holding the module or type. In order to access these properties they need to be at least internal. Realsig addresses this using nested classes and class initialization function, but the type checker knows nothing about realsig so the old non realsig approach is operant. The problem in the example with tuples is that the F# sees a tuple and generates a closure to represent it as a type. This type is generated as an internal type. Now the function The fix is probably something along the lines of ensuring closures have an F# representation or something, so that these types of visibility checks work correctly, or failing that pass down the scope of the binding containing the closure, although that probably has many implementation difficulties of their own. I'm sorry this is such a long explanation, but every time I look at this PR I struggle with it, because the code is fine, it does what it says on the tin, and it causes the examples to work. However, it introduces a wart that no one will understand in future years. Similar to the reasons I still haven't figured out how to make the optimizer understand realsig fully, because the optimizing codegen has similar warts. Thanks for looking at this. Kevin |
I sort of came to the same conclusion but currently the visibility checks are very ad-hoc feels like playing wacamole. I hope at least this PR bring attention to this 8 years old bug. |
KevinRansom
commented
Apr 8, 2025
@edgarfgp , thanks mate, I shall close this. I will add the bug to the list of things about realsig to address. |
Description
BEFORE
AFTER
Fixes#4161
Checklist