Fix #18841: let _ = &expr now compiles like let x = &expr#19811
Conversation
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>
❗ Release notes required
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
T-Gro
left a comment
There was a problem hiding this comment.
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(viastripTyEqns+isByrefLikeTyconRef) covers both actual byrefs (byref<T>,inref<T>,outref<T>) and[<IsByRefLike>]structs likeSpan<T>.- When the shortcut is skipped, the code falls through to the
| _ ->case which creates apatternInputtemporary with the byref-like type, producing a properExpr.Letthat 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 —
isByrefLikeTyis cached viaTryIsByRefLikeand 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.
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: