merge return-tuple branch

This commit is contained in:
collin 2022-07-09 20:55:30 -07:00
commit ed79f5c957
492 changed files with 2115 additions and 2262 deletions

22
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -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
);
}
}

View File

@ -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::*;

View 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);

View File

@ -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 {

View File

@ -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),
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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),
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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())
}
fn reconstruct_circuit_init(&mut self, input: CircuitInitExpression) -> (Expression, Self::AdditionalOutput) {
(Expression::CircuitInit(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,
})
}
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.

View File

@ -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.

View File

@ -17,5 +17,8 @@
pub mod integer_type;
pub use integer_type::*;
pub mod tuple;
pub use tuple::*;
pub mod type_;
pub use type_::*;

View 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(",")
)
}
}

View File

@ -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"),
}
}

View File

@ -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()
}

View File

@ -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"))
}

View File

@ -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.

View File

@ -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,19 +336,29 @@ 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(&Token::LeftParen) {
// Eat a method call on a type
expr = self.parse_method_call_expression(expr, name)?
} else {
// Eat a circuit member access.
expr = Expression::Access(AccessExpression::Member(MemberAccess {
span: expr.span(),
inner: Box::new(expr),
name,
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
expr = self.parse_method_call_expression(expr, name)?
} else {
// Eat a circuit member access.
expr = Expression::Access(AccessExpression::Member(MemberAccess {
span: expr.span(),
inner: Box::new(expr),
name,
}))
}
}
} else if self.eat(&Token::DoubleColon) {
// Eat a core circuit constant or core circuit function call.
@ -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)
}

View File

@ -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.

View File

@ -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)?;

View File

@ -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()?;

View File

@ -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()
}
}
}

View File

@ -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)),
},
));
}

View File

@ -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!(

View File

@ -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),

View File

@ -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.")
}

View File

@ -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"),
}
}

View File

@ -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;
}

View File

@ -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> {

View 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(),
}
}
}

View File

@ -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)
}
}

View File

@ -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 {
None
}
}
/// 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
}
}
/// 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
}
}
/// 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)
}
/// 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 {
AstError::shadowed_circuit(symbol, insert.span).into()
};
return Err(err);
parent.variable_in_parent_scope(symbol)
}
} else {
false
}
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(())
/// 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 lookup_fn(&self, symbol: Symbol) -> Option<&&'a Function> {
self.functions.get(&symbol)
/// 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
}
}
pub fn lookup_circuit(&self, symbol: &Symbol) -> Option<&&'a Circuit> {
self.circuits.get(symbol)
}
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
self.variables.lookup_variable(symbol)
}
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(),
};
}
pub fn pop_variable_scope(&mut self) {
let parent = self.variables.parent.clone().unwrap();
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)
}
}

View File

@ -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(())
}
}

View File

@ -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(())

View File

@ -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(
core_instruction.num_args(),
access.args.len(),
input.span(),
)
.into(),
);
self.emit_err(TypeCheckerError::incorrect_num_args_to_call(
core_instruction.num_args(),
access.args.len(),
input.span(),
));
}
// 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());
None
self.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
}
}
_expr => None, // todo: Add support for associated constants (u8::MAX).
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
}
}
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_
}
UnaryOperation::Not => {
// Assert boolean, integer types only.
self.assert_bool_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
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)
// 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(),
));
}
// 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
}
}
// 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(),
input.span(),
)
.into(),
);
}
// Check function argument types.
func.input
.iter()
.zip(input.arguments.iter())
.for_each(|(expected, argument)| {
self.visit_expression(argument, &Some(expected.get_variable().type_));
});
Some(ret)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()).into());
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_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(),
));
}
// Check circuit member types.
circ.members
expected_types
.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));
}
} else {
self.handler.emit_err(
TypeCheckerError::unknown_sym("circuit member variable", name, name.span()).into(),
);
};
.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("circuit", &input.name.name, input.name.span()).into());
// Tuples must be explicitly typed in testnet3.
self.emit_err(TypeCheckerError::invalid_tuple(input.span()));
None
}
}
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)
}
UnaryOperation::AbsWrapped => {
// Only signed integer types.
self.assert_signed_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
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;
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)
}
}
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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(", ")
}

View File

@ -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())
}
}

View File

@ -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 {

View File

@ -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"

View File

@ -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);

View File

@ -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,
}
);

View File

@ -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 {

View File

@ -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,
}

View File

@ -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,
}
);

View File

@ -1,6 +1,6 @@
/*
namespace: Compile
expectation: Pass
expectation: Fail
input_file: input/dummy.in
*/

View File

@ -1,6 +1,6 @@
/*
namespace: Compile
expectation: Pass
expectation: Fail
inputs:
- inline.in: |
[main]

View 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);
}

View 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.
}

View 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.
}

View 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);
}

View 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);
}

View File

@ -0,0 +1,10 @@
/*
namespace: Compile
expectation: Fail
input_file:
- inputs/bool_bool.in
*/
function main(a: bool, b: bool) -> (bool) {
return (a);
}

View File

@ -0,0 +1,10 @@
/*
namespace: Compile
expectation: Fail
input_file:
- inputs/bool_bool.in
*/
function main(a: bool, b: bool) -> () {
return ();
}

View File

@ -0,0 +1,3 @@
[main]
a: bool = true;
b: bool = false;

View 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.
}

View 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);
}

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: fe880c907d0257c9fc8314b8b98cabd8a8282b587d2d618408cc3cd8e528fda5
initial_ast: d2bf8199011f00bef93c6cec36528966c69908982ea4a6f2e515c98c258edf25
symbol_table: dc40d8435dfe2399cda1bb0c53eb941b4719e40a7e9a2f038b98d7613d538b4e

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 00f5aba05e4efae5a125eb52f02f16400132085b8a34919d910aa40c6c405a22
initial_ast: d9baeb1448040c61f5e7f779270eb767a29b5f2fd23a60a680aad138327999e7
symbol_table: e8029086f943767e4f98014b3991bff8336cb6472c2d2dc3d51b4b13e5c72ea5

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 03e9df3bd1409f4af9e2a7f55130bc52f27d41f32a624ffa27f0ab114bf6fbf4
initial_ast: ea3c9a73110ccc7684863543cf563ec78349402c460a75317b776af11d46f781
symbol_table: 25f09247dfa86534ff321b8e1b2ca1666c92751d37a611d62e56b8cfac96261d

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -6,4 +6,3 @@ outputs:
- initial_input_ast: ec3cfeb93ea66a530150a5c5e2bd396688b3ef9b9fb0bcb961c62dac4daa064e
- initial_input_ast: cb1d48114c10b2b732ad47a46fc8d05bf7a3e783da89e7f00065244bfc8d15c8
initial_ast: 1bbe35b9a1a767668b4ff0d689873caded5d92ea7886aad2355a404511d76199
symbol_table: 5fddde57167344769d00e423fb56692291d57ac8c953c512b82a4626bbdcb6c9

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
initial_ast: 8e4ab3450ec4ffbdba78fc0e1450c319bf538fd716af967419c8ce116ccd3d0e
symbol_table: f36863240edb9fb5fb852c212a9ae1db491ee8243d0469fc155592964595e7d0

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 7a1c39dec2388ab801496ceb17ca85665d2f515269929925b7cc9018e14297ea
- initial_input_ast: 650984ca5077d11a815889421656b7735b4c6bd320bdf68b4deb87dfc0f49388
initial_ast: d84cf01e1fddeb09983dc4f7e868ae999b7b9ab4dff9d4286108f81aefe80677
symbol_table: 4fd4e476609947028fbffe357ffb9d962e96c30a9abe3677d75675ae37b12587

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
initial_ast: d135ca0877ca63f6c31be572178a69508de9cfb81e258c4aec425861241f84c3
symbol_table: c8dd46774e298ef70fc87f89ecb8b5f23f63b1f2401f337fc97ad83b54e85871

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
initial_ast: 42cf44d6821d7bd9d2c0222d2a673df9ff9c199f583e79a8b15b8eec53e2aea0
symbol_table: 8ed9a73e996562abfe75837cfbf2103a4d9213291298206f4f63a7dac808cbc1

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 19f1be52a19445695f23724e1979b362dd3fcf31aace997c829e2206dc1cccbe
- initial_input_ast: d2fc1992beaf062678bbf6c3e862820dbbea39926589afcdc46c19c8669f0e37
initial_ast: b8707d1d3f6c111db2515d4093d15b4739765bfb9e114ed345ebedce0c04024d
symbol_table: e73ad99357809180c0f1290a435d566d56110b8599a1ef2ec2a17d78b21628a8

View File

@ -8,4 +8,3 @@ outputs:
- initial_input_ast: 4c5eeffd0306b20c8deece509782b81ea8583245f650e40a4a300d517f6ed5f4
- initial_input_ast: a56b3f9908dec2acaed302691d4fe0c2cf046f0deb8f188f617e042e75502f71
initial_ast: 6738dda9dfa2cce86f92f9d28e0e0750870156f6b5d6146d063baa221f88df3f
symbol_table: 91630eda77eaf1e355744e663ceba26a0c3f860d3f69e8e46b03f5464d16950f

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: f1af7e79dff9ede0d2a1c88d5d22801cb3dfe3a9fb34e93bca646e29a61e9f65
initial_ast: 80124fe1a7297907bc27330cfe87117bee204a9f2b8acce053b0778568415a31
symbol_table: 4a29c4b5af2ad1879798454b95b7dd04ae7ecd48289c5f3e7a1e19eaf3921c3b

View File

@ -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 | ^"

View File

@ -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"

View File

@ -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"

View File

@ -4,5 +4,4 @@ expectation: Pass
outputs:
- output:
- initial_input_ast: no input
initial_ast: 8507cd1753f4d2a835fa34d3d784487d89d595ea415d51145dd7291a839159c2
symbol_table: e6f85704fccd0ca0f0461ae54cb604159a5f41a2175aad6b76bd534166f1bc6b
initial_ast: 06b7428058f95519ae12657c24f873f86f43f30f9715ee3f1c0192e632775d32

View File

@ -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 | ^"

View File

@ -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 | ^"

View File

@ -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"

View File

@ -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 | ^"

View File

@ -4,5 +4,4 @@ expectation: Pass
outputs:
- output:
- initial_input_ast: 29f6139d908d390f890f04d8ee620757d29b7f71cd48c46ff65bc1e70aae840c
initial_ast: 630995cc22fb6ec613f02e3aaa18392770158b2bbaf5aa1736c0bf71dd7357ce
symbol_table: dc48ad542739351f7ab87e4add0f0a3e3fcc500195bdfa7d2eb3e79e29ef0916
initial_ast: 9a9a861c36d5b4a6207e542505b8fa338721cd6cb57904577b425c733f0ae480

View File

@ -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 | ^"

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 15a1f00a6c0ca8141202e45e534b7afd196e9391c184a4efd94f0d0ccf04a59d
initial_ast: 0ef8a9cfc447ad3fd1cb0275e91b1d005b7f230a02bf87e0f8ad56be86daa23e
symbol_table: f8c971e501487f7a368a50fd1941c3fb70684b041478fe615a91f088796e301b

View File

@ -6,4 +6,3 @@ outputs:
- initial_input_ast: 8b94c0dbc84f44bd29c614b87947e625ad136549ea29ff18233ba5b01ce63c9b
- initial_input_ast: a62874e75304ab81d487909be1c6b1efa2e5756a2980b46e3bb1368586c3ee83
initial_ast: 487dcbee6a433329c15b3cd0b23ecc259d4df455906438f3e6cf348ebd63ee02
symbol_table: f4e056be00b25dfd854a5be8197aeb205436bb0ee11cfe06701531ea086e038c

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 14cd2c781b154a9037de84e945cfb348e9c587cef94d3e1f3be83e4306f92a0e
initial_ast: d718de3086bc78a00a392e3c2b46dfa8f76084909bad3c98a5a6759df73efb27
symbol_table: d46f6eb98259f34d32a60788aa178efa34166bcc6ba1058e2ff5f8327a129b9c

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: fd19d82c3aba921f01b37174e3eb7fb603438506fe511657e21235b9fb3647d2
initial_ast: 2a99ef2515c58607e4b617f93c74838b6f2afed28e9e2c2eed658cea6d729b2d
symbol_table: 559484bc163178bf54b169f5dd573167771566aa993055b6a28f0c1a759339bc

View File

@ -6,4 +6,3 @@ outputs:
- initial_input_ast: 06fad995841b833ef5074920ae270b93bf82ad60b6c8b440c66b8bc5018aaa72
- initial_input_ast: 34bd981451bdeb915a2de749b969c8804c06e44a9f4473f36d6efac7617baead
initial_ast: f8315b82b0a05e0e69fb8b0342b46cbee976ec20d62e0edd2f066bf51acd81d6
symbol_table: 560afbb790df70bfc770d5c2f966428a37baf94a5c0f5312ad445456d33a2cd9

View File

@ -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 | ^"

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 0961f603812e241567b6e3ef5adb458309f1829eb2c08a216efccb17bea89faf
initial_ast: 3602c929b2256f4ed5cec2ba9d3e967b029a7dc9717a371b32a356425cf9892b
symbol_table: 720c2aafae77c261ed1640d4080f9a73657638baa30e54a5e10e2323b6f6eca0

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: f18a0e019ca4719c4c4ef5b7313f562c3bc9581819d161d84566e706f3765249
initial_ast: f319125731635279a198fb2df8c0446475024c70829e9de32fa5f43c38079862
symbol_table: e5159343ab03573032873783b28058a482dd401d534a0d3af03790a5286ba470

View File

@ -5,4 +5,3 @@ outputs:
- output:
- initial_input_ast: 16910a94cf1f803ae6425ae6bee9422b01651c2c243b5e46807dc3191d169e64
initial_ast: 52824ac2e84097578faf0ff92f7ca840d2de30e8454a540886123a2cf79192ae
symbol_table: 757bb967973b87466c01be1a9dc78d30701964e0d234e0e65d1bbcbd3072370f

View File

@ -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 | ^"

View File

@ -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 | ^^^^^"

View File

@ -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"

View File

@ -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"

View File

@ -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