mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-24 10:52:29 +03:00
merge return-tuple branch
This commit is contained in:
commit
ed79f5c957
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -194,9 +194,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.65"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
@ -341,6 +341,12 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
@ -503,12 +509,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
|
||||
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"cast 0.3.0",
|
||||
"clap 2.34.0",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
@ -533,7 +539,7 @@ version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"cast 0.2.7",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
@ -1498,9 +1504,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.28.4"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -59,7 +59,7 @@ git = "https://github.com/AleoHQ/snarkVM.git"
|
||||
rev = "18ee4e5"
|
||||
|
||||
[dependencies.backtrace]
|
||||
version = "0.3.65"
|
||||
version = "0.3.66"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "3.1"
|
||||
|
8
build.rs
8
build.rs
@ -45,15 +45,15 @@ fn compare_license_text(path: &Path, expected_lines: &[&str]) {
|
||||
for (i, (file_line, expected_line)) in reader.lines().zip(expected_lines).enumerate() {
|
||||
let file_line =
|
||||
file_line.unwrap_or_else(|_| panic!("Can't read line {} in file \"{}\"!", i + 1, path.display()));
|
||||
|
||||
assert!(
|
||||
&file_line == expected_line,
|
||||
assert_eq!(
|
||||
&file_line,
|
||||
expected_line,
|
||||
"Line {} in file \"{}\" was expected to contain the license text \"{}\", but contains \"{}\" instead! \
|
||||
Consult the expected license text in \".resources/license_header\"",
|
||||
i + 1,
|
||||
path.display(),
|
||||
expected_line,
|
||||
file_line,
|
||||
file_line
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod member_access;
|
||||
pub use member_access::*;
|
||||
mod associated_constant_access;
|
||||
pub use associated_constant_access::*;
|
||||
|
||||
mod associated_function_access;
|
||||
pub use associated_function_access::*;
|
||||
|
||||
mod associated_constant_access;
|
||||
pub use associated_constant_access::*;
|
||||
mod member_access;
|
||||
pub use member_access::*;
|
||||
|
||||
mod tuple_access;
|
||||
pub use tuple_access::*;
|
||||
|
40
compiler/ast/src/access/tuple_access.rs
Normal file
40
compiler/ast/src/access/tuple_access.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Expression, Node, PositiveNumber};
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// An tuple access expression, e.g., `tuple.index`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct TupleAccess {
|
||||
/// An expression evaluating to some tuple type, e.g., `(5, 2)`.
|
||||
pub tuple: Box<Expression>,
|
||||
/// The index to access in the tuple expression. E.g., `0` for `(5, 2)` would yield `5`.
|
||||
pub index: PositiveNumber,
|
||||
/// The span for the entire expression `tuple.index`.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for TupleAccess {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}.{}", self.tuple, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_node_impl!(TupleAccess);
|
@ -16,6 +16,7 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// A number string guaranteed to be positive.
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
|
||||
@ -30,6 +31,10 @@ impl PositiveNumber {
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.value.eq("0")
|
||||
}
|
||||
|
||||
pub fn to_usize(&self) -> usize {
|
||||
usize::from_str(&self.value).expect("failed to parse positive number")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PositiveNumber {
|
||||
|
@ -27,30 +27,32 @@ pub enum AccessExpression {
|
||||
// Array(ArrayAccess),
|
||||
// /// An expression accessing a range of an array.
|
||||
// ArrayRange(ArrayRangeAccess),
|
||||
/// An expression accessing a field in a structure, e.g., `circuit_var.field`.
|
||||
Member(MemberAccess),
|
||||
// /// Access to a tuple field using its position, e.g., `tuple.1`.
|
||||
// Tuple(TupleAccess),
|
||||
/// Access to an associated variable of a circuit e.g `u8::MAX`.
|
||||
AssociatedConstant(AssociatedConstant),
|
||||
/// Access to an associated function of a circuit e.g `Pedersen64::hash()`.
|
||||
AssociatedFunction(AssociatedFunction),
|
||||
/// An expression accessing a field in a structure, e.g., `circuit_var.field`.
|
||||
Member(MemberAccess),
|
||||
/// Access to a tuple field using its position, e.g., `tuple.1`.
|
||||
Tuple(TupleAccess),
|
||||
}
|
||||
|
||||
impl Node for AccessExpression {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
AccessExpression::Member(n) => n.span(),
|
||||
AccessExpression::AssociatedConstant(n) => n.span(),
|
||||
AccessExpression::AssociatedFunction(n) => n.span(),
|
||||
AccessExpression::Member(n) => n.span(),
|
||||
AccessExpression::Tuple(n) => n.span(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_span(&mut self, span: Span) {
|
||||
match self {
|
||||
AccessExpression::Member(n) => n.set_span(span),
|
||||
AccessExpression::AssociatedConstant(n) => n.set_span(span),
|
||||
AccessExpression::AssociatedFunction(n) => n.set_span(span),
|
||||
AccessExpression::Member(n) => n.set_span(span),
|
||||
AccessExpression::Tuple(n) => n.set_span(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,12 +62,10 @@ impl fmt::Display for AccessExpression {
|
||||
use AccessExpression::*;
|
||||
|
||||
match self {
|
||||
// Array(access) => access.fmt(f),
|
||||
// ArrayRange(access) => access.fmt(f),
|
||||
Member(access) => access.fmt(f),
|
||||
// Tuple(access) => access.fmt(f),
|
||||
AssociatedConstant(access) => access.fmt(f),
|
||||
AssociatedFunction(access) => access.fmt(f),
|
||||
Member(access) => access.fmt(f),
|
||||
Tuple(access) => access.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ impl fmt::Display for CircuitVariableInitializer {
|
||||
|
||||
/// A circuit initialization expression, e.g., `Foo { bar: 42, baz }`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CircuitInitExpression {
|
||||
pub struct CircuitExpression {
|
||||
/// The name of the structure type to initialize.
|
||||
pub name: Identifier,
|
||||
/// Initializer expressions for each of the fields in the circuit.
|
||||
@ -50,7 +50,7 @@ pub struct CircuitInitExpression {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for CircuitInitExpression {
|
||||
impl fmt::Display for CircuitExpression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {{ ", self.name)?;
|
||||
for member in self.members.iter() {
|
||||
@ -61,4 +61,4 @@ impl fmt::Display for CircuitInitExpression {
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_node_impl!(CircuitInitExpression);
|
||||
crate::simple_node_impl!(CircuitExpression);
|
||||
|
@ -18,9 +18,9 @@ use crate::GroupLiteral;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A literal expression.
|
||||
/// A literal.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum LiteralExpression {
|
||||
pub enum Literal {
|
||||
// todo: deserialize values here
|
||||
/// An address literal, e.g., `aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9`.
|
||||
Address(String, #[serde(with = "leo_span::span_json")] Span),
|
||||
@ -41,7 +41,7 @@ pub enum LiteralExpression {
|
||||
String(String, #[serde(with = "leo_span::span_json")] Span),
|
||||
}
|
||||
|
||||
impl fmt::Display for LiteralExpression {
|
||||
impl fmt::Display for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self {
|
||||
Self::Address(address, _) => write!(f, "{}", address),
|
||||
@ -55,7 +55,7 @@ impl fmt::Display for LiteralExpression {
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for LiteralExpression {
|
||||
impl Node for Literal {
|
||||
fn span(&self) -> Span {
|
||||
match &self {
|
||||
Self::Address(_, span)
|
@ -38,32 +38,37 @@ pub use err::*;
|
||||
mod ternary;
|
||||
pub use ternary::*;
|
||||
|
||||
mod tuple_init;
|
||||
pub use tuple_init::*;
|
||||
|
||||
mod unary;
|
||||
pub use unary::*;
|
||||
|
||||
mod value;
|
||||
pub use value::*;
|
||||
mod literal;
|
||||
pub use literal::*;
|
||||
|
||||
/// Expression that evaluates to a value.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Expression {
|
||||
/// A circuit access expression, e.g., `Foo.bar`.
|
||||
Access(AccessExpression),
|
||||
/// An identifier expression.
|
||||
Identifier(Identifier),
|
||||
/// A literal expression.
|
||||
Literal(LiteralExpression),
|
||||
/// A binary expression, e.g., `42 + 24`.
|
||||
Binary(BinaryExpression),
|
||||
/// A call expression, e.g., `my_fun(args)`.
|
||||
Call(CallExpression),
|
||||
/// An expression constructing a circuit like `Foo { bar: 42, baz }`.
|
||||
CircuitInit(CircuitInitExpression),
|
||||
Circuit(CircuitExpression),
|
||||
/// An expression of type "error".
|
||||
/// Will result in a compile error eventually.
|
||||
Err(ErrExpression),
|
||||
/// An identifier.
|
||||
Identifier(Identifier),
|
||||
/// A literal expression.
|
||||
Literal(Literal),
|
||||
/// A ternary conditional expression `cond ? if_expr : else_expr`.
|
||||
Ternary(TernaryExpression),
|
||||
/// A tuple expression e.g., `(foo, 42, true)`.
|
||||
Tuple(TupleExpression),
|
||||
/// An unary expression.
|
||||
Unary(UnaryExpression),
|
||||
}
|
||||
@ -73,13 +78,14 @@ impl Node for Expression {
|
||||
use Expression::*;
|
||||
match self {
|
||||
Access(n) => n.span(),
|
||||
Identifier(n) => n.span(),
|
||||
Literal(n) => n.span(),
|
||||
Binary(n) => n.span(),
|
||||
Call(n) => n.span(),
|
||||
CircuitInit(n) => n.span(),
|
||||
Circuit(n) => n.span(),
|
||||
Err(n) => n.span(),
|
||||
Identifier(n) => n.span(),
|
||||
Literal(n) => n.span(),
|
||||
Ternary(n) => n.span(),
|
||||
Tuple(n) => n.span(),
|
||||
Unary(n) => n.span(),
|
||||
}
|
||||
}
|
||||
@ -88,13 +94,14 @@ impl Node for Expression {
|
||||
use Expression::*;
|
||||
match self {
|
||||
Access(n) => n.set_span(span),
|
||||
Identifier(n) => n.set_span(span),
|
||||
Literal(n) => n.set_span(span),
|
||||
Binary(n) => n.set_span(span),
|
||||
Call(n) => n.set_span(span),
|
||||
CircuitInit(n) => n.set_span(span),
|
||||
Circuit(n) => n.set_span(span),
|
||||
Identifier(n) => n.set_span(span),
|
||||
Literal(n) => n.set_span(span),
|
||||
Err(n) => n.set_span(span),
|
||||
Ternary(n) => n.set_span(span),
|
||||
Tuple(n) => n.set_span(span),
|
||||
Unary(n) => n.set_span(span),
|
||||
}
|
||||
}
|
||||
@ -105,13 +112,14 @@ impl fmt::Display for Expression {
|
||||
use Expression::*;
|
||||
match &self {
|
||||
Access(n) => n.fmt(f),
|
||||
Identifier(n) => n.fmt(f),
|
||||
Literal(n) => n.fmt(f),
|
||||
Binary(n) => n.fmt(f),
|
||||
Call(n) => n.fmt(f),
|
||||
CircuitInit(n) => n.fmt(f),
|
||||
Circuit(n) => n.fmt(f),
|
||||
Err(n) => n.fmt(f),
|
||||
Identifier(n) => n.fmt(f),
|
||||
Literal(n) => n.fmt(f),
|
||||
Ternary(n) => n.fmt(f),
|
||||
Tuple(n) => n.fmt(f),
|
||||
Unary(n) => n.fmt(f),
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,30 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/// This module contains the ASG error definitions.
|
||||
pub mod asg_errors;
|
||||
pub use self::asg_errors::*;
|
||||
use super::*;
|
||||
|
||||
/// A tuple construction expression, e.g., `(foo, false, 42)`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct TupleExpression {
|
||||
/// The elements of the tuple.
|
||||
/// In the example above, it would be `foo`, `false`, and `42`.
|
||||
pub elements: Vec<Expression>,
|
||||
/// The span from `(` to `)`.
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for TupleExpression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"({})",
|
||||
self.elements
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
crate::simple_node_impl!(TupleExpression);
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Expression, GroupLiteral, IntegerType, LiteralExpression, Node, Type, UnaryOperation};
|
||||
use crate::{Expression, GroupLiteral, IntegerType, Literal, Node, Type, UnaryOperation};
|
||||
use leo_errors::{InputError, LeoError, Result};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -34,11 +34,11 @@ impl TryFrom<(Type, Expression)> for InputValue {
|
||||
fn try_from(value: (Type, Expression)) -> Result<Self> {
|
||||
Ok(match value {
|
||||
(type_, Expression::Literal(lit)) => match (type_, lit) {
|
||||
(Type::Address, LiteralExpression::Address(value, _)) => Self::Address(value),
|
||||
(Type::Boolean, LiteralExpression::Boolean(value, _)) => Self::Boolean(value),
|
||||
(Type::Field, LiteralExpression::Field(value, _)) => Self::Field(value),
|
||||
(Type::Group, LiteralExpression::Group(value)) => Self::Group(*value),
|
||||
(Type::IntegerType(expected), LiteralExpression::Integer(actual, value, span)) => {
|
||||
(Type::Address, Literal::Address(value, _)) => Self::Address(value),
|
||||
(Type::Boolean, Literal::Boolean(value, _)) => Self::Boolean(value),
|
||||
(Type::Field, Literal::Field(value, _)) => Self::Field(value),
|
||||
(Type::Group, Literal::Group(value)) => Self::Group(*value),
|
||||
(Type::IntegerType(expected), Literal::Integer(actual, value, span)) => {
|
||||
if expected == actual {
|
||||
Self::Integer(expected, value)
|
||||
} else {
|
||||
|
@ -27,31 +27,47 @@ pub trait ExpressionReconstructor {
|
||||
fn reconstruct_expression(&mut self, input: Expression) -> (Expression, Self::AdditionalOutput) {
|
||||
match input {
|
||||
Expression::Access(access) => self.reconstruct_access(access),
|
||||
Expression::Identifier(identifier) => self.reconstruct_identifier(identifier),
|
||||
Expression::Literal(value) => self.reconstruct_literal(value),
|
||||
Expression::Binary(binary) => self.reconstruct_binary(binary),
|
||||
Expression::Call(call) => self.reconstruct_call(call),
|
||||
Expression::CircuitInit(circuit) => self.reconstruct_circuit_init(circuit),
|
||||
Expression::Unary(unary) => self.reconstruct_unary(unary),
|
||||
Expression::Ternary(ternary) => self.reconstruct_ternary(ternary),
|
||||
Expression::Circuit(circuit) => self.reconstruct_circuit_init(circuit),
|
||||
Expression::Err(err) => self.reconstruct_err(err),
|
||||
Expression::Identifier(identifier) => self.reconstruct_identifier(identifier),
|
||||
Expression::Literal(value) => self.reconstruct_literal(value),
|
||||
Expression::Ternary(ternary) => self.reconstruct_ternary(ternary),
|
||||
Expression::Tuple(tuple) => self.reconstruct_tuple(tuple),
|
||||
Expression::Unary(unary) => self.reconstruct_unary(unary),
|
||||
}
|
||||
}
|
||||
|
||||
fn reconstruct_identifier(&mut self, input: Identifier) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Identifier(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_literal(&mut self, input: LiteralExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Literal(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_access(&mut self, input: AccessExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Access(input), Default::default())
|
||||
(
|
||||
Expression::Access(match input {
|
||||
AccessExpression::AssociatedFunction(function) => {
|
||||
AccessExpression::AssociatedFunction(AssociatedFunction {
|
||||
ty: function.ty,
|
||||
name: function.name,
|
||||
args: function
|
||||
.args
|
||||
.into_iter()
|
||||
.map(|arg| self.reconstruct_expression(arg).0)
|
||||
.collect(),
|
||||
span: function.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reconstruct_circuit_init(&mut self, input: CircuitInitExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::CircuitInit(input), Default::default())
|
||||
AccessExpression::Member(member) => AccessExpression::Member(MemberAccess {
|
||||
inner: Box::new(self.reconstruct_expression(*member.inner).0),
|
||||
name: member.name,
|
||||
span: member.span,
|
||||
}),
|
||||
AccessExpression::Tuple(tuple) => AccessExpression::Tuple(TupleAccess {
|
||||
tuple: Box::new(self.reconstruct_expression(*tuple.tuple).0),
|
||||
index: tuple.index,
|
||||
span: tuple.span,
|
||||
}),
|
||||
expr => expr,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_binary(&mut self, input: BinaryExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
@ -66,29 +82,6 @@ pub trait ExpressionReconstructor {
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_unary(&mut self, input: UnaryExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Unary(UnaryExpression {
|
||||
receiver: Box::new(self.reconstruct_expression(*input.receiver).0),
|
||||
op: input.op,
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_ternary(&mut self, input: TernaryExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Ternary(TernaryExpression {
|
||||
condition: Box::new(self.reconstruct_expression(*input.condition).0),
|
||||
if_true: Box::new(self.reconstruct_expression(*input.if_true).0),
|
||||
if_false: Box::new(self.reconstruct_expression(*input.if_false).0),
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_call(&mut self, input: CallExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Call(CallExpression {
|
||||
@ -104,9 +97,58 @@ pub trait ExpressionReconstructor {
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_circuit_init(&mut self, input: CircuitExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Circuit(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_err(&mut self, input: ErrExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Err(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_identifier(&mut self, input: Identifier) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Identifier(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_literal(&mut self, input: Literal) -> (Expression, Self::AdditionalOutput) {
|
||||
(Expression::Literal(input), Default::default())
|
||||
}
|
||||
|
||||
fn reconstruct_ternary(&mut self, input: TernaryExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Ternary(TernaryExpression {
|
||||
condition: Box::new(self.reconstruct_expression(*input.condition).0),
|
||||
if_true: Box::new(self.reconstruct_expression(*input.if_true).0),
|
||||
if_false: Box::new(self.reconstruct_expression(*input.if_false).0),
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_tuple(&mut self, input: TupleExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Tuple(TupleExpression {
|
||||
elements: input
|
||||
.elements
|
||||
.into_iter()
|
||||
.map(|element| self.reconstruct_expression(element).0)
|
||||
.collect(),
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reconstruct_unary(&mut self, input: UnaryExpression) -> (Expression, Self::AdditionalOutput) {
|
||||
(
|
||||
Expression::Unary(UnaryExpression {
|
||||
receiver: Box::new(self.reconstruct_expression(*input.receiver).0),
|
||||
op: input.op,
|
||||
span: input.span,
|
||||
}),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A Reconstructor trait for statements in the AST.
|
||||
|
@ -27,35 +27,35 @@ pub trait ExpressionVisitor<'a> {
|
||||
|
||||
fn visit_expression(&mut self, input: &'a Expression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
match input {
|
||||
Expression::Access(expr) => self.visit_access(expr, additional),
|
||||
Expression::CircuitInit(expr) => self.visit_circuit_init(expr, additional),
|
||||
Expression::Identifier(expr) => self.visit_identifier(expr, additional),
|
||||
Expression::Literal(expr) => self.visit_literal(expr, additional),
|
||||
Expression::Binary(expr) => self.visit_binary(expr, additional),
|
||||
Expression::Unary(expr) => self.visit_unary(expr, additional),
|
||||
Expression::Ternary(expr) => self.visit_ternary(expr, additional),
|
||||
Expression::Call(expr) => self.visit_call(expr, additional),
|
||||
Expression::Err(expr) => self.visit_err(expr, additional),
|
||||
Expression::Access(access) => self.visit_access(access, additional),
|
||||
Expression::Binary(binary) => self.visit_binary(binary, additional),
|
||||
Expression::Call(call) => self.visit_call(call, additional),
|
||||
Expression::Circuit(circuit) => self.visit_circuit_init(circuit, additional),
|
||||
Expression::Err(err) => self.visit_err(err, additional),
|
||||
Expression::Identifier(identifier) => self.visit_identifier(identifier, additional),
|
||||
Expression::Literal(literal) => self.visit_literal(literal, additional),
|
||||
Expression::Ternary(ternary) => self.visit_ternary(ternary, additional),
|
||||
Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
|
||||
Expression::Unary(unary) => self.visit_unary(unary, additional),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_access(&mut self, _input: &'a AccessExpression, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
fn visit_access(&mut self, input: &'a AccessExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
match input {
|
||||
AccessExpression::AssociatedFunction(function) => {
|
||||
function.args.iter().for_each(|arg| {
|
||||
self.visit_expression(arg, &Default::default());
|
||||
});
|
||||
}
|
||||
AccessExpression::Member(member) => {
|
||||
self.visit_expression(&member.inner, additional);
|
||||
}
|
||||
AccessExpression::Tuple(tuple) => {
|
||||
self.visit_expression(&tuple.tuple, additional);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fn visit_circuit_init(
|
||||
&mut self,
|
||||
_input: &'a CircuitInitExpression,
|
||||
_additional: &Self::AdditionalInput,
|
||||
) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, _input: &'a LiteralExpression, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
@ -65,18 +65,6 @@ pub trait ExpressionVisitor<'a> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_unary(&mut self, input: &'a UnaryExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
self.visit_expression(&input.receiver, additional);
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_ternary(&mut self, input: &'a TernaryExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
self.visit_expression(&input.condition, additional);
|
||||
self.visit_expression(&input.if_true, additional);
|
||||
self.visit_expression(&input.if_false, additional);
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, input: &'a CallExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
input.arguments.iter().for_each(|expr| {
|
||||
self.visit_expression(expr, additional);
|
||||
@ -84,9 +72,44 @@ pub trait ExpressionVisitor<'a> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_circuit_init(
|
||||
&mut self,
|
||||
_input: &'a CircuitExpression,
|
||||
_additional: &Self::AdditionalInput,
|
||||
) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_ternary(&mut self, input: &'a TernaryExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
self.visit_expression(&input.condition, additional);
|
||||
self.visit_expression(&input.if_true, additional);
|
||||
self.visit_expression(&input.if_false, additional);
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_tuple(&mut self, input: &'a TupleExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
input.elements.iter().for_each(|expr| {
|
||||
self.visit_expression(expr, additional);
|
||||
});
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_unary(&mut self, input: &'a UnaryExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
self.visit_expression(&input.receiver, additional);
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Visitor trait for statements in the AST.
|
||||
|
@ -17,5 +17,8 @@
|
||||
pub mod integer_type;
|
||||
pub use integer_type::*;
|
||||
|
||||
pub mod tuple;
|
||||
pub use tuple::*;
|
||||
|
||||
pub mod type_;
|
||||
pub use type_::*;
|
||||
|
55
compiler/ast/src/types/tuple.rs
Normal file
55
compiler/ast/src/types/tuple.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Type;
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::Span;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, ops::Deref};
|
||||
|
||||
/// A type list of at least two types.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Tuple(Vec<Type>);
|
||||
|
||||
impl Tuple {
|
||||
/// Returns a new `Type::Tuple` enumeration.
|
||||
pub fn try_new(elements: Vec<Type>, span: Span) -> Result<Type> {
|
||||
match elements.len() {
|
||||
0 => Err(AstError::empty_tuple(span).into()),
|
||||
1 => Err(AstError::one_element_tuple(span).into()),
|
||||
_ => Ok(Type::Tuple(Tuple(elements))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Tuple {
|
||||
type Target = Vec<Type>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Tuple {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"({})",
|
||||
self.0.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")
|
||||
)
|
||||
}
|
||||
}
|
@ -14,13 +14,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Identifier, IntegerType};
|
||||
use crate::{Identifier, IntegerType, Tuple};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Explicit type used for defining a variable or expression type
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Type {
|
||||
// Data types
|
||||
/// The `address` type.
|
||||
@ -39,6 +39,8 @@ pub enum Type {
|
||||
IntegerType(IntegerType),
|
||||
/// A reference to a built in type.
|
||||
Identifier(Identifier),
|
||||
/// A static tuple of at least one type.
|
||||
Tuple(Tuple),
|
||||
|
||||
/// Placeholder for a type that could not be resolved or was not well-formed.
|
||||
/// Will eventually lead to a compile error.
|
||||
@ -51,6 +53,7 @@ impl Type {
|
||||
///
|
||||
/// Flattens array syntax: `[[u8; 1]; 2] == [u8; (2, 1)] == true`
|
||||
///
|
||||
// TODO: Does not seem to flatten?
|
||||
pub fn eq_flat(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Type::Address, Type::Address)
|
||||
@ -60,6 +63,10 @@ impl Type {
|
||||
| (Type::Scalar, Type::Scalar)
|
||||
| (Type::String, Type::String) => true,
|
||||
(Type::IntegerType(left), Type::IntegerType(right)) => left.eq(right),
|
||||
(Type::Tuple(left), Type::Tuple(right)) => left
|
||||
.iter()
|
||||
.zip(right.iter())
|
||||
.all(|(left_type, right_type)| left_type.eq_flat(right_type)),
|
||||
(Type::Identifier(left), Type::Identifier(right)) => left.matches(right),
|
||||
_ => false,
|
||||
}
|
||||
@ -77,6 +84,7 @@ impl fmt::Display for Type {
|
||||
Type::String => write!(f, "string"),
|
||||
Type::IntegerType(ref integer_type) => write!(f, "{}", integer_type),
|
||||
Type::Identifier(ref variable) => write!(f, "circuit {}", variable),
|
||||
Type::Tuple(ref tuple) => write!(f, "{}", tuple),
|
||||
Type::Err => write!(f, "error"),
|
||||
}
|
||||
}
|
||||
|
@ -146,23 +146,23 @@ impl<'a> Compiler<'a> {
|
||||
///
|
||||
/// Runs the symbol table pass.
|
||||
///
|
||||
pub fn symbol_table_pass(&self) -> Result<SymbolTable<'_>> {
|
||||
pub fn symbol_table_pass(&self) -> Result<SymbolTable> {
|
||||
CreateSymbolTable::do_pass((&self.ast, self.handler))
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs the type checker pass.
|
||||
///
|
||||
pub fn type_checker_pass(&'a self, symbol_table: &mut SymbolTable<'_>) -> Result<()> {
|
||||
TypeChecker::do_pass((&self.ast, &mut symbol_table.clone(), self.handler))
|
||||
pub fn type_checker_pass(&'a self, symbol_table: SymbolTable) -> Result<SymbolTable> {
|
||||
TypeChecker::do_pass((&self.ast, self.handler, symbol_table))
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs the compiler stages.
|
||||
///
|
||||
pub fn compiler_stages(&self) -> Result<SymbolTable<'_>> {
|
||||
let mut st = self.symbol_table_pass()?;
|
||||
self.type_checker_pass(&mut st)?;
|
||||
pub fn compiler_stages(&self) -> Result<SymbolTable> {
|
||||
let st = self.symbol_table_pass()?;
|
||||
let st = self.type_checker_pass(st)?;
|
||||
Ok(st)
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ impl<'a> Compiler<'a> {
|
||||
/// Returns a compiled Leo program and prints the resulting bytecode.
|
||||
/// TODO: Remove when code generation is ready to be integrated into the compiler.
|
||||
///
|
||||
pub fn compile_and_generate_bytecode(&mut self) -> Result<(SymbolTable<'_>, String)> {
|
||||
pub fn compile_and_generate_bytecode(&mut self) -> Result<(SymbolTable, String)> {
|
||||
self.parse_program()?;
|
||||
let symbol_table = self.compiler_stages()?;
|
||||
|
||||
@ -182,7 +182,7 @@ impl<'a> Compiler<'a> {
|
||||
///
|
||||
/// Returns a compiled Leo program.
|
||||
///
|
||||
pub fn compile(&mut self) -> Result<SymbolTable<'_>> {
|
||||
pub fn compile(&mut self) -> Result<SymbolTable> {
|
||||
self.parse_program()?;
|
||||
self.compiler_stages()
|
||||
}
|
||||
|
@ -102,7 +102,6 @@ struct OutputItem {
|
||||
struct CompileOutput {
|
||||
pub output: Vec<OutputItem>,
|
||||
pub initial_ast: String,
|
||||
pub symbol_table: String,
|
||||
}
|
||||
|
||||
/// Get the path of the `input_file` given in `input` into `list`.
|
||||
@ -132,9 +131,9 @@ fn collect_all_inputs(test: &Test) -> Result<Vec<PathBuf>, String> {
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<SymbolTable<'_>, LeoError> {
|
||||
let mut st = parsed.symbol_table_pass()?;
|
||||
parsed.type_checker_pass(&mut st)?;
|
||||
fn compile_and_process<'a>(parsed: &'a mut Compiler<'a>) -> Result<SymbolTable, LeoError> {
|
||||
let st = parsed.symbol_table_pass()?;
|
||||
let st = parsed.type_checker_pass(st)?;
|
||||
Ok(st)
|
||||
}
|
||||
|
||||
@ -198,6 +197,7 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
let mut output_items = Vec::with_capacity(inputs.len());
|
||||
|
||||
if inputs.is_empty() {
|
||||
handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
output_items.push(OutputItem {
|
||||
initial_input_ast: "no input".to_string(),
|
||||
});
|
||||
@ -205,14 +205,13 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
for input in inputs {
|
||||
let mut parsed = parsed.clone();
|
||||
handler.extend_if_error(parsed.parse_input(input))?;
|
||||
handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
let initial_input_ast = hash_file("/tmp/output/initial_input_ast.json");
|
||||
|
||||
output_items.push(OutputItem { initial_input_ast });
|
||||
}
|
||||
}
|
||||
|
||||
let symbol_table = handler.extend_if_error(compile_and_process(&mut parsed))?;
|
||||
|
||||
let initial_ast = hash_file("/tmp/output/initial_ast.json");
|
||||
|
||||
if fs::read_dir("/tmp/output").is_ok() {
|
||||
@ -222,7 +221,6 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
|
||||
let final_output = CompileOutput {
|
||||
output: output_items,
|
||||
initial_ast,
|
||||
symbol_table: hash_content(&symbol_table.to_string()),
|
||||
};
|
||||
Ok(serde_yaml::to_value(&final_output).expect("serialization failed"))
|
||||
}
|
||||
|
@ -87,11 +87,16 @@ impl<'a> ParserContext<'a> {
|
||||
self.prev_token = mem::replace(&mut self.token, next_token);
|
||||
}
|
||||
|
||||
/// Checks whether the current token is `token`.
|
||||
/// Checks whether the current token is `Token`.
|
||||
pub(super) fn check(&self, tok: &Token) -> bool {
|
||||
&self.token.token == tok
|
||||
}
|
||||
|
||||
/// Checks whether the current token is a `Token::Int(_)`.
|
||||
pub(super) fn check_int(&self) -> bool {
|
||||
matches!(&self.token.token, Token::Integer(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the next token is equal to the given token.
|
||||
/// Advances the parser to the next token.
|
||||
pub(super) fn eat(&mut self, token: &Token) -> bool {
|
||||
@ -115,7 +120,7 @@ impl<'a> ParserContext<'a> {
|
||||
|
||||
/// Emit the error `err`.
|
||||
pub(super) fn emit_err(&self, err: ParserError) {
|
||||
self.handler.emit_err(err.into());
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
/// Emit the error `err`.
|
||||
@ -136,7 +141,7 @@ impl<'a> ParserContext<'a> {
|
||||
|
||||
/// Eats the next token if its an identifier and returns it.
|
||||
pub(super) fn eat_identifier(&mut self) -> Option<Identifier> {
|
||||
if let Token::Ident(name) = self.token.token {
|
||||
if let Token::Identifier(name) = self.token.token {
|
||||
self.bump();
|
||||
return Some(self.mk_ident_prev(name));
|
||||
}
|
||||
@ -144,9 +149,23 @@ impl<'a> ParserContext<'a> {
|
||||
}
|
||||
|
||||
/// Expects an [`Identifier`], or errors.
|
||||
pub(super) fn expect_ident(&mut self) -> Result<Identifier> {
|
||||
pub(super) fn expect_identifier(&mut self) -> Result<Identifier> {
|
||||
self.eat_identifier()
|
||||
.ok_or_else(|| ParserError::unexpected_str(&self.token.token, "ident", self.token.span).into())
|
||||
.ok_or_else(|| ParserError::unexpected_str(&self.token.token, "identifier", self.token.span).into())
|
||||
}
|
||||
|
||||
///
|
||||
/// Removes the next token if it is a [`Token::Integer(_)`] and returns it, or [None] if
|
||||
/// the next token is not a [`Token::Integer(_)`] or if the next token does not exist.
|
||||
///
|
||||
pub fn eat_integer(&mut self) -> Result<(PositiveNumber, Span)> {
|
||||
let token = self.token.token.clone();
|
||||
if let Token::Integer(value) = token {
|
||||
self.bump();
|
||||
Ok((PositiveNumber { value }, self.token.span))
|
||||
} else {
|
||||
Err(ParserError::unexpected(&self.token.token, "integer literal", self.token.span).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Eats any of the given `tokens`, returning `true` if anything was eaten.
|
||||
|
@ -296,7 +296,7 @@ impl ParserContext<'_> {
|
||||
};
|
||||
|
||||
// Parse the circuit member name (can be variable or function name).
|
||||
let member_name = self.expect_ident()?;
|
||||
let member_name = self.expect_identifier()?;
|
||||
|
||||
// Check if there are arguments.
|
||||
Ok(Expression::Access(if self.check(&Token::LeftParen) {
|
||||
@ -320,8 +320,8 @@ impl ParserContext<'_> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses a tuple of expressions.
|
||||
fn parse_expr_tuple(&mut self) -> Result<(Vec<Expression>, bool, Span)> {
|
||||
/// Parses a tuple of `Expression` AST nodes.
|
||||
pub(crate) fn parse_expr_tuple(&mut self) -> Result<(Vec<Expression>, bool, Span)> {
|
||||
self.parse_paren_comma_list(|p| p.parse_expression().map(Some))
|
||||
}
|
||||
|
||||
@ -336,8 +336,17 @@ impl ParserContext<'_> {
|
||||
let mut expr = self.parse_primary_expression()?;
|
||||
loop {
|
||||
if self.eat(&Token::Dot) {
|
||||
// Parse the method name.
|
||||
let name = self.expect_ident()?;
|
||||
if self.check_int() {
|
||||
// Eat a tuple member access.
|
||||
let (index, span) = self.eat_integer()?;
|
||||
expr = Expression::Access(AccessExpression::Tuple(TupleAccess {
|
||||
tuple: Box::new(expr),
|
||||
index,
|
||||
span,
|
||||
}))
|
||||
} else {
|
||||
// Parse identifier name.
|
||||
let name = self.expect_identifier()?;
|
||||
|
||||
if self.check(&Token::LeftParen) {
|
||||
// Eat a method call on a type
|
||||
@ -350,6 +359,7 @@ impl ParserContext<'_> {
|
||||
name,
|
||||
}))
|
||||
}
|
||||
}
|
||||
} else if self.eat(&Token::DoubleColon) {
|
||||
// Eat a core circuit constant or core circuit function call.
|
||||
expr = self.parse_associated_access_expression(expr)?;
|
||||
@ -374,9 +384,7 @@ impl ParserContext<'_> {
|
||||
/// tuple initialization expression or an affine group literal.
|
||||
fn parse_tuple_expression(&mut self) -> Result<Expression> {
|
||||
if let Some(gt) = self.eat_group_partial().transpose()? {
|
||||
return Ok(Expression::Literal(LiteralExpression::Group(Box::new(
|
||||
GroupLiteral::Tuple(gt),
|
||||
))));
|
||||
return Ok(Expression::Literal(Literal::Group(Box::new(GroupLiteral::Tuple(gt)))));
|
||||
}
|
||||
|
||||
let (mut tuple, trailing, span) = self.parse_expr_tuple()?;
|
||||
@ -384,7 +392,7 @@ impl ParserContext<'_> {
|
||||
if !trailing && tuple.len() == 1 {
|
||||
Ok(tuple.swap_remove(0))
|
||||
} else {
|
||||
Err(ParserError::unexpected("A tuple expression.", "A valid expression.", span).into())
|
||||
Ok(Expression::Tuple(TupleExpression { elements: tuple, span }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,11 +402,11 @@ impl ParserContext<'_> {
|
||||
let (advanced, gc) = self.look_ahead(*dist, |t0| match &t0.token {
|
||||
Token::Add => Some((1, GroupCoordinate::SignHigh)),
|
||||
Token::Minus => self.look_ahead(*dist + 1, |t1| match &t1.token {
|
||||
Token::Int(value) => Some((2, GroupCoordinate::Number(format!("-{}", value), t1.span))),
|
||||
Token::Integer(value) => Some((2, GroupCoordinate::Number(format!("-{}", value), t1.span))),
|
||||
_ => Some((1, GroupCoordinate::SignLow)),
|
||||
}),
|
||||
Token::Underscore => Some((1, GroupCoordinate::Inferred)),
|
||||
Token::Int(value) => Some((1, GroupCoordinate::Number(value.clone(), t0.span))),
|
||||
Token::Integer(value) => Some((1, GroupCoordinate::Number(value.clone(), t0.span))),
|
||||
_ => None,
|
||||
})?;
|
||||
*dist += advanced;
|
||||
@ -451,7 +459,7 @@ impl ParserContext<'_> {
|
||||
}
|
||||
|
||||
fn parse_circuit_member(&mut self) -> Result<CircuitVariableInitializer> {
|
||||
let identifier = self.expect_ident()?;
|
||||
let identifier = self.expect_identifier()?;
|
||||
let expression = if self.eat(&Token::Colon) {
|
||||
// Parse individual circuit variable declarations.
|
||||
Some(self.parse_expression()?)
|
||||
@ -465,12 +473,12 @@ impl ParserContext<'_> {
|
||||
/// 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> {
|
||||
pub fn parse_circuit_init_expression(&mut self, identifier: Identifier) -> Result<Expression> {
|
||||
let (members, _, end) = self.parse_list(Delimiter::Brace, Some(Token::Comma), |p| {
|
||||
p.parse_circuit_member().map(Some)
|
||||
})?;
|
||||
|
||||
Ok(Expression::CircuitInit(CircuitInitExpression {
|
||||
Ok(Expression::Circuit(CircuitExpression {
|
||||
span: identifier.span + end,
|
||||
name: identifier,
|
||||
members,
|
||||
@ -493,7 +501,7 @@ impl ParserContext<'_> {
|
||||
self.bump();
|
||||
|
||||
Ok(match token {
|
||||
Token::Int(value) => {
|
||||
Token::Integer(value) => {
|
||||
let suffix_span = self.token.span;
|
||||
let full_span = span + suffix_span;
|
||||
let assert_no_whitespace = |x| assert_no_whitespace(span, suffix_span, &value, x);
|
||||
@ -501,44 +509,42 @@ impl ParserContext<'_> {
|
||||
// Literal followed by `field`, e.g., `42field`.
|
||||
Some(Token::Field) => {
|
||||
assert_no_whitespace("field")?;
|
||||
Expression::Literal(LiteralExpression::Field(value, full_span))
|
||||
Expression::Literal(Literal::Field(value, full_span))
|
||||
}
|
||||
// Literal followed by `group`, e.g., `42group`.
|
||||
Some(Token::Group) => {
|
||||
assert_no_whitespace("group")?;
|
||||
Expression::Literal(LiteralExpression::Group(Box::new(GroupLiteral::Single(
|
||||
value, full_span,
|
||||
))))
|
||||
Expression::Literal(Literal::Group(Box::new(GroupLiteral::Single(value, full_span))))
|
||||
}
|
||||
// Literal followed by `scalar` e.g., `42scalar`.
|
||||
Some(Token::Scalar) => {
|
||||
assert_no_whitespace("scalar")?;
|
||||
Expression::Literal(LiteralExpression::Scalar(value, full_span))
|
||||
Expression::Literal(Literal::Scalar(value, full_span))
|
||||
}
|
||||
// Literal followed by other type suffix, e.g., `42u8`.
|
||||
Some(suffix) => {
|
||||
assert_no_whitespace(&suffix.to_string())?;
|
||||
let int_ty = Self::token_to_int_type(suffix).expect("unknown int type token");
|
||||
Expression::Literal(LiteralExpression::Integer(int_ty, value, full_span))
|
||||
Expression::Literal(Literal::Integer(int_ty, value, full_span))
|
||||
}
|
||||
None => return Err(ParserError::implicit_values_not_allowed(value, span).into()),
|
||||
}
|
||||
}
|
||||
Token::True => Expression::Literal(LiteralExpression::Boolean(true, span)),
|
||||
Token::False => Expression::Literal(LiteralExpression::Boolean(false, span)),
|
||||
Token::True => Expression::Literal(Literal::Boolean(true, span)),
|
||||
Token::False => Expression::Literal(Literal::Boolean(false, span)),
|
||||
Token::AddressLit(addr) => {
|
||||
if addr.parse::<Address<Testnet2>>().is_err() {
|
||||
self.emit_err(ParserError::invalid_address_lit(&addr, span));
|
||||
}
|
||||
Expression::Literal(LiteralExpression::Address(addr, span))
|
||||
Expression::Literal(Literal::Address(addr, span))
|
||||
}
|
||||
Token::StaticString(value) => Expression::Literal(LiteralExpression::String(value, span)),
|
||||
Token::Ident(name) => {
|
||||
Token::StaticString(value) => Expression::Literal(Literal::String(value, span)),
|
||||
Token::Identifier(name) => {
|
||||
let ident = Identifier { name, span };
|
||||
if !self.disallow_circuit_construction && self.check(&Token::LeftCurly) {
|
||||
// Parse circuit and records inits as circuit expressions.
|
||||
// Enforce circuit or record type later at type checking.
|
||||
self.parse_circuit_expression(ident)?
|
||||
self.parse_circuit_init_expression(ident)?
|
||||
} else {
|
||||
Expression::Identifier(ident)
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl ParserContext<'_> {
|
||||
let (id, function) = self.parse_function()?;
|
||||
functions.insert(id, function);
|
||||
}
|
||||
Token::Ident(sym::test) => return Err(ParserError::test_function(self.token.span).into()),
|
||||
Token::Identifier(sym::test) => return Err(ParserError::test_function(self.token.span).into()),
|
||||
Token::Function => {
|
||||
let (id, function) = self.parse_function()?;
|
||||
functions.insert(id, function);
|
||||
@ -54,7 +54,7 @@ impl ParserContext<'_> {
|
||||
fn unexpected_item(token: &SpannedToken) -> ParserError {
|
||||
ParserError::unexpected(
|
||||
&token.token,
|
||||
[Token::Function, Token::Circuit, Token::Ident(sym::test)]
|
||||
[Token::Function, Token::Circuit, Token::Identifier(sym::test)]
|
||||
.iter()
|
||||
.map(|x| format!("'{}'", x))
|
||||
.collect::<Vec<_>>()
|
||||
@ -105,9 +105,9 @@ impl ParserContext<'_> {
|
||||
|
||||
/// Parses `IDENT: TYPE`.
|
||||
fn parse_member(&mut self) -> Result<(Identifier, Type)> {
|
||||
let name = self.expect_ident()?;
|
||||
let name = self.expect_identifier()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_all_types()?.0;
|
||||
let type_ = self.parse_single_type()?.0;
|
||||
|
||||
Ok((name, type_))
|
||||
}
|
||||
@ -154,7 +154,7 @@ impl ParserContext<'_> {
|
||||
pub(super) fn parse_circuit(&mut self) -> Result<(Identifier, Circuit)> {
|
||||
let is_record = matches!(&self.token.token, Token::Record);
|
||||
let start = self.expect_any(&[Token::Circuit, Token::Record])?;
|
||||
let circuit_name = self.expect_ident()?;
|
||||
let circuit_name = self.expect_identifier()?;
|
||||
|
||||
self.expect(&Token::LeftCurly)?;
|
||||
let (members, end) = self.parse_circuit_members()?;
|
||||
@ -197,11 +197,10 @@ impl ParserContext<'_> {
|
||||
/// Returns a [`FunctionInput`] AST node if the next tokens represent a function parameter.
|
||||
fn parse_function_parameter(&mut self) -> Result<FunctionInput> {
|
||||
let mode = self.parse_function_parameter_mode()?;
|
||||
|
||||
let name = self.expect_ident()?;
|
||||
let name = self.expect_identifier()?;
|
||||
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_all_types()?.0;
|
||||
let type_ = self.parse_single_type()?.0;
|
||||
Ok(FunctionInput::Variable(FunctionInputVariable::new(
|
||||
name, mode, type_, name.span,
|
||||
)))
|
||||
@ -221,7 +220,7 @@ impl ParserContext<'_> {
|
||||
fn parse_function(&mut self) -> Result<(Identifier, Function)> {
|
||||
// Parse `function IDENT`.
|
||||
let start = self.expect(&Token::Function)?;
|
||||
let name = self.expect_ident()?;
|
||||
let name = self.expect_identifier()?;
|
||||
|
||||
// Parse parameters.
|
||||
let (inputs, ..) = self.parse_paren_comma_list(|p| p.parse_function_parameter().map(Some))?;
|
||||
@ -229,7 +228,7 @@ impl ParserContext<'_> {
|
||||
// Parse return type.
|
||||
self.expect(&Token::Arrow)?;
|
||||
self.disallow_circuit_construction = true;
|
||||
let output = self.parse_all_types()?.0;
|
||||
let output = self.parse_any_type()?.0;
|
||||
self.disallow_circuit_construction = false;
|
||||
|
||||
// Parse the function body.
|
||||
|
@ -42,11 +42,11 @@ impl ParserContext<'_> {
|
||||
/// Returns [`Section`].
|
||||
fn parse_section(&mut self) -> Result<Section> {
|
||||
self.expect(&Token::LeftSquare)?;
|
||||
let section = self.expect_ident()?;
|
||||
let section = self.expect_identifier()?;
|
||||
self.expect(&Token::RightSquare)?;
|
||||
|
||||
let mut definitions = Vec::new();
|
||||
while let Token::Const | Token::Constant | Token::Public | Token::Ident(_) = self.token.token {
|
||||
while let Token::Const | Token::Constant | Token::Public | Token::Identifier(_) = self.token.token {
|
||||
definitions.push(self.parse_input_definition()?);
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ impl ParserContext<'_> {
|
||||
fn parse_input_definition(&mut self) -> Result<Definition> {
|
||||
let mode = self.parse_function_parameter_mode()?;
|
||||
|
||||
let name = self.expect_ident()?;
|
||||
let name = self.expect_identifier()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let (type_, span) = self.parse_non_ident_types()?;
|
||||
self.expect(&Token::Assign)?;
|
||||
|
@ -101,9 +101,9 @@ impl ParserContext<'_> {
|
||||
/// Returns an [`IterationStatement`] AST node if the next tokens represent an iteration statement.
|
||||
fn parse_loop_statement(&mut self) -> Result<IterationStatement> {
|
||||
let start_span = self.expect(&Token::For)?;
|
||||
let ident = self.expect_ident()?;
|
||||
let ident = self.expect_identifier()?;
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_all_types()?;
|
||||
let type_ = self.parse_single_type()?;
|
||||
self.expect(&Token::In)?;
|
||||
|
||||
// Parse iteration range.
|
||||
@ -158,7 +158,7 @@ impl ParserContext<'_> {
|
||||
fn parse_console_statement(&mut self) -> Result<ConsoleStatement> {
|
||||
let keyword = self.expect(&Token::Console)?;
|
||||
self.expect(&Token::Dot)?;
|
||||
let function = self.expect_ident()?;
|
||||
let function = self.expect_identifier()?;
|
||||
let function = match function.name {
|
||||
sym::assert => {
|
||||
self.expect(&Token::LeftParen)?;
|
||||
@ -189,7 +189,7 @@ impl ParserContext<'_> {
|
||||
/// Returns a [`VariableName`] AST node if the next tokens represent a variable name with
|
||||
/// valid keywords.
|
||||
fn parse_variable_name(&mut self, decl_ty: Declare, _span: Span) -> Result<VariableName> {
|
||||
let name = self.expect_ident()?;
|
||||
let name = self.expect_identifier()?;
|
||||
Ok(VariableName {
|
||||
span: name.span,
|
||||
mutable: matches!(decl_ty, Declare::Let),
|
||||
@ -222,7 +222,7 @@ impl ParserContext<'_> {
|
||||
};
|
||||
|
||||
self.expect(&Token::Colon)?;
|
||||
let type_ = self.parse_all_types()?;
|
||||
let type_ = self.parse_any_type()?;
|
||||
|
||||
self.expect(&Token::Assign)?;
|
||||
let expr = self.parse_expression()?;
|
||||
|
@ -74,7 +74,7 @@ impl ParserContext<'_> {
|
||||
|
||||
/// Returns a [`(Type, Span)`] tuple of AST nodes if the next token represents a type.
|
||||
/// Also returns the span of the parsed token.
|
||||
pub fn parse_all_types(&mut self) -> Result<(Type, Span)> {
|
||||
pub fn parse_single_type(&mut self) -> Result<(Type, Span)> {
|
||||
Ok(if let Some(ident) = self.eat_identifier() {
|
||||
let span = ident.span;
|
||||
(Type::Identifier(ident), span)
|
||||
@ -82,4 +82,22 @@ impl ParserContext<'_> {
|
||||
self.parse_non_ident_types()?
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a [`(Type, Span)`] where `Type` is a `Type::Tuple` AST node.
|
||||
pub fn parse_tuple_type(&mut self) -> Result<(Type, Span)> {
|
||||
// todo: catch and return error for nested tuple type.
|
||||
let (types, _, span) = self.parse_paren_comma_list(|p| p.parse_single_type().map(Some))?;
|
||||
let elements = types.into_iter().map(|(type_, _)| type_).collect::<Vec<_>>();
|
||||
|
||||
Ok((Tuple::try_new(elements, span)?, span))
|
||||
}
|
||||
|
||||
/// Returns a [`(Type, Span)`] where `Type` is a tuple or single type.
|
||||
pub fn parse_any_type(&mut self) -> Result<(Type, Span)> {
|
||||
if self.peek_is_left_par() {
|
||||
self.parse_tuple_type()
|
||||
} else {
|
||||
self.parse_single_type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ impl Token {
|
||||
int.push(c);
|
||||
}
|
||||
|
||||
Ok((int.len(), Token::Int(int)))
|
||||
Ok((int.len(), Token::Integer(int)))
|
||||
}
|
||||
|
||||
/// Returns a tuple: [(token length, token)] if the next token can be eaten, otherwise returns [`None`].
|
||||
@ -326,7 +326,7 @@ impl Token {
|
||||
"u32" => Token::U32,
|
||||
"u64" => Token::U64,
|
||||
"u128" => Token::U128,
|
||||
_ => Token::Ident(Symbol::intern(&ident)),
|
||||
_ => Token::Identifier(Symbol::intern(&ident)),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ pub(crate) use self::lexer::*;
|
||||
|
||||
use leo_errors::Result;
|
||||
use leo_span::span::{BytePos, Pos, Span};
|
||||
|
||||
use std::iter;
|
||||
|
||||
/// Creates a new vector of spanned tokens from a given file path and source code text.
|
||||
@ -66,7 +65,7 @@ pub(crate) fn tokenize_iter(mut input: &str, mut lo: BytePos) -> impl '_ + Itera
|
||||
mod tests {
|
||||
use super::*;
|
||||
use leo_span::{source_map::FileName, symbol::create_session_if_not_set_then};
|
||||
use std::fmt::Write as _;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[test]
|
||||
fn test_tokenizer() {
|
||||
@ -149,7 +148,7 @@ mod tests {
|
||||
let tokens = tokenize(&sf.src, sf.start_pos).unwrap();
|
||||
let mut output = String::new();
|
||||
for SpannedToken { token, .. } in tokens.iter() {
|
||||
write!(output, "{} ", token).expect("failed to write to string");
|
||||
write!(output, "{} ", token).expect("failed to write string");
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
@ -27,8 +27,8 @@ pub enum Token {
|
||||
CommentLine(String),
|
||||
CommentBlock(String),
|
||||
StaticString(String),
|
||||
Ident(Symbol),
|
||||
Int(String),
|
||||
Identifier(Symbol),
|
||||
Integer(String),
|
||||
True,
|
||||
False,
|
||||
AddressLit(String),
|
||||
@ -208,8 +208,8 @@ impl fmt::Display for Token {
|
||||
CommentLine(s) => write!(f, "{}", s),
|
||||
CommentBlock(s) => write!(f, "{}", s),
|
||||
StaticString(s) => write!(f, "\"{}\"", s),
|
||||
Ident(s) => write!(f, "{}", s),
|
||||
Int(s) => write!(f, "{}", s),
|
||||
Identifier(s) => write!(f, "{}", s),
|
||||
Integer(s) => write!(f, "{}", s),
|
||||
True => write!(f, "true"),
|
||||
False => write!(f, "false"),
|
||||
AddressLit(s) => write!(f, "{}", s),
|
||||
|
@ -15,10 +15,7 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::CodeGenerator;
|
||||
use leo_ast::{
|
||||
AccessExpression, BinaryExpression, BinaryOperation, CallExpression, CircuitInitExpression, ErrExpression,
|
||||
Expression, Identifier, LiteralExpression, MemberAccess, TernaryExpression, UnaryExpression, UnaryOperation,
|
||||
};
|
||||
use leo_ast::{AccessExpression, BinaryExpression, BinaryOperation, CallExpression, CircuitExpression, ErrExpression, Expression, Identifier, Literal, MemberAccess, TernaryExpression, TupleExpression, UnaryExpression, UnaryOperation};
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
@ -32,12 +29,13 @@ impl<'a> CodeGenerator<'a> {
|
||||
Expression::Access(expr) => self.visit_access(expr),
|
||||
Expression::Binary(expr) => self.visit_binary(expr),
|
||||
Expression::Call(expr) => self.visit_call(expr),
|
||||
Expression::CircuitInit(expr) => self.visit_circuit_init(expr),
|
||||
Expression::Circuit(expr) => self.visit_circuit_init(expr),
|
||||
Expression::Err(expr) => self.visit_err(expr),
|
||||
Expression::Identifier(expr) => self.visit_identifier(expr),
|
||||
Expression::Ternary(expr) => self.visit_ternary(expr),
|
||||
Expression::Unary(expr) => self.visit_unary(expr),
|
||||
Expression::Literal(expr) => self.visit_value(expr),
|
||||
Expression::Ternary(expr) => self.visit_ternary(expr),
|
||||
Expression::Tuple(expr) => self.visit_tuple(expr),
|
||||
Expression::Unary(expr) => self.visit_unary(expr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +43,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
(self.variable_mapping.get(&input.name).unwrap().clone(), String::new())
|
||||
}
|
||||
|
||||
fn visit_value(&mut self, input: &'a LiteralExpression) -> (String, String) {
|
||||
fn visit_value(&mut self, input: &'a Literal) -> (String, String) {
|
||||
(format!("{}", input), String::new())
|
||||
}
|
||||
|
||||
@ -150,7 +148,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
(destination_register, instructions)
|
||||
}
|
||||
|
||||
fn visit_circuit_init(&mut self, input: &'a CircuitInitExpression) -> (String, String) {
|
||||
fn visit_circuit_init(&mut self, input: &'a CircuitExpression) -> (String, String) {
|
||||
// Initialize instruction builder strings.
|
||||
let mut instructions = String::new();
|
||||
let mut circuit_init_instruction = format!(" {} ", input.name.to_string().to_lowercase());
|
||||
@ -198,6 +196,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
AccessExpression::Member(access) => self.visit_member_access(access),
|
||||
AccessExpression::AssociatedConstant(_) => todo!(),
|
||||
AccessExpression::AssociatedFunction(_) => todo!(),
|
||||
AccessExpression::Tuple(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +221,10 @@ impl<'a> CodeGenerator<'a> {
|
||||
(destination_register, instructions)
|
||||
}
|
||||
|
||||
fn visit_tuple(&mut self, _input: &'a TupleExpression) -> (String, String) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn visit_err(&mut self, _input: &'a ErrExpression) -> (String, String) {
|
||||
unreachable!("`ErrExpression`s should not be in the AST at this phase of compilation.")
|
||||
}
|
||||
|
@ -28,14 +28,15 @@ impl<'a> CodeGenerator<'a> {
|
||||
| Type::Group
|
||||
| Type::Scalar
|
||||
| Type::String
|
||||
| Type::IntegerType(..) => format!("{}", input),
|
||||
| Type::IntegerType(..)
|
||||
| Type::Tuple(..) => format!("{}", input),
|
||||
Type::Identifier(ident) => {
|
||||
if let Some(type_) = self.composite_mapping.get(&ident.name) {
|
||||
format!("{}.{}", ident.to_string().to_lowercase(), type_)
|
||||
} else {
|
||||
unreachable!("All composite types should be known at this phase of compilation")
|
||||
}
|
||||
}
|
||||
},
|
||||
Type::Err => unreachable!("Error types should not exist at this phase of compilation"),
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/// A pass consuming a `Program` and possibly returning an `Ast`.
|
||||
/// A compiler pass consuming `Self::Input` and returning `Self::Output`.
|
||||
pub trait Pass {
|
||||
type Input;
|
||||
type Output;
|
||||
|
||||
/// Runs the compiler pass.
|
||||
fn do_pass(input: Self::Input) -> Self::Output;
|
||||
}
|
||||
|
@ -19,21 +19,23 @@ use leo_errors::emitter::Handler;
|
||||
|
||||
use crate::SymbolTable;
|
||||
|
||||
/// A compiler pass during which the `SymbolTable` is created.
|
||||
/// Note that this pass only creates the initial entries for functions and circuits.
|
||||
/// The table is populated further during the type checking pass.
|
||||
pub struct CreateSymbolTable<'a> {
|
||||
symbol_table: SymbolTable<'a>,
|
||||
/// The `SymbolTable` constructed by this compiler pass.
|
||||
pub(crate) symbol_table: SymbolTable,
|
||||
/// The error handler.
|
||||
handler: &'a Handler,
|
||||
}
|
||||
|
||||
impl<'a> CreateSymbolTable<'a> {
|
||||
pub fn new(handler: &'a Handler) -> Self {
|
||||
Self {
|
||||
symbol_table: SymbolTable::default(),
|
||||
symbol_table: Default::default(),
|
||||
handler,
|
||||
}
|
||||
}
|
||||
pub fn symbol_table(self) -> SymbolTable<'a> {
|
||||
self.symbol_table
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionVisitor<'a> for CreateSymbolTable<'a> {
|
||||
|
44
compiler/passes/src/symbol_table/function_symbol.rs
Normal file
44
compiler/passes/src/symbol_table/function_symbol.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_ast::{Function, FunctionInput, Type};
|
||||
use leo_span::Span;
|
||||
|
||||
use crate::SymbolTable;
|
||||
|
||||
/// An entry for a function in the symbol table.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionSymbol {
|
||||
/// The index associated with the scope in the parent symbol table.
|
||||
pub(crate) id: usize,
|
||||
/// The output type of the function.
|
||||
pub(crate) output: Type,
|
||||
/// The `Span` associated with the function.
|
||||
pub(crate) span: Span,
|
||||
/// The inputs to the function.
|
||||
pub(crate) input: Vec<FunctionInput>,
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
pub(crate) fn new_function_symbol(id: usize, func: &Function) -> FunctionSymbol {
|
||||
FunctionSymbol {
|
||||
id,
|
||||
output: func.output.clone(),
|
||||
span: func.span,
|
||||
input: func.input.clone(),
|
||||
}
|
||||
}
|
||||
}
|
@ -17,12 +17,12 @@
|
||||
pub mod create;
|
||||
pub use create::*;
|
||||
|
||||
pub mod function_symbol;
|
||||
pub use function_symbol::*;
|
||||
|
||||
pub mod table;
|
||||
pub use table::*;
|
||||
|
||||
pub mod variable_scope;
|
||||
pub use variable_scope::*;
|
||||
|
||||
pub mod variable_symbol;
|
||||
pub use variable_symbol::*;
|
||||
|
||||
@ -33,13 +33,14 @@ use leo_errors::{emitter::Handler, Result};
|
||||
|
||||
impl<'a> Pass for CreateSymbolTable<'a> {
|
||||
type Input = (&'a Ast, &'a Handler);
|
||||
type Output = Result<SymbolTable<'a>>;
|
||||
type Output = Result<SymbolTable>;
|
||||
|
||||
/// Runs the compiler pass.
|
||||
fn do_pass((ast, handler): Self::Input) -> Self::Output {
|
||||
let mut visitor = CreateSymbolTable::new(handler);
|
||||
visitor.visit_program(ast.as_repr());
|
||||
handler.last_err()?;
|
||||
|
||||
Ok(visitor.symbol_table())
|
||||
Ok(visitor.symbol_table)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use leo_ast::{Circuit, Function};
|
||||
use leo_errors::{AstError, Result};
|
||||
@ -22,100 +22,160 @@ use leo_span::{Span, Symbol};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{VariableScope, VariableSymbol};
|
||||
use crate::{FunctionSymbol, VariableSymbol};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SymbolTable<'a> {
|
||||
/// Maps function names to function definitions.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SymbolTable {
|
||||
/// The parent scope if it exists.
|
||||
/// For example if we are in a if block inside a function.
|
||||
pub(crate) parent: Option<Box<SymbolTable>>,
|
||||
/// Functions represents the name of each function mapped to the AST's function definition.
|
||||
/// This field is populated at a first pass.
|
||||
functions: IndexMap<Symbol, &'a Function>,
|
||||
pub functions: IndexMap<Symbol, FunctionSymbol>,
|
||||
/// 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>,
|
||||
pub circuits: IndexMap<Symbol, Circuit>,
|
||||
/// The variables defined in a scope.
|
||||
/// This field is populated as necessary.
|
||||
pub(crate) variables: IndexMap<Symbol, VariableSymbol>,
|
||||
/// The index of the current scope.
|
||||
pub(crate) scope_index: usize,
|
||||
/// The sub-scopes of this scope.
|
||||
pub(crate) scopes: Vec<RefCell<SymbolTable>>,
|
||||
}
|
||||
|
||||
impl<'a> SymbolTable<'a> {
|
||||
impl SymbolTable {
|
||||
/// Recursively checks if the symbol table contains an entry for the given symbol.
|
||||
/// Leo does not allow any variable shadowing or overlap between different symbols.
|
||||
pub fn check_shadowing(&self, symbol: Symbol, span: Span) -> Result<()> {
|
||||
if self.functions.contains_key(&symbol) {
|
||||
if self.variables.contains_key(&symbol) {
|
||||
Err(AstError::shadowed_variable(symbol, span).into())
|
||||
} else if self.functions.contains_key(&symbol) {
|
||||
Err(AstError::shadowed_function(symbol, span).into())
|
||||
} else if let Some(existing) = self.circuits.get(&symbol) {
|
||||
match existing.is_record {
|
||||
true => Err(AstError::shadowed_record(symbol, span).into()),
|
||||
false => Err(AstError::shadowed_circuit(symbol, span).into()),
|
||||
}
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.check_shadowing(symbol, span)
|
||||
} else {
|
||||
self.variables.check_shadowing(symbol, span)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_variables(&mut self) {
|
||||
self.variables.clear();
|
||||
/// Returns the current scope index.
|
||||
/// Increments the scope index.
|
||||
pub fn scope_index(&mut self) -> usize {
|
||||
let index = self.scope_index;
|
||||
self.scope_index += 1;
|
||||
index
|
||||
}
|
||||
|
||||
pub fn insert_fn(&mut self, symbol: Symbol, insert: &'a Function) -> Result<()> {
|
||||
/// Inserts a function into the symbol table.
|
||||
pub fn insert_fn(&mut self, symbol: Symbol, insert: &Function) -> Result<()> {
|
||||
self.check_shadowing(symbol, insert.span)?;
|
||||
self.functions.insert(symbol, insert);
|
||||
let id = self.scope_index();
|
||||
self.functions.insert(symbol, Self::new_function_symbol(id, insert));
|
||||
self.scopes.push(Default::default());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_circuit(&mut self, symbol: Symbol, insert: &'a Circuit) -> Result<()> {
|
||||
if let Some(existing) = self.circuits.get(&symbol) {
|
||||
// Error if the circuit or record already exists.
|
||||
let err = if existing.is_record {
|
||||
AstError::shadowed_record(symbol, insert.span).into()
|
||||
/// Inserts a circuit into the symbol table.
|
||||
pub fn insert_circuit(&mut self, symbol: Symbol, insert: &Circuit) -> Result<()> {
|
||||
self.check_shadowing(symbol, insert.span)?;
|
||||
self.circuits.insert(symbol, insert.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inserts a variable into the symbol table.
|
||||
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> {
|
||||
self.check_shadowing(symbol, insert.span)?;
|
||||
self.variables.insert(symbol, insert);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new scope for the block and stores it in the symbol table.
|
||||
pub fn insert_block(&mut self) -> usize {
|
||||
self.scopes.push(RefCell::new(Default::default()));
|
||||
self.scope_index()
|
||||
}
|
||||
|
||||
/// Attempts to lookup a function in the symbol table.
|
||||
pub fn lookup_fn(&self, symbol: &Symbol) -> Option<&FunctionSymbol> {
|
||||
if let Some(func) = self.functions.get(symbol) {
|
||||
Some(func)
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.lookup_fn(symbol)
|
||||
} else {
|
||||
AstError::shadowed_circuit(symbol, insert.span).into()
|
||||
};
|
||||
return Err(err);
|
||||
None
|
||||
}
|
||||
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);
|
||||
Ok(())
|
||||
/// Attempts to lookup a circuit in the symbol table.
|
||||
pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&Circuit> {
|
||||
if let Some(circ) = self.circuits.get(symbol) {
|
||||
Some(circ)
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.lookup_circuit(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_fn(&self, symbol: Symbol) -> Option<&&'a Function> {
|
||||
self.functions.get(&symbol)
|
||||
/// Attempts to lookup a variable in the symbol table.
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol> {
|
||||
if let Some(var) = self.variables.get(symbol) {
|
||||
Some(var)
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.lookup_variable(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&&'a Circuit> {
|
||||
self.circuits.get(symbol)
|
||||
/// Returns true if the variable exists in the local scope
|
||||
pub fn variable_in_local_scope(&self, symbol: &Symbol) -> bool {
|
||||
self.variables.contains_key(symbol)
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
|
||||
self.variables.lookup_variable(symbol)
|
||||
/// Returns true if the variable exists in any parent scope
|
||||
pub fn variable_in_parent_scope(&self, symbol: &Symbol) -> bool {
|
||||
if let Some(parent) = self.parent.as_ref() {
|
||||
if parent.variables.contains_key(symbol) {
|
||||
true
|
||||
} else {
|
||||
parent.variable_in_parent_scope(symbol)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_variable_scope(&mut self) {
|
||||
let current_scope = self.variables.clone();
|
||||
self.variables = VariableScope {
|
||||
parent: Some(Box::new(current_scope)),
|
||||
variables: Default::default(),
|
||||
};
|
||||
/// Returns a mutable reference to the `VariableSymbol` if it exists in the symbol table.
|
||||
pub fn lookup_variable_mut(&mut self, symbol: &Symbol) -> Option<&mut VariableSymbol> {
|
||||
if let Some(var) = self.variables.get_mut(symbol) {
|
||||
Some(var)
|
||||
} else if let Some(parent) = self.parent.as_mut() {
|
||||
parent.lookup_variable_mut(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_variable_scope(&mut self) {
|
||||
let parent = self.variables.parent.clone().unwrap();
|
||||
/// Returns the scope associated with the function symbol, if it exists in the symbol table.
|
||||
pub fn get_fn_scope(&self, symbol: &Symbol) -> Option<&RefCell<Self>> {
|
||||
if let Some(func) = self.functions.get(symbol) {
|
||||
self.scopes.get(func.id)
|
||||
} else if let Some(parent) = self.parent.as_ref() {
|
||||
parent.get_fn_scope(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
self.variables = *parent;
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SymbolTable<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "SymbolTable")?;
|
||||
|
||||
for func in self.functions.values() {
|
||||
write!(f, "{func}")?;
|
||||
}
|
||||
|
||||
for circ in self.circuits.values() {
|
||||
write!(f, "{circ}")?;
|
||||
}
|
||||
|
||||
write!(f, "{}", self.variables)
|
||||
/// Returns the scope associated with `index`, if it exists in the symbol table.
|
||||
pub fn get_block_scope(&self, index: usize) -> Option<&RefCell<Self>> {
|
||||
self.scopes.get(index)
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::VariableSymbol;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct VariableScope<'a> {
|
||||
/// The parent scope of variables if it exists.
|
||||
/// For example if we are in a if block inside a function.
|
||||
/// The parent would be the functions variables and inputs.
|
||||
/// This field is populated as necessary.
|
||||
pub(crate) parent: Option<Box<VariableScope<'a>>>,
|
||||
/// The variables defined in a scope.
|
||||
/// This field is populated as necessary.
|
||||
pub(crate) variables: IndexMap<Symbol, VariableSymbol<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> VariableScope<'a> {
|
||||
pub fn check_shadowing(&self, symbol: Symbol, span: Span) -> Result<()> {
|
||||
if self.variables.contains_key(&symbol) {
|
||||
Err(AstError::shadowed_variable(symbol, span).into())
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.check_shadowing(symbol, span)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.parent = None;
|
||||
self.variables.clear();
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
|
||||
if let Some(var) = self.variables.get(symbol) {
|
||||
Some(var)
|
||||
} else if let Some(parent) = &self.parent {
|
||||
parent.lookup_variable(symbol)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for VariableScope<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "VariableScope")?;
|
||||
self.parent
|
||||
.as_ref()
|
||||
.map(|parent| write!(f, "parent {parent}"))
|
||||
.transpose()?;
|
||||
|
||||
for (sym, var) in self.variables.iter() {
|
||||
write!(f, "{sym} {var}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ use std::fmt::Display;
|
||||
use leo_ast::{ParamMode, Type};
|
||||
use leo_span::Span;
|
||||
|
||||
/// An enumeration of the different types of declarations.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Declaration {
|
||||
Const,
|
||||
@ -38,14 +39,18 @@ impl Display for Declaration {
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry for a variable in the symbol table.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VariableSymbol<'a> {
|
||||
pub type_: &'a Type,
|
||||
pub struct VariableSymbol {
|
||||
/// The `Type` of the variable.
|
||||
pub type_: Type,
|
||||
/// The `Span` associated with the variable.
|
||||
pub span: Span,
|
||||
/// The type of declaration for the variable.
|
||||
pub declaration: Declaration,
|
||||
}
|
||||
|
||||
impl<'a> Display for VariableSymbol<'a> {
|
||||
impl Display for VariableSymbol {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {}", self.declaration, self.type_)?;
|
||||
Ok(())
|
||||
|
@ -43,138 +43,31 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
fn visit_expression(&mut self, input: &'a Expression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
match input {
|
||||
Expression::Access(expr) => self.visit_access(expr, expected),
|
||||
Expression::Identifier(expr) => self.visit_identifier(expr, expected),
|
||||
Expression::Literal(expr) => self.visit_literal(expr, expected),
|
||||
Expression::Binary(expr) => self.visit_binary(expr, expected),
|
||||
Expression::Call(expr) => self.visit_call(expr, expected),
|
||||
Expression::CircuitInit(expr) => self.visit_circuit_init(expr, expected),
|
||||
Expression::Err(expr) => self.visit_err(expr, expected),
|
||||
Expression::Ternary(expr) => self.visit_ternary(expr, expected),
|
||||
Expression::Access(access) => self.visit_access(access, expected),
|
||||
Expression::Binary(binary) => self.visit_binary(binary, expected),
|
||||
Expression::Call(call) => self.visit_call(call, expected),
|
||||
Expression::Circuit(circuit) => self.visit_circuit_init(circuit, expected),
|
||||
Expression::Identifier(identifier) => self.visit_identifier(identifier, expected),
|
||||
Expression::Err(err) => self.visit_err(err, expected),
|
||||
Expression::Literal(literal) => self.visit_literal(literal, expected),
|
||||
Expression::Ternary(ternary) => self.visit_ternary(ternary, expected),
|
||||
Expression::Tuple(tuple) => self.visit_tuple(tuple, expected),
|
||||
Expression::Unary(expr) => self.visit_unary(expr, expected),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
if let Some(circuit) = self.symbol_table.clone().lookup_circuit(&var.name) {
|
||||
Some(self.assert_expected_option(Type::Identifier(circuit.identifier), expected, circuit.span()))
|
||||
} else if let Some(var) = self.symbol_table.clone().lookup_variable(&var.name) {
|
||||
Some(self.assert_expected_option(*var.type_, expected, var.span))
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()).into());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, input: &'a LiteralExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
Some(match input {
|
||||
LiteralExpression::Address(_, _) => self.assert_expected_option(Type::Address, expected, input.span()),
|
||||
LiteralExpression::Boolean(_, _) => self.assert_expected_option(Type::Boolean, expected, input.span()),
|
||||
LiteralExpression::Field(_, _) => self.assert_expected_option(Type::Field, expected, input.span()),
|
||||
LiteralExpression::Integer(type_, str_content, _) => {
|
||||
match type_ {
|
||||
IntegerType::I8 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i8>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I16 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i16>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I32 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i32>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I64 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i64>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::I128 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i128>().is_err() {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()).into());
|
||||
}
|
||||
}
|
||||
IntegerType::U8 if str_content.parse::<u8>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()).into()),
|
||||
IntegerType::U16 if str_content.parse::<u16>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()).into()),
|
||||
IntegerType::U32 if str_content.parse::<u32>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()).into()),
|
||||
IntegerType::U64 if str_content.parse::<u64>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()).into()),
|
||||
IntegerType::U128 if str_content.parse::<u128>().is_err() => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()).into()),
|
||||
_ => {}
|
||||
}
|
||||
self.assert_expected_option(Type::IntegerType(*type_), expected, input.span())
|
||||
}
|
||||
LiteralExpression::Group(_) => self.assert_expected_option(Type::Group, expected, input.span()),
|
||||
LiteralExpression::Scalar(_, _) => self.assert_expected_option(Type::Scalar, expected, input.span()),
|
||||
LiteralExpression::String(_, _) => self.assert_expected_option(Type::String, expected, input.span()),
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_access(&mut self, input: &'a AccessExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
// CAUTION: This implementation only allows access to core circuits.
|
||||
match input {
|
||||
AccessExpression::AssociatedFunction(access) => {
|
||||
// Check core circuit name and function.
|
||||
if let Some(core_instruction) = self.assert_core_circuit_call(&access.ty, &access.name) {
|
||||
if let Some(core_instruction) = self.check_core_circuit_call(&access.ty, &access.name) {
|
||||
// Check num input arguments.
|
||||
if core_instruction.num_args() != access.args.len() {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_args_to_call(
|
||||
self.emit_err(TypeCheckerError::incorrect_num_args_to_call(
|
||||
core_instruction.num_args(),
|
||||
access.args.len(),
|
||||
input.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
// Check first argument type.
|
||||
@ -190,29 +83,242 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
// Check return type.
|
||||
Some(self.assert_expected_option(core_instruction.return_type(), expected, access.span()))
|
||||
return Some(self.assert_and_return_type(core_instruction.return_type(), expected, access.span()));
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()).into());
|
||||
self.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
|
||||
}
|
||||
}
|
||||
AccessExpression::Tuple(access) => {
|
||||
if let Some(type_) = self.visit_expression(&access.tuple, &None) {
|
||||
match type_ {
|
||||
Type::Tuple(tuple) => {
|
||||
// Check out of range access.
|
||||
let index = access.index.to_usize();
|
||||
if index > tuple.len() - 1 {
|
||||
self.emit_err(TypeCheckerError::tuple_out_of_range(index, tuple.len(), access.span()));
|
||||
} else {
|
||||
// Lookup type of tuple index.
|
||||
let actual = tuple.get(index).expect("failed to get tuple index").clone();
|
||||
if let Some(expected) = expected {
|
||||
// Emit error for mismatched types.
|
||||
if !actual.eq_flat(expected) {
|
||||
self.emit_err(TypeCheckerError::type_should_be(
|
||||
&actual,
|
||||
expected,
|
||||
access.span(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Return type of tuple index.
|
||||
return Some(actual);
|
||||
}
|
||||
}
|
||||
type_ => {
|
||||
self.emit_err(TypeCheckerError::type_should_be(type_, "tuple", access.span()));
|
||||
}
|
||||
}
|
||||
self.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
|
||||
}
|
||||
}
|
||||
AccessExpression::Member(access) => {
|
||||
// Check that the type of `inner` in `inner.name` is a circuit.
|
||||
match self.visit_expression(&access.inner, &None) {
|
||||
Some(Type::Identifier(identifier)) => {
|
||||
// Retrieve the circuit definition associated with `identifier`.
|
||||
let circ = self.symbol_table.borrow().lookup_circuit(&identifier.name).cloned();
|
||||
if let Some(circ) = circ {
|
||||
// Check that `access.name` is a member of the circuit.
|
||||
match circ
|
||||
.members
|
||||
.iter()
|
||||
.find(|circuit_member| circuit_member.name() == access.name.name)
|
||||
{
|
||||
// Case where `access.name` is a member of the circuit.
|
||||
Some(CircuitMember::CircuitVariable(_, type_)) => return Some(type_.clone()),
|
||||
// Case where `access.name` is not a member of the circuit.
|
||||
None => {
|
||||
self.emit_err(TypeCheckerError::invalid_circuit_variable(
|
||||
&access.name,
|
||||
&circ,
|
||||
access.name.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::invalid_circuit(&access.inner, access.inner.span()));
|
||||
}
|
||||
}
|
||||
Some(type_) => {
|
||||
self.emit_err(TypeCheckerError::type_should_be(type_, "circuit", access.inner.span()));
|
||||
}
|
||||
None => {
|
||||
self.emit_err(TypeCheckerError::could_not_determine_type(
|
||||
&access.inner,
|
||||
access.inner.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
AccessExpression::AssociatedConstant(..) => {} // todo: Add support for associated constants (u8::MAX).
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn visit_circuit_init(&mut self, input: &'a CircuitExpression, additional: &Self::AdditionalInput) -> Self::Output {
|
||||
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
|
||||
if let Some(circ) = circ {
|
||||
// Check circuit type name.
|
||||
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
|
||||
|
||||
// Check number of circuit members.
|
||||
if circ.members.len() != input.members.len() {
|
||||
self.emit_err(TypeCheckerError::incorrect_num_circuit_members(
|
||||
circ.members.len(),
|
||||
input.members.len(),
|
||||
input.span(),
|
||||
));
|
||||
}
|
||||
|
||||
// Check circuit member types.
|
||||
circ.members
|
||||
.iter()
|
||||
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
|
||||
// Lookup circuit variable name.
|
||||
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
|
||||
if let Some(expr) = &actual.expression {
|
||||
self.visit_expression(expr, &Some(ty.clone()));
|
||||
}
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::missing_circuit_member(
|
||||
circ.identifier,
|
||||
name,
|
||||
input.span(),
|
||||
));
|
||||
};
|
||||
});
|
||||
|
||||
Some(ret)
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::unknown_sym(
|
||||
"circuit",
|
||||
&input.name.name,
|
||||
input.name.span(),
|
||||
));
|
||||
None
|
||||
}
|
||||
}
|
||||
_expr => None, // todo: Add support for associated constants (u8::MAX).
|
||||
|
||||
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(&var.name) {
|
||||
Some(self.assert_and_return_type(Type::Identifier(circuit.identifier), expected, var.span))
|
||||
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(&var.name) {
|
||||
Some(self.assert_and_return_type(var.type_.clone(), expected, var.span))
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
Some(match input {
|
||||
Literal::Address(_, _) => self.assert_and_return_type(Type::Address, expected, input.span()),
|
||||
Literal::Boolean(_, _) => self.assert_and_return_type(Type::Boolean, expected, input.span()),
|
||||
Literal::Field(_, _) => self.assert_and_return_type(Type::Field, expected, input.span()),
|
||||
Literal::Integer(type_, str_content, _) => {
|
||||
match type_ {
|
||||
IntegerType::I8 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i8>().is_err() {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
|
||||
}
|
||||
}
|
||||
IntegerType::I16 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i16>().is_err() {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
|
||||
}
|
||||
}
|
||||
IntegerType::I32 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i32>().is_err() {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
|
||||
}
|
||||
}
|
||||
IntegerType::I64 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i64>().is_err() {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
|
||||
}
|
||||
}
|
||||
IntegerType::I128 => {
|
||||
let int = if self.negate {
|
||||
format!("-{str_content}")
|
||||
} else {
|
||||
str_content.clone()
|
||||
};
|
||||
|
||||
if int.parse::<i128>().is_err() {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
|
||||
}
|
||||
}
|
||||
IntegerType::U8 if str_content.parse::<u8>().is_err() => {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()))
|
||||
}
|
||||
IntegerType::U16 if str_content.parse::<u16>().is_err() => {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()))
|
||||
}
|
||||
IntegerType::U32 if str_content.parse::<u32>().is_err() => {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()))
|
||||
}
|
||||
IntegerType::U64 if str_content.parse::<u64>().is_err() => {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()))
|
||||
}
|
||||
IntegerType::U128 if str_content.parse::<u128>().is_err() => {
|
||||
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.assert_and_return_type(Type::IntegerType(*type_), expected, input.span())
|
||||
}
|
||||
Literal::Group(_) => self.assert_and_return_type(Type::Group, expected, input.span()),
|
||||
Literal::Scalar(_, _) => self.assert_and_return_type(Type::Scalar, expected, input.span()),
|
||||
Literal::String(_, _) => self.assert_and_return_type(Type::String, expected, input.span()),
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_binary(&mut self, input: &'a BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
|
||||
match input.op {
|
||||
BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => {
|
||||
// Assert equal boolean types.
|
||||
self.assert_expected_option(Type::Boolean, destination, input.span());
|
||||
// Only boolean types.
|
||||
self.assert_bool_type(destination, input.span());
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
}
|
||||
BinaryOperation::BitwiseAnd | BinaryOperation::BitwiseOr | BinaryOperation::Xor => {
|
||||
// Assert equal boolean or integer types.
|
||||
// Only boolean or integer types.
|
||||
self.assert_bool_int_type(destination, input.span());
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
@ -220,7 +326,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
}
|
||||
BinaryOperation::Add => {
|
||||
// Assert equal field, group, scalar, or integer types.
|
||||
// Only field, group, scalar, or integer types.
|
||||
self.assert_field_group_scalar_int_type(destination, input.span());
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
@ -228,7 +334,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
}
|
||||
BinaryOperation::Sub => {
|
||||
// Assert equal field, group, or integer types.
|
||||
// Only field, group, or integer types.
|
||||
self.assert_field_group_int_type(destination, input.span());
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
@ -236,24 +342,34 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
}
|
||||
BinaryOperation::Mul => {
|
||||
// Assert field, group or integer types.
|
||||
// Operation returns field, group or integer types.
|
||||
self.assert_field_group_int_type(destination, input.span());
|
||||
|
||||
let t1 = self.visit_expression(&input.left, &None);
|
||||
let t2 = self.visit_expression(&input.right, &None);
|
||||
|
||||
// Allow `group` * `scalar` multiplication.
|
||||
// Allow group * scalar multiplication.
|
||||
match (t1, t2) {
|
||||
(Some(Type::Group), other) => {
|
||||
self.assert_expected_type(&other, Type::Scalar, input.right.span());
|
||||
Some(self.assert_expected_type(destination, Type::Group, input.span()))
|
||||
(Some(Type::Group), right) => {
|
||||
// Right type must be scalar.
|
||||
self.assert_scalar_type(&right, input.right.span());
|
||||
|
||||
// Operation returns group.
|
||||
self.assert_group_type(destination, input.span());
|
||||
|
||||
Some(Type::Group)
|
||||
}
|
||||
(other, Some(Type::Group)) => {
|
||||
self.assert_expected_type(&other, Type::Scalar, input.left.span());
|
||||
Some(self.assert_expected_type(destination, Type::Group, input.span()))
|
||||
(left, Some(Type::Group)) => {
|
||||
// Left must be scalar.
|
||||
self.assert_scalar_type(&left, input.left.span());
|
||||
|
||||
// Operation returns group.
|
||||
self.assert_group_type(destination, input.span());
|
||||
|
||||
Some(Type::Group)
|
||||
}
|
||||
(t1, t2) => {
|
||||
// Assert equal field or integer types.
|
||||
// Otherwise, only field or integer types.
|
||||
self.assert_field_int_type(destination, input.span());
|
||||
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
@ -261,7 +377,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
BinaryOperation::Div => {
|
||||
// Assert equal field or integer types.
|
||||
// Only field or integer types.
|
||||
self.assert_field_int_type(destination, input.span());
|
||||
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
@ -270,7 +386,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
}
|
||||
BinaryOperation::Pow => {
|
||||
// Assert field or integer types.
|
||||
// Operation returns field or integer types.
|
||||
self.assert_field_int_type(destination, input.span());
|
||||
|
||||
let t1 = self.visit_expression(&input.left, &None);
|
||||
@ -278,23 +394,39 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
// Allow field * field.
|
||||
match (t1, t2) {
|
||||
(Some(Type::Field), type_) => {
|
||||
self.assert_expected_type(&type_, Type::Field, input.right.span());
|
||||
Some(self.assert_expected_type(destination, Type::Field, input.span()))
|
||||
(Some(Type::Field), right) => {
|
||||
// Right must be field.
|
||||
self.assert_field_type(&right, input.right.span());
|
||||
|
||||
// Operation returns field.
|
||||
self.assert_field_type(destination, input.span());
|
||||
|
||||
Some(Type::Field)
|
||||
}
|
||||
(type_, Some(Type::Field)) => {
|
||||
self.assert_expected_type(&type_, Type::Field, input.left.span());
|
||||
Some(self.assert_expected_type(destination, Type::Field, input.span()))
|
||||
(left, Some(Type::Field)) => {
|
||||
// Left must be field.
|
||||
self.assert_field_type(&left, input.left.span());
|
||||
|
||||
// Operation returns field.
|
||||
self.assert_field_type(destination, input.span());
|
||||
|
||||
Some(Type::Field)
|
||||
}
|
||||
(Some(t1), t2) => {
|
||||
// Allow integer t2 magnitude (u8, u16, u32)
|
||||
self.assert_magnitude_type(&t2, input.right.span());
|
||||
Some(self.assert_expected_type(destination, t1, input.span()))
|
||||
(Some(left), right) => {
|
||||
// Left type is checked to be an integer by above.
|
||||
// Right type must be magnitude (u8, u16, u32).
|
||||
self.assert_magnitude_type(&right, input.right.span());
|
||||
|
||||
// Operation returns left type.
|
||||
self.assert_type(destination, &left, input.span());
|
||||
|
||||
Some(left)
|
||||
}
|
||||
(None, t2) => {
|
||||
// Allow integer t2 magnitude (u8, u16, u32)
|
||||
// Lhs type is checked to be an integer by above.
|
||||
// Rhs type must be magnitude (u8, u16, u32).
|
||||
self.assert_magnitude_type(&t2, input.right.span());
|
||||
*destination
|
||||
destination.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,20 +437,22 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
|
||||
match (t1, t2) {
|
||||
(Some(Type::IntegerType(_)), t2) => {
|
||||
// Assert rhs is integer.
|
||||
// Check rhs is integer and give detailed error message.
|
||||
self.assert_int_type(&t2, input.left.span());
|
||||
}
|
||||
(t1, Some(Type::IntegerType(_))) => {
|
||||
// Assert lhs is integer.
|
||||
// Check lhs is integer and give detailed error message.
|
||||
self.assert_int_type(&t1, input.right.span());
|
||||
}
|
||||
(t1, t2) => {
|
||||
self.assert_eq_types(t1, t2, input.span());
|
||||
self.check_eq_types(&t1, &t2, input.span());
|
||||
}
|
||||
}
|
||||
|
||||
// Assert destination is boolean.
|
||||
Some(self.assert_expected_type(destination, Type::Boolean, input.span()))
|
||||
// Operation returns a boolean.
|
||||
self.assert_bool_type(destination, input.span());
|
||||
|
||||
Some(Type::Boolean)
|
||||
}
|
||||
BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Lte | BinaryOperation::Gte => {
|
||||
// Assert left and right are equal field, scalar, or integer types.
|
||||
@ -328,46 +462,47 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
match (t1, t2) {
|
||||
(Some(Type::Address), _) | (_, Some(Type::Address)) => {
|
||||
// Emit an error for address comparison.
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::compare_address(input.op, input.span()).into());
|
||||
self.emit_err(TypeCheckerError::compare_address(input.op, input.span()));
|
||||
}
|
||||
(Some(Type::Field), t2) => {
|
||||
// Assert rhs is field.
|
||||
self.assert_expected_type(&t2, Type::Field, input.left.span());
|
||||
self.assert_field_type(&t2, input.right.span());
|
||||
}
|
||||
(t1, Some(Type::Field)) => {
|
||||
// Assert lhs is field.
|
||||
self.assert_expected_type(&t1, Type::Field, input.right.span());
|
||||
self.assert_field_type(&t1, input.left.span());
|
||||
}
|
||||
(Some(Type::Scalar), t2) => {
|
||||
// Assert rhs is scalar.
|
||||
self.assert_expected_type(&t2, Type::Scalar, input.left.span());
|
||||
self.assert_scalar_type(&t2, input.right.span());
|
||||
}
|
||||
(t1, Some(Type::Scalar)) => {
|
||||
// Assert lhs is scalar.
|
||||
self.assert_expected_type(&t1, Type::Scalar, input.right.span());
|
||||
self.assert_scalar_type(&t1, input.left.span());
|
||||
}
|
||||
(Some(Type::IntegerType(_)), t2) => {
|
||||
// Assert rhs is integer.
|
||||
self.assert_int_type(&t2, input.left.span());
|
||||
self.assert_int_type(&t2, input.right.span());
|
||||
}
|
||||
(t1, Some(Type::IntegerType(_))) => {
|
||||
// Assert lhs is integer.
|
||||
self.assert_int_type(&t1, input.right.span());
|
||||
self.assert_int_type(&t1, input.left.span());
|
||||
}
|
||||
(_, _) => {
|
||||
// Not enough info to assert type.
|
||||
}
|
||||
}
|
||||
|
||||
// Assert destination is boolean.
|
||||
Some(self.assert_expected_type(destination, Type::Boolean, input.span()))
|
||||
// Operation returns a boolean.
|
||||
self.assert_bool_type(destination, input.span());
|
||||
|
||||
Some(Type::Boolean)
|
||||
}
|
||||
BinaryOperation::AddWrapped
|
||||
| BinaryOperation::SubWrapped
|
||||
| BinaryOperation::DivWrapped
|
||||
| BinaryOperation::MulWrapped => {
|
||||
// Assert equal integer types.
|
||||
// Only integer types.
|
||||
self.assert_int_type(destination, input.span);
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, destination);
|
||||
@ -379,12 +514,14 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
| BinaryOperation::Shr
|
||||
| BinaryOperation::ShrWrapped
|
||||
| BinaryOperation::PowWrapped => {
|
||||
// Assert left and destination are equal integer types.
|
||||
self.assert_int_type(destination, input.span);
|
||||
let t1 = self.visit_expression(&input.left, destination);
|
||||
let t2 = self.visit_expression(&input.right, &None);
|
||||
|
||||
// Assert left and destination are equal integer types.
|
||||
self.assert_int_type(&t1, input.left.span());
|
||||
self.assert_int_type(destination, input.span);
|
||||
|
||||
// Assert right type is a magnitude (u8, u16, u32).
|
||||
let t2 = self.visit_expression(&input.right, &None);
|
||||
self.assert_magnitude_type(&t2, input.right.span());
|
||||
|
||||
return_incorrect_type(t1, t2, destination)
|
||||
@ -392,68 +529,40 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
|
||||
match input.op {
|
||||
UnaryOperation::Abs => {
|
||||
// Assert integer type only.
|
||||
self.assert_signed_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::AbsWrapped => {
|
||||
// Assert integer type only.
|
||||
self.assert_signed_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Double => {
|
||||
// Assert field and group type only.
|
||||
self.assert_field_group_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Inverse => {
|
||||
// Assert field type only.
|
||||
self.assert_expected_type(destination, Type::Field, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Negate => {
|
||||
let prior_negate_state = self.negate;
|
||||
self.negate = true;
|
||||
fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
match &*input.function {
|
||||
Expression::Identifier(ident) => {
|
||||
// Note: The function symbol lookup is performed outside of the `if let Some(func) ...` block to avoid a RefCell lifetime bug in Rust.
|
||||
// Do not move it into the `if let Some(func) ...` block or it will keep `self.symbol_table` alive for the entire block and will be very memory inefficient!
|
||||
let func = self.symbol_table.borrow().lookup_fn(&ident.name).cloned();
|
||||
if let Some(func) = func {
|
||||
let ret = self.assert_and_return_type(func.output, expected, func.span);
|
||||
|
||||
let type_ = self.visit_expression(&input.receiver, destination);
|
||||
self.negate = prior_negate_state;
|
||||
match type_.as_ref() {
|
||||
Some(
|
||||
Type::IntegerType(
|
||||
IntegerType::I8
|
||||
| IntegerType::I16
|
||||
| IntegerType::I32
|
||||
| IntegerType::I64
|
||||
| IntegerType::I128,
|
||||
)
|
||||
| Type::Field
|
||||
| Type::Group,
|
||||
) => {}
|
||||
Some(t) => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::type_is_not_negatable(t, input.receiver.span()).into()),
|
||||
_ => {}
|
||||
};
|
||||
type_
|
||||
// Check number of function arguments.
|
||||
if func.input.len() != input.arguments.len() {
|
||||
self.emit_err(TypeCheckerError::incorrect_num_args_to_call(
|
||||
func.input.len(),
|
||||
input.arguments.len(),
|
||||
input.span(),
|
||||
));
|
||||
}
|
||||
UnaryOperation::Not => {
|
||||
// Assert boolean, integer types only.
|
||||
self.assert_bool_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
|
||||
// Check function argument types.
|
||||
func.input
|
||||
.iter()
|
||||
.zip(input.arguments.iter())
|
||||
.for_each(|(expected, argument)| {
|
||||
self.visit_expression(argument, &Some(expected.get_variable().type_.clone()));
|
||||
});
|
||||
|
||||
Some(ret)
|
||||
} else {
|
||||
self.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()));
|
||||
None
|
||||
}
|
||||
UnaryOperation::Square => {
|
||||
// Assert field type only.
|
||||
self.assert_expected_type(destination, Type::Field, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::SquareRoot => {
|
||||
// Assert field or scalar type.
|
||||
self.assert_field_scalar_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
// TODO: Is this case sufficient?
|
||||
expr => self.visit_expression(expr, expected),
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,85 +575,82 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
|
||||
return_incorrect_type(t1, t2, expected)
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
match &*input.function {
|
||||
Expression::Identifier(ident) => {
|
||||
if let Some(func) = self.symbol_table.clone().lookup_fn(ident.name) {
|
||||
let ret = self.assert_expected_option(func.output, expected, func.span());
|
||||
|
||||
// Check number of function arguments.
|
||||
if func.input.len() != input.arguments.len() {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_args_to_call(
|
||||
func.input.len(),
|
||||
input.arguments.len(),
|
||||
fn visit_tuple(&mut self, input: &'a TupleExpression, expected: &Self::AdditionalInput) -> Self::Output {
|
||||
// Check the expected tuple types if they are known.
|
||||
if let Some(Type::Tuple(expected_types)) = expected {
|
||||
// Check actual length is equal to expected length.
|
||||
if expected_types.len() != input.elements.len() {
|
||||
self.emit_err(TypeCheckerError::incorrect_tuple_length(
|
||||
expected_types.len(),
|
||||
input.elements.len(),
|
||||
input.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
// Check function argument types.
|
||||
func.input
|
||||
expected_types
|
||||
.iter()
|
||||
.zip(input.arguments.iter())
|
||||
.for_each(|(expected, argument)| {
|
||||
self.visit_expression(argument, &Some(expected.get_variable().type_));
|
||||
.zip(input.elements.iter())
|
||||
.for_each(|(expected, expr)| {
|
||||
self.visit_expression(expr, &Some(expected.clone()));
|
||||
});
|
||||
|
||||
Some(ret)
|
||||
Some(Type::Tuple(expected_types.clone()))
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()).into());
|
||||
// Tuples must be explicitly typed in testnet3.
|
||||
self.emit_err(TypeCheckerError::invalid_tuple(input.span()));
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
expr => self.visit_expression(expr, expected),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_circuit_init(
|
||||
&mut self,
|
||||
input: &'a CircuitInitExpression,
|
||||
additional: &Self::AdditionalInput,
|
||||
) -> Self::Output {
|
||||
if let Some(circ) = self.symbol_table.clone().lookup_circuit(&input.name.name) {
|
||||
// Check circuit type name.
|
||||
let ret = self.assert_expected_circuit(circ.identifier, additional, input.name.span());
|
||||
|
||||
// Check number of circuit members.
|
||||
if circ.members.len() != input.members.len() {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_circuit_members(
|
||||
circ.members.len(),
|
||||
input.members.len(),
|
||||
input.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
|
||||
match input.op {
|
||||
UnaryOperation::Abs => {
|
||||
// Only signed integer types.
|
||||
self.assert_signed_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
|
||||
// Check circuit member types.
|
||||
circ.members
|
||||
.iter()
|
||||
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
|
||||
// Lookup circuit variable name.
|
||||
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
|
||||
if let Some(expr) = &actual.expression {
|
||||
self.visit_expression(expr, &Some(*ty));
|
||||
UnaryOperation::AbsWrapped => {
|
||||
// Only signed integer types.
|
||||
self.assert_signed_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
} else {
|
||||
self.handler.emit_err(
|
||||
TypeCheckerError::unknown_sym("circuit member variable", name, name.span()).into(),
|
||||
);
|
||||
};
|
||||
});
|
||||
UnaryOperation::Double => {
|
||||
// Only field or group types.
|
||||
self.assert_field_group_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Inverse => {
|
||||
// Only field types.
|
||||
self.assert_field_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Negate => {
|
||||
let prior_negate_state = self.negate;
|
||||
self.negate = true;
|
||||
|
||||
Some(ret)
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("circuit", &input.name.name, input.name.span()).into());
|
||||
None
|
||||
let type_ = self.visit_expression(&input.receiver, destination);
|
||||
self.negate = prior_negate_state;
|
||||
|
||||
// Only field, group, or signed integer types.
|
||||
self.assert_field_group_signed_int_type(&type_, input.receiver.span());
|
||||
type_
|
||||
}
|
||||
UnaryOperation::Not => {
|
||||
// Only boolean or integer types.
|
||||
self.assert_bool_int_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::Square => {
|
||||
// Only field type.
|
||||
self.assert_field_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
UnaryOperation::SquareRoot => {
|
||||
// Only field or scalar type.
|
||||
self.assert_field_scalar_type(destination, input.span());
|
||||
self.visit_expression(&input.receiver, destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,26 +15,33 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||
|
||||
use leo_ast::*;
|
||||
use leo_errors::TypeCheckerError;
|
||||
|
||||
use leo_span::sym;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_function(&mut self, input: &'a Function) {
|
||||
let prev_st = std::mem::take(&mut self.symbol_table);
|
||||
self.symbol_table
|
||||
.swap(prev_st.borrow().get_fn_scope(&input.name()).unwrap());
|
||||
self.symbol_table.borrow_mut().parent = Some(Box::new(prev_st.into_inner()));
|
||||
|
||||
self.has_return = false;
|
||||
self.symbol_table.clear_variables();
|
||||
self.parent = Some(input.name());
|
||||
input.input.iter().for_each(|i| {
|
||||
let input_var = i.get_variable();
|
||||
self.check_ident_type(&Some(input_var.type_));
|
||||
self.check_core_type_conflict(&Some(input_var.type_.clone()));
|
||||
|
||||
// Check for conflicting variable names.
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
input_var.identifier.name,
|
||||
VariableSymbol {
|
||||
type_: &input_var.type_,
|
||||
type_: input_var.type_.clone(),
|
||||
span: input_var.identifier.span(),
|
||||
declaration: Declaration::Input(input_var.mode()),
|
||||
},
|
||||
@ -45,19 +52,22 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
self.visit_block(&input.block);
|
||||
|
||||
if !self.has_return {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()).into());
|
||||
self.emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()));
|
||||
}
|
||||
|
||||
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
|
||||
self.symbol_table.swap(prev_st.get_fn_scope(&input.name()).unwrap());
|
||||
self.symbol_table = RefCell::new(prev_st);
|
||||
}
|
||||
|
||||
fn visit_circuit(&mut self, input: &'a Circuit) {
|
||||
// Check for conflicting circuit member names.
|
||||
// Check for conflicting circuit/record member names.
|
||||
let mut used = HashSet::new();
|
||||
if !input.members.iter().all(|member| used.insert(member.name())) {
|
||||
self.handler.emit_err(if input.is_record {
|
||||
TypeCheckerError::duplicate_record_variable(input.name(), input.span()).into()
|
||||
self.emit_err(if input.is_record {
|
||||
TypeCheckerError::duplicate_record_variable(input.name(), input.span())
|
||||
} else {
|
||||
TypeCheckerError::duplicate_circuit_member(input.name(), input.span()).into()
|
||||
TypeCheckerError::duplicate_circuit_member(input.name(), input.span())
|
||||
});
|
||||
}
|
||||
|
||||
@ -70,12 +80,18 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||
{
|
||||
Some((_, actual_ty)) if expected_ty.eq_flat(actual_ty) => {} // All good, found + right type!
|
||||
Some((field, _)) => {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::record_var_wrong_type(field, expected_ty, input.span()).into());
|
||||
self.emit_err(TypeCheckerError::record_var_wrong_type(
|
||||
field,
|
||||
expected_ty,
|
||||
input.span(),
|
||||
));
|
||||
}
|
||||
None => {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::required_record_variable(need, expected_ty, input.span()).into());
|
||||
self.emit_err(TypeCheckerError::required_record_variable(
|
||||
need,
|
||||
expected_ty,
|
||||
input.span(),
|
||||
));
|
||||
}
|
||||
};
|
||||
check_has_field(sym::owner, Type::Address);
|
||||
|
@ -14,19 +14,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||
|
||||
use leo_ast::*;
|
||||
use leo_errors::TypeCheckerError;
|
||||
|
||||
use crate::{Declaration, TypeChecker, VariableSymbol};
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
fn visit_return(&mut self, input: &'a ReturnStatement) {
|
||||
// we can safely unwrap all self.parent instances because
|
||||
// statements should always have some parent block
|
||||
let parent = self.parent.unwrap();
|
||||
let return_type = &self.symbol_table.borrow().lookup_fn(&parent).map(|f| f.output.clone());
|
||||
self.check_core_type_conflict(return_type);
|
||||
|
||||
let return_type = &self.symbol_table.lookup_fn(parent).map(|f| f.output);
|
||||
self.check_ident_type(return_type);
|
||||
self.has_return = true;
|
||||
|
||||
self.visit_expression(&input.expression, return_type);
|
||||
@ -40,14 +42,14 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
};
|
||||
|
||||
input.variable_names.iter().for_each(|v| {
|
||||
self.check_ident_type(&Some(input.type_));
|
||||
self.check_core_type_conflict(&Some(input.type_.clone()));
|
||||
|
||||
self.visit_expression(&input.value, &Some(input.type_));
|
||||
self.visit_expression(&input.value, &Some(input.type_.clone()));
|
||||
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
v.identifier.name,
|
||||
VariableSymbol {
|
||||
type_: &input.type_,
|
||||
type_: input.type_.clone(),
|
||||
span: input.span(),
|
||||
declaration: declaration.clone(),
|
||||
},
|
||||
@ -61,33 +63,30 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
let var_name = match input.place {
|
||||
Expression::Identifier(id) => id,
|
||||
_ => {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::invalid_assignment_target(input.place.span()).into());
|
||||
self.emit_err(TypeCheckerError::invalid_assignment_target(input.place.span()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let var_type = if let Some(var) = self.symbol_table.lookup_variable(&var_name.name) {
|
||||
let var_type = if let Some(var) = self.symbol_table.borrow_mut().lookup_variable(&var_name.name) {
|
||||
// TODO: Check where this check is moved to in `improved-flattening`.
|
||||
match &var.declaration {
|
||||
Declaration::Const => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::cannont_assign_to_const_var(var_name, var.span).into()),
|
||||
Declaration::Input(ParamMode::Const) => self
|
||||
.handler
|
||||
.emit_err(TypeCheckerError::cannont_assign_to_const_input(var_name, var.span).into()),
|
||||
Declaration::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
|
||||
Declaration::Input(ParamMode::Const) => {
|
||||
self.emit_err(TypeCheckerError::cannot_assign_to_const_input(var_name, var.span))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(*var.type_)
|
||||
Some(var.type_.clone())
|
||||
} else {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span).into());
|
||||
self.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span));
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
if var_type.is_some() {
|
||||
self.check_ident_type(&var_type);
|
||||
self.check_core_type_conflict(&var_type);
|
||||
self.visit_expression(&input.value, &var_type);
|
||||
}
|
||||
}
|
||||
@ -101,10 +100,14 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
fn visit_iteration(&mut self, input: &'a IterationStatement) {
|
||||
if let Err(err) = self.symbol_table.insert_variable(
|
||||
let iter_type = &Some(input.type_.clone());
|
||||
self.assert_int_type(iter_type, input.variable.span);
|
||||
self.check_core_type_conflict(iter_type);
|
||||
|
||||
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
|
||||
input.variable.name,
|
||||
VariableSymbol {
|
||||
type_: &input.type_,
|
||||
type_: input.type_.clone(),
|
||||
span: input.span(),
|
||||
declaration: Declaration::Const,
|
||||
},
|
||||
@ -112,8 +115,6 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
let iter_type = &Some(input.type_);
|
||||
self.check_ident_type(iter_type);
|
||||
self.visit_expression(&input.start, iter_type);
|
||||
self.visit_expression(&input.stop, iter_type);
|
||||
}
|
||||
@ -130,8 +131,13 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, input: &'a Block) {
|
||||
// creates a new sub-scope since we are in a block.
|
||||
self.symbol_table.push_variable_scope();
|
||||
// Creates a new sub-scope since we are in a block.
|
||||
let scope_index = self.symbol_table.borrow_mut().insert_block();
|
||||
let previous_symbol_table = std::mem::take(&mut self.symbol_table);
|
||||
self.symbol_table
|
||||
.swap(previous_symbol_table.borrow().get_block_scope(scope_index).unwrap());
|
||||
self.symbol_table.borrow_mut().parent = Some(Box::new(previous_symbol_table.into_inner()));
|
||||
|
||||
input.statements.iter().for_each(|stmt| {
|
||||
match stmt {
|
||||
Statement::Return(stmt) => self.visit_return(stmt),
|
||||
@ -143,6 +149,11 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||
Statement::Block(stmt) => self.visit_block(stmt),
|
||||
};
|
||||
});
|
||||
self.symbol_table.pop_variable_scope();
|
||||
|
||||
let previous_symbol_table = *self.symbol_table.borrow_mut().parent.take().unwrap();
|
||||
// TODO: Is this swap necessary?
|
||||
self.symbol_table
|
||||
.swap(previous_symbol_table.get_block_scope(scope_index).unwrap());
|
||||
self.symbol_table = RefCell::new(previous_symbol_table);
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,19 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use crate::SymbolTable;
|
||||
|
||||
use leo_ast::{Identifier, IntegerType, Node, Type};
|
||||
use leo_core::*;
|
||||
use leo_errors::{emitter::Handler, TypeCheckerError};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
use crate::SymbolTable;
|
||||
use indexmap::IndexSet;
|
||||
use itertools::Itertools;
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub struct TypeChecker<'a> {
|
||||
pub(crate) symbol_table: &'a mut SymbolTable<'a>,
|
||||
pub(crate) symbol_table: RefCell<SymbolTable>,
|
||||
pub(crate) handler: &'a Handler,
|
||||
pub(crate) parent: Option<Symbol>,
|
||||
pub(crate) has_return: bool,
|
||||
@ -32,6 +35,14 @@ pub struct TypeChecker<'a> {
|
||||
pub(crate) algorithms_types: IndexSet<Symbol>,
|
||||
}
|
||||
|
||||
const BOOLEAN_TYPE: Type = Type::Boolean;
|
||||
|
||||
const FIELD_TYPE: Type = Type::Field;
|
||||
|
||||
const GROUP_TYPE: Type = Type::Group;
|
||||
|
||||
const SCALAR_TYPE: Type = Type::Scalar;
|
||||
|
||||
const INT_TYPES: [Type; 10] = [
|
||||
Type::IntegerType(IntegerType::I8),
|
||||
Type::IntegerType(IntegerType::I16),
|
||||
@ -59,41 +70,11 @@ const MAGNITUDE_TYPES: [Type; 3] = [
|
||||
Type::IntegerType(IntegerType::U32),
|
||||
];
|
||||
|
||||
const fn create_type_superset<const S: usize, const A: usize, const O: usize>(
|
||||
subset: [Type; S],
|
||||
additional: [Type; A],
|
||||
) -> [Type; O] {
|
||||
let mut superset: [Type; O] = [Type::IntegerType(IntegerType::U8); O];
|
||||
let mut i = 0;
|
||||
while i < S {
|
||||
superset[i] = subset[i];
|
||||
i += 1;
|
||||
}
|
||||
let mut j = 0;
|
||||
while j < A {
|
||||
superset[i + j] = additional[j];
|
||||
j += 1;
|
||||
}
|
||||
superset
|
||||
}
|
||||
|
||||
const BOOL_INT_TYPES: [Type; 11] = create_type_superset(INT_TYPES, [Type::Boolean]);
|
||||
|
||||
const FIELD_INT_TYPES: [Type; 11] = create_type_superset(INT_TYPES, [Type::Field]);
|
||||
|
||||
const FIELD_GROUP_INT_TYPES: [Type; 12] = create_type_superset(FIELD_INT_TYPES, [Type::Group]);
|
||||
|
||||
const FIELD_GROUP_SCALAR_INT_TYPES: [Type; 13] = create_type_superset(FIELD_GROUP_INT_TYPES, [Type::Scalar]);
|
||||
|
||||
const FIELD_GROUP_TYPES: [Type; 2] = [Type::Field, Type::Group];
|
||||
|
||||
const FIELD_SCALAR_TYPES: [Type; 2] = [Type::Field, Type::Scalar];
|
||||
|
||||
impl<'a> TypeChecker<'a> {
|
||||
/// Returns a new type checker given a symbol table and error handler.
|
||||
pub fn new(symbol_table: &'a mut SymbolTable<'a>, handler: &'a Handler) -> Self {
|
||||
pub fn new(symbol_table: SymbolTable, handler: &'a Handler) -> Self {
|
||||
Self {
|
||||
symbol_table,
|
||||
symbol_table: RefCell::new(symbol_table),
|
||||
handler,
|
||||
parent: None,
|
||||
has_return: false,
|
||||
@ -105,21 +86,202 @@ impl<'a> TypeChecker<'a> {
|
||||
|
||||
/// Emits a type checker error.
|
||||
pub(crate) fn emit_err(&self, err: TypeCheckerError) {
|
||||
self.handler.emit_err(err.into());
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
self.emit_err(TypeCheckerError::core_type_name_conflict(&ident.name, ident.span()));
|
||||
/// Emits an error to the handler if the given type is invalid.
|
||||
fn check_type(&self, is_valid: impl Fn(&Type) -> bool, error_string: String, type_: &Option<Type>, span: Span) {
|
||||
if let Some(type_) = type_ {
|
||||
if !is_valid(type_) {
|
||||
self.emit_err(TypeCheckerError::expected_one_type_of(error_string, type_, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error if the two given types are not equal.
|
||||
pub(crate) fn check_eq_types(&self, t1: &Option<Type>, t2: &Option<Type>, span: Span) {
|
||||
match (t1, t2) {
|
||||
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
|
||||
(Some(type_), None) | (None, Some(type_)) => {
|
||||
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this method when you know the actual type.
|
||||
/// Emits an error to the handler if the `actual` type is not equal to the `expected` type.
|
||||
pub(crate) fn assert_and_return_type(&self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
|
||||
if let Some(expected) = expected {
|
||||
if !actual.eq_flat(expected) {
|
||||
self.emit_err(TypeCheckerError::type_should_be(actual.clone(), expected, span));
|
||||
}
|
||||
}
|
||||
|
||||
actual
|
||||
}
|
||||
|
||||
/// Emits an error to the error handler if the `actual` type is not equal to the `expected` type.
|
||||
pub(crate) fn assert_type(&self, actual: &Option<Type>, expected: &Type, span: Span) {
|
||||
self.check_type(
|
||||
|actual: &Type| actual.eq_flat(expected),
|
||||
expected.to_string(),
|
||||
actual,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the error handler if the actual type is not equal to any of the expected types.
|
||||
pub(crate) fn assert_one_of_types(&self, actual: &Option<Type>, expected: &[Type], span: Span) {
|
||||
self.check_type(
|
||||
|actual: &Type| expected.iter().any(|t: &Type| t == actual),
|
||||
types_to_string(expected),
|
||||
actual,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a boolean.
|
||||
pub(crate) fn assert_bool_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| BOOLEAN_TYPE.eq(type_),
|
||||
BOOLEAN_TYPE.to_string(),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field.
|
||||
pub(crate) fn assert_field_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(|type_: &Type| FIELD_TYPE.eq(type_), FIELD_TYPE.to_string(), type_, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a group.
|
||||
pub(crate) fn assert_group_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(|type_: &Type| GROUP_TYPE.eq(type_), GROUP_TYPE.to_string(), type_, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a scalar.
|
||||
pub(crate) fn assert_scalar_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| SCALAR_TYPE.eq(type_),
|
||||
SCALAR_TYPE.to_string(),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not an integer.
|
||||
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| INT_TYPES.contains(type_),
|
||||
types_to_string(&INT_TYPES),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a signed integer.
|
||||
pub(crate) fn assert_signed_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| SIGNED_INT_TYPES.contains(type_),
|
||||
types_to_string(&SIGNED_INT_TYPES),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
|
||||
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| MAGNITUDE_TYPES.contains(type_),
|
||||
types_to_string(&MAGNITUDE_TYPES),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a boolean or an integer.
|
||||
pub(crate) fn assert_bool_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| BOOLEAN_TYPE.eq(type_) | INT_TYPES.contains(type_),
|
||||
format!("{}, {}", BOOLEAN_TYPE, types_to_string(&INT_TYPES)),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or integer.
|
||||
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| FIELD_TYPE.eq(type_) | INT_TYPES.contains(type_),
|
||||
format!("{}, {}", FIELD_TYPE, types_to_string(&INT_TYPES)),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or group.
|
||||
pub(crate) fn assert_field_group_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_),
|
||||
format!("{}, {}", FIELD_TYPE, GROUP_TYPE),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or scalar.
|
||||
pub(crate) fn assert_field_scalar_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| FIELD_TYPE.eq(type_) | SCALAR_TYPE.eq(type_),
|
||||
format!("{}, {}", FIELD_TYPE, SCALAR_TYPE),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field, group, or integer.
|
||||
pub(crate) fn assert_field_group_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | INT_TYPES.contains(type_),
|
||||
format!("{}, {}, {}", FIELD_TYPE, GROUP_TYPE, types_to_string(&INT_TYPES),),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field, group, or signed integer.
|
||||
pub(crate) fn assert_field_group_signed_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | SIGNED_INT_TYPES.contains(type_),
|
||||
format!("{}, {}, {}", FIELD_TYPE, GROUP_TYPE, types_to_string(&SIGNED_INT_TYPES),),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field, group, scalar or integer.
|
||||
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.check_type(
|
||||
|type_: &Type| {
|
||||
FIELD_TYPE.eq(type_) | GROUP_TYPE.eq(type_) | SCALAR_TYPE.eq(type_) | INT_TYPES.contains(type_)
|
||||
},
|
||||
format!(
|
||||
"{}, {}, {}, {}",
|
||||
FIELD_TYPE,
|
||||
GROUP_TYPE,
|
||||
SCALAR_TYPE,
|
||||
types_to_string(&INT_TYPES),
|
||||
),
|
||||
type_,
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
/// Emits an error if the `circuit` is not a core library circuit.
|
||||
/// Emits an error if the `function` is not supported by the circuit.
|
||||
pub(crate) fn assert_core_circuit_call(&self, circuit: &Type, function: &Identifier) -> Option<CoreInstruction> {
|
||||
pub(crate) fn check_core_circuit_call(&self, circuit: &Type, function: &Identifier) -> Option<CoreInstruction> {
|
||||
if let Type::Identifier(ident) = circuit {
|
||||
// Lookup core circuit
|
||||
match CoreInstruction::from_symbols(ident.name, function.name) {
|
||||
@ -137,19 +299,8 @@ impl<'a> TypeChecker<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Emits an error if the two given types are not equal.
|
||||
pub(crate) fn assert_eq_types(&self, t1: Option<Type>, t2: Option<Type>, span: Span) {
|
||||
match (t1, t2) {
|
||||
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
|
||||
(Some(type_), None) | (None, Some(type_)) => {
|
||||
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
pub(crate) fn check_expected_circuit(&mut self, circuit: Identifier, expected: &Option<Type>, span: Span) -> Type {
|
||||
if let Some(Type::Identifier(expected)) = expected {
|
||||
if !circuit.matches(expected) {
|
||||
self.emit_err(TypeCheckerError::type_should_be(circuit.name, expected.name, span));
|
||||
@ -159,84 +310,17 @@ impl<'a> TypeChecker<'a> {
|
||||
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 {
|
||||
if !actual.eq_flat(expected) {
|
||||
self.emit_err(TypeCheckerError::type_should_be(actual, expected, span));
|
||||
/// Emits an error if the given type conflicts with a core library type.
|
||||
pub(crate) fn check_core_type_conflict(&self, type_: &Option<Type>) {
|
||||
// todo: deprecate this method.
|
||||
if let Some(Type::Identifier(ident)) = type_ {
|
||||
if self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name) {
|
||||
self.emit_err(TypeCheckerError::core_type_name_conflict(&ident.name, ident.span()));
|
||||
}
|
||||
}
|
||||
|
||||
actual
|
||||
}
|
||||
|
||||
/// Returns the given `expected` type and emits an error if the `actual` type does not match.
|
||||
/// `span` should be the location of the expected type.
|
||||
pub(crate) fn assert_expected_type(&mut self, actual: &Option<Type>, expected: Type, span: Span) -> Type {
|
||||
if let Some(actual) = actual {
|
||||
if !actual.eq_flat(&expected) {
|
||||
self.emit_err(TypeCheckerError::type_should_be(actual, expected, span));
|
||||
}
|
||||
}
|
||||
|
||||
expected
|
||||
}
|
||||
|
||||
/// Emits an error to the error handler if the given type is not equal to any of the expected types.
|
||||
pub(crate) fn assert_one_of_types(&self, type_: &Option<Type>, expected: &[Type], span: Span) {
|
||||
if let Some(type_) = type_ {
|
||||
if !expected.iter().any(|t: &Type| t == type_) {
|
||||
self.emit_err(TypeCheckerError::expected_one_type_of(
|
||||
expected.iter().map(|t| t.to_string() + ",").collect::<String>(),
|
||||
type_,
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a boolean or an integer.
|
||||
pub(crate) fn assert_bool_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &BOOL_INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or integer.
|
||||
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &FIELD_INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or group.
|
||||
pub(crate) fn assert_field_group_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &FIELD_GROUP_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field or scalar.
|
||||
pub(crate) fn assert_field_scalar_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &FIELD_SCALAR_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field, group, or integer.
|
||||
pub(crate) fn assert_field_group_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &FIELD_GROUP_INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a field, group, scalar or integer.
|
||||
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &FIELD_GROUP_SCALAR_INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not an integer.
|
||||
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a signed integer.
|
||||
pub(crate) fn assert_signed_int_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &SIGNED_INT_TYPES, span)
|
||||
}
|
||||
|
||||
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
|
||||
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
|
||||
self.assert_one_of_types(type_, &MAGNITUDE_TYPES, span)
|
||||
}
|
||||
}
|
||||
|
||||
fn types_to_string(types: &[Type]) -> String {
|
||||
types.iter().map(|type_| type_.to_string()).join(", ")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod check_expressions;
|
||||
|
||||
pub use check_expressions::*;
|
||||
|
||||
pub mod check_program;
|
||||
@ -32,14 +33,14 @@ use leo_ast::{Ast, ProgramVisitor};
|
||||
use leo_errors::{emitter::Handler, Result};
|
||||
|
||||
impl<'a> Pass for TypeChecker<'a> {
|
||||
type Input = (&'a Ast, &'a mut SymbolTable<'a>, &'a Handler);
|
||||
type Output = Result<()>;
|
||||
type Input = (&'a Ast, &'a Handler, SymbolTable);
|
||||
type Output = Result<SymbolTable>;
|
||||
|
||||
fn do_pass((ast, symbol_table, handler): Self::Input) -> Self::Output {
|
||||
let mut visitor = TypeChecker::new(symbol_table, handler);
|
||||
fn do_pass((ast, handler, st): Self::Input) -> Self::Output {
|
||||
let mut visitor = TypeChecker::new(st, handler);
|
||||
visitor.visit_program(ast.as_repr());
|
||||
handler.last_err()?;
|
||||
|
||||
Ok(())
|
||||
Ok(visitor.symbol_table.take())
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ function mint(r0: address, r1: u64) -> Token {
|
||||
|
||||
// The `transfer` function sends the specified number of tokens
|
||||
// to the receiver from the provided token record.
|
||||
function transfer(r0: Token, r1: Receiver) -> Token {
|
||||
function transfer(r0: Token, r1: Receiver) -> (Token, 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
|
||||
@ -42,8 +42,7 @@ function transfer(r0: Token, r1: Receiver) -> Token {
|
||||
// with the change amount for the sender.
|
||||
let r4: Token = mint(r0.owner, r0.amount);
|
||||
|
||||
// return (r3, r4);
|
||||
return r3;
|
||||
return (r3, r4);
|
||||
}
|
||||
|
||||
function main() -> u8 {
|
||||
|
@ -19,7 +19,7 @@ edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
[dependencies.backtrace]
|
||||
version = "0.3.65"
|
||||
version = "0.3.66"
|
||||
|
||||
[dependencies.leo-span]
|
||||
path = "../span"
|
||||
|
@ -205,8 +205,8 @@ impl Handler {
|
||||
}
|
||||
|
||||
/// Emit the error `err`.
|
||||
pub fn emit_err(&self, err: LeoError) {
|
||||
self.inner.borrow_mut().emit_err(err);
|
||||
pub fn emit_err<E: Into<LeoError>>(&self, err: E) {
|
||||
self.inner.borrow_mut().emit_err(err.into());
|
||||
}
|
||||
|
||||
/// Emit the error `err`.
|
||||
@ -282,9 +282,9 @@ mod tests {
|
||||
let res: Result<(), _> = Handler::with(|h| {
|
||||
let s = Span::default();
|
||||
assert_eq!(h.err_count(), 0);
|
||||
h.emit_err(ParserError::invalid_import_list(s).into());
|
||||
h.emit_err(ParserError::invalid_import_list(s));
|
||||
assert_eq!(h.err_count(), 1);
|
||||
h.emit_err(ParserError::unexpected_eof(s).into());
|
||||
h.emit_err(ParserError::unexpected_eof(s));
|
||||
assert_eq!(h.err_count(), 2);
|
||||
Err(ParserError::spread_in_array_init(s).into())
|
||||
});
|
||||
@ -293,8 +293,8 @@ mod tests {
|
||||
|
||||
let res: Result<(), _> = Handler::with(|h| {
|
||||
let s = Span::default();
|
||||
h.emit_err(ParserError::invalid_import_list(s).into());
|
||||
h.emit_err(ParserError::unexpected_eof(s).into());
|
||||
h.emit_err(ParserError::invalid_import_list(s));
|
||||
h.emit_err(ParserError::unexpected_eof(s));
|
||||
Ok(())
|
||||
});
|
||||
assert_eq!(count_err(res.unwrap_err().to_string()), 2);
|
||||
|
@ -1,534 +0,0 @@
|
||||
// Copyright (C) 2019-2022 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::create_errors;
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
create_errors!(
|
||||
/// AsgError enum that represents all the errors for the `leo-asg` crate.
|
||||
AsgError,
|
||||
exit_code_mask: 3000i32,
|
||||
error_code_prefix: "ASG",
|
||||
|
||||
/// For when a circuit of the specified type is unresolved.
|
||||
/// Note that the type for a circuit is represented by a name.
|
||||
@formatted
|
||||
unresolved_circuit {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to resolve circuit: '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a circuit member of the specified name is unresolved.
|
||||
@formatted
|
||||
unresolved_circuit_member {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"illegal reference to non-existant member '{}' of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user is initializing a circuit, and it's missing circuit member.
|
||||
@formatted
|
||||
missing_circuit_member {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"missing circuit member '{}' for initialization of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user is initializing a circuit, and they declare a cirucit member twice.
|
||||
@formatted
|
||||
overridden_circuit_member {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"cannot declare circuit member '{}' more than once for initialization of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user is defining a circuit, and they define a circuit member multiple times.
|
||||
@formatted
|
||||
redefined_circuit_member {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"cannot declare circuit member '{}' multiple times in circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user is initializing a circuit, and they add an extra circuit member.
|
||||
@formatted
|
||||
extra_circuit_member {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"extra circuit member '{}' for initialization of circuit '{}' is not allowed",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user attempts to assign to a function.
|
||||
@formatted
|
||||
illegal_function_assign {
|
||||
args: (name: impl Display),
|
||||
msg: format!("attempt to assign to function '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call a circuit variable as a function.
|
||||
@formatted
|
||||
circuit_variable_call {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!("cannot call variable member '{}' of circuit '{}'", name, circuit_name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call an invalid circuit static function.
|
||||
@formatted
|
||||
circuit_static_call_invalid {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"cannot call static function '{}' of circuit '{}' from target",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call a mutable circuit member function from immutable context.
|
||||
@formatted
|
||||
circuit_member_mut_call_invalid {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"cannot call mutable member function '{}' of circuit '{}' from immutable context",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call a circuit member function from static context.
|
||||
@formatted
|
||||
circuit_member_call_invalid {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!(
|
||||
"cannot call member function '{}' of circuit '{}' from static context",
|
||||
name, circuit_name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to index into a non-array type.
|
||||
@formatted
|
||||
index_into_non_array {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to index into non-array '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries index with an invalid integer.
|
||||
@formatted
|
||||
invalid_assign_index {
|
||||
args: (name: impl Display, num: impl Display),
|
||||
msg: format!("failed to index array with invalid integer '{}'[{}]", name, num),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to index an array range, with a left value greater than right value.
|
||||
@formatted
|
||||
invalid_backwards_assignment {
|
||||
args: (name: impl Display, left: impl Display, right: impl Display),
|
||||
msg: format!(
|
||||
"failed to index array range for assignment with left > right '{}'[{}..{}]",
|
||||
name, left, right
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to create a constant varaible from non constant values.
|
||||
@formatted
|
||||
invalid_const_assign {
|
||||
args: (name: impl Display),
|
||||
msg: format!(
|
||||
"failed to create const variable(s) '{}' with non constant values.",
|
||||
name
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines a function with the same name twice.
|
||||
@formatted
|
||||
duplicate_function_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a function named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines variable with the same name twice.
|
||||
@formatted
|
||||
duplicate_variable_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a variable named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to index into a non-tuple type.
|
||||
@formatted
|
||||
index_into_non_tuple {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to index into non-tuple '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries access a tuple index out of bounds.
|
||||
@formatted
|
||||
tuple_index_out_of_bounds {
|
||||
args: (index: impl Display),
|
||||
msg: format!("tuple index out of bounds: '{}'", index),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries access a array index out of bounds.
|
||||
@formatted
|
||||
array_index_out_of_bounds {
|
||||
args: (index: impl Display),
|
||||
msg: format!("array index out of bounds: '{}'", index),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries have either side of a ternary return different variable types.
|
||||
@formatted
|
||||
ternary_different_types {
|
||||
args: (left: impl Display, right: impl Display),
|
||||
msg: format!("ternary sides had different types: left {}, right {}", left, right),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when an array size cannot be inferred.
|
||||
@formatted
|
||||
unknown_array_size {
|
||||
args: (),
|
||||
msg: "array size cannot be inferred, add explicit types",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user passes more arguements to a function than expected.
|
||||
@formatted
|
||||
unexpected_call_argument_count {
|
||||
args: (expected: impl Display, got: impl Display),
|
||||
msg: format!("function call expected {} arguments, got {}", expected, got),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For whan a function is unresolved.
|
||||
@formatted
|
||||
unresolved_function {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to resolve function: '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a type cannot be resolved.
|
||||
@formatted
|
||||
unresolved_type {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to resolve type for variable definition '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user passes a type, but another was expected.
|
||||
@formatted
|
||||
unexpected_type {
|
||||
args: (expected: impl Display, received: impl Display),
|
||||
msg: format!(
|
||||
"unexpected type, expected: '{}', received: '{}'",
|
||||
expected,
|
||||
received,
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a constant value was expected, but a non-constant one was received.
|
||||
@formatted
|
||||
unexpected_nonconst {
|
||||
args: (),
|
||||
msg: "expected const, found non-const value",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For whan a variable is unresolved.
|
||||
@formatted
|
||||
unresolved_reference {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to resolve variable reference '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a boolean value cannot be parsed.
|
||||
@formatted
|
||||
invalid_boolean {
|
||||
args: (value: impl Display),
|
||||
msg: format!("failed to parse boolean value '{}'", value),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a char value cannot be parsed.
|
||||
@formatted
|
||||
invalid_char {
|
||||
args: (value: impl Display),
|
||||
msg: format!("failed to parse char value '{}'", value),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when an int value cannot be parsed.
|
||||
@formatted
|
||||
invalid_int {
|
||||
args: (value: impl Display),
|
||||
msg: format!("failed to parse int value '{}'", value),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to negate an unsigned integer.
|
||||
@formatted
|
||||
unsigned_negation {
|
||||
args: (),
|
||||
msg: "cannot negate unsigned integer",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to assign to an immutable variable.
|
||||
@formatted
|
||||
immutable_assignment {
|
||||
args: (name: impl Display),
|
||||
msg: format!("illegal assignment to immutable variable '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a function is missing a return statement.
|
||||
@formatted
|
||||
function_missing_return {
|
||||
args: (name: impl Display),
|
||||
msg: format!("function '{}' missing return for all paths", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a function fails to resolve the correct return.
|
||||
@formatted
|
||||
function_return_validation {
|
||||
args: (name: impl Display, description: impl Display),
|
||||
msg: format!("function '{}' failed to validate return path: '{}'", name, description),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the type for an input variable could not be infered.
|
||||
@formatted
|
||||
input_ref_needs_type {
|
||||
args: (category: impl Display, name: impl Display),
|
||||
msg: format!("could not infer type for input in '{}': '{}'", category, name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call a test function.
|
||||
@formatted
|
||||
call_test_function {
|
||||
args: (),
|
||||
msg: "cannot call test function",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to define a circuit function as a test function.
|
||||
@formatted
|
||||
circuit_test_function {
|
||||
args: (),
|
||||
msg: "cannot have test function as member of circuit",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// Failed to parse index.
|
||||
@formatted
|
||||
parse_index_error {
|
||||
args: (),
|
||||
msg: "failed to parse index",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// Failed to parse array dimensions.
|
||||
@formatted
|
||||
parse_dimension_error {
|
||||
args: (),
|
||||
msg: "failed to parse dimension",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when there is an illegal ast structure.
|
||||
@formatted
|
||||
illegal_ast_structure {
|
||||
args: (details: impl Display),
|
||||
msg: format!("illegal ast structure: {}", details),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to reference an input varaible but none is in scope.
|
||||
@formatted
|
||||
illegal_input_variable_reference {
|
||||
args: (),
|
||||
msg: "attempted to reference input when none is in scope",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For the ASG receives an big Self, which should never happen
|
||||
/// as they should be resolved in an earlier compiler phase.
|
||||
@formatted
|
||||
unexpected_big_self {
|
||||
args: (),
|
||||
msg: "received a Self statement, which should never happen.",
|
||||
help: Some("Something went wrong during canonicalization, or you ran the ASG on an uncanonicalized AST.".to_string()),
|
||||
}
|
||||
|
||||
/// For when a import of the specified name is unresolved.
|
||||
@formatted
|
||||
unresolved_import {
|
||||
args: (name: impl Display),
|
||||
msg: format!("failed to resolve import: '{}'", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines an alias with the same name twice.
|
||||
@formatted
|
||||
duplicate_alias_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("an alias named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines an circuit with the same name twice.
|
||||
@formatted
|
||||
duplicate_circuit_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a circuit named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines a function input with the same name twice.
|
||||
@formatted
|
||||
duplicate_function_input_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a function input named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user defines a global const with the same name twice.
|
||||
@formatted
|
||||
duplicate_global_const_definition {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a global const named \"{}\" already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a function input shadows a global const.
|
||||
@formatted
|
||||
function_input_cannot_shadow_global_const {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a function input cannot be named `{}` as a global const with that name already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a variable definition shadows a global const.
|
||||
@formatted
|
||||
function_variable_cannot_shadow_global_const {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a variable cannot be named `{}` as a global const with that name already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a variable definition shadows a function input.
|
||||
@formatted
|
||||
function_variable_cannot_shadow_other_function_variable {
|
||||
args: (name: impl Display),
|
||||
msg: format!("a variable cannot be named `{}` as a function input or variable with that name already exists in this scope", name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when operator is used on an unsupported type.
|
||||
@formatted
|
||||
operator_allowed_only_for_type {
|
||||
args: (operator: impl Display, type_: impl Display, received: impl Display),
|
||||
msg: format!("operator '{}' is only allowed for type '{}', received: '{}'", operator, type_, received),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to call a circuit variable as a function.
|
||||
@formatted
|
||||
circuit_const_call {
|
||||
args: (circuit_name: impl Display, name: impl Display),
|
||||
msg: format!("cannot call const member '{}' of circuit '{}'", name, circuit_name),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when `input` variable is accessed inside a const function.
|
||||
@formatted
|
||||
illegal_input_variable_reference_in_const_function {
|
||||
args: (),
|
||||
msg: "input access is illegal in const functions",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when non-const function is called from a const context.
|
||||
@formatted
|
||||
calling_non_const_in_const_context {
|
||||
args: (),
|
||||
msg: "non-const functions cannot be called from const context",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when const function modifier is added to the main function.
|
||||
@formatted
|
||||
main_cannot_be_const {
|
||||
args: (),
|
||||
msg: "main function cannot be const",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when const function has non-const inputs or self.
|
||||
@formatted
|
||||
const_function_cannot_have_inputs {
|
||||
args: (),
|
||||
msg: "const function cannot have non-const input",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when `main` is annotated.
|
||||
@formatted
|
||||
main_cannot_have_annotations {
|
||||
args: (),
|
||||
msg: "main function cannot have annotations",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when unsupported annotation is added.
|
||||
@formatted
|
||||
unsupported_annotation {
|
||||
args: (name: impl Display),
|
||||
msg: format!("annotation `{}` does not exist", name),
|
||||
help: None,
|
||||
}
|
||||
);
|
@ -106,7 +106,7 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// This error is for when a user tries to use the library and programatically inject an import
|
||||
/// This error is for when a user tries to use the library and programmatically inject an import
|
||||
/// on the rust side.
|
||||
@backtraced
|
||||
injected_programs {
|
||||
@ -131,14 +131,22 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to define a tuple dimension of 1.
|
||||
/// For when a user tries to define an empty tuple.
|
||||
@formatted
|
||||
invalid_tuple_dimension_size {
|
||||
empty_tuple {
|
||||
args: (),
|
||||
msg: "tuples of 1 element are not allowed",
|
||||
msg: "Tuples of zero elements are not allowed.",
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user tries to define a tuple dimension of one.
|
||||
@formatted
|
||||
one_element_tuple {
|
||||
args: (),
|
||||
msg: "Tuples of one element are not allowed.",
|
||||
help: Some("Try defining a single type by removing the parenthesis `( )`".to_string()),
|
||||
}
|
||||
|
||||
/// For when a user shadows a function.
|
||||
@formatted
|
||||
shadowed_function {
|
||||
|
@ -67,8 +67,8 @@ create_messages!(
|
||||
/// For when the parser encountered an unexpected list of tokens.
|
||||
@formatted
|
||||
unexpected {
|
||||
args: (got: impl Display, expected: impl Display),
|
||||
msg: format!("expected {} -- got '{}'", expected, got),
|
||||
args: (found: impl Display, expected: impl Display),
|
||||
msg: format!("expected {} -- found '{}'", expected, found),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@ -83,15 +83,15 @@ create_messages!(
|
||||
/// For when the parser encountered an unexpected identifier.
|
||||
@formatted
|
||||
unexpected_ident {
|
||||
args: (got: impl Display, expected: &[impl Display]),
|
||||
args: (found: impl Display, expected: &[impl Display]),
|
||||
msg: format!(
|
||||
"unexpected identifier: expected {} -- got '{}'",
|
||||
"unexpected identifier: expected {} -- found '{}'",
|
||||
expected
|
||||
.iter()
|
||||
.map(|x| format!("'{}'", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
got
|
||||
found
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
@ -99,16 +99,16 @@ create_messages!(
|
||||
/// For when the parser encountered an unexpected statement.
|
||||
@formatted
|
||||
unexpected_statement {
|
||||
args: (got: impl Display, expected: impl Display),
|
||||
msg: format!("unexpected statement: expected '{}', got '{}'", expected, got),
|
||||
args: (found: impl Display, expected: impl Display),
|
||||
msg: format!("unexpected statement: expected '{}', found '{}'", expected, found),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the parser encountered an unexpected string.
|
||||
@formatted
|
||||
unexpected_str {
|
||||
args: (got: impl Display, expected: impl Display),
|
||||
msg: format!("unexpected string: expected '{}', got '{}'", expected, got),
|
||||
args: (found: impl Display, expected: impl Display),
|
||||
msg: format!("unexpected string: expected '{}', found '{}'", expected, found),
|
||||
help: None,
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ create_messages!(
|
||||
|
||||
/// For when the user tries to assign to a const input.
|
||||
@formatted
|
||||
cannont_assign_to_const_input {
|
||||
cannot_assign_to_const_input {
|
||||
args: (input: impl Display),
|
||||
msg: format!(
|
||||
"Cannot assign to const input `{input}`",
|
||||
@ -43,7 +43,7 @@ create_messages!(
|
||||
|
||||
/// For when the user tries to assign to a const input.
|
||||
@formatted
|
||||
cannont_assign_to_const_var {
|
||||
cannot_assign_to_const_var {
|
||||
args: (var: impl Display),
|
||||
msg: format!(
|
||||
"Cannot assign to const variable `{var}`",
|
||||
@ -56,7 +56,17 @@ create_messages!(
|
||||
type_should_be {
|
||||
args: (type_: impl Display, expected: impl Display),
|
||||
msg: format!(
|
||||
"Found type `{type_}` but type `{expected}` was expected",
|
||||
"Expected type `{expected}` but type `{type_}` was found",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the type checker cannot determine the type of an expression.
|
||||
@formatted
|
||||
could_not_determine_type {
|
||||
args: (expr: impl Display),
|
||||
msg: format!(
|
||||
"Could not determine the type of `{expr}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
@ -201,12 +211,22 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user is missing a circuit member during initialization.
|
||||
@formatted
|
||||
missing_circuit_member {
|
||||
args: (circuit: impl Display, member: impl Display),
|
||||
msg: format!(
|
||||
"Circuit initialization expression for `{circuit}` is missing member `{member}`.",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// An invalid access call is made e.g., `bool::MAX`
|
||||
@formatted
|
||||
invalid_access_expression {
|
||||
invalid_core_circuit_call {
|
||||
args: (expr: impl Display),
|
||||
msg: format!(
|
||||
"Invalid method call to {expr}."
|
||||
"{expr} is not a valid core circuit call."
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
@ -271,4 +291,25 @@ create_messages!(
|
||||
msg: format!("Comparison `{operator}` is not supported for the address type."),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
incorrect_tuple_length {
|
||||
args: (expected: impl Display, actual: impl Display),
|
||||
msg: format!("Expected a tuple of length `{expected}` found length `{actual}`"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@formatted
|
||||
invalid_tuple {
|
||||
args: (),
|
||||
msg: "Tuples must be explicitly typed in Leo".to_string(),
|
||||
help: Some("The function definition must match the function return statement".to_string()),
|
||||
}
|
||||
|
||||
@formatted
|
||||
tuple_out_of_range {
|
||||
args: (index: impl Display, length: impl Display),
|
||||
msg: format!("Tuple index `{index}` out of range for a tuple with length `{length}`"),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
expectation: Fail
|
||||
input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
expectation: Fail
|
||||
inputs:
|
||||
- inline.in: |
|
||||
[main]
|
||||
|
11
tests/compiler/tuple/access.leo
Normal file
11
tests/compiler/tuple/access.leo
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, bool) = (a, b);
|
||||
return (t.0, t.1);
|
||||
}
|
12
tests/compiler/tuple/access_negative_fail.leo
Normal file
12
tests/compiler/tuple/access_negative_fail.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.-1); // Index `t.-1` is invalid.
|
||||
}
|
12
tests/compiler/tuple/access_out_of_bounds_fail.leo
Normal file
12
tests/compiler/tuple/access_out_of_bounds_fail.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.2); // Index `t.2` is out of bounds.
|
||||
}
|
12
tests/compiler/tuple/declare_fail.leo
Normal file
12
tests/compiler/tuple/declare_fail.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, bool) = (a, 1u64); // We should be declaring to a boolean, not a u64.
|
||||
|
||||
return (t.0, t.1);
|
||||
}
|
10
tests/compiler/tuple/function_return.leo
Normal file
10
tests/compiler/tuple/function_return.leo
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
return (a, b);
|
||||
}
|
10
tests/compiler/tuple/function_return_single_fail.leo
Normal file
10
tests/compiler/tuple/function_return_single_fail.leo
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool) {
|
||||
return (a);
|
||||
}
|
10
tests/compiler/tuple/function_return_zero_fail.leo
Normal file
10
tests/compiler/tuple/function_return_zero_fail.leo
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> () {
|
||||
return ();
|
||||
}
|
3
tests/compiler/tuple/inputs/bool_bool.in
Normal file
3
tests/compiler/tuple/inputs/bool_bool.in
Normal file
@ -0,0 +1,3 @@
|
||||
[main]
|
||||
a: bool = true;
|
||||
b: bool = false;
|
12
tests/compiler/tuple/return_statement_fail.leo
Normal file
12
tests/compiler/tuple/return_statement_fail.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, u64) {
|
||||
let t: (bool, bool) = (a, b);
|
||||
|
||||
return (t.0, t.1); // The second element should be type u64 as in the function declaration.
|
||||
}
|
12
tests/compiler/tuple/type_fail.leo
Normal file
12
tests/compiler/tuple/type_fail.leo
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
input_file:
|
||||
- inputs/bool_bool.in
|
||||
*/
|
||||
|
||||
function main(a: bool, b: bool) -> (bool, bool) {
|
||||
let t: (bool, u64) = (a, b); // We should expect a boolean, not a u64.
|
||||
|
||||
return (t.0, t.1);
|
||||
}
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: fe880c907d0257c9fc8314b8b98cabd8a8282b587d2d618408cc3cd8e528fda5
|
||||
initial_ast: d2bf8199011f00bef93c6cec36528966c69908982ea4a6f2e515c98c258edf25
|
||||
symbol_table: dc40d8435dfe2399cda1bb0c53eb941b4719e40a7e9a2f038b98d7613d538b4e
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 00f5aba05e4efae5a125eb52f02f16400132085b8a34919d910aa40c6c405a22
|
||||
initial_ast: d9baeb1448040c61f5e7f779270eb767a29b5f2fd23a60a680aad138327999e7
|
||||
symbol_table: e8029086f943767e4f98014b3991bff8336cb6472c2d2dc3d51b4b13e5c72ea5
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 03e9df3bd1409f4af9e2a7f55130bc52f27d41f32a624ffa27f0ab114bf6fbf4
|
||||
initial_ast: ea3c9a73110ccc7684863543cf563ec78349402c460a75317b776af11d46f781
|
||||
symbol_table: 25f09247dfa86534ff321b8e1b2ca1666c92751d37a611d62e56b8cfac96261d
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372025]: Comparison `>` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x > sender;\n | ^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372027]: Comparison `>` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x > sender;\n | ^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372025]: Comparison `>=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x >= sender;\n | ^^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372027]: Comparison `>=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x >= sender;\n | ^^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372025]: Comparison `<` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x < sender;\n | ^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372027]: Comparison `<` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x < sender;\n | ^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372025]: Comparison `<=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x <= sender;\n | ^^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372027]: Comparison `<=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x <= sender;\n | ^^^^^^^^^^^\n"
|
||||
|
@ -6,4 +6,3 @@ outputs:
|
||||
- initial_input_ast: ec3cfeb93ea66a530150a5c5e2bd396688b3ef9b9fb0bcb961c62dac4daa064e
|
||||
- initial_input_ast: cb1d48114c10b2b732ad47a46fc8d05bf7a3e783da89e7f00065244bfc8d15c8
|
||||
initial_ast: 1bbe35b9a1a767668b4ff0d689873caded5d92ea7886aad2355a404511d76199
|
||||
symbol_table: 5fddde57167344769d00e423fb56692291d57ac8c953c512b82a4626bbdcb6c9
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
|
||||
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
|
||||
initial_ast: 8e4ab3450ec4ffbdba78fc0e1450c319bf538fd716af967419c8ce116ccd3d0e
|
||||
symbol_table: f36863240edb9fb5fb852c212a9ae1db491ee8243d0469fc155592964595e7d0
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 7a1c39dec2388ab801496ceb17ca85665d2f515269929925b7cc9018e14297ea
|
||||
- initial_input_ast: 650984ca5077d11a815889421656b7735b4c6bd320bdf68b4deb87dfc0f49388
|
||||
initial_ast: d84cf01e1fddeb09983dc4f7e868ae999b7b9ab4dff9d4286108f81aefe80677
|
||||
symbol_table: 4fd4e476609947028fbffe357ffb9d962e96c30a9abe3677d75675ae37b12587
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
|
||||
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
|
||||
initial_ast: d135ca0877ca63f6c31be572178a69508de9cfb81e258c4aec425861241f84c3
|
||||
symbol_table: c8dd46774e298ef70fc87f89ecb8b5f23f63b1f2401f337fc97ad83b54e85871
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
|
||||
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
|
||||
initial_ast: 42cf44d6821d7bd9d2c0222d2a673df9ff9c199f583e79a8b15b8eec53e2aea0
|
||||
symbol_table: 8ed9a73e996562abfe75837cfbf2103a4d9213291298206f4f63a7dac808cbc1
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 19f1be52a19445695f23724e1979b362dd3fcf31aace997c829e2206dc1cccbe
|
||||
- initial_input_ast: d2fc1992beaf062678bbf6c3e862820dbbea39926589afcdc46c19c8669f0e37
|
||||
initial_ast: b8707d1d3f6c111db2515d4093d15b4739765bfb9e114ed345ebedce0c04024d
|
||||
symbol_table: e73ad99357809180c0f1290a435d566d56110b8599a1ef2ec2a17d78b21628a8
|
||||
|
@ -8,4 +8,3 @@ outputs:
|
||||
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
|
||||
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
|
||||
initial_ast: 6738dda9dfa2cce86f92f9d28e0e0750870156f6b5d6146d063baa221f88df3f
|
||||
symbol_table: 91630eda77eaf1e355744e663ceba26a0c3f860d3f69e8e46b03f5464d16950f
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: f1af7e79dff9ede0d2a1c88d5d22801cb3dfe3a9fb34e93bca646e29a61e9f65
|
||||
initial_ast: 80124fe1a7297907bc27330cfe87117bee204a9f2b8acce053b0778568415a31
|
||||
symbol_table: 4a29c4b5af2ad1879798454b95b7dd04ae7ecd48289c5f3e7a1e19eaf3921c3b
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:7:16\n |\n 7 | function Foo() {}\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:7:16\n |\n 7 | function Foo() {}\n | ^"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372019]: Circuit Bar defined with more than one member with the same name.\n --> compiler-test:3:1\n |\n 3 | circuit Bar {\n 4 | x: u32,\n 5 | x: u32,\n 6 | }\n | ^\n"
|
||||
- "Error [ETYC0372021]: Circuit Bar defined with more than one member with the same name.\n --> compiler-test:3:1\n |\n 3 | circuit Bar {\n 4 | x: u32,\n 5 | x: u32,\n 6 | }\n | ^\n"
|
||||
|
@ -1,8 +1,5 @@
|
||||
---
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: acb54d555ee391d519919827f5949bf1600d18004ce461097d062f91108563ba
|
||||
initial_ast: 2fd04130d93c29b06f1288c682dd1ce37731ef0762f8e8d70d994aa97fa55c1e
|
||||
symbol_table: 862016bdb0f3f5516b1c9311b674207d4fc2a533066ef43e2a80e413119eeab7
|
||||
- "Error [EAST0372016]: circuit `Bar` shadowed by\n --> compiler-test:8:5\n |\n 8 | const Bar: u32 = 66u32;\n | ^^^^^^^^^^^^^^^^^^^^^^\n"
|
||||
|
@ -4,5 +4,4 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: no input
|
||||
initial_ast: 8507cd1753f4d2a835fa34d3d784487d89d595ea415d51145dd7291a839159c2
|
||||
symbol_table: e6f85704fccd0ca0f0461ae54cb604159a5f41a2175aad6b76bd534166f1bc6b
|
||||
initial_ast: 06b7428058f95519ae12657c24f873f86f43f30f9715ee3f1c0192e632775d32
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
|
@ -1,8 +1,5 @@
|
||||
---
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: no input
|
||||
initial_ast: 9489ab9b78bd31ac516e1674a83c6b35708238174df88374a7b19cef3d4a8e8a
|
||||
symbol_table: 53d31f0d39f8ceb7cf079812e6e3a00e0bdb507ca9c11a1c0158433d8d25a476
|
||||
- "Error [ETYC0372006]: Unknown variable `b`\n --> compiler-test:10:13\n |\n 10 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372004]: Could not determine the type of `b`\n --> compiler-test:10:13\n |\n 10 | return (b.x == a.x) == y;\n | ^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
|
@ -4,5 +4,4 @@ expectation: Pass
|
||||
outputs:
|
||||
- output:
|
||||
- initial_input_ast: 29f6139d908d390f890f04d8ee620757d29b7f71cd48c46ff65bc1e70aae840c
|
||||
initial_ast: 630995cc22fb6ec613f02e3aaa18392770158b2bbaf5aa1736c0bf71dd7357ce
|
||||
symbol_table: dc48ad542739351f7ab87e4add0f0a3e3fcc500195bdfa7d2eb3e79e29ef0916
|
||||
initial_ast: 9a9a861c36d5b4a6207e542505b8fa338721cd6cb57904577b425c733f0ae480
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:7:17\n |\n 7 | function main() {\n | ^"
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 15a1f00a6c0ca8141202e45e534b7afd196e9391c184a4efd94f0d0ccf04a59d
|
||||
initial_ast: 0ef8a9cfc447ad3fd1cb0275e91b1d005b7f230a02bf87e0f8ad56be86daa23e
|
||||
symbol_table: f8c971e501487f7a368a50fd1941c3fb70684b041478fe615a91f088796e301b
|
||||
|
@ -6,4 +6,3 @@ outputs:
|
||||
- initial_input_ast: 8b94c0dbc84f44bd29c614b87947e625ad136549ea29ff18233ba5b01ce63c9b
|
||||
- initial_input_ast: a62874e75304ab81d487909be1c6b1efa2e5756a2980b46e3bb1368586c3ee83
|
||||
initial_ast: 487dcbee6a433329c15b3cd0b23ecc259d4df455906438f3e6cf348ebd63ee02
|
||||
symbol_table: f4e056be00b25dfd854a5be8197aeb205436bb0ee11cfe06701531ea086e038c
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 14cd2c781b154a9037de84e945cfb348e9c587cef94d3e1f3be83e4306f92a0e
|
||||
initial_ast: d718de3086bc78a00a392e3c2b46dfa8f76084909bad3c98a5a6759df73efb27
|
||||
symbol_table: d46f6eb98259f34d32a60788aa178efa34166bcc6ba1058e2ff5f8327a129b9c
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: fd19d82c3aba921f01b37174e3eb7fb603438506fe511657e21235b9fb3647d2
|
||||
initial_ast: 2a99ef2515c58607e4b617f93c74838b6f2afed28e9e2c2eed658cea6d729b2d
|
||||
symbol_table: 559484bc163178bf54b169f5dd573167771566aa993055b6a28f0c1a759339bc
|
||||
|
@ -6,4 +6,3 @@ outputs:
|
||||
- initial_input_ast: 06fad995841b833ef5074920ae270b93bf82ad60b6c8b440c66b8bc5018aaa72
|
||||
- initial_input_ast: 34bd981451bdeb915a2de749b969c8804c06e44a9f4473f36d6efac7617baead
|
||||
initial_ast: f8315b82b0a05e0e69fb8b0342b46cbee976ec20d62e0edd2f066bf51acd81d6
|
||||
symbol_table: 560afbb790df70bfc770d5c2f966428a37baf94a5c0f5312ad445456d33a2cd9
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 0961f603812e241567b6e3ef5adb458309f1829eb2c08a216efccb17bea89faf
|
||||
initial_ast: 3602c929b2256f4ed5cec2ba9d3e967b029a7dc9717a371b32a356425cf9892b
|
||||
symbol_table: 720c2aafae77c261ed1640d4080f9a73657638baa30e54a5e10e2323b6f6eca0
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: f18a0e019ca4719c4c4ef5b7313f562c3bc9581819d161d84566e706f3765249
|
||||
initial_ast: f319125731635279a198fb2df8c0446475024c70829e9de32fa5f43c38079862
|
||||
symbol_table: e5159343ab03573032873783b28058a482dd401d534a0d3af03790a5286ba470
|
||||
|
@ -5,4 +5,3 @@ outputs:
|
||||
- output:
|
||||
- initial_input_ast: 16910a94cf1f803ae6425ae6bee9422b01651c2c243b5e46807dc3191d169e64
|
||||
initial_ast: 52824ac2e84097578faf0ff92f7ca840d2de30e8454a540886123a2cf79192ae
|
||||
symbol_table: 757bb967973b87466c01be1a9dc78d30701964e0d234e0e65d1bbcbd3072370f
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370005]: expected -> -- got '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
- "Error [EPAR0370005]: expected -> -- found '{'\n --> compiler-test:3:17\n |\n 3 | function main() {\n | ^"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370009]: unexpected string: expected 'formatted static_string', got 'hello'\n --> compiler-test:5:17\n |\n 5 | console.log(hello);\n | ^^^^^"
|
||||
- "Error [EPAR0370009]: unexpected string: expected 'formatted static_string', found 'hello'\n --> compiler-test:5:17\n |\n 5 | console.log(hello);\n | ^^^^^"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372014]: The type ComputeKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public compute_key: ComputeKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372015]: The type ComputeKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public compute_key: ComputeKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [ETYC0372014]: The type PrivateKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public private_key: PrivateKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
|
||||
- "Error [ETYC0372015]: The type PrivateKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public private_key: PrivateKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- "Error [EPAR0370009]: unexpected string: expected 'ident', got 'record'\n --> compiler-test:4:22\n |\n 4 | function main(public record: Record, a: bool) -> bool {\n | ^^^^^^"
|
||||
- "Error [EPAR0370009]: unexpected string: expected 'identifier', found 'record'\n --> compiler-test:4:22\n |\n 4 | function main(public record: Record, a: bool) -> bool {\n | ^^^^^^"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user