fix circuit type checking

This commit is contained in:
collin 2022-06-17 18:32:49 -07:00
parent 5a0186b93d
commit 52395bba45
37 changed files with 223 additions and 458 deletions

View File

@ -52,7 +52,7 @@ pub struct CircuitInitExpression {
impl fmt::Display for CircuitInitExpression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {{", self.name)?;
write!(f, "{} {{ ", self.name)?;
for member in self.members.iter() {
write!(f, "{}", member)?;
write!(f, ", ")?;

View File

@ -28,21 +28,21 @@ use leo_span::{sym, Symbol};
use indexmap::IndexSet;
/// Returns `true` if the given symbol matches the name of a core circuit.
pub fn is_core_circuit(circuit: Symbol) -> bool {
match circuit {
sym::bhp256
| sym::bhp512
| sym::bhp768
| sym::bhp1024
| sym::ped64
| sym::ped128
| sym::psd2
| sym::psd4
| sym::psd8 => true,
_ => false,
}
}
// /// Returns `true` if the given symbol matches the name of a core circuit.
// pub fn is_core_circuit(circuit: Symbol) -> bool {
// match circuit {
// sym::bhp256
// | sym::bhp512
// | sym::bhp768
// | sym::bhp1024
// | sym::ped64
// | sym::ped128
// | sym::psd2
// | sym::psd4
// | sym::psd8 => true,
// _ => false,
// }
// }
/// A core instruction that maps directly to an AVM bytecode instruction.
#[derive(Clone, PartialEq, Eq)]

View File

@ -406,24 +406,26 @@ impl ParserContext<'_> {
Some(Ok(gt))
}
fn parse_circuit_member(&mut self) -> Result<CircuitVariableInitializer> {
let identifier = self.expect_ident()?;
let expression = if self.eat(&Token::Colon) {
// Parse individual circuit variable declarations.
Some(self.parse_expression()?)
} else {
None
};
Ok(CircuitVariableInitializer { identifier, expression })
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// circuit initialization expression.
/// let foo = Foo { x: 1u8 };
pub fn parse_circuit_expression(&mut self, identifier: Identifier) -> Result<Expression> {
let (members, _, span) = self.parse_list(Delimiter::Brace, Some(Token::Comma), |p| {
let expression = if p.eat(&Token::Colon) {
// Parse individual circuit variable declarations.
Some(p.parse_expression()?)
} else {
None
};
let (members, _, end) = self.parse_list(Delimiter::Brace, Some(Token::Comma), |p| p.parse_circuit_member().map(Some))?;
Ok(Some(CircuitVariableInitializer {
identifier: p.expect_ident()?,
expression,
}))
})?;
Ok(Expression::CircuitInit(CircuitInitExpression {
span: &identifier.span + &span,
span: identifier.span + end,
name: identifier,
members,
}))
@ -480,7 +482,7 @@ impl ParserContext<'_> {
Token::StaticString(value) => Expression::Value(ValueExpression::String(value, span)),
Token::Ident(name) => {
let ident = Identifier { name, span };
if !self.disallow_circuit_construction && self.eat(&Token::LeftCurly) {
if !self.disallow_circuit_construction && self.check(&Token::LeftCurly) {
self.parse_circuit_expression(ident)?
} else {
Expression::Identifier(ident)
@ -491,7 +493,7 @@ impl ParserContext<'_> {
name: sym::SelfUpper,
span,
};
if !self.disallow_circuit_construction && self.eat(&Token::LeftCurly) {
if !self.disallow_circuit_construction && self.check(&Token::LeftCurly) {
self.parse_circuit_expression(ident)?
} else {
Expression::Identifier(ident)

View File

@ -163,8 +163,11 @@ impl ParserContext<'_> {
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member function.
pub fn parse_member_function_declaration(&mut self) -> Result<CircuitMember> {
if self.peek_is_function() {
// CAUTION: function members are unstable for testnet3.
let function = self.parse_function()?;
Ok(CircuitMember::CircuitFunction(Box::new(function.1)))
return Err(ParserError::circuit_functions_unstable(function.1.span()).into())
// Ok(CircuitMember::CircuitFunction(Box::new(function.1)))
} else {
return Err(Self::unexpected_item(&self.token).into());
}
@ -246,7 +249,9 @@ impl ParserContext<'_> {
// Parse return type.
self.expect(&Token::Arrow)?;
self.disallow_circuit_construction = true;
let output = self.parse_all_types()?.0;
self.disallow_circuit_construction = false;
// Parse the function body.
let block = self.parse_block()?;

View File

@ -47,4 +47,11 @@ impl<'a> ProgramVisitor<'a> for CreateSymbolTable<'a> {
}
VisitResult::SkipChildren
}
fn visit_circuit(&mut self, input: &'a Circuit) -> VisitResult {
if let Err(err) = self.symbol_table.insert_circuit(input.name(), input) {
self.handler.emit_err(err);
}
VisitResult::SkipChildren
}
}

View File

@ -16,7 +16,7 @@
use std::fmt::Display;
use leo_ast::Function;
use leo_ast::{Circuit, Function};
use leo_errors::{AstError, Result};
use leo_span::{Span, Symbol};
@ -26,9 +26,12 @@ use crate::{VariableScope, VariableSymbol};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SymbolTable<'a> {
/// Functions represents the name of each function mapped to the Ast's function definition.
/// Maps function names to function definitions.
/// This field is populated at a first pass.
functions: IndexMap<Symbol, &'a Function>,
/// Maps circuit names to circuit definitions.
/// This field is populated at a first pass.
circuits: IndexMap<Symbol, &'a Circuit>,
/// Variables represents functions variable definitions and input variables.
/// This field is not populated till necessary.
pub(crate) variables: VariableScope<'a>,
@ -54,6 +57,15 @@ impl<'a> SymbolTable<'a> {
Ok(())
}
pub fn insert_circuit(&mut self, symbol: Symbol, insert: &'a Circuit) -> Result<()> {
if self.circuits.contains_key(&symbol) {
// Return an error if the circuit name has already been inserted.
return Err(AstError::shadowed_circuit(symbol, insert.span).into())
}
self.circuits.insert(symbol, insert);
Ok(())
}
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol<'a>) -> Result<()> {
self.check_shadowing(&symbol, insert.span)?;
self.variables.variables.insert(symbol, insert);
@ -64,6 +76,10 @@ impl<'a> SymbolTable<'a> {
self.functions.get(symbol)
}
pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&&'a Circuit> {
self.circuits.get(symbol)
}
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
self.variables.lookup_variable(symbol)
}
@ -91,6 +107,10 @@ impl<'a> Display for SymbolTable<'a> {
write!(f, "{func}")?;
}
for circ in self.circuits.values() {
write!(f, "{circ}")?;
}
write!(f, "{}", self.variables)
}
}

View File

@ -15,7 +15,6 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use leo_ast::*;
use leo_core::is_core_circuit;
use leo_errors::TypeCheckerError;
use crate::TypeChecker;
@ -66,8 +65,8 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor.visit_identifier(input) {
return if is_core_circuit(input.name) {
Some(Type::Identifier(*input))
return if let Some(circuit) = self.visitor.symbol_table.clone().lookup_circuit(&input.name) {
Some(self.visitor.assert_expected_option(Type::Identifier(circuit.identifier.clone()), expected, circuit.span()))
} else if let Some(var) = self.visitor.symbol_table.clone().lookup_variable(&input.name) {
Some(self.visitor.assert_expected_option(*var.type_, expected, var.span))
} else {
@ -602,9 +601,45 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
fn visit_circuit_init(
&mut self,
_input: &'a CircuitInitExpression,
_additional: &Self::AdditionalInput,
input: &'a CircuitInitExpression,
additional: &Self::AdditionalInput,
) -> Option<Self::Output> {
unimplemented!("disable circuit inits for now")
if let Some(circ) = self.visitor.symbol_table.clone().lookup_circuit(&input.name.name) {
// Check circuit type name.
let ret = self.visitor.assert_expected_circuit(circ.identifier, additional, input.name.span());
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.visitor.handler.emit_err(
TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
)
.into(),
);
}
// Check circuit member types
circ.members
.iter()
.zip(input.members.iter())
.for_each(|(expected, actual)| {
match expected {
CircuitMember::CircuitVariable(_name, type_) => {
if let Some(expr) = &actual.expression {
self.visit_expression(expr, &Some(*type_));
}
}
_ => {/* Circuit functions cannot be inside circuit init expressions */}
}
});
Some(ret)
} else {
self.visitor
.handler
.emit_err(TypeCheckerError::unknown_sym("circuit", &input.name.name, input.name.span()).into());
None
}
}
}

View File

@ -53,8 +53,4 @@ impl<'a> ProgramVisitorDirector<'a> for Director<'a> {
}
}
}
fn visit_circuit(&mut self, _input: &'a Circuit) {
todo!()
}
}

View File

@ -98,9 +98,9 @@ impl<'a> TypeChecker<'a> {
/// Emits an error if the given type conflicts with a core library type.
pub(crate) fn check_ident_type(&self, type_: &Option<Type>) {
if let Some(Type::Identifier(ident)) = type_ {
if !(self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name)) {
if self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name) {
self.handler
.emit_err(TypeCheckerError::invalid_core_type(&ident.name, ident.span()).into());
.emit_err(TypeCheckerError::core_type_name_conflict(&ident.name, ident.span()).into());
}
}
}
@ -140,6 +140,18 @@ impl<'a> TypeChecker<'a> {
}
}
/// Returns the `circuit` type and emits an error if the `expected` type does not match.
pub(crate) fn assert_expected_circuit(&mut self, circuit: Identifier, expected: &Option<Type>, span: Span) -> Type {
if let Some(Type::Identifier(expected)) = expected {
if expected.name != circuit.name {
self.handler
.emit_err(TypeCheckerError::type_should_be(circuit.name, expected.name, span).into());
}
}
Type::Identifier(circuit)
}
/// Returns the given `actual` type and emits an error if the `expected` type does not match.
pub(crate) fn assert_expected_option(&mut self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
if let Some(expected) = expected {

View File

@ -1,6 +1,6 @@
[project]
name = "hello-world"
version = "0.2.0"
version = "0.3.0"
description = "Logs hello world"
license = "LICENSE-MIT"

View File

@ -1,3 +1,8 @@
circuit Foo {
x: u8,
y: u8,
}
function main(a: u8) -> field {
return BHP257::hash(a);
return BHP256::hash(a);
}

View File

@ -1 +0,0 @@
outputs/

View File

@ -1,12 +0,0 @@
[project]
name = "linear-regression"
version = "0.1.0"
description = "The linear-regression package"
license = "MIT"
[remote]
author = "aleo"
[target]
curve = "bls12_377"
proving_system = "groth16"

View File

@ -1,20 +0,0 @@
# linear-regression
## Build Guide
To compile this Leo program, run:
```bash
leo build
```
To test this Leo program, run:
```bash
leo test
```
## Development
To output the number of constraints, run:
```bash
leo build -d
```

View File

@ -1,7 +0,0 @@
// The program input for light-cuddly-cyan-polo/src/main.leo
[main]
x: i32 = 1i32;
y: i32 = 2i32;
[registers]
r0: [i32; 2] = [1i32, 2i32];

View File

@ -1,26 +0,0 @@
// The program state for linear-regression/src/main.leo
[[public]]
[state]
leaf_index: u32 = 0;
root: [u8; 32] = [0; 32];
[[private]]
[record]
serial_number: [u8; 64] = [0; 64];
commitment: [u8; 32] = [0; 32];
owner: address = aleo1daxej63vwrmn2zhl4dymygagh89k5d2vaw6rjauueme7le6k2q8sjn0ng9;
is_dummy: bool = false;
value: u64 = 0;
payload: [u8; 32] = [0; 32];
birth_program_id: [u8; 48] = [0; 48];
death_program_id: [u8; 48] = [0; 48];
serial_number_nonce: [u8; 32] = [0; 32];
commitment_randomness: [u8; 32] = [0; 32];
[state_leaf]
path: [u8; 128] = [0; 128];
memo: [u8; 32] = [0; 32];
network_id: u8 = 0;
leaf_randomness: [u8; 32] = [0; 32];

View File

@ -1,65 +0,0 @@
circuit Point {
x: i32,
y: i32,
function new(x: i32, y: i32) -> Self {
return Self { x, y };
}
}
circuit LinearRegression {
points: [Point; 5],
// Instantiates a linear regression circuit.
function new(points: [Point; 5]) -> Self {
return Self { points };
}
// Return the slope of the linear regression.
function slope(self) -> i32 {
let num_points = 5i32;
// Calculate the sums.
let x_sum = 0i32;
let y_sum = 0i32;
let xy_sum = 0i32;
let x2_sum = 0i32;
for i in 0..5 {
x_sum += self.points[i].x;
y_sum += self.points[i].y;
xy_sum += self.points[i].x * self.points[i].y;
x2_sum += self.points[i].x * self.points[i].x;
}
let numerator = (num_points * xy_sum) - (x_sum * y_sum);
let denominator = (num_points * x2_sum) - (x_sum * x_sum);
let slope = numerator / denominator;
return slope;
}
// Return the offset of the linear regression.
function offset(self, slope: i32) -> i32 {
let num_points = 5i32;
// Calculate the sum.
let x_sum = 0i32;
let y_sum = 0i32;
for i in 0..5 {
x_sum += self.points[i].x;
y_sum += self.points[i].y;
}
return (y_sum - slope * x_sum) / num_points;
}
}
function main (x: i32, y: i32) -> [i32; 2] {
let points: [Point; 5] = [
Point{x: x + 1, y: y + 1},
Point{x: x + 2, y: y + 2},
Point{x: x + 3, y: y + 3},
Point{x: x + 4, y: y + 4},
Point{x: x + 5, y: y + 5}
];
let reg = LinearRegression::new(points);
let slope = reg.slope();
let offset = reg.offset(slope);
return [slope, offset];
}

View File

@ -1,12 +0,0 @@
[project]
name = "pedersen-hash"
version = "0.1.0"
description = "A 256bit hash function"
license = "LICENSE-MIT"
[remote]
author = "aleo"
[target]
curve = "bls12_377"
proving_system = "groth16"

View File

@ -1 +0,0 @@
# pedersen-hash

View File

@ -1,8 +0,0 @@
[main]
hash_input: [bool; 256] = [true; 256];
[constants]
parameters: [group; 256] = [1group; 256];
[registers]
r0: group = (1, 0)group;

View File

@ -1,25 +0,0 @@
circuit PedersenHash {
parameters: [group; 256];
// Instantiates a Pedersen hash circuit
function new(parameters: [group; 256]) -> Self {
return Self { parameters: parameters };
}
function hash(self, bits: [bool; 256]) -> group {
let digest: group = 0group;
for i in 0..256 {
if bits[i] {
digest += self.parameters[i];
}
}
return digest;
}
}
// The 'pedersen-hash' main function.
function main(hash_input: [bool; 256], const parameters: [group; 256]) -> group {
const pedersen = PedersenHash::new(parameters);
return pedersen.hash(hash_input);
}

View File

@ -1,2 +0,0 @@
outputs/
/.leo

View File

@ -1,12 +0,0 @@
[project]
name = "silly-sudoku"
version = "0.1.3"
description = "A simple Sudoku puzzle grid"
license = "MIT"
[remote]
author = "howard"
[target]
curve = "bls12_377"
proving_system = "groth16"

View File

@ -1,23 +0,0 @@
# silly-sudoku
A simple Sudoku puzzle grid in Leo.
## Walkthrough
Start by defining a puzzle grid:
```
[[0, 4, 6],
[3, 0, 9],
[7, 5, 0]]
```
We treat all 0's as empty cells in the grid.
Next, generate an answer and construct it as a puzzle grid solution:
```
[[8, 4, 6],
[3, 1, 9],
[7, 5, 2]]
```
The SillySudoku circuit will proceed to verify that the solution grid matches the starting puzzle grid,
and check that each number between 1 - 9 is used exactly once.

View File

@ -1,12 +0,0 @@
// The program input for tmp-test/src/main.leo
[main]
puzzle: [u8; (3, 3)] = [[0, 2, 0],
[0, 0, 6],
[0, 8, 9]];
answer: [u8; (3, 3)] = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]];
[registers]
r: bool = false;

View File

@ -1,26 +0,0 @@
// The program state for tmp-test/src/main.leo
[[public]]
[state]
leaf_index: u32 = 0;
root: [u8; 32] = [0; 32];
[[private]]
[record]
serial_number: [u8; 64] = [0; 64];
commitment: [u8; 32] = [0; 32];
owner: address = aleo1daxej63vwrmn2zhl4dymygagh89k5d2vaw6rjauueme7le6k2q8sjn0ng9;
is_dummy: bool = false;
value: u64 = 0;
payload: [u8; 32] = [0; 32];
birth_program_id: [u8; 48] = [0; 48];
death_program_id: [u8; 48] = [0; 48];
serial_number_nonce: [u8; 32] = [0; 32];
commitment_randomness: [u8; 32] = [0; 32];
[state_leaf]
path: [u8; 128] = [0; 128];
memo: [u8; 32] = [0; 32];
network_id: u8 = 0;
leaf_randomness: [u8; 32] = [0; 32];

View File

@ -1,14 +0,0 @@
// The program input for tmp-test/src/main.leo
[main]
puzzle: [u8; (3, 3)] = [[1, 0, 5],
[0, 2, 0],
[7, 0, 0]];
answer: [u8; (3, 3)] = [[1, 4, 5],
[3, 2, 6],
[7, 8, 9]];
expected: bool = true;
[registers]
r: bool = false;

View File

@ -1,70 +0,0 @@
/**
* The SillySudoku circuit
*
* This circuit generates a silly Sudoku puzzle,
* by constructing a 3x3 puzzle grid with some preset numbers 1-9,
* and requiring an answer where each number is used exactly once.
*
* -----------
* | 5 | 8 | 3 |
* |-----------|
* | 2 | 7 | 4 |
* |-----------|
* | 1 | 9 | 6 |
* -----------
*/
circuit SillySudoku {
// The starting grid values for the Sudoku puzzle.
// Unset cells on the puzzle grid are set to 0.
puzzle_grid: [u8; (3, 3)];
/**
* Returns true if a given Sudoku answer is correct.
*
* Verifies a given answer by iterating through the Sudoku puzzle,
* and checking that each number is set exactly once.
*/
function solve(self, answer: [u8; (3, 3)]) -> bool {
// The result boolean is set to true, if the answer is correct.
let result = true;
// An array that tracks the numbers used on the Sudoku grid.
let seen = [false; 9];
// Iterate through the Sudoku grid and check each cell.
for i in 0..3 {
for j in 0..3 {
// Fetch the current cell value for the Sudoku grid.
let grid_value = self.puzzle_grid[i][j];
// Fetch the current cell value for the given answer.
let answer_value = answer[i][j];
// Set the index by subtracting 1 from the answer value.
let index = answer_value - 1;
// Check if this number has already been used on the grid.
let already_seen: bool = seen[index];
// If this number is already used, the answer is incorrect.
// Sets the result to false.
if already_seen {
result = false;
}
// If the cell is not empty, and the grid value doesn't match
// the answer value, the answer is incorrect.
// Sets the result to false.
if (grid_value != 0 && grid_value != answer_value) {
result = false;
}
// Sets the answer value as seen.
seen[index] = true;
}
}
// Returns true if all numbers 1-9 have been seen exactly once.
return result;
}
}

View File

@ -1,71 +0,0 @@
import lib.SillySudoku;
// The `silly-sudoku` main function
function main(puzzle: [u8; (3, 3)], answer: [u8; (3, 3)]) -> bool {
console.log("Starting Sudoku solver...");
console.log("{}", puzzle);
// Instantiate the Sudoku puzzle.
let sudoku = SillySudoku { puzzle_grid: puzzle };
console.log("Checking Sudoku answer...");
console.log("{}", answer);
// Evaluate the Sudoku puzzle with the given answer.
let result = sudoku.solve(answer);
console.log("The answer is {}.", result);
return result;
}
// Tests that the `silly-sudoku` circuit outputs true on a correct answer.
@test
function test_solve_pass() {
let puzzle: [u8; (3, 3)] = [[0, 2, 0],
[0, 0, 6],
[0, 8, 9]];
let answer: [u8; (3, 3)] = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]];
// Runs the Sudoku checker.
let result = main(puzzle, answer);
// Expects the result to be true.
console.assert(true == result);
}
// Tests that the `silly-sudoku` circuit outputs false on an incorrect answer.
@test
function test_solve_fail() {
let puzzle: [u8; (3, 3)] = [[0, 2, 0],
[0, 0, 6],
[0, 8, 0]];
let answer: [u8; (3, 3)] = [[1, 2, 3],
[4, 5, 6],
[7, 8, 8]]; // We have an extra `8` in this column!
// Runs the Sudoku checker.
let result = main(puzzle, answer);
// Expects the result to be false.
console.assert(false == result);
}
// Test that the `silly-sudoku` circuit outputs the expected value on a custom test input.
@test(test_input)
function test_solve_with_input(
puzzle: [u8; (3, 3)],
answer: [u8; (3, 3)],
expected: bool
) {
// Runs the Sudoku checker.
let result = main(puzzle, answer);
console.log("expected {}, got {}", expected, result);
console.assert(expected == result);
}

11
examples/token/Leo.toml Normal file
View File

@ -0,0 +1,11 @@
[project]
name = "token"
version = "0.3.0"
description = "A simple token"
license = "LICENSE-MIT"
[remote]
author = "aleo"
[target]
proving_system = "marlin"

View File

@ -0,0 +1,3 @@
[main]
[registers]

View File

@ -0,0 +1,50 @@
// CAUTION: Work in progress
circuit Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
balance: u64,
// The token amount.
amount: u64,
}
circuit Receiver {
// The token owner.
owner: Address,
// The token amount.
amount: u64,
}
function mint(r0: address, r1: u64) -> Token {
return Token {
owner: r0,
balance: 0u64,
amount: r1,
};
}
// The `transfer` function sends the specified number of tokens
// to the receiver from the provided token record.
function transfer(r0: Token, r1: Receiver) -> Token {
// Checks the given token record has sufficient balance.
// This `sub` operation is safe, and the proof will fail
// if an overflow occurs. The output register `r2` holds
// the change amount to be returned to the sender.
let r2: u64 = r0.amount - r1.amount;
// Calls the `mint` function to produce a token record
// for the specified receiver.
let r3: Token = mint(r1.owner, r1.amount);
// Calls the `mint` function to produce a token record
// with the change amount for the sender.
let r4: Token = mint(r0.owner, r0.amount);
// return (r3, r4);
return r3
}
function main() -> u8 {
return 1u8;
}

View File

@ -147,6 +147,14 @@ create_messages!(
help: None,
}
/// For when a user shadows a circuit.
@formatted
shadowed_circuit {
args: (circ: impl Display),
msg: format!("circuit `{circ}` shadowed by"),
help: None,
}
/// For when a user shadows a variable.
@formatted
shadowed_variable {

View File

@ -374,4 +374,12 @@ create_messages!(
msg: "Arbitrary methods calls are not supported. Only special ones are.",
help: None,
}
/// Circuit functions are unstable in testnet3.
@formatted
circuit_functions_unstable {
args: (),
msg: "Circuit functions are currently an unstable feature and are disabled in Leo for testnet3",
help: None,
}
);

View File

@ -153,12 +153,12 @@ create_messages!(
help: None,
}
/// For when an invalid core type is used.
/// For when a circuit is created with the same name as a core type.
@formatted
invalid_core_type {
core_type_name_conflict {
args: (type_: impl Display),
msg: format!(
"The type {type_} is not a valid core type.",
"The type {type_} is a reserved core type name.",
),
help: None,
}
@ -172,4 +172,14 @@ create_messages!(
),
help: None,
}
/// For when the user tries initialize a circuit with the incorrect number of args.
@formatted
incorrect_num_circuit_members {
args: (expected: impl Display, received: impl Display),
msg: format!(
"Circuit expected `{expected}` members, but got `{received}`",
),
help: None,
}
);