mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-24 02:31:44 +03:00
type check record init expressions
This commit is contained in:
parent
18a74cfb85
commit
21c6a2167a
@ -57,7 +57,7 @@ pub enum Expression {
|
||||
Binary(BinaryExpression),
|
||||
/// A call expression, e.g., `my_fun(args)`.
|
||||
Call(CallExpression),
|
||||
/// An expression constructing a structure like `Foo { bar: 42, baz }`.
|
||||
/// An expression constructing a circuit like `Foo { bar: 42, baz }`.
|
||||
CircuitInit(CircuitInitExpression),
|
||||
/// An expression of type "error".
|
||||
/// Will result in a compile error eventually.
|
||||
|
@ -225,6 +225,7 @@ pub trait ProgramVisitorDirector<'a>: VisitorDirector<'a> + StatementVisitorDire
|
||||
.values()
|
||||
.for_each(|function| self.visit_function(function));
|
||||
input.circuits.values().for_each(|circuit| self.visit_circuit(circuit));
|
||||
input.records.values().for_each(|record| self.visit_record(record));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl RecordVariable {
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Symbol {
|
||||
return self.ident.name
|
||||
return self.ident.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use super::*;
|
||||
use leo_errors::{ParserError, Result};
|
||||
use leo_span::sym;
|
||||
|
||||
use snarkvm_dpc::{prelude::Address, testnet2::Testnet2};
|
||||
|
||||
@ -537,17 +536,8 @@ impl ParserContext<'_> {
|
||||
Token::Ident(name) => {
|
||||
let ident = Identifier { name, span };
|
||||
if !self.disallow_circuit_construction && self.check(&Token::LeftCurly) {
|
||||
self.parse_circuit_expression(ident)?
|
||||
} else {
|
||||
Expression::Identifier(ident)
|
||||
}
|
||||
}
|
||||
Token::SelfUpper => {
|
||||
let ident = Identifier {
|
||||
name: sym::SelfUpper,
|
||||
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)?
|
||||
} else {
|
||||
Expression::Identifier(ident)
|
||||
|
@ -185,7 +185,9 @@ impl ParserContext<'_> {
|
||||
let actual_type = self.parse_all_types()?.0;
|
||||
|
||||
if expected_name != actual_name.name || expected_type != actual_type {
|
||||
self.emit_err(ParserError::required_record_variable(expected_name, expected_type, actual_name.span()).into());
|
||||
self.emit_err(
|
||||
ParserError::required_record_variable(expected_name, expected_type, actual_name.span()).into(),
|
||||
);
|
||||
}
|
||||
|
||||
// Emit an error for a record variable without an ending comma or semicolon.
|
||||
|
@ -89,7 +89,6 @@ pub enum Token {
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
SelfUpper,
|
||||
Record,
|
||||
|
||||
// Regular Keywords
|
||||
@ -144,7 +143,6 @@ pub const KEYWORD_TOKENS: &[Token] = &[
|
||||
Token::Record,
|
||||
Token::Return,
|
||||
Token::SelfLower,
|
||||
Token::SelfUpper,
|
||||
Token::Scalar,
|
||||
Token::Static,
|
||||
Token::String,
|
||||
@ -190,7 +188,6 @@ impl Token {
|
||||
Token::Return => sym::Return,
|
||||
Token::Scalar => sym::scalar,
|
||||
Token::SelfLower => sym::SelfLower,
|
||||
Token::SelfUpper => sym::SelfUpper,
|
||||
Token::Static => sym::Static,
|
||||
Token::String => sym::string,
|
||||
Token::True => sym::True,
|
||||
@ -270,7 +267,6 @@ impl fmt::Display for Token {
|
||||
U32 => write!(f, "u32"),
|
||||
U64 => write!(f, "u64"),
|
||||
U128 => write!(f, "u128"),
|
||||
SelfUpper => write!(f, "Self"),
|
||||
Record => write!(f, "record"),
|
||||
|
||||
Circuit => write!(f, "circuit"),
|
||||
|
@ -54,4 +54,11 @@ impl<'a> ProgramVisitor<'a> for CreateSymbolTable<'a> {
|
||||
}
|
||||
VisitResult::SkipChildren
|
||||
}
|
||||
|
||||
fn visit_record(&mut self, input: &'a Record) -> VisitResult {
|
||||
if let Err(err) = self.symbol_table.insert_record(input.name(), input) {
|
||||
self.handler.emit_err(err);
|
||||
}
|
||||
VisitResult::SkipChildren
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use leo_ast::{Circuit, Function};
|
||||
use leo_ast::{Circuit, Function, Record};
|
||||
use leo_errors::{AstError, Result};
|
||||
use leo_span::{Span, Symbol};
|
||||
|
||||
@ -32,6 +32,9 @@ pub struct SymbolTable<'a> {
|
||||
/// Maps circuit names to circuit definitions.
|
||||
/// This field is populated at a first pass.
|
||||
circuits: IndexMap<Symbol, &'a Circuit>,
|
||||
/// Maps record names to record definitions.
|
||||
/// This field is populated at a first pass.
|
||||
records: IndexMap<Symbol, &'a Record>,
|
||||
/// Variables represents functions variable definitions and input variables.
|
||||
/// This field is not populated till necessary.
|
||||
pub(crate) variables: VariableScope<'a>,
|
||||
@ -62,10 +65,27 @@ impl<'a> SymbolTable<'a> {
|
||||
// Return an error if the circuit name has already been inserted.
|
||||
return Err(AstError::shadowed_circuit(symbol, insert.span).into());
|
||||
}
|
||||
if self.records.contains_key(&symbol) {
|
||||
// Return an error if the record name has already been inserted.
|
||||
return Err(AstError::shadowed_record(symbol, insert.span).into());
|
||||
}
|
||||
self.circuits.insert(symbol, insert);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_record(&mut self, symbol: Symbol, insert: &'a Record) -> Result<()> {
|
||||
if self.circuits.contains_key(&symbol) {
|
||||
// Return an error if the circuit name has already been inserted.
|
||||
return Err(AstError::shadowed_circuit(symbol, insert.span).into());
|
||||
}
|
||||
if self.records.contains_key(&symbol) {
|
||||
// Return an error if the record name has already been inserted.
|
||||
return Err(AstError::shadowed_record(symbol, insert.span).into());
|
||||
}
|
||||
self.records.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);
|
||||
@ -80,6 +100,10 @@ impl<'a> SymbolTable<'a> {
|
||||
self.circuits.get(symbol)
|
||||
}
|
||||
|
||||
pub fn lookup_record(&self, symbol: &Symbol) -> Option<&&'a Record> {
|
||||
self.records.get(symbol)
|
||||
}
|
||||
|
||||
pub fn lookup_variable(&self, symbol: &Symbol) -> Option<&VariableSymbol<'a>> {
|
||||
self.variables.lookup_variable(symbol)
|
||||
}
|
||||
@ -111,6 +135,10 @@ impl<'a> Display for SymbolTable<'a> {
|
||||
write!(f, "{circ}")?;
|
||||
}
|
||||
|
||||
for rec in self.records.values() {
|
||||
write!(f, "{rec}")?;
|
||||
}
|
||||
|
||||
write!(f, "{}", self.variables)
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,12 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
|
||||
expected,
|
||||
circuit.span(),
|
||||
));
|
||||
} else if let Some(record) = self.visitor.symbol_table.clone().lookup_record(&var.name) {
|
||||
return Some(self.visitor.assert_expected_option(
|
||||
Type::Identifier(record.identifier.clone()),
|
||||
expected,
|
||||
record.span(),
|
||||
));
|
||||
} else if let VisitResult::VisitChildren = self.visitor.visit_identifier(var) {
|
||||
if let Some(var) = self.visitor.symbol_table.clone().lookup_variable(&var.name) {
|
||||
return Some(self.visitor.assert_expected_option(*var.type_, expected, var.span));
|
||||
@ -624,7 +630,62 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
|
||||
input: &'a CircuitInitExpression,
|
||||
additional: &Self::AdditionalInput,
|
||||
) -> Option<Self::Output> {
|
||||
if let Some(circ) = self.visitor.symbol_table.clone().lookup_circuit(&input.name.name) {
|
||||
// Type check record init expression.
|
||||
if let Some(expected) = self.visitor.symbol_table.clone().lookup_record(&input.name.name) {
|
||||
// Check record type name.
|
||||
let ret = self
|
||||
.visitor
|
||||
.assert_expected_circuit(expected.identifier, additional, input.name.span());
|
||||
|
||||
// Check number of record data variables.
|
||||
if expected.data.len() != input.members.len() - 2 {
|
||||
self.visitor.handler.emit_err(
|
||||
TypeCheckerError::incorrect_num_record_variables(
|
||||
expected.data.len(),
|
||||
input.members.len(),
|
||||
input.span(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
// Check record variable types.
|
||||
input.members.iter().for_each(|actual| {
|
||||
// Check record owner.
|
||||
if actual.identifier.matches(&expected.owner.ident) {
|
||||
if let Some(owner_expr) = &actual.expression {
|
||||
self.visit_expression(owner_expr, &Some(Type::Address));
|
||||
}
|
||||
}
|
||||
|
||||
// Check record balance.
|
||||
if actual.identifier.matches(&expected.balance.ident) {
|
||||
if let Some(balance_expr) = &actual.expression {
|
||||
self.visit_expression(balance_expr, &Some(Type::IntegerType(IntegerType::U64)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check record data variable.
|
||||
if let Some(expected_var) = expected
|
||||
.data
|
||||
.iter()
|
||||
.find(|member| member.ident.matches(&actual.identifier))
|
||||
{
|
||||
if let Some(var_expr) = &actual.expression {
|
||||
self.visit_expression(var_expr, &Some(expected_var.type_));
|
||||
}
|
||||
} else {
|
||||
self.visitor.handler.emit_err(
|
||||
TypeCheckerError::unknown_sym("record variable", actual.identifier, actual.identifier.span())
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Some(ret)
|
||||
} else if let Some(circ) = self.visitor.symbol_table.clone().lookup_circuit(&input.name.name) {
|
||||
// Type check circuit init expression.
|
||||
|
||||
// Check circuit type name.
|
||||
let ret = self
|
||||
.visitor
|
||||
|
@ -68,6 +68,7 @@ impl<'a> ProgramVisitorDirector<'a> for Director<'a> {
|
||||
}
|
||||
|
||||
fn visit_record(&mut self, input: &'a Record) {
|
||||
println!("visit record");
|
||||
if let VisitResult::VisitChildren = self.visitor_ref().visit_record(input) {
|
||||
// Check for conflicting record member names.
|
||||
let mut used = HashSet::new();
|
||||
|
@ -147,7 +147,7 @@ impl<'a> TypeChecker<'a> {
|
||||
/// Returns the `circuit` type and emits an error if the `expected` type does not match.
|
||||
pub(crate) fn assert_expected_circuit(&mut self, circuit: Identifier, expected: &Option<Type>, span: Span) -> Type {
|
||||
if let Some(Type::Identifier(expected)) = expected {
|
||||
if expected.name != circuit.name {
|
||||
if !circuit.matches(expected) {
|
||||
self.handler
|
||||
.emit_err(TypeCheckerError::type_should_be(circuit.name, expected.name, span).into());
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
|
||||
record Token {
|
||||
circuit Token {
|
||||
// The token owner.
|
||||
owner: address,
|
||||
// The Aleo balance (in gates).
|
||||
balance: u64,
|
||||
// The token amount.
|
||||
amount: u64,
|
||||
data: u64
|
||||
}
|
||||
|
||||
function mint() -> Token {
|
||||
let tok: Token = Token { balance: 1u64, data: 1u64};
|
||||
return tok;
|
||||
}
|
||||
|
||||
function main(a: u8) -> group {
|
||||
|
||||
return Pedersen64::hash(a);
|
||||
}
|
@ -42,7 +42,7 @@ function transfer(r0: Token, r1: Receiver) -> Token {
|
||||
let r4: Token = mint(r0.owner, r0.amount);
|
||||
|
||||
// return (r3, r4);
|
||||
return r3
|
||||
return r3;
|
||||
}
|
||||
|
||||
function main() -> u8 {
|
||||
|
@ -155,6 +155,14 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user shadows a record.
|
||||
@formatted
|
||||
shadowed_record {
|
||||
args: (record: impl Display),
|
||||
msg: format!("record `{record}` shadowed by"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when a user shadows a variable.
|
||||
@formatted
|
||||
shadowed_variable {
|
||||
|
@ -191,6 +191,16 @@ create_messages!(
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// For when the user tries initialize a circuit with the incorrect number of args.
|
||||
@formatted
|
||||
incorrect_num_record_variables {
|
||||
args: (expected: impl Display, received: impl Display),
|
||||
msg: format!(
|
||||
"Record expected `{expected}` variables, but got `{received}`",
|
||||
),
|
||||
help: None,
|
||||
}
|
||||
|
||||
/// An invalid access call is made e.g., `bool::MAX`
|
||||
@formatted
|
||||
invalid_access_expression {
|
||||
|
Loading…
Reference in New Issue
Block a user