sleigh silently accepts constructors whose patterns cannot be distinguished, unless -l is passed
Describe the bug
When two constructors have patterns that overlap without being identical, sleigh
builds the language without a word of complaint and picks a winner. The conflict is
only reported if you pass -l, and then it is an error that stops the build.
The default is set in the SleighCompile constructor:
lenientconflicterrors = true;
and the errors are gated on it:
if (!lenientconflicterrors) {
VectorSTL<String> cerrors = props.getConflictErrors();
for (int i = 0; i < cerrors.size(); ++i) {
errors += 1;
Msg.error(this, cerrors.get(i));
}
}
Identical patterns go through getIdentErrors(), which is not gated, so those are
always reported. It is specifically the overlapping case that is silent.
I understand the leniency is deliberate — real specs would be painful to write if
every overlap were fatal. What I would like to change is that the default is
silent, not that it is permissive. As things stand you can develop a whole
processor module, never pass -l, and never learn that some of your instructions
are decoded by whichever constructor happened to win. That is what happened to me
across four GPU ISA modules: none of them could build under -l, so every .sla
that had ever shipped from them was built with conflicts resolved arbitrarily, and
nothing had ever said so.
The failure is quiet in the worst way — the grammar works, it just sometimes decodes to the wrong instruction, and the wrong one is a legitimate constructor from your own spec.
To Reproduce
conflict2.slaspec:define endian=little; define alignment=1; define space ram type=ram_space size=4 default; define space register type=register_space size=4; define register offset=0 size=4 [ r0 r1 ]; define token instr(8) opc = (0,3) xx = (4,7) ; # Patterns overlap but are not identical: the byte 0x21 matches both. :"FOO" is opc=1 { r0 = 1; } :"BAR" is xx=2 { r0 = 2; }sleigh -e conflict2.slaspecProduces
conflict2.sla. No warning, no error, no mention of the overlap.sleigh -l -e conflict2.slaspecERROR conflict2.slaspec:14: Constructor patterns cannot be distinguished: InstructionPattern{0010....} table "instruction" constructor from conflict2.slaspec:14 InstructionPattern{....0001} table "instruction" constructor from conflict2.slaspec:13 ERROR No output producedLoad the
.slafrom step 2 and disassemble the byte0x21, which satisfies both patterns. It comes back asFOO. Which of the two wins is not something the spec author chose or was told about.
Expected behavior
Report the conflicts by default, as warnings, and keep -l as the switch that
promotes them to errors. A spec author would then see
WARN conflict2.slaspec:14: Constructor patterns cannot be distinguished: ...
on an ordinary build, and could decide whether each one is intentional. Nothing that currently compiles would stop compiling.
If changing the default is too disruptive, a one-line summary at the end of a
build — 12 constructor pattern conflicts resolved arbitrarily; run with -l for detail — would be enough to stop this being invisible.
Screenshots
N/A.
Attachments
The full spec is inline above.
Environment (please complete the following information):
- OS: Ubuntu 22.04.5 LTS (kernel 5.15.0-190)
- Java Version: OpenJDK 21.0.12
- Ghidra Version: 12.1.3 (build 2026-Aug-17)
- Ghidra Origin: official GitHub distro
Additional context
I appreciate this may be working as designed, in which case treat it as a request to make the default louder rather than stricter. The reason I think it is worth changing is the asymmetry: the cost of a spurious warning is that someone reads a line of build output, and the cost of the current silence is a grammar that decodes some instructions as the wrong instruction with no indication anywhere that a choice was made.
Source: NationalSecurityAgency/ghidra