# Value

`Value` is the simulator's dynamically-typed value: a tagged union that holds one of a fixed set of scalar or vector types, chosen at runtime. It is the common currency for parsed literals, evaluated expressions, parameters, and introspected data - anything whose concrete type is not known until the netlist is parsed or an expression is evaluated. `Value` is declared and mostly implemented inline in `value.h`, with the larger algorithms (conversion, comparison, output) in `value.cpp`.

## Types

`ValueType` packs a scalar kind into the low bits and a vector marker into `VectorBit` (`8`) so that a vector type is always its scalar type ORed with `VectorBit`:

| Scalar | Code | Vector | Code |
|---|---|---|---|
| `Int` | 0 | `IntVec` | 8 |
| `Real` | 1 | `RealVec` | 9 |
| `String` | 2 | `StringVec` | 10 |
| `Value` | 4 | `ValueVec` | 12 |

`Int` is `IntegerValue` (`int32_t`, from `common.h`), `Real` is `double`, and `String` is `std::string`. `ValueVec` is a vector of `Value` itself - a heterogeneous list, used e.g. for the result of a netlist list expression. `Value` as a scalar type code is never actually stored in a `Value` (a `Value` cannot directly hold a `Value`); it exists only so the introspection layer (`introspection.h`/`introspection.cpp`, described alongside `docs/internals`'s struct-reflection mechanism) has a type tag for struct fields of type `Value`.

`DEFINE_FLAG_OPERATORS(ValueType)` (from `flags.h`) gives `ValueType` bitwise operators, which is what lets `scalarType()` (`type_ & ~VectorBit`), `isVector()` (`type_ & VectorBit`), and `isNumeric()` (scalar type is `Int` or `Real`) be implemented as one-line masks instead of a switch.

`type_pair(Type, Type)` packs two `Type`s into one `uint32_t` at compile time so a function that dispatches on a *pair* of types (conversion, equality) can do it with a single flat `switch` over `type_pair(...)` cases instead of a nested switch.

## Storage

The payload is a union of the two inline scalars (`iVal`, `rVal`) and five owning pointers (`sVal`, `iVec`, `rVec`, `sVec`, `vVec`) for `String` and the four vector kinds, tagged by `type_`. `Int` and `Real` live directly in the union; every other type is heap-allocated with `new` and owned exclusively by the `Value`.

`Value` hand-implements the full set of special member functions because the union prevents the compiler from generating them:

- **Copy constructor** deep-copies: scalars are copied by value, everything else gets a fresh `new` allocation copying the source container.
- **Move constructor** steals the pointer for heap-backed types and nulls the source's pointer (so the moved-from `Value`'s destructor, which will still run with the same `type_`, does not double-free).
- **Destructor** only needs to `delete` the heap-backed alternatives (`String` and the four vector types); it has no cases for `Int`/`Real` because they need no cleanup. `convert()` reuses this same reduced switch when it destroys and re-tags a `Value` in place - see the comment at `value.cpp:96` explaining that omission is intentional, not a missed case.
- **Copy assignment** (`operator=(const Value&)`) is copy-and-swap: it copies the source into a temporary and swaps, so it is correct under self-assignment for free. The type-specific assignment overloads (`operator=(Int)`, `operator=(const String&)`, `operator=(const IntVector&)`, ...) instead destroy the current contents and then either write the new scalar inline or heap-allocate a fresh copy.
- **Move assignment** from another `Value` destroys the current contents and move-constructs in place. The typed rvalue overloads (`operator=(String&&)`, `operator=(IntVector&&)`, ...) are more careful: if the `Value` already holds that exact type, they move-assign into the *existing* heap container instead of freeing it and allocating a new one - avoiding an allocation when, for example, a `Value` holding a `RealVector` is repeatedly overwritten with new `RealVector`s of similar size in a loop.

`swap()` (a `friend` found by ADL) is implemented generically via move-construct/destroy/placement-new rather than per-type, since a byte-for-byte union swap plus type tag swap is exactly what a move does.

## Typed access and compile-time mapping

`val<T>()` / `val<const T>() const` are template member functions with a full specialization per `T` (`Int`, `Real`, `String`, `IntVector`, ...) that return a reference into the union. There is no runtime check that `type_` actually matches `T` - the caller is expected to have checked `type()` (or to know it structurally, e.g. from a `type_pair` switch case that already establishes it) before calling `val<T>()`.

A handful of template mechanisms map between the C++ type and its `ValueType` tag, both directions, entirely at compile time so that generic code can be written once and instantiated per type:

- `typeCode<T>()` maps a C++ type to its `Type` tag (`typeCode<Real>() == Type::Real`).
- `TypeTag_<Type>` / `CodeToType<Type>` map a `Type` tag back to its C++ type; this direction needs a struct specialization per tag because a template *value* argument can't be used to select a `type` alias directly.
- `ScalarType_<T>` / `ScalarType<T>` map any type to its scalar counterpart (`RealVector -> Real`), with `ValueVector -> Value` reflecting that a heterogeneous list's "scalar type" is `Value` itself.
- `VectorType_<T, vectorize>` / `VectorType<T, vectorize>` map a scalar type to its vector counterpart only when the `vectorize` bool template parameter is `true`; with `vectorize=false` it is the identity. This lets one template be instantiated twice - once for a scalar code path, once for the corresponding vector code path - by flipping a bool rather than writing the logic twice.
- `IsVectorType<T>` is the compile-time counterpart of `isVector()`, for code that branches on `T` instead of on a runtime `Type`.

## Conversion

`convert(Type to, Value& dest, Status& s)` and `convertInPlace(Type to, Status& s)` both follow the [Status](status.md) convention: they return `bool` and only use `s` to explain a failure. Only numeric widening/narrowing is supported directly, scalar `Int<->Real` and elementwise `IntVec<->RealVec`; any other pairing (e.g. `String -> Int`) falls through to `s.set(Status::BadConversion, ...)` and returns `false`. A zero-length vector is special-cased to convert trivially to any other vector type, since there are no elements that would need converting and thus no possible type mismatch to fail on.

`convert()` writes into a separate `dest` `Value`, reusing its existing heap storage when `dest` is already tagged `to` and only destroying and reallocating when it is not. `convertInPlace()` mutates `*this` by building the converted container and move-assigning it in, going through the same move-assignment path described above.

`getScalar(Value& v, Int ndx, Status& s)` extracts one element of a vector `Value` into `v` by index, failing with `Status::BadConversion` if `*this` is not a vector and `Status::Range` if `ndx` is out of bounds. The index is typed `Int` - the vector index is itself a `Value`-compatible integer - rather than `size_t`, since indices usually arrive as evaluated expression results.

## Comparison, condition, and output

`operator==` is implemented per `type_pair`, comparing element-by-element for vectors and directly for scalars (`valueEqual<Tl, Tr>`); only the listed pairs (`Int`/`Int`, `Int`/`Real`, `Real`/`Real`, `String`/`String`, and the four matching vector pairs) are handled; any other type pair falls through to `return false` - comparing across incompatible types (e.g. a `String` to an `IntVec`) is simply "not equal," not an error.

`checkCondition(Status& s)` (inline in the header) interprets a `Value` as a boolean for control flow: nonzero for `Int`/`Real`, non-empty for `String`. It returns `std::tuple<bool, bool>` - `(ok, condition)` - and sets `Status::Unsupported` when the type has no boolean interpretation (vectors and `Value`). This is what evaluates `@if`/`@elseif` conditions during circuit elaboration (see `hierdevice.cpp` and [conditional netlist blocks](../cir-conditional.md)).

`operator<<` / `str()` print `Int`/`Real` raw, `String` double-quoted, `IntVec`/`RealVec`/`StringVec` as a bracketed (`[...]`), comma-separated list, and `ValueVec` the same way but brace-delimited (`{...}`) to set it apart from the other vector types; `ValueVec` recurses through `operator<<` on each element, so nested lists print correctly. `typeCodeToName(Type)` / `typeName()` give the human-readable type names ("integer", "real vector", ...) used in conversion error messages and elsewhere.
