Conversation
… in `+`/`-`/`*` (#469) A wildcard `Int` has no exact width, only a minimum one, and adapts to the bit-accurate operand. That adaptation also demanded the value FIT the operand, so `vr0(SInt(16)) :== 180 - 5 * rbow_id(UInt(3))` was rejected for `180` not fitting 3 bits, even though the assignment context is 16 bits wide. (Not the reported `*` vs `+`/`-` asymmetry: `5` fits `u3` and `180` does not, whatever the operator.) The minimum width now counts as an actual width when the result width of `+`, `-` or `*` is computed, so the result is simply the wider of the two operands. That subsumes both behaviors: a minimum within the other operand's width leaves it adapting exactly as before, and one beyond it widens the operation instead of failing. The reporter's line elaborates, and target-context widening then re-evaluates the whole cone at 16 bits, reproducing the Verilog it translates. This needs the other operand's width at compile time as well, so a parametric width, or an `Int` whose value is not a literal, keeps adapting and must fit: the widths of a design must not depend on an applied parameter value, and a result width is a type, so a minimum the type system cannot see could only degrade it. A literal that already fits keeps taking the adapting path, leaving the IR of every legal design untouched. `-`, `/` and `%` stay LHS-dominant. A literal too wide to adapt is then the wider operand and satisfies that by definition, but a negative one meeting an unsigned operand does not, and is rejected at compile time with the message its elaboration-time counterpart already reports. Subtraction over parametric widths is now decided by proof (`IntParamRef.widthFitGE`) rather than skipped when the widths do not resolve. Six shapes, the reporter's among them, are SAT-proven equivalent to hand-written Verilog via a yosys miter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on (#470) A port of a sub-design instance is not a member of the instantiating design: `inst.port` hands back the child's `Dcl`, while the parent only ever holds a `PortByNameSelect` representative, minted lazily by `refTW` at reference time. `MutableDB.setMember` looks its original member up in the current design context, so every front-end op that revises a member in place crashed with a raw `NoSuchElementException` on such a port. The argument-less `.resize` is one of those: it marks its operand with `ir.ResizeTag`. `refTW`'s foreign-port handling is split into a pure `isForeignPort` predicate and a member-planting `foreignPortSelectOpt`, and the two revising ops now answer separately, by kind: - a tag describes the *use* of the port, so it lands on the local representative (`DFVal.revisableHere`), making `c.o.resize` produce exactly what `c.o.resize(16)` does; - a name is a property of the *declaration*, so there is nothing local to put it on and `setName` reports an elaboration error naming the design that does get to set it. Its `@metaContextForward(0)` is dropped, since the plugin never stamps applies of forwarding symbols and the error would otherwise carry no position. Both redirects are skipped during meta-programming, where `setMember` revises without touching a design context. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
, #471) Fixes #471 and #467: a bit-select with a parameter-dependent index (`v(N - 1)`) made unrelated connections fail as "Unsupported read-to-read connection", on lines far from the select that caused it. `departial` only folded a LITERAL index, so a parametric one took the runtime-index fallback and recorded a write covering the WHOLE value. That falsely-proven overlap made the next connection on the value classify as a read, which flipped its direction and recorded the wrong sink, and the corruption then cascaded into nets that never touched a parameter. The analysis answers two kinds of question that have opposite tolerances for an unresolved parameter, and it was serving both from one `contains` query. They are now separated: - Directionality is structural and must hold for every parameter assignment. `departial` composes any elaboration-fixed index into the slice (literal -> concrete, design-parameter expression -> symbolic); an iterator, a static function's formal and a runtime value keep the whole-value fallback. The `VAR` rule reads a value as already-driven only on a PROVEN overlap (`hasProvenNet`), so opacity leaves the flow undecided instead of guessing. - Legality may be deferred to wherever the parameters resolve. A collision is decided by symbolic proof; failing that, by folding both bit ranges at the elaborated parameter values (`DataCalc.foldConst`, root defaults included); failing that, the check is skipped. This retires the "cannot be proven disjoint" error, whose rejection was the guess this replaces. - `getConnToMap`'s pending passes are progress-guarded, since proven-only classification makes repeat-undecidable nets possible; a pass that settles nothing is followed by one pass under the old conservative reading as the tiebreak, which keeps every previously-accepted shape accepted. A second driver on a variable also arrives as a read-to-read pairing (the first write makes the variable read as a source), so a genuine double-drive reported the unactionable "read-to-read" even with literal indices. It now names the variable and the earlier write. Verified with verilator and ghdl on the emitted output: the index survives as `v[N - 1]` / `v(N - 1)`, so the design stays parametric. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…f `always_ff` `always_ff` guarantees a single driver for everything the process writes, while a `VAR.SHARED` is multi-driven by design (one clocked process per RAM port), so the two are contradictory and conforming tools reject the pair. Every process writing a shared variable now renders as a plain `always`, which carries no such guarantee. Only the writers degrade: a process merely reading the shared variable is unconstrained and keeps its `always_ff`. Fixes #473 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…arameter port list A module body declaration is positionally after the header that names it, so a derived port width emitted as a body `localparam` and referenced from the ANSI port list is a use-before-declare, which a conforming frontend rejects. Those constants now join the parameter port list, spelled `localparam` where 1800-2009 allows it there and `parameter` in the earlier ANSI dialects. Under a non-ANSI header (v95) everything stays in the body, where they now lead the port declarations instead of trailing them. A width/length query follows the VHDL printer and keeps its native `$bits`/ `$size` spelling only over a CONSTANT argument: the query may now print into a parameter port list entry, where naming a port has the same defect. Over a non-constant argument the width parameter expression is inlined, as the pre- SystemVerilog dialects already did. VHDL solves the same restriction a stage earlier, by converting these into design parameters (`LocalToDesignParams`), which is why that stage stays VHDL-only. Fixes #472 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A language keyword that carries a guarantee needs the IR fact that contradicts it, and a sibling backend's fix mechanism tells you the rule rather than the layer it belongs at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`protected object hdl` was reachable from user code after all: an implicit conversion into an unbounded ascription (`val acc: Bits[Int] <> VAL = a.bits`) selected the member through the object rather than through its package-level export forwarder, and the inaccessible prefix was reported at the user's own line as `illegal access to protected object hdl in package dfhdl` — naming an object no user code mentions. Making it public removes the only spelling that can fail; the leading underscores keep it out of the way, since `import dfhdl.*` re-exports every member anyway. Fixes #468 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An error naming a DFHDL-internal symbol at the user's line is a resolution fact, not a reporting bug: find the qualifier in the typer tree, and fix it by publishing the namespace object rather than by chasing the resolution. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ths (#476, #474) Two independent defects in the elaboration-time width algebra. #476: `UInt(1) + 1` (and any other 1-bit arithmetic, literal or not) failed with "Signed value width must be larger than 1". The target-context widening rule compared the target against the value's width by wrapping the latter in a freshly built `DFXInt(true, funcWidth, BitAccurate)`, purely so `compareWidths` had a type to take, and that construction runs `SInt`'s own width constraint. The comparison now decides on the two IR width refs directly, which also stops minting a throwaway ref per call. #474: a bitwise `^`/`&`/`|` whose operand widths were not both statically known performed no width check at all, so `Bits(LEN) ^ Bits[8]` elaborated silently and emitted an operation the backend zero-extends. The elaboration half of the `LW == RW` check now covers that branch, reporting the same message as the compile-time half with each width rendered relative to the error site. The proof keeps design parameters OPAQUE (`IntParamRef.isProvablyEqualTo`), which is what makes it sound: the resolving comparison reads a parameter's DEFAULT while the design's own body elaborates, so `LEN` defaulting to 8 would "prove equal" to an 8-bit constant and the mismatch would only materialize at an instantiation site applying `LEN = 16`. Symbolic re-spelling still matches (`Bits(W + 1)` against `Bits(1 + W)`). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A domain type built only as a value carrier runs that type's own constraints, so an operation can fail on a rule belonging to a type the user never wrote (#476). And an elaboration-time rule about a design's OWN legality must keep design parameters opaque, since the resolving comparison reads a parameter's default while that design's body elaborates (#474) -- which also means a root design and an instantiated sub-design are different test subjects for any parameter-sensitive rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A static assertion is an `assert` in a concurrent design or domain body whose condition and message are constant. It states an elaboration-time contract of the design rather than a runtime condition, so it stays concurrent all the way down and renders as an elaboration-time construct. The species is derived structurally (`TextOut.isStaticAssert`), with no new IR op and no marker tag, so re-elaborating the printed `assert(...)` reconstructs it. `ToED` exempts such an assertion, together with the constant cone computing its condition and message, from the process sweep, and `OrderMembers` ranks the pair right after the constant declarations so the output reads them as a contract header. Verilog had no correct rendering for a concurrent text output at all: an immediate assertion and a system-task call are statements, never module items, so a body assertion printed illegal code. It now prints per dialect: an elaboration system task under a generate-`if` from 1800-2009 (checked by synthesis too), an `initial assert ... else` under sv2005, and an `initial` guard with `$display` (plus `$finish` on Fatal) under v95/v2001. VHDL's concurrent assertion was already correct. Everything else stays a runtime statement, and a concurrent event-driven position has no runtime to attach one to: the lowering that gives an RT/DF body statement one does not apply to a body that is already ED. `DB.textOutCheck` therefore rejects it at elaboration and points at `process` / `initial`, instead of letting the user discover it in illegal generated HDL. RT/DF bodies and HDL method bodies are exempt. Two adjacent fixes fall out: `DropProcessAll` now counts a text output's reads when it builds an explicit sensitivity list (a text-output-only `process(all)` previously became `always @()` under v95 and VHDL-93), and DFacsimile reports a static assertion on the first committed cycle only, rather than repeating an elaboration-time contract every cycle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Running the suite, then scalafmt, then the suite again is pure waste: formatting rewrites the very spec files the first run exercised. The position-sensitive tests section is where this already bit, so the general rule lands there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`val posW = assert(W > 0, ...)` now carries `posW` to the backend as a statement label. The name was already stamped on the member's meta by the plugin; `TextOut` becomes `DFMember.Named` so the rest of the pipeline can read it, and the DFHDL printer emits the `val` binding so the name survives the print / re-elaborate round trip. VHDL labels a statement directly, concurrent and sequential alike. Verilog names a block rather than a statement, so a named assertion becomes a named block: the generate block of the elaboration form (which also settles the implicit `genblk<n>` a linter complains about), the `initial` block of the older dialects, and a plain statement label where 1800 immediate assertions are available. An anonymous assertion keeps the tightest form the dialect allows, unchanged. A label lives in the enclosing module/architecture namespace even when the statement sits inside a process, which the per-block scoping of `UniqueNames` does not model: a label sharing a port's name made both Verilator and GHDL reject the design. Text outputs are therefore uniquified per DESIGN, in a pass of their own so that a collision renames the label and never the declaration it collided with. Also fixes the arity of `$fatal` in an assertion's else-branch, which was missing the mandatory finish number. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A parametric width relation that elaboration can neither prove nor
refute was handled two ways, and both were wrong. The `DFXInt` TC leaf
rejected it ("undefined compared to"), demanding a `.resize` of code
that is very probably correct; the `ArithCheck` arm accepted it in
silence, letting a parameter override truncate.
Such a relation is now ACCEPTED, with the fit it needs stated in the
design as a static assertion, so every instantiation checks at its own
elaboration what this one could not:
class Bar(val W: Int <> CONST = 8) extends RTDesign:
val x = UInt(W) <> IN
val z = UInt(16) <> OUT
z := x
val constraint = assert(16 >= W, s"Design parameter violation found. Expected: 16 >= W", Severity.Fatal)
reaching sv2009 as a module-scope `if (!(16 >= W)) begin : constraint
$fatal(1, ...); end` and VHDL as a labeled concurrent assert. A provably
violated relation stays the hard error it was, reporting the check's own
call-site text.
The condition IS the record: an undecided check tags the guard it built
`AutoConstraint` and leaves it where it stands. The end of the design
body collects the tagged guards from the design context in member order,
drops the ones stating a relation another already states (compared as
relations through `IntExprCalc.sameDiff`, so `W + W >= 8` and `2 * W >= 8`
are one), clones each survivor's cone into the body and plants the
assertion over the clone. Cloning is what lets a check inside a
conditional block state its relation at the design's level; the original
is then read by nothing and the existing end-of-design sweep collects it,
which is also what makes a dropped constraint free. The tag is consumed
there, so no member of a finished design carries one.
What the assertion reports is derived from the condition and nothing
else. A check's message describes ONE operation, while the assertion
describes the design's interface: dedup merges several operations into
one statement, and its reader instantiates the generated module rather
than having written the assignment.
Only the elaboration root generates constraints, which is the same scope
as the problem: a sub-design's parameters are fixed by the instantiation
elaborating it, so its widths resolve and the relation is decided then.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The long-lived server accumulates heap across the many compile/test cycles a bug fix takes, and once it reports time spent in GC every later cycle is slower than the restart would have cost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…esign's own assertions Several operations assuming related relations is the normal case rather than the exception: `z(16) := x(W)` and `n(8) := x(W)` assume `16 >= W` and `8 >= W`, and once the second is stated the first says nothing. Every comparison now normalizes onto one canonical form, `IntExprCalc.linearDiff(lhs, rhs) >= 0`, and two relations are comparable exactly when their symbolic terms cancel, the one with the smaller constant being the stronger. Materialization keeps a constraint only when nothing already kept implies it, and drops anything kept that IT implies. Deduplication falls out as the case where two imply each other, so the separate dedup pass is gone. The body's own static assertions are read as facts, when their severity makes them requirements rather than reports (`Error` and `Fatal`, not `Info`/`Warning`). The relation is one-way and deliberately so: a user assertion is never removed, subsumed or rewritten, but a generated constraint it already implies is dropped. Having written `assert(W <= 8, ...)`, the user should not then be shown a generated `16 >= W` beside it. Normalizing every comparison onto the same shape is what lets a user's `W <= 8` be compared with a generated `16 >= W` without either being rewritten; a strict comparison is the non-strict one over integers, one tighter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…m in testApps Verilog-95 has no `signed` keyword, so `<`, `>`, `<=` and `>=` over signed values are macros in dfhdl_defs.vh rather than native operators. Nothing executed them, and both were wrong: - SIGNED_GREATER_EQUAL was `(a > b) || a != b`, so `>=` answered true for every operand pair. - All four read an operand's sign bit by BIT-SELECT, which requires an indexable primary. A negative literal operand emitted `-4'd2[3]`, which iverilog rejects outright, so a comparison against a negative literal was uncompilable rather than merely wrong. The sign is read by SHIFT now, so any expression is a legal operand. util.SignedCmpSim checks all 256 operand pairs, and a negative literal operand, against an independent reference: flipping the sign bit maps two's complement onto offset binary, where an unsigned comparison answers the signed one, and unsigned comparisons emit plain operators in every dialect. testApps now iterates a design list rather than hardcoding CipherSim, so both run against every installed tool and dialect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comparison rules showed `u8 == u4` as an error without saying what to do instead, which leaves the reader to pick a `.resize` direction, and only one of the two is safe. Both pages now show closing the difference by widening the NARROWER operand, and spell out that narrowing the wider one compiles clean while asking a different question. The Verilog transition guide gets the contrast it needs: Verilog extends relational operands for you, but by context determination, which extends more than the operand. A narrower operand that is an expression is evaluated at the wider operand's width, so `(x + x) < a` never wraps there while its DFHDL transcription wraps first and widens after. Every snippet was compiled before being written down. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two IR tags that each permit ONE direction of a context-decided width adjustment, and the `DFXInt` methods that apply them. A tag is a PERMISSION, not an assertion: where the direction it permits does not apply it contributes nothing, and the context's ordinary width rules decide, error included. This is what an operation with no designated target has no other way of saying. An assignment names its target, so `u8 := u4` can extend implicitly. A comparison's operands are symmetric, and nothing in `a < b` privileges widening the narrower over narrowing the wider, so the tag marks WHICH operand adapts and the other one's width is the destination. No consumer yet: no check reads either tag, so nothing changes behaviour. `ir.ResizeTag` and the no-argument `.resize` are untouched and still the working spelling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eck.message` Checkpoint of the auto-constraint work carried over from the previous session, not yet reviewed. `Checked` gains `Check1.message`/`Check2.message`, so one rule has one message text whichever of its three halves reports it: the compile-time reduction, the check's own runtime test over `Int`s, or a symbolic decision over width EXPRESSIONS. `DFDecimal`'s width checks are refactored onto a shared `fitCheck` three-way (provably fits, provably violated, undecidable) with `widthFitCheck` and the new `wildcardFitCheck` as its two callers, and the undecidable arms are wired at the arithmetic, TC-conversion, wildcard-fit and comparison sites. Specs move with it: the flipped elaboration checks, the auto constraints pinned in the `Blinker` and `named text output` printouts, and the "The " message prefixes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`hdl.scala` exports `DFBits.Val.Ops.*` and `DFDecimal.Val.Ops.*` into one namespace, so a method name defined on both types produces two export forwarders with the same erased name. Scalac does not report that as a double definition; it crashes in the JVM backend with `ClassBType.info not yet assigned`. `resize` never hit it because the `DFXInt` side has carried `@targetName` since it was written, which is the pattern followed here. Worth knowing when the next method is added to both types: the failure names an arbitrary unrelated class, survives a clean, and is invariant to placement, so it reads as anything but a name collision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`AutoConstraint.permitsWidthAdjust` is the one decision, and the three sites that used to ask `hasTag[ResizeTag]` now ask it: the `Bits` and `DFXInt` assignment conversions, and the upper-bound argument of `UInt.until`-style indexing. A permission covers ONE direction, so where it does not apply it answers false and the site's own width rule decides and reports, unchanged. That is what makes `u8 := u4.truncate` elaborate to exactly what `u8 := u4` does, and `u4 := u8.extend` report the ordinary "RHS value width (8) is larger than the LHS variable width (4)". An undecidable pair is covered, stating the relation it relies on as a design constraint, on the same three-way as every other width decision over parameters. `ResizeTag` still short-circuits to true, so `.resize` behaves exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pins the undecidable third of the permission decision, which the core specs cannot reach: the adjustment is covered, and the relation it relies on reaches the backend as the design's own static assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… side A comparison names no target, so nothing in `a < b` says which operand should move. The permission says it, and the other operand's width is what it adapts to, which makes `a > b.extend` and `b.extend < a` the same comparison written two ways. Two permissions in one comparison is an elaboration error: each would be adapting to a width the other is still free to change. `conv` is handed only the receiver's TYPE, and a permission sits on a VALUE, so the argument conversion moves into `convArg` and `apply` decides between the two operands with the argument materialized once. A wildcard `Int` argument keeps the right to adapt, having no width of its own for the receiver to adapt to. The anonymized context is passed explicitly to the four sites that build operands rather than given for the whole method, so the comparison itself still takes the enclosing context and keeps the name of the binding it feeds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ncate` Every call site now names the direction it actually needs, chosen from the widths at that site rather than mechanically: a narrowing assignment or index becomes `.truncate`, a widening one `.extend`. The permission only covers its own direction, so a wrong choice would not compile, which makes the suite the check on all seventeen. `DFBits.Val.Ops` gains the candidate-based pair too, for the sites that adjust the width of something that is not yet a `Bits` value: the ALU resizes a comparison RESULT. The documentation follows, in the examples and in the Width Adjustment reference, which now describes the two as permissions rather than as an automatic adjustment, including what they say in a context with no designated target. `.resize` is untouched and still works; the one call site left on it is the test whose subject is that tag (issue #470). The diagnostics that recommend `.resize` are a separate change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Picks up platforms e43dae7, which moves `Digit.scala`'s two memory-index adjustments off the argument-less `.resize`. Kept out of ed3720a because a submodule's contents cannot ride in a parent commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
They all recommended `.resize`, which is now one of four spellings and the only one that says
nothing about direction. A width check knows which way the mismatch goes, so it can say:
The argument width (12) is different than the receiver width (8).
Consider `.truncate` to narrow it to the receiver width, or `.resize(8)` to state the
width explicitly.
The two compile-time checks branch at the type level, having both widths as literals. Their
elaboration-time twin cannot, since a parametric width leaves the direction open, so it offers
both. The subtraction diagnostic names only `.truncate`: nothing there can be fixed by widening,
and pointing at `.extend` would send the reader down a dead end.
Fourteen pinned expectations follow. That the parametric cases in `ElaborationChecksSpec` all take
the direction-agnostic form, and the literal ones do not, is itself the check that the two
variants land where they should.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rsion Two candidate conversions carried `ResizeTag` across the conversion but not the two new tags, so a permission was silently dropped whenever a value crossed between `Bits` and an integer type. The failure was self-refuting rather than subtle: `b8 := u4.extend` reported the width mismatch and recommended `.extend`, which was already written. A permission is about the VALUE's width, and this conversion leaves that width alone, so it has to survive. `AutoConstraint.carryWidthAdjustPermission` is now the one place that says so, and both conversions use it. The reverse direction hid the same hole: a numeric assignment extends on its own, so a lost `.extend` there changes nothing, and only a narrowing exposes it. Both directions are pinned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It permits both widening and truncation under one spelling, so the code never records which was meant and no check can be made of the direction the author did not intend. `.extend` and `.truncate` each permit one, and `.resize(width)` states the width outright. Annotation only: the implementation is untouched and existing code keeps working, warned rather than broken. The elaboration error is a separate decision. The last caller moves with it. That test pins a permission landing on the local representative of a sub-design's port rather than on the foreign declaration (issue #470), which is a property of the tag mechanism and not of `.resize` in particular, so `.truncate` serves it just as well. With it migrated the whole build compiles free of deprecation warnings, which is the check that the earlier migration missed nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…other operand (#478) A wildcard `Int` has no width of its own, so it first adapts to the other operand's. That width is exactly what a wider target context replaces, and the adaptation was surviving the replacement: the literal evaluated at the other operand's width and only then widened, which truncates it when that width is the smaller and, when it is parametric, leaves the design constrained to hold a value nothing in the widened expression puts there. With a literal operand width the same expression already did the documented thing, which is what made this a defect rather than a design choice. The widening now looks through the adaptation and re-adapts the wildcard at the target, checking the fit THERE, and retracts the constraint the narrow adaptation assumed: a fit check answers its guard instead of raising it, and the guard is tied to the value that makes the assumption, so superseding that value drops it. Also always enumerates a materialized constraint from `constraint_0`. A design with a single constraint was naming it plain `constraint`, which is a SystemVerilog keyword and cannot label the generated elaboration block; nothing reached it before, since every existing case had two or more. Fixes #478 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n relies on A wildcard `Int` adapts, and every check bounding that adaptation compares two WIDTHS, which presumes the wildcard has one. A Scala `Int` always does. An overridable design parameter has none, for this elaboration or any other, so its width AND its sign are both unknown and every arm simply fell through: `x(UInt(16)) <> V` emitted `assign x = 16'(V);` with no contract at all, silently truncating whatever the instantiation supplies. What the adaptation relies on there is a bound on the VALUE, stated as the width that value needs: `clog2(v + 1)` bits hold an unsigned `v`, and `clog2(max(v + 1, -v)) + 1` a signed one, both exactly and for every `v`. Through `clog2` rather than as `v <= 2 ** width - 1` deliberately, since a 64-bit target would overflow the 32-bit integer arithmetic the generated HDL evaluates the contract in. The sign being unknown too, an unsigned target requires non-negativity as well, in the same constraint: the halves are what one adaptation needs together, and reporting a violation of one without the other names half a contract. The fit half discharges like any other width relation, and usually does, a value derived from the parameter the target's width is derived from needing exactly that width. The sign half must not go through the same discharge, whose premise is that both sides are widths. Nothing is stated for a target that is itself a wildcard, which adapts to nothing, nor for a wildcard the design body cannot read: a `for` iterator has no value at elaboration for the same reason a parameter has none, but unlike a parameter it has none in the finished design either. `IntParam` gains the negation its algebra was missing, so the guard reads `-V` rather than `0 - V`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The index has only ever held LF, but a Windows clone with core.autocrlf=true expands that to CRLF on checkout while much of the tooling writes LF back, so files drift into mixed endings and git announces the round-trip every time it touches one. Of the 911 tracked files here, 369 had CRLF and 28 had both within a single file. Attributes override whatever core.autocrlf each developer happens to have, which puts the policy in the repository rather than in every clone's configuration and makes a checkout byte-identical on every platform. Batch files are the exception and keep CRLF, since cmd.exe wants it. The binary formats are listed explicitly so that no text heuristic can misfire and corrupt one with an EOL conversion, even though git detects them on its own today. The working tree was converted in place to match, which changes no file's content. The same policy landed in the platforms, ips and benchmarks submodules, whose indexes were likewise already LF. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each of platforms, ips and benchmarks now carries the same .gitattributes, so their recorded revisions move to it. That is the only change in all three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 12, 2026
…dened `>>` states what it assumed Target-context widening re-evaluates an anonymous cone at the target width on the strength of agreeing with the narrow evaluation whatever the target turns out to be. That holds for `+`/`-`/`*`, which commute with truncation, and for `<<`, which is a multiplication and commutes with it too. It does not hold for `>>`, which drops the bits a narrow evaluation brings down, so the agreement rests on the target really being at least as wide as the operand. Where the two widths cannot be compared the rule assumes it, and the design now states what was assumed. That assumption is the same relation the assignment's own width fit already states about the same cone, a shift's type being its left operand's type, and the two dedup. The one place it is the sole statement of it is under a width-adjustment permission, which is what makes the assignment skip its fit. Which is how the permission turned out to be fighting the widening. `.truncate` states that the target is NARROWER, the exact contradiction of what an undecided comparison optimistically assumes, and the widening was winning: the cone was re-evaluated at the target, which for `>>` computes something else than what was written, leaving the design to state both directions at once and hold for a single width. An undecided comparison now reads the permission, so `.truncate` is what decides there and the cone keeps its own width to narrow as asked. Only there: a permission whose direction does not apply contributes nothing, so a provably wider target widens as it always did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…al to land on (#463) `clearConsole` defaulted to true whenever scala-cli or Metals was detected, so every run printed `ESC c` no matter where the output went: on a real terminal that discarded the scrollback above it, and in captured output it left a stray `c` glued to the banner. Detecting the launcher says nothing about whether anything can meaningfully be cleared. `isTTY` answers that question, and `isColorTerminal` answers the adjacent one for the printer's ANSI codes, which had the same defect of being emitted into pipes and log files. `System.console() != null` is not the test on every JDK: since JDK 22 a Console can also be returned for redirected streams, and only `Console.isTerminal()` tells the two apart. We emit Java 17 bytecode, so that method is looked up reflectively, and its absence (JDK 17-21) means a non-null Console already implies a terminal. Color additionally keeps the launchers that capture our streams and forward them to a console of their own: sbt (whose server JVM owns no terminal at all under `sbtn`), IntelliJ IDEA and Metals all render ANSI on the other end, so gating color on `isTTY` alone would have turned the entire sbt dev flow monochrome. `NO_COLOR` and `TERM=dumb` opt out, and `FORCE_COLOR` opts back in for a terminal we cannot detect as one, such as MinTTY under Git Bash on Windows. The tool runner's `os.Inherit` choice moves to `isTTY` as well, which is the same decision it already made on any JDK below 22 and the correct one above it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ejecting what it cannot prove `-`, `/` and `%` all take the LHS width and convert the RHS to it, so all three need the RHS to fit, and the shared arithmetic check already decided that three ways: proven, proven violated, and undecided, the last stated as a constraint of the design like every other adaptation states its own. `-` carried a proof of its own on top which rejected the undecided answer outright, so one operation in three answered "cannot tell" with "no" while its two siblings answered it. The apparent inconsistency between literal and parametric widths was not one. `u8 - u16` and `u8 / u16` are both compile errors, and a parametric pair that is violated for every valid assignment is still an elaboration error. Those are the provably-violated answer, which stays a rejection everywhere; only the undecided middle moves, and it moves to where the rest of the language already had it. The rejection was also advertising a remedy it refused: the check never read the RHS's width-adjustment permission, so `a - b.truncate` failed with the message recommending `.truncate`. The permission is now read for the width relation it covers, as the assignment and the comparison already read it, with signedness still checked either way since that is not a permission's to give. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.