Help us shape the future of Go! Take the 2026 Go Developer Survey

consteval

package
v0.21.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package consteval evaluates WGSL const-expressions: the shader-creation-time evaluation that the WGSL specification requires for const declarations and that naga also applies to constant operands in function bodies.

It is the only constant evaluator in naga, used by every lowering context, like Rust naga's proc::ConstantEvaluator (ADR-007). It has two layers:

  • The numeric kernel works on scalars and vectors of ir.LiteralValue: Math, Binary, Unary, Cast, Relational and Select. It has no arena.
  • The Evaluator applies the kernel to an expression arena, appending the evaluated result, as Rust's try_eval_and_append does.

A result is always finite and representable: an operation whose result would overflow, leave its function's domain, divide by zero or not fit the target type returns an *Error instead, as the specification requires ("A shader-creation error results if any const-expression of floating-point type overflows or evaluates to NaN or infinity"). Arithmetic on concrete integer types wraps modulo 2^bitwidth, also as specified.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotImplemented: the operation is legal but cannot be evaluated
	// here. A function body keeps it as a run-time expression.
	ErrNotImplemented = errors.New("not implemented")
	// ErrInvalidBinaryArgs: operand types a binary operator does not accept
	// here. A function body keeps the expression and leaves it to
	// validation (Rust InvalidBinaryOpArgs).
	ErrInvalidBinaryArgs = errors.New("invalid binary operator arguments")
	// ErrInvalidOperand: an operand a math, conversion, unary or relational
	// operation does not accept (Rust InvalidMathArg, InvalidCastArg,
	// InvalidUnaryOpArg, InvalidRelationalArg). Unlike invalid binary
	// arguments it is an error in every context.
	ErrInvalidOperand = errors.New("invalid operand")
	// ErrNotConst: an operand is not a const-expression.
	ErrNotConst = errors.New("not a const-expression")
	// ErrOverrideInConst: an override-expression where a const-expression
	// is required.
	ErrOverrideInConst = errors.New("override-expression in a const-expression")
	// ErrOverflow: the result is not finite or does not fit its type.
	ErrOverflow = errors.New("overflow")
	// ErrOutOfDomain: an argument is outside the function's domain.
	ErrOutOfDomain = errors.New("argument out of domain")
	// ErrDivisionByZero: integer division by zero, or signed MIN / -1.
	ErrDivisionByZero = errors.New("division by zero")
	// ErrRemainderByZero: integer remainder by zero, or signed MIN % -1.
	ErrRemainderByZero = errors.New("remainder by zero")
	// ErrInvalidClamp: clamp with low > high.
	ErrInvalidClamp = errors.New("invalid clamp")
	// ErrBitRange: extractBits/insertBits offset + count exceeds the width.
	ErrBitRange = errors.New("bit range out of bounds")
	// ErrShiftOverflow: a shift amount or shifted value out of range.
	ErrShiftOverflow = errors.New("shift overflow")
	// ErrNotRepresentable: a value cannot be converted to the target type.
	ErrNotRepresentable = errors.New("value not representable")
)

Sentinel errors classify evaluation failures; every error the package returns is an *Error that wraps one of them, so callers test the class with errors.Is(err, consteval.ErrOverflow). They mirror the variants of Rust naga's ConstantEvaluatorError that a WGSL const-expression can produce.

Functions

func FallsBack

func FallsBack(err error) bool

FallsBack reports whether, in a function body, an expression whose evaluation failed with err is kept as a run-time expression instead of being a shader-creation error. It is Rust naga's rule in try_eval_and_append: only NotImplemented and InvalidBinaryOpArgs fall back; overflow, domain, division and range errors are errors wherever the operands are constant.

func RoundF16 added in v0.20.1

func RoundF16(x float64) (float32, bool)

RoundF16 rounds x to the nearest binary16 value as roundF16 does; ok is false for NaN, infinities and values beyond the finite binary16 range.

func RustEvaluates

func RustEvaluates(fun ir.MathFunction) bool

RustEvaluates reports whether Rust naga v30 evaluates the math function. A function body keeps the others as run-time expressions, as Rust does, so that its IR matches Rust's; a const-expression evaluates them anyway.

func RustEvaluatesRelational

func RustEvaluatesRelational(fun ir.RelationalFunction) bool

RustEvaluatesRelational reports whether Rust naga v30 evaluates the function; a function body keeps isNan and isInf as run-time expressions to match Rust's IR.

func ScalarOf

func ScalarOf(lit ir.LiteralValue) ir.ScalarType

ScalarOf returns the scalar type of a literal. Abstract types have width 8, the width of their representation (int64 and float64).

Types

type Arena

type Arena interface {
	Expression(h ir.ExpressionHandle) ir.ExpressionKind
	// Append adds an expression and returns its handle. In a function
	// arena it keeps expressions that need no Emit (Literal, Constant,
	// ZeroValue, ...) out of the pending Emit range, as Rust's append_expr.
	Append(kind ir.ExpressionKind) ir.ExpressionHandle
	// IsGlobal reports whether the arena is Module.GlobalExpressions.
	IsGlobal() bool
}

Arena is the expression arena the evaluator reads operands from and appends to: a function's expressions or Module.GlobalExpressions.

type Behavior

type Behavior int

Behavior selects what the evaluator may leave unevaluated, like Rust naga's Behavior / WgslRestrictions.

const (
	// BehaviorConst: a const-expression (module or function-local const).
	// Every expression must evaluate; anything else is an error.
	BehaviorConst Behavior = iota + 1
	// BehaviorRuntime: a function body. Expressions with constant operands
	// are evaluated; the rest, and operations Rust naga does not evaluate,
	// are appended as run-time expressions.
	BehaviorRuntime
	// BehaviorOverride: an override initializer. Constant operands are
	// evaluated; override-expressions are appended as they are.
	BehaviorOverride
)

type Error

type Error struct {
	Class error
	Msg   string
}

Error is an evaluation error: a class (one of the Err sentinels) and the full message, worded like Rust naga's where Rust reports the condition.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the class, for errors.Is.

type Evaluator

type Evaluator struct {
	Module   *ir.Module
	Arena    Arena
	Types    Types
	Behavior Behavior
	// contains filtered or unexported fields
}

Evaluator evaluates expressions over an arena, as Rust naga's ConstantEvaluator::try_eval_and_append does, and tracks the evaluation stage of every expression in the arena (Rust's ExpressionKindTracker). One Evaluator serves one arena for its whole lifetime.

func (*Evaluator) CastArray

CastArray converts the leaf scalars of the const-expression h to the scalar type to, as Rust naga's cast_array does for WGSL's automatic conversions and concretization: an array constructor is converted element by element into a new constructor of the converted array type, a zero array becomes a zero value of the converted type, and anything else is an As conversion.

func (*Evaluator) ForceRuntime

func (e *Evaluator) ForceRuntime(h ir.ExpressionHandle)

ForceRuntime marks h as a run-time expression although its operands are constant: WGSL says an expression referring to a let-bound value is not a const-expression (Rust force_non_const).

func (*Evaluator) IsConst

func (e *Evaluator) IsConst(h ir.ExpressionHandle) bool

IsConst reports whether h is a const-expression.

func (*Evaluator) IsConstOrOverride

func (e *Evaluator) IsConstOrOverride(h ir.ExpressionHandle) bool

IsConstOrOverride reports whether h is a const- or override-expression.

func (*Evaluator) Literal

func (e *Evaluator) Literal(h ir.ExpressionHandle) (lit ir.LiteralValue, ok bool)

Literal returns the value of the scalar const-expression h, looking through constants and zero values (Rust get_const_val). ok is false when h is not a const-expression or not a scalar.

func (*Evaluator) TryEvalAndAppend

func (e *Evaluator) TryEvalAndAppend(kind ir.ExpressionKind) (ir.ExpressionHandle, error)

TryEvalAndAppend appends kind to the arena, evaluated if it can be, and returns the handle of the result.

  • Every operand a const-expression: kind is evaluated and its value (Literal, Compose, Splat, ZeroValue, or for a constant in the module arena the constant's initializer) is the result.
  • An operand an override or run-time expression: kind is appended unchanged when the behavior allows that, else it is an error.
  • In a function body an operation Rust naga does not evaluate is appended unchanged as a run-time expression (Rust's IR shape), after it is checked: a value the specification makes a shader-creation error is reported, which Rust does not do.

The error says why kind is not a valid expression here.

type Types

type Types interface {
	// Type returns the handle of the anonymous type inner, adding it if
	// the module has none.
	Type(inner ir.TypeInner) ir.TypeHandle
	// Array returns the handle of the array of base with the given size,
	// with the stride of base's layout.
	Array(base ir.TypeHandle, size ir.ArraySize) ir.TypeHandle
}

Types registers result types.

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value is the operand and result of the numeric kernel: a scalar, or a vector of 2 to 4 components that all have the same scalar type.

func Binary

func Binary(op ir.BinaryOperator, a, b Value) (Value, error)

Binary evaluates a binary operator on scalars or vectors.

Both operands must have the same scalar type, except the right operand of a shift, which is u32. Vector operands are combined component-wise; a vector and a scalar combine only with + - * / %, the scalar staying on its side. Comparisons yield bool components.

Concrete integer + - * wrap; abstract integer arithmetic is overflow-checked; float results must be finite. Invalid operand types are ErrInvalidBinaryArgs, which a function body keeps as a run-time expression.

func Cast

func Cast(v Value, to ir.ScalarType) (Value, error)

Cast converts each component of v to the scalar type to, as the WGSL conversion built-ins (value constructors such as i32(e)) and automatic conversions of abstract values do:

  • integer to integer of the same width reinterprets the bits;
  • float to integer truncates toward zero and saturates to the value of the target closest to the result that the source float type can represent exactly (WGSL "scalar floating point to integral conversion");
  • integer or float to float rounds to nearest, ties to even;
  • anything to bool is "not zero", bool to a number is 0 or 1.

Converting an abstract value is range-checked: a value the target cannot represent is a NotRepresentable error, with Rust naga's message. A conversion whose float result would be infinite is the same error for every source type (the specification forbids a non-finite const-expression; Rust naga v30 instead yields an f16 infinity from integer and f32 sources). Concrete values never convert to abstract types (ErrInvalidOperand).

func Math

func Math(fun ir.MathFunction, args ...Value) (Value, error)

Math evaluates a math built-in function on constant arguments.

Component-wise functions take scalars or vectors; every argument must have the same shape and scalar type (the caller converts abstract arguments and splats scalars first, as the WGSL overload rules require).

Float results are computed in float64 and rounded once to the argument's type. Rust naga computes f32 and f16 in f32 libm; a transcendental result can therefore differ from Rust's in the last place, never by more.

func Relational

func Relational(fun ir.RelationalFunction, v Value) (Value, error)

Relational evaluates all, any, isNan and isInf. all and any reduce a bool vector to a bool and return a bool scalar unchanged. isNan and isInf are false for every component: a constant is always finite (Rust v30 does not evaluate them; see RustEvaluatesRelational).

func Scalar

func Scalar(lit ir.LiteralValue) Value

Scalar returns a scalar value.

func Select

func Select(reject, accept, cond Value) (Value, error)

Select is select(reject, accept, condition): accept where the condition is true. A bool scalar condition chooses for every component; a bool vector chooses per component. As in Rust naga, accept is first converted to reject's scalar type, and a failed conversion is an error even where the condition picks reject.

func Unary

func Unary(op ir.UnaryOperator, v Value) (Value, error)

Unary evaluates a unary operator component-wise. Negation wraps for signed integers (-MIN == MIN, as defined) and flips the sign of floats, including zero; unsigned and bool operands are invalid.

func Vector

func Vector(comps ...ir.LiteralValue) Value

Vector returns a vector value. It panics unless there are 2 to 4 components of one scalar type: a malformed vector is a bug in the caller, not a shader error.

func (Value) Component

func (v Value) Component(i int) ir.LiteralValue

Component returns component i.

func (Value) Components

func (v Value) Components() []ir.LiteralValue

Components returns the components; the caller must not modify the slice.

func (Value) IsScalar

func (v Value) IsScalar() bool

IsScalar reports whether v is a scalar.

func (Value) Len

func (v Value) Len() int

Len returns the number of components: 1 for a scalar.

func (Value) Scalar

func (v Value) Scalar() ir.ScalarType

Scalar returns the scalar type of the components.

func (Value) Size

func (v Value) Size() ir.VectorSize

Size returns the vector size; it is only meaningful for a vector.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL