Ops
ECLAIR has analogues of many of Rust's overloadable operators. These are the traits like Neg
, Add
, Mul
, etc that overload the behavior of the -
, +
, *
operators.
By now you can probably guess how ECLAIR defines analogues of these traits: function signatures pick up a mutable reference compiler: &mut COM
to a compiler type and use this to generate constraints, when appropriate. For the native compiler COM = ()
, each trait is identical to its pure Rust analogue.
For example, the pure Rust trait Add<Rhs>
:
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
is replaced with the ECLAIR trait Add<Rhs, COM>
:
pub trait Add<Rhs = Self, COM = ()> {
type Output;
fn add(self, rhs: Rhs, compiler: &mut COM) -> Self::Output;
}
Now a line like
output = lhs.add(rhs, &mut compiler);
causes compiler
to allocate a variable output
and constrain the value of output
to be the sum lhs + rhs
.
This straightforward transformation of traits is carried out for the following traits: Neg
, Not
, Add
, BitAnd
, BitOr
, BitXor
, Div
, Mul
, Rem
, Shl
, Shr
, Sub
, AddAssign
, BitAndAssign
, BitOrAssign
, BitXorAssign
, DivAssign
, MulAssign
, RemAssign
, ShlAssign
, ShrAssign
, SubAssign
. See the Rust documentation of these traits for more information.
Compiler Reflection
When some type T
implements Add<COM>
, this means that the compiler type COM
knows how to generate constraints that enforce correct addition of values of type T
. It can be useful to express this as a property of COM
rather than a property of T
, which ECLAIR does through "compiler reflection."
When T: Add<COM>
, compiler reflection means that COM: HasAdd<T>
. Explicitly, the HasAdd<T>
trait is
pub trait HasAdd<L, R = L> {
/// Output Type
/// The resulting type after applying the `+` operator.
type Output;
/// Performs the `lhs + rhs` operation over the
/// `self` compiler.
fn add(&mut self, lhs: L, rhs: R) -> Self::Output;
}
So the line
output = lhs.add(rhs, &mut compiler);
is equivalent to
output = compiler.add(lhs, rhs);
For each of the traits listed above we include an implementation of the corresponding reflection trait:
impl<COM, L, R> HasAdd<L, R> for COM
where
L: Add<R, COM>
{
fn add(&mut self, lhs: L, rhs: R) -> Self::Output {
lhs.add(rhs, self)
}
}
With this HasAdd
trait we can now use a trait bound COM: HasAdd<T>
to express the requirement that certain other ECLAIR traits or circuits make sense only for compilers that can add values of type T
.