merge testnet3

This commit is contained in:
collin 2022-11-04 12:46:32 -07:00
commit e6c3700945
20 changed files with 128 additions and 24 deletions

View File

@ -315,7 +315,7 @@ pub trait ProgramReconstructor: StatementReconstructor {
imports: input
.imports
.into_iter()
.map(|(id, import)| (id, self.reconstruct_import(import)))
.map(|(id, import)| (id, (self.reconstruct_import(import.0), import.1)))
.collect(),
program_scopes: input
.program_scopes

View File

@ -193,7 +193,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
/// A Visitor trait for the program represented by the AST.
pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
fn visit_program(&mut self, input: &'a Program) {
input.imports.values().for_each(|import| self.visit_import(import));
input.imports.values().for_each(|import| self.visit_import(&import.0));
input
.program_scopes

View File

@ -24,6 +24,8 @@ pub use program_scope::*;
use crate::Identifier;
use leo_span::Span;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -32,7 +34,7 @@ use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Program {
/// A map from import names to import definitions.
pub imports: IndexMap<Identifier, Program>,
pub imports: IndexMap<Identifier, (Program, Span)>,
/// A map from program names to program scopes.
pub program_scopes: IndexMap<ProgramId, ProgramScope>,
}

View File

@ -389,7 +389,7 @@ impl ParserContext<'_> {
} else {
// Eat a struct member access.
expr = Expression::Access(AccessExpression::Member(MemberAccess {
span: expr.span(),
span: expr.span() + name.span(),
inner: Box::new(expr),
name,
}))

View File

@ -71,10 +71,11 @@ impl ParserContext<'_> {
)
}
// TODO: remove import resolution from parser.
/// Parses an import statement `import foo.leo;`.
pub(super) fn parse_import(&mut self) -> Result<(Identifier, Program)> {
pub(super) fn parse_import(&mut self) -> Result<(Identifier, (Program, Span))> {
// Parse `import`.
let _start = self.expect(&Token::Import)?;
let start = self.expect(&Token::Import)?;
// Parse `foo`.
let import_name = self.expect_identifier()?;
@ -86,7 +87,7 @@ impl ParserContext<'_> {
return Err(ParserError::leo_imports_only(self.token.span).into());
}
let _end = self.expect(&Token::Semicolon)?;
let end = self.expect(&Token::Semicolon)?;
// Tokenize and parse import file.
// Todo: move this to a different module.
@ -114,7 +115,7 @@ impl ParserContext<'_> {
// Use the parser to construct the imported abstract syntax tree (ast).
let program_ast = parse_ast(self.handler, &prg_sf.src, prg_sf.start_pos)?;
Ok((import_name, program_ast.into_repr()))
Ok((import_name, (program_ast.into_repr(), start + end)))
}
/// Parsers a program scope `program foo.aleo { ... }`.

View File

@ -34,7 +34,7 @@ impl<'a> CodeGenerator<'a> {
&input
.imports
.iter()
.map(|(identifier, imported_program)| self.visit_import(identifier, imported_program))
.map(|(identifier, (imported_program, _))| self.visit_import(identifier, imported_program))
.join("\n"),
);

View File

@ -152,7 +152,7 @@ impl ProgramConsumer for StaticSingleAssigner<'_> {
imports: input
.imports
.into_iter()
.map(|(name, import)| (name, self.consume_program(import)))
.map(|(name, (import, span))| (name, (self.consume_program(import), span)))
.collect(),
program_scopes: input
.program_scopes

View File

@ -38,7 +38,7 @@ pub struct FunctionSymbol {
/// Is this function a transition, inlined, or a regular function?.
pub call_type: CallType,
/// The `Span` associated with the function.
pub(crate) span: Span,
pub(crate) _span: Span,
/// The inputs to the function.
pub(crate) input: Vec<Input>,
/// Metadata associated with the finalize block.
@ -51,7 +51,7 @@ impl SymbolTable {
id,
output_type: func.output_type.clone(),
call_type: func.call_type,
span: func.span,
_span: func.span,
input: func.input.clone(),
finalize: func.finalize.as_ref().map(|finalize| FinalizeData {
input: finalize.input.clone(),

View File

@ -14,13 +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/>.
use crate::TypeChecker;
use leo_ast::*;
use leo_errors::emitter::Handler;
use leo_errors::TypeCheckerError;
use leo_span::{sym, Span};
use std::str::FromStr;
use crate::TypeChecker;
use std::str::FromStr;
fn return_incorrect_type(t1: Option<Type>, t2: Option<Type>, expected: &Option<Type>) -> Option<Type> {
match (t1, t2) {
@ -143,7 +144,14 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
// Check that `access.name` is a member of the struct.
match struct_.members.iter().find(|member| member.name() == access.name.name) {
// Case where `access.name` is a member of the struct.
Some(Member { type_, .. }) => return Some(type_.clone()),
Some(Member { type_, .. }) => {
// Check that the type of `access.name` is the same as `expected`.
return Some(self.assert_and_return_type(
type_.clone(),
expected,
access.span(),
));
}
// Case where `access.name` is not a member of the struct.
None => {
self.emit_err(TypeCheckerError::invalid_struct_variable(
@ -454,7 +462,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
}
}
let ret = self.assert_and_return_type(func.output_type, expected, func.span);
let ret = self.assert_and_return_type(func.output_type, expected, input.span());
// Check number of function arguments.
if func.input.len() != input.arguments.len() {
@ -537,11 +545,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Default::default()
}
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(var) = self.symbol_table.borrow().lookup_variable(var.name) {
Some(self.assert_and_return_type(var.type_.clone(), expected, var.span))
fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(var) = self.symbol_table.borrow().lookup_variable(input.name) {
Some(self.assert_and_return_type(var.type_.clone(), expected, input.span()))
} else {
self.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
self.emit_err(TypeCheckerError::unknown_sym("variable", input.name, input.span()));
None
}
}

View File

@ -287,4 +287,32 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Unset `is_transition_function` flag.
self.is_transition_function = false;
}
fn visit_program(&mut self, input: &'a Program) {
match self.is_imported {
// If the program is imported, then it is not allowed to import any other programs.
true => {
input.imports.values().for_each(|(_, span)| {
self.emit_err(TypeCheckerError::imported_program_cannot_import_program(*span))
});
}
// Otherwise, typecheck the imported programs.
false => {
// Set `self.is_imported`.
let previous_is_imported = core::mem::replace(&mut self.is_imported, true);
// Typecheck the imported programs.
input.imports.values().for_each(|import| self.visit_import(&import.0));
// Set `self.is_imported` to its previous state.
self.is_imported = previous_is_imported;
}
}
// Typecheck the program scopes.
input
.program_scopes
.values()
.for_each(|scope| self.visit_program_scope(scope));
}
}

View File

@ -39,6 +39,8 @@ pub struct TypeChecker<'a> {
pub(crate) is_transition_function: bool,
/// Whether or not we are currently traversing a finalize block.
pub(crate) is_finalize: bool,
/// Whether or not we are currently traversing an imported program.
pub(crate) is_imported: bool,
}
const BOOLEAN_TYPE: Type = Type::Boolean;
@ -95,6 +97,7 @@ impl<'a> TypeChecker<'a> {
has_return: false,
has_finalize: false,
is_finalize: false,
is_imported: false,
}
}

View File

@ -437,4 +437,11 @@ create_messages!(
msg: format!("Strings are not yet supported."),
help: None,
}
@formatted
imported_program_cannot_import_program {
args: (),
msg: format!("An imported program cannot import another program."),
help: None,
}
);

View File

@ -0,0 +1,26 @@
/*
namespace: Compile
expectation: Fail
*/
program test.aleo {
function f1(a: u8) -> u8 {
let b: u8 = a + 1u8;
return b;
}
function f3(u2: u8, u3: i16) -> u8 {
return 0u8;
}
transition main(id_type: i8, s: u8) -> u8 {
let x: i8 = 1i8;
x = f1(1u8);
let y: i8 = 1i8;
let z: i16 = 100i16;
y = f3(y, z);
return s;
}
}

View File

@ -0,0 +1,19 @@
/*
namespace: Compile
expectation: Fail
*/
program test.aleo {
struct s1 {
f1: u32,
f2: u32
}
transition main(id_type: i8, s: s1) -> i8 {
let x: i8 = s.f1;
x = x + 1i8;
return x;
}
}

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372003]: Expected type `i8` but type `u8` was found\n --> compiler-test:16:13\n |\n 16 | x = f1(1u8);\n | ^^^^^^^\nError [ETYC0372003]: Expected type `i8` but type `u8` was found\n --> compiler-test:20:13\n |\n 20 | y = f3(y, z);\n | ^^^^^^^^\nError [ETYC0372003]: Expected type `u8` but type `i8` was found\n --> compiler-test:20:16\n |\n 20 | y = f3(y, z);\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:4:28\n |\n 4 | transition main(a: u8, foo: Foo) -> u8 {\n | ^^^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:8:38\n |\n 8 | transition returns_foo(a: u8) -> Foo {\n | ^^^\nError [ETYC0372003]: Expected type `Foo` but type `u8` was found\n --> compiler-test:8:28\n |\n 8 | transition returns_foo(a: u8) -> Foo {\n | ^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:8:5\n |\n 8 | transition returns_foo(a: u8) -> Foo {\n 9 | return a;\n 10 | }}\n | ^\n"
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:4:28\n |\n 4 | transition main(a: u8, foo: Foo) -> u8 {\n | ^^^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:8:38\n |\n 8 | transition returns_foo(a: u8) -> Foo {\n | ^^^\nError [ETYC0372003]: Expected type `Foo` but type `u8` was found\n --> compiler-test:9:16\n |\n 9 | return a;\n | ^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:8:5\n |\n 8 | transition returns_foo(a: u8) -> Foo {\n 9 | return a;\n 10 | }}\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372003]: Expected type `address` but type `u64` was found\n --> compiler-test:13:32\n |\n 13 | function mint(r0: address, r1: u64) -> Token {\n | ^^\nError [ETYC0372003]: Expected type `u64` but type `address` was found\n --> compiler-test:13:19\n |\n 13 | function mint(r0: address, r1: u64) -> Token {\n | ^^\nError [ETYC0372047]: Cannot call another function from a standard function.\n --> compiler-test:23:24\n |\n 23 | let t: Token = mint(x, c);\n | ^^^^^^^^^^\n"
- "Error [ETYC0372003]: Expected type `address` but type `u64` was found\n --> compiler-test:15:20\n |\n 15 | owner: r1, // This variable should be type address.\n | ^^\nError [ETYC0372003]: Expected type `u64` but type `address` was found\n --> compiler-test:17:21\n |\n 17 | amount: r0, // This variable should be type u64.\n | ^^\nError [ETYC0372047]: Cannot call another function from a standard function.\n --> compiler-test:23:24\n |\n 23 | let t: Token = mint(x, c);\n | ^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372003]: Expected type `boolean` but type `u32` was found\n --> compiler-test:4:19\n |\n 4 | function main(x: u32) {\n | ^\nError [EAST0372011]: variable `x` shadowed by\n --> compiler-test:5:9\n |\n 5 | let x: bool = true ? x: true;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
- "Error [ETYC0372003]: Expected type `boolean` but type `u32` was found\n --> compiler-test:5:30\n |\n 5 | let x: bool = true ? x: true;\n | ^\nError [EAST0372011]: variable `x` shadowed by\n --> compiler-test:5:9\n |\n 5 | let x: bool = true ? x: true;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372029]: Standard functions cannot have modes associated with their inputs.\n --> compiler-test:8:25\n |\n 8 | function main(const x: u8, y: bool) -> bool {\n | ^\n |\n = Consider removing the mode or using the keyword `transition` instead of `function`.\nError [ETYC0372005]: Unknown variable `b`\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372004]: Could not determine the type of `b`\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372003]: Expected type `u8` but type `no type` was found\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^^^^^^^^\n"
- "Error [ETYC0372029]: Standard functions cannot have modes associated with their inputs.\n --> compiler-test:8:25\n |\n 8 | function main(const x: u8, y: bool) -> bool {\n | ^\n |\n = Consider removing the mode or using the keyword `transition` instead of `function`.\nError [ETYC0372005]: Unknown variable `b`\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372004]: Could not determine the type of `b`\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372003]: Expected type `u8` but type `no type` was found\n --> compiler-test:11:17\n |\n 11 | return (b.x == a.x) == y;\n | ^^^^^^^^^^\n"

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372003]: Expected type `i8` but type `u32` was found\n --> compiler-test:11:21\n |\n 11 | let x: i8 = s.f1;\n | ^^^^\n"