-
Notifications
You must be signed in to change notification settings - Fork 788
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
Parenthesized/double unit literal ()
→ (())
required in certain scenarios
#16254
Comments
Since |
Hmm, it looks like this is where it happens: fsharp/src/Compiler/Checking/CheckExpressions.fs Lines 6752 to 6794 in 75cd8c7
specifically fsharp/src/Compiler/Checking/CheckExpressions.fs Line 6760 in 75cd8c7
and fsharp/src/Compiler/Checking/CheckExpressions.fs Lines 6790 to 6791 in 75cd8c7
My guess is that some kind of tweak could be made to the filter above or in here (the callsite of the above) to account for unit: fsharp/src/Compiler/Checking/CheckExpressions.fs Lines 6912 to 6927 in 75cd8c7
Although we'd obviously need to make sure any change there didn't break anything else. |
@brianrourkeboll Specifically, what happens in the following scenario if we decide that type C<'T> =
abstract M : unit -> unit
abstract M : 'T -> unit
let _ =
{
new C<unit> with
override _.M () = ()
override _.M (()) = ()
} |
@vzarytovskii That's a good callout, although the same problem already exists for any other type, as in > type C<'T> =
- abstract M : unit -> unit
- abstract M : 'T -> unit
- let _ =
- {
- new C<int> with
- override _.M (_ : int) = ()
- override _.M (_ : int) = ()
- };;
{
--^
stdin(14,3): error FS0359: More than one override implements 'M: int -> unit' The only way to disambiguate between the two overloads in such a case would be to use named arguments, but that applies regardless of argument type. |
I guess the only thing I'm concerned about is that if we change how method resolution works, we need to make sure that something which was compiling before (and choosing a correct overload) still compiles and works as before. |
Hmm, you are right that your example currently compiles and might not if this oddity were "fixed," depending on the fix. It is a pretty unique scenario, though, since the It looks like the mismatch happens here: Where The 0 appears to come from here: Specifically:
I think in theory we could say that 1 ≈ 0 when the binding's argument type was instantiated to We could even keep the special example you gave compiling by searching for the existence of a non-generic method with the same signature and keeping the existing behavior for that... But that would admittedly be pretty ugly. |
Basically the existing code that does (or does not do) "single unit elimination" just doesn't handle the generic scenario: fsharp/src/Compiler/SyntaxTree/SyntaxTreeOps.fs Lines 352 to 354 in 9e357ca
fsharp/src/Compiler/SyntaxTree/SyntaxTreeOps.fs Lines 627 to 635 in 9e357ca
That's why the double fsharp/src/Compiler/TypedTree/TypedTreeOps.fs Lines 2689 to 2701 in 9e357ca
Indeed, an override of internal sealed class o@4 : C<Unit>
{
void C<Unit>.M(Unit _arg1)
{
}
} The reason single fsharp/src/Compiler/Checking/CheckExpressions.fs Lines 6913 to 6929 in 9e357ca
It is perhaps noteworthy that the compiler actually avoids this problem altogether when the return type is generic and you attempt to instantiate > type C<'T> = abstract member M : 'T -> 'T
- let _ = { new C<unit> with member _.M (()) = () }
- ;;
let _ = { new C<unit> with member _.M (()) = () }
------------------------------------^
stdin(2,37): error FS0017: The member 'M: unit -> unit' is specialized with 'unit' but 'unit' can't be used as return type of an abstract method parameterized on return type. Anyway, I think in theory this could be worked around, but I'm not personally sure it's worth the work or added complexity in the compiler, unless we planned to take a fresh approach to unit-elimination where it was handled more consistently throughout. |
* Sometimes we can't tell just by looking at the untyped AST whether we need parens, since their necessity may be dictated by type information located elsewhere. Compare, e.g., dotnet#16254, which probably has a similar underlying cause.
* Fix a few more paren corner cases * Match-like exprs in `if` exprs, `while` exprs, and `for` exprs. Also `let` exprs. * Nested, dangling `as` patterns. * Outlaw `match` exprs (where the first `|` is leftward of the `m` in `match) * Single-line comments (`//`, `///`). Multiline comments (`(*…*)`) would be… rather more difficult to handle. * Double-parenthesized tuples in method applications, since we can't tell purely syntactically whether the tuple might be the first parameter of a method whose next parameter is an implied outref parameter: `x.TryGetValue ((y, z))` i.e., `x.TryGetValue ((y, z), &value)` * Multiline tuple patterns in `let`-bindings. These need parens if the bound expression starts on the same column. * Handle typed pats in getters & setters * Double parens oddities * Sometimes we can't tell just by looking at the untyped AST whether we need parens, since their necessity may be dictated by type information located elsewhere. Compare, e.g., #16254, which probably has a similar underlying cause. * Keep parens for parenzed app preceded by prefix op * Keep parens around tuple in interp string * More nested match fun * No space when expr would reparse as prefix op * No space when expr would reparse as prefix op * No space when expr would reparse as prefix op * Update release notes * Remove unfinished multiline comment stuff * Keep parens around dot-get that would be adjacent * E.g., removing parens in place from ```fsharp Debug.Assert((xT.DeclaringType :?> ProvidedTypeDefinition).BelongsToTargetModel) ``` would result in the the argument to `Assert` becoming `(xT.DeclaringType :?> ProvidedTypeDefinition)`. The `.BelongsToTargetModel` would then be parsed as a get on the return value of _that_ call. * Fantomas
A parenthesized unit literal
()
→(())
is required as an argument pattern when overriding/implementing a generic method whereunit
is the generic type argument.Repro steps
Provide the steps required to reproduce the problem:
abstract M : 'T -> …
).unit
, the argument pattern must be(())
; using only()
yieldserror FS0768: The member 'M' does not accept the correct number of arguments. 1 argument(s) are expected, but 0 were given
.Single
()
usually meansunit
Double
(())
is required when overriding/implementing a generic method whereunit
is the generic type argumentDouble
(())
compiles:but not
()
:even though this is fine:
as is
Expected behavior
()
should meanunit
everywhere.Actual behavior
(())
is required to representunit
in certain scenarios.Known workarounds
N/A.
Related information
.NET SDK 8.0.100-rc.2.23502.2 (and probably going far back—I remember running into this in years past).
The text was updated successfully, but these errors were encountered: