From 2547350bea1ea247bdac58bf86eccb4a75230365 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 26 May 2026 10:49:08 +0200 Subject: [PATCH 1/6] Add RED tests for issue #18841 (let _ = &expr triggers FS0421) Five tests demonstrate the bug; two regression guards confirm the named-binding and plain-discard paths still compile. All RED tests currently fail with FS0421 as expected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DataExpressions/AddressOf/AddressOf.fs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/DataExpressions/AddressOf/AddressOf.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/DataExpressions/AddressOf/AddressOf.fs index 4f9ceb2067d..4005d80bf5d 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/DataExpressions/AddressOf/AddressOf.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Expressions/DataExpressions/AddressOf/AddressOf.fs @@ -27,3 +27,123 @@ module AddressOf = |> shouldFail |> withErrorCode 431 |> withDiagnosticMessageMatches "byref" + + // Issue #18841: let _ = &expr should compile (parity with let x = &expr) + + [] + let ``Issue 18841 - let discard with address-of struct value compiles`` () = + Fsx """ +module Test + +type S = + struct + val Field: int + end + +let test () = + let s = S() + let _ = &s + let a = &s + ignore a + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - let discard with address-of mutable local compiles`` () = + Fsx """ +module Test +let test () = + let mutable x = 42 + let _ = &x + () + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - let discard with address-of struct field compiles`` () = + Fsx """ +module Test + +[] +type S = { mutable Field: int } + +let test () = + let mutable s = { Field = 0 } + let _ = &s.Field + () + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - let discard with parenthesized address-of compiles`` () = + Fsx """ +module Test + +type S = + struct + val Field: int + end + +let test () = + let s = S() + let _ = &(s) + () + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - let discard with address-of inside function parameter compiles`` () = + Fsx """ +module Test + +type S = + struct + val Field: int + end + +let f (s: S) = + let _ = &s + () + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - named binding let x = &s still compiles (regression guard)`` () = + Fsx """ +module Test + +type S = + struct + val Field: int + end + +let test () = + let s = S() + let a = &s + ignore a + """ + |> typecheck + |> shouldSucceed + + [] + let ``Issue 18841 - plain let discard with no address-of still compiles`` () = + Fsx """ +module Test + +type S = + struct + val Field: int + end + +let test () = + let s = S() + let _ = s + () + """ + |> typecheck + |> shouldSucceed From e0562a67cbbece607b55c997cda7fe6bffca9c3b Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 26 May 2026 11:06:53 +0200 Subject: [PATCH 2/6] Fix #18841: let _ = &expr now compiles like let x = &expr The wildcard short-circuit in TcLetBinding rewrote `let _ = e` as a Sequential, which caused PostInferenceChecks to evaluate `e` with PermitByRefExpr.No and emit FS0421 for address-of expressions. Skip the short-circuit when the RHS has a byref-like type so the binding keeps its real `Expr.Let` shape and the byref-permitting context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/Checking/Expressions/CheckExpressions.fs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 6636901e460..c1f50c94256 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -11863,9 +11863,14 @@ and TcLetBinding (cenv: cenv) isUse env containerInfo declKind tpenv (synBinds, let rhsExpr = mkTypeLambda m generalizedTypars (rhsExpr, tauTy) match checkedPat with - // Don't introduce temporary or 'let' for 'match against wild' or 'match against unit' - - | TPat_wild _ | TPat_const (Const.Unit, _) when not isUse && not isFixed && isNil generalizedTypars -> + // Don't introduce temporary or 'let' for 'match against wild' or 'match against unit', + // unless the RHS is a byref-like value (e.g. `let _ = &s`). For byref-like RHS we + // must keep a real `Expr.Let` so that PostInferenceChecks treats the binding as + // permitting byref expressions, matching the behaviour of `let v = &s`. + // See issue dotnet/fsharp#18841. + + | TPat_wild _ | TPat_const (Const.Unit, _) + when not isUse && not isFixed && isNil generalizedTypars && not (isByrefLikeTy g m tauTy) -> let mkSequentialBind (tm, tmty) = mkSequential m rhsExpr tm, tmty (buildExpr >> mkSequentialBind, env, tpenv) | _ -> From 908cc5aa5389b9cb2228923ae00da9b2ca9931d9 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 26 May 2026 11:44:12 +0200 Subject: [PATCH 3/6] Apply remaining changes --- .executor-pid | 1 + 1 file changed, 1 insertion(+) create mode 100644 .executor-pid diff --git a/.executor-pid b/.executor-pid new file mode 100644 index 00000000000..634f4111a99 --- /dev/null +++ b/.executor-pid @@ -0,0 +1 @@ +25504 \ No newline at end of file From 2ca5a186f8e59a58fadc6b1ccb95761be05b63e3 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 26 May 2026 13:12:50 +0200 Subject: [PATCH 4/6] Add release notes entry for #18841 fix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 7d4162f804e..ef15b8a73f0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Fix FS0421 "The address of the variable cannot be used at this point" incorrectly raised for the discard pattern `let _ = &expr` when `let x = &expr` compiles. ([Issue #18841](https://github.com/dotnet/fsharp/issues/18841), [PR #19811](https://github.com/dotnet/fsharp/pull/19811)) * Honor `--nowarn` and `--warnaserror` for warnings emitted during command-line option parsing ([Issue #19576](https://github.com/dotnet/fsharp/issues/19576), [PR #19776](https://github.com/dotnet/fsharp/pull/19776)) * Fix `[]` prefix attributes being silently dropped on class members, and fix false-positive `AllowMultiple=false` errors when `[]` and `[]` are applied to the same binding. ([Issue #17904](https://github.com/dotnet/fsharp/issues/17904), [Issue #19020](https://github.com/dotnet/fsharp/issues/19020), [PR #19738](https://github.com/dotnet/fsharp/pull/19738)) * Fix attributes on return type of unparenthesized tuple methods being silently dropped from IL. ([Issue #462](https://github.com/dotnet/fsharp/issues/462), [PR #19714](https://github.com/dotnet/fsharp/pull/19714)) From 9a12c42edf59e783a392f6060d1e3fef6c0253f7 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 26 May 2026 13:20:11 +0200 Subject: [PATCH 5/6] Apply remaining changes --- .copilot-prompt.txt | 16 ++++++++++++++++ .executor-pid | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .copilot-prompt.txt delete mode 100644 .executor-pid diff --git a/.copilot-prompt.txt b/.copilot-prompt.txt new file mode 100644 index 00000000000..172f3350161 --- /dev/null +++ b/.copilot-prompt.txt @@ -0,0 +1,16 @@ +Create a pull request for the current branch against main in dotnet/fsharp. + +1. Run `git log origin/main..HEAD --oneline` and `git diff origin/main --stat` to understand the changes +2. Create the PR with `gh pr create`: + - Title: concise, max 72 chars, describes what changed + - Body: starts with "Fixes #18841" on its own line, then a blank line, then 1-2 sentences of commentary + - Target repo: dotnet/fsharp + +Run exactly: +``` +gh pr create --repo dotnet/fsharp --title "YOUR TITLE" --body "Fixes #18841 + +YOUR BRIEF DESCRIPTION" --head fix/issue-18841 +``` + +3. After creating, output the PR URL on its own line. \ No newline at end of file diff --git a/.executor-pid b/.executor-pid deleted file mode 100644 index 634f4111a99..00000000000 --- a/.executor-pid +++ /dev/null @@ -1 +0,0 @@ -25504 \ No newline at end of file From e2f39ba598db7aae4d6101f6b126dd03c19db138 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 26 May 2026 14:30:34 +0200 Subject: [PATCH 6/6] Remove executor artifact --- .copilot-prompt.txt | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .copilot-prompt.txt diff --git a/.copilot-prompt.txt b/.copilot-prompt.txt deleted file mode 100644 index 172f3350161..00000000000 --- a/.copilot-prompt.txt +++ /dev/null @@ -1,16 +0,0 @@ -Create a pull request for the current branch against main in dotnet/fsharp. - -1. Run `git log origin/main..HEAD --oneline` and `git diff origin/main --stat` to understand the changes -2. Create the PR with `gh pr create`: - - Title: concise, max 72 chars, describes what changed - - Body: starts with "Fixes #18841" on its own line, then a blank line, then 1-2 sentences of commentary - - Target repo: dotnet/fsharp - -Run exactly: -``` -gh pr create --repo dotnet/fsharp --title "YOUR TITLE" --body "Fixes #18841 - -YOUR BRIEF DESCRIPTION" --head fix/issue-18841 -``` - -3. After creating, output the PR URL on its own line. \ No newline at end of file