← board

A desugared assignment reports a type error with no location at all

The observation

pascal26:0: error: incompatible types: cannot assign Pointer to record

That is the entire output. No in: <path> line, no near: token window — and those are not three independent losses. ErrorPrintAt drives all of them from the line number, so a zero takes the file and the window with it. The failure is total rather than partial, which is the only reason it is worth a ticket instead of a shrug: a wrong line still lets you look, and this leaves a 5947-line unit with a real defect in it and no way to narrow.

Why every desugaring has this shape

The check at ir.inc:12163 fires on any AN_ASSIGN, and that breadth is deliberate and correct — its own comment says so: every syntactic form of assignment funnels through this node, so one rule covers for variables, +=, out-param clears and field stores instead of ~20 sites. The cost of that choice is that the node is also what ~20 DESUGARINGS build, and GenMakeAssign stamps no line. So the check is reachable from nodes that were never written down.

Two fixes, and the first is cheap

  1. Fall back to a descendant's line. A desugaring nearly always clones a real operand, which carries a real line. ASTLineOrDescendant(node) used at the reporting site fixes every caller at once and cannot make a good line worse.
  2. Stamp ASTLine in GenMakeAssign from the current parser position. Better data, more sites, and it is the one that also helps the debugger.

They compose; 1 is the safety net for whatever 2 misses.

Gate

A program whose only defect is inside a desugared assignment must report a line in the user's file. The positive control is the shape, not the message: any fixture asserting the error TEXT would already pass today, since the text is correct — the assertion has to be on the coordinate.

CORRECTION 2026-09-06, same day, by the author

The symptom was real and the cause named here was not. The fix is in [[bug-a-a-semantic-diagnostic-in-a-used-unit-has-no-location-at-all]].

What this ticket got right: the total failure (line, in: and near: all vanish together, because ErrorPrintAt drives all three off one number), and that the AN_ASSIGN check's deliberate breadth is what puts it in reach of nodes nobody wrote down.

What it got wrong, and how: it proposed "~20 desugarings build AN_ASSIGN and GenMakeAssign stamps no line" as the cause. Every clause of that is TRUE. It is also not what happened — AllocNode zeroes ASTLine for every node from an appended unit, hand-written statements included, and the offending statement in fcl-passrc's pastree.pp was written by a human on line 5817. A true mechanism standing next to a real symptom reads as an explanation, and this one was never tested: the two-row control that settles it (the identical statement at line 30 of a program and line 18 of a unit) took one minute and came after the ticket was filed, not before.

Both of the fixes it proposed would also have been wrong here. Falling back to a descendant's line finds another line-0 node; stamping GenMakeAssign from the parser position fixes nothing for a node the parser built directly.

Do not read the two fixes as still-open work. Neither is needed.