-
Notifications
You must be signed in to change notification settings - Fork 844
Fix 9565 - The nullable interop feature caused an issue with byrefs when enabled. #9566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Good catch! We'll want to take this for 16.7 for sure. |
|
@cartermp, no need to do that. it only occurs when using --langver:preview or 5.0. And by definition they are currently preview. The next preview release is early enough to get this in :-) |
|
@cartermp of course if it's merged by friday, then 16.7 will be the next preview :-) |
|
I don't understand the fix or the test change. What actually works and what doesn't with what features enabled/disabled? Why is this a good fix? It looks like we're now only running the |
It feels like we should be explicitly running that test with both |
|
It might be a good idea to always run the tests in 'preview' and only be explicit with versions when we want to test disabling features. |
|
@TIHan, the thing is, the test that fails, fails because of the new code, but is not a really a related feature. If we always test preview, then we may discover that we have the opposite problem, where adding a feature works in preview, but some other existing feature regresses. I think we probably pretty much need two distinct test passes. One with preview by default and one with released by default. Anyway, I thought I had added the legacy version of the test, it's just my addled brain didn't. And now I also agree with don, the fix, fixes the issue by turning the feature off for a class of types, it's a rubbish fix. I have one I can at least explain, if you are around Will, I would like to discuss the new version |
|
I think this fix feels like band-aid to a larger problem, since the fact that we're dealing with a nonoptional argument is apparently not handled correctly in the catch-all case of that pattern match. So perhaps a more appropriate fix should be done there? We don't want to have a massive test matrix for every permeation of language features. It would be hell to maintain and make our CI times unbearable. This is exactly the sort of thing that promoting features out of preview will also catch. |
|
@cartermp, |
|
Oh joy! |
TIHan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great.
I have one final suggestion:
We should add a test that looks for the language version error.
| let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr) | ||
| preBinder, { NamedArgIdOpt = None; CalledArg = calledArg; CallerArg = callerArg } | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: don't need an extra space?
|
I'd really like to see the minimal example that demonstrates the actual problem being solved here. All I see in the discussion above is that the "byrefs" test started failing when the feature is activated?
I'd say the opposite - the fix feels like it is too large for what must be a single failing case. |
|
I think we'd welcome an alternative fix if it's smaller. It's one class of failing case exemplified here, bit surfaces in many individual cases since they test different combinations of byref kinds: type C() =
static member f1 (x: inref<'T>) = 1
let f2 (x: outref<'T>) = C.f1(&x)Do you disagree with the analysis in the PR text? |
|
@dsyme , I will catch up with you next week on teams, however, this isn't really a big fix at all, indeed it is more like merely a refactoring. Firstly, the code was bifurcated and did similar things in each bifurcation. If the feature nullable interop wasn't enabled then it did this code: In the case the feature was enabled it does this code instead: callerArgExpre is returned in the exact same circumstance that assignedArg is called with the feature disabled, and is an expression for fetching the value of assignedArg. So if we can just fit in a language version check the old code can go away. It turns out that MakeNullableExpreIf needed is a good place to do that check. Now the rest of the changes, were due to the observation, that whenever the newcode returns callerArg, it is in fact just returning an expression that fetches the value for assignedArg. computed here. So we simply eliminate the need to calculate the expression in those cases by returning an option instead. And then when we have None return assignedArg. So ... and here is the bit you won't like ... the reason that byrefs actually failed, is because when we compute CallerArg2 here for expressions that contain byrefs<T, U> we create the expression incorrectly using byref. hich cause the typechecker to not recognize the expression we pass back, yielding the error messages from the explanation above. The reason this refactoring doesn't show those errors is because when we don't have to create a new expression we use the original assignedArg that was computed elsewhere and passed into this function. Rather than only fixing the | NotOptional I fixed all of the places where we return callerArg2, this reduces the work we have to do, and as a side effect fixes the byref issue. Anyway, I will call you Monday and we can discuss it in persion, if you have any other issues. Thanks Kevin |
|
@dsyme note that the lack of this fix is also preventing some of our infrastructure PRs from moving across branches. We want to get this in, in part, so that we can have regular code flow through the repo. |
I'll take a look at the issue in depth, partly to determine what went wrong with my coding techniques to ensure no change for existing code. |
|
So my approach is to look at the difference between what change happened for type C() =
static member f1 (x: inref<'T>) = 1
let f2 (x: outref<'T>) = C.f1(&x)Here are my notes to self: current master (with --langversion:5.0) this code returns: previous releases this code (and also current master with --langversion:4.7 this code) : The difference here is that in the result Now, I rewrote the code on the Specifically in this case we are allowing an My proposal is that we make a simpler fix, specifically just adding these lines: | NotOptional when not (isNullableTy g calledArgTy) ->
if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m))
assignedArgSee #9629 |
|
closing in favour of: #9629 |
Fixes #9565 . The nullable interop feature caused an issue with byrefs when enabled.
Eliminate redundent pattern match
The function is called with an assignedArg from this value is retrieved the callerArg and then the callerArgExpr. If the assigned arg is a byref'2<T, U> later on when we recreate the assigned arg due to a bug in the compiler due to having multiple representations of byref: byref'1 and byref'2<T, U>. The callerArgExpr is recreated with the wrong byref type, which messes with postinference checking. Will is going to look at how best to address that issue.
However, this bug addresses the issue by not recreating the assignedArg, when a NullableExpression is not needed.
callerArgExprOpt now returns an option of expression, if it is None then the original assignedArg is sufficient.
Updated tests to run on 5.0 and 4.7.
An indent change, when reviewing tell github to ignore whitespace changes.
repro:
do a release build of master branch
pushd C:\kevinransom\fsharp\tests\fsharp\core\byrefs
C:\kevinransom\fsharp\tests\FSharp.Test.Utilities....\artifacts\bin\fsc\Release\net472\fsc.exe -r:System.Core.dll --nowarn:20 --define:COMPILED -o:test.exe -g test.fsx --langversion:5.0
Observe the error messages:
/cc @dsyme, please take a look.