Skip to content

Fix #18841: let _ = &expr now compiles like let x = &expr#19811

Open
T-Gro wants to merge 6 commits into
mainfrom
fix/issue-18841
Open

Fix #18841: let _ = &expr now compiles like let x = &expr#19811
T-Gro wants to merge 6 commits into
mainfrom
fix/issue-18841

Conversation

@T-Gro
Copy link
Copy Markdown
Member

@T-Gro T-Gro commented May 26, 2026

Summary

Fixes #18841

let _ = &expr was incorrectly triggering FS0421 ("The address of the variable cannot be used at this point") while the equivalent let x = &expr compiled fine.

Root Cause

The wildcard short-circuit in TcLetBinding rewrote let _ = e as a Sequential expression, which caused PostInferenceChecks to evaluate � with PermitByRefExpr.No and emit FS0421 for address-of expressions.

Fix

Skip the wildcard short-circuit when the RHS has a byref-like type so the binding keeps its real Expr.Let shape and the byref-permitting context is preserved.

Tests

Added component tests in AddressOf.fs covering:

  • let _ = &expr (the reported bug)
  • Various forms: struct fields, array elements, mutable locals
  • Regression guards confirming named bindings and plain discards still work

T-Gro and others added 3 commits May 26, 2026 10:49
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>
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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

Copilot and others added 3 commits May 26, 2026 13:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label May 26, 2026
Copy link
Copy Markdown
Member Author

@T-Gro T-Gro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Verdict: LGTM

Root Cause Analysis

The fix correctly identifies the problem: TcLetBinding has a shortcut that rewrites let _ = expr into Expr.Sequential(expr, continuation). In PostInferenceChecks.CheckExprLinear, the first expression of a NormalSeq sequential is checked via CheckExprNoByrefs (line 1038), which uses PermitByRefExpr.No. This rejects &expr with FS0421.

In contrast, Expr.Let bindings (line 1042-1055) set bindingContext = PermitByRefExpr.YesReturnable when the variable has a byref type, correctly allowing the RHS to be a byref expression.

Fix Correctness

The guard not (isByrefLikeTy g m tauTy) is the right check:

  • isByrefLikeTy (via stripTyEqns + isByrefLikeTyconRef) covers both actual byrefs (byref<T>, inref<T>, outref<T>) and [<IsByRefLike>] structs like Span<T>.
  • When the shortcut is skipped, the code falls through to the | _ -> case which creates a patternInput temporary with the byref-like type, producing a proper Expr.Let that PostInferenceChecks handles correctly.

Comment Quality

The inline comment explaining the rationale is excellent — it references the issue, explains why the exception is needed, and describes the expected invariant.

Test Coverage

Good coverage of the key scenarios (struct values, mutable locals, struct fields, parenthesized forms, function parameters) plus regression guards for named bindings and non-byref discards.

Minor style note: the new tests use Fsx + typecheck while existing tests in the file use getCompilation + compile. This is actually fine per the ComponentTests guidelines which recommend typecheck as the cheapest action for type-error-only scenarios.

No Concerns

  • No performance concern — isByrefLikeTy is cached via TryIsByRefLike and called once per let binding.
  • No soundness gap — the TPat_const(Const.Unit, _) arm is harmlessly guarded too (a unit-typed expr can't be byref-like).
  • No binary compatibility impact.

@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

Can't take address of a struct value and set it to a discard pattern

1 participant