Skip to content

Commit

Permalink
Attributes and symbols in preparation for the test framework.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikebenfield committed Nov 18, 2024
1 parent 1e31a90 commit 74bae04
Show file tree
Hide file tree
Showing 15 changed files with 54 additions and 15 deletions.
1 change: 1 addition & 0 deletions compiler/ast/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl Function {
Variant::Inline => write!(f, "inline ")?,
Variant::Function | Variant::AsyncFunction => write!(f, "function ")?,
Variant::Transition | Variant::AsyncTransition => write!(f, "transition ")?,
Variant::Interpret => write!(f, "interpret")?,
}
write!(f, "{}", self.identifier)?;

Expand Down
1 change: 1 addition & 0 deletions compiler/ast/src/functions/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum Variant {
Transition,
AsyncTransition,
AsyncFunction,
Interpret,
}

impl Variant {
Expand Down
1 change: 1 addition & 0 deletions compiler/ast/src/stub/function_stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ impl FunctionStub {
Variant::Inline => write!(f, "inline ")?,
Variant::Function | Variant::AsyncFunction => write!(f, "function ")?,
Variant::Transition | Variant::AsyncTransition => write!(f, "transition ")?,
Variant::Interpret => write!(f, "interpret")?,
}
write!(f, "{}", self.identifier)?;

Expand Down
1 change: 1 addition & 0 deletions compiler/compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ impl<'a, N: Network> Compiler<'a, N> {
&self.type_table,
self.compiler_options.build.conditional_block_max_depth,
self.compiler_options.build.disable_conditional_branch_type_checking,
false, // is_test
))?;
if self.compiler_options.output.type_checked_symbol_table {
self.write_symbol_table_to_json("type_checked_symbol_table.json", &symbol_table)?;
Expand Down
7 changes: 4 additions & 3 deletions compiler/parser/src/parser/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl<N: Network> ParserContext<'_, N> {
let (id, mapping) = self.parse_mapping()?;
mappings.push((id, mapping));
}
Token::At | Token::Async | Token::Function | Token::Transition | Token::Inline => {
Token::At | Token::Async | Token::Function | Token::Transition | Token::Inline | Token::Interpret => {
let (id, function) = self.parse_function()?;

// Partition into transitions and functions so that we don't have to sort later.
Expand Down Expand Up @@ -334,8 +334,9 @@ impl<N: Network> ParserContext<'_, N> {
// Parse a potential async signifier.
let (is_async, start_async) =
if self.token.token == Token::Async { (true, self.expect(&Token::Async)?) } else { (false, Span::dummy()) };
// Parse `<variant> IDENT`, where `<variant>` is `function`, `transition`, or `inline`.
// Parse `<variant> IDENT`, where `<variant>` is `function`, `transition`, `inline`, or `interpret`.
let (variant, start) = match self.token.token.clone() {
Token::Interpret => (Variant::Interpret, self.expect(&Token::Interpret)?),
Token::Inline => (Variant::Inline, self.expect(&Token::Inline)?),
Token::Function => {
(if is_async { Variant::AsyncFunction } else { Variant::Function }, self.expect(&Token::Function)?)
Expand All @@ -344,7 +345,7 @@ impl<N: Network> ParserContext<'_, N> {
if is_async { Variant::AsyncTransition } else { Variant::Transition },
self.expect(&Token::Transition)?,
),
_ => self.unexpected("'function', 'transition', or 'inline'")?,
_ => self.unexpected("'function', 'transition', 'inline', or 'interpret'")?,
};
let name = self.expect_identifier()?;

Expand Down
1 change: 1 addition & 0 deletions compiler/parser/src/tokenizer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ impl Token {
"import" => Token::Import,
"in" => Token::In,
"inline" => Token::Inline,
"interpret" => Token::Interpret,
"let" => Token::Let,
"leo" => Token::Leo,
"mapping" => Token::Mapping,
Expand Down
4 changes: 4 additions & 0 deletions compiler/parser/src/tokenizer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ pub enum Token {
Import,
In,
Inline,
Interpret,
Let,
Mapping,
Network,
Expand Down Expand Up @@ -205,6 +206,7 @@ pub const KEYWORD_TOKENS: &[Token] = &[
Token::Import,
Token::In,
Token::Inline,
Token::Interpret,
Token::Let,
Token::Mapping,
Token::Network,
Expand Down Expand Up @@ -262,6 +264,7 @@ impl Token {
Token::Import => sym::import,
Token::In => sym::In,
Token::Inline => sym::inline,
Token::Interpret => sym::interpret,
Token::Let => sym::Let,
Token::Leo => sym::leo,
Token::Mapping => sym::mapping,
Expand Down Expand Up @@ -396,6 +399,7 @@ impl fmt::Display for Token {
Import => write!(f, "import"),
In => write!(f, "in"),
Inline => write!(f, "inline"),
Interpret => write!(f, "interpret"),
Let => write!(f, "let"),
Mapping => write!(f, "mapping"),
Network => write!(f, "network"),
Expand Down
2 changes: 1 addition & 1 deletion compiler/passes/src/code_generation/visit_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl<'a> CodeGenerator<'a> {
Variant::Transition | Variant::AsyncTransition => format!("\nfunction {}:\n", function.identifier),
Variant::Function => format!("\nclosure {}:\n", function.identifier),
Variant::AsyncFunction => format!("\nfinalize {}:\n", self.finalize_caller.unwrap()),
Variant::Inline => return String::new(),
Variant::Inline | Variant::Interpret => return String::new(),
};

// Construct and append the input declarations of the function.
Expand Down
8 changes: 5 additions & 3 deletions compiler/passes/src/function_inlining/inline_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ impl ExpressionReconstructor for FunctionInliner<'_> {

(result, inlined_statements)
}
Variant::Function | Variant::AsyncFunction | Variant::Transition | Variant::AsyncTransition => {
(Expression::Call(input), Default::default())
}
Variant::Function
| Variant::AsyncFunction
| Variant::Transition
| Variant::AsyncTransition
| Variant::Interpret => (Expression::Call(input), Default::default()),
}
}
}
2 changes: 1 addition & 1 deletion compiler/passes/src/type_checking/check_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl<'a, N: Network> ExpressionVisitor<'a> for TypeChecker<'a, N> {
// Check core struct name and function.
if let Some(core_instruction) = self.get_core_function_call(&access.variant, &access.name) {
// Check that operation is not restricted to finalize blocks.
if self.scope_state.variant != Some(Variant::AsyncFunction)
if !matches!(self.scope_state.variant, Some(Variant::AsyncFunction) | Some(Variant::Interpret))
&& core_instruction.is_finalize_command()
{
self.emit_err(TypeCheckerError::operation_must_be_in_finalize_block(input.span()));
Expand Down
14 changes: 11 additions & 3 deletions compiler/passes/src/type_checking/check_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,18 @@ impl<'a, N: Network> ProgramVisitor<'a> for TypeChecker<'a, N> {

fn visit_function(&mut self, function: &'a Function) {
// Check that the function's annotations are valid.
// Note that Leo does not natively support any specific annotations.
let valid_annotations = [sym::should_fail, sym::native_test, sym::interpreted_test];
for annotation in function.annotations.iter() {
// TODO: Change to compiler warning.
self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span))
// All Leo annotations currently apply only to test code.
if !self.is_test || !valid_annotations.contains(&annotation.identifier.name) {
// TODO: Change to compiler warning.
self.emit_err(TypeCheckerError::unknown_annotation(annotation, annotation.span));
}
}

// `interpret` can only be used for tests.
if !self.is_test && function.variant == Variant::Interpret {
self.emit_err(TypeCheckerError::interpret_outside_test(function.span));
}

// Set type checker variables for function variant details.
Expand Down
8 changes: 7 additions & 1 deletion compiler/passes/src/type_checking/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub struct TypeChecker<'a, N: Network> {
pub(crate) async_function_input_types: IndexMap<Location, Vec<Type>>,
/// The set of used composites.
pub(crate) used_structs: IndexSet<Symbol>,
/// Are we compiling tests?
pub(crate) is_test: bool,
// Allows the type checker to be generic over the network.
phantom: PhantomData<N>,
}
Expand Down Expand Up @@ -109,6 +111,7 @@ impl<'a, N: Network> TypeChecker<'a, N> {
handler: &'a Handler,
max_depth: usize,
disabled: bool,
is_test: bool,
) -> Self {
let struct_names = symbol_table.structs.keys().map(|loc| loc.name).collect();
let function_names = symbol_table.functions.keys().map(|loc| loc.name).collect();
Expand All @@ -124,6 +127,7 @@ impl<'a, N: Network> TypeChecker<'a, N> {
await_checker: AwaitChecker::new(max_depth, !disabled),
async_function_input_types: IndexMap::new(),
used_structs: IndexSet::new(),
is_test,
phantom: Default::default(),
}
}
Expand Down Expand Up @@ -1375,7 +1379,9 @@ impl<'a, N: Network> TypeChecker<'a, N> {
// Check that the function context matches.
if self.scope_state.variant == Some(Variant::AsyncFunction) && !finalize_op {
self.handler.emit_err(TypeCheckerError::invalid_operation_inside_finalize(name, span))
} else if self.scope_state.variant != Some(Variant::AsyncFunction) && finalize_op {
} else if finalize_op
&& !matches!(self.scope_state.variant, Some(Variant::AsyncFunction) | Some(Variant::Interpret))
{
self.handler.emit_err(TypeCheckerError::invalid_operation_outside_finalize(name, span))
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/passes/src/type_checking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ use leo_errors::{Result, emitter::Handler};
use snarkvm::prelude::Network;

impl<'a, N: Network> Pass for TypeChecker<'a, N> {
type Input = (&'a Ast, &'a Handler, SymbolTable, &'a TypeTable, usize, bool);
type Input = (&'a Ast, &'a Handler, SymbolTable, &'a TypeTable, usize, bool, bool);
type Output = Result<(SymbolTable, StructGraph, CallGraph)>;

fn do_pass((ast, handler, st, tt, max_depth, await_checking): Self::Input) -> Self::Output {
let mut visitor = TypeChecker::<N>::new(st, tt, handler, max_depth, await_checking);
fn do_pass((ast, handler, st, tt, max_depth, await_checking, is_test): Self::Input) -> Self::Output {
let mut visitor = TypeChecker::<N>::new(st, tt, handler, max_depth, await_checking, is_test);
visitor.visit_program(ast.as_repr());
handler.last_err().map_err(|e| *e)?;

Expand Down
6 changes: 6 additions & 0 deletions compiler/span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ symbols! {
False: "false",
True: "true",

// annotations
should_fail,
native_test,
interpreted_test,

// general keywords
As: "as",
assert,
Expand All @@ -250,6 +255,7 @@ symbols! {
increment,
inline,
input,
interpret,
Let: "let",
leo,
main,
Expand Down
7 changes: 7 additions & 0 deletions errors/src/errors/type_checker/type_checker_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,4 +894,11 @@ create_messages!(
msg: format!("Cannot define a function with no parameters."),
help: None,
}

@formatted
interpret_outside_test {
args: (),
msg: "Cannot define an `interpret` function outside of tests.".to_string(),
help: None,
}
);

0 comments on commit 74bae04

Please sign in to comment.