use super::node_dependencies;
use super::{TypedAstNode, TypedAstNodeContent, TypedDeclaration, TypedFunctionDeclaration};
use crate::build_config::BuildConfig;
use crate::control_flow_analysis::ControlFlowGraph;
use crate::ident::Ident;
use crate::parse_tree::Purity;
use crate::semantic_analysis::{ast_node::Mode, Namespace, TypeCheckArguments};
use crate::span::Span;
use crate::{error::*, type_engine::*};
use crate::{AstNode, ParseTree};
use std::collections::{HashMap, HashSet};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TreeType<'sc> {
Predicate,
Script,
Contract,
Library { name: Ident<'sc> },
}
#[derive(Debug, Clone)]
pub enum TypedParseTree<'sc> {
Script {
main_function: TypedFunctionDeclaration<'sc>,
namespace: Namespace<'sc>,
declarations: Vec<TypedDeclaration<'sc>>,
all_nodes: Vec<TypedAstNode<'sc>>,
},
Predicate {
main_function: TypedFunctionDeclaration<'sc>,
namespace: Namespace<'sc>,
declarations: Vec<TypedDeclaration<'sc>>,
all_nodes: Vec<TypedAstNode<'sc>>,
},
Contract {
abi_entries: Vec<TypedFunctionDeclaration<'sc>>,
namespace: Namespace<'sc>,
declarations: Vec<TypedDeclaration<'sc>>,
all_nodes: Vec<TypedAstNode<'sc>>,
},
Library {
namespace: Namespace<'sc>,
all_nodes: Vec<TypedAstNode<'sc>>,
},
}
impl<'sc> TypedParseTree<'sc> {
pub(crate) fn all_nodes(&self) -> &[TypedAstNode<'sc>] {
use TypedParseTree::*;
match self {
Library { all_nodes, .. } => all_nodes,
Script { all_nodes, .. } => all_nodes,
Contract { all_nodes, .. } => all_nodes,
Predicate { all_nodes, .. } => all_nodes,
}
}
pub fn into_namespace(self) -> Namespace<'sc> {
use TypedParseTree::*;
match self {
Library { namespace, .. } => namespace,
Script { namespace, .. } => namespace,
Contract { namespace, .. } => namespace,
Predicate { namespace, .. } => namespace,
}
}
pub(crate) fn type_check(
parsed: ParseTree<'sc>,
initial_namespace: Namespace<'sc>,
tree_type: &TreeType<'sc>,
build_config: &BuildConfig,
dead_code_graph: &mut ControlFlowGraph<'sc>,
dependency_graph: &mut HashMap<String, HashSet<String>>,
) -> CompileResult<'sc, Self> {
let mut new_namespace = initial_namespace.clone();
let mut warnings = Vec::new();
let mut errors = Vec::new();
let ordered_nodes = check!(
node_dependencies::order_ast_nodes_by_dependency(parsed.root_nodes),
return err(warnings, errors),
warnings,
errors
);
let typed_nodes = check!(
TypedParseTree::type_check_nodes(
ordered_nodes,
&mut new_namespace,
build_config,
dead_code_graph,
dependency_graph,
),
return err(warnings, errors),
warnings,
errors
);
TypedParseTree::validate_typed_nodes(
typed_nodes,
parsed.span,
new_namespace,
tree_type,
warnings,
errors,
)
}
fn type_check_nodes(
nodes: Vec<AstNode<'sc>>,
namespace: &mut Namespace<'sc>,
build_config: &BuildConfig,
dead_code_graph: &mut ControlFlowGraph<'sc>,
dependency_graph: &mut HashMap<String, HashSet<String>>,
) -> CompileResult<'sc, Vec<TypedAstNode<'sc>>> {
let mut warnings = Vec::new();
let mut errors = Vec::new();
let typed_nodes = nodes
.into_iter()
.map(|node| {
TypedAstNode::type_check(TypeCheckArguments {
checkee: node.clone(),
namespace,
crate_namespace: None,
return_type_annotation: insert_type(TypeInfo::Unknown),
help_text: "",
self_type: insert_type(TypeInfo::Contract),
build_config,
dead_code_graph,
dependency_graph,
mode: Mode::NonAbi,
opts: Default::default(),
})
})
.filter_map(|res| res.ok(&mut warnings, &mut errors))
.collect();
if !errors.is_empty() {
err(warnings, errors)
} else {
ok(typed_nodes, warnings, errors)
}
}
fn validate_typed_nodes(
typed_tree_nodes: Vec<TypedAstNode<'sc>>,
span: Span<'sc>,
namespace: Namespace<'sc>,
tree_type: &TreeType<'sc>,
warnings: Vec<CompileWarning<'sc>>,
mut errors: Vec<CompileError<'sc>>,
) -> CompileResult<'sc, Self> {
let all_nodes = typed_tree_nodes.clone();
let mut mains = Vec::new();
let mut declarations = Vec::new();
let mut abi_entries = Vec::new();
for node in typed_tree_nodes {
match node.content {
TypedAstNodeContent::Declaration(TypedDeclaration::FunctionDeclaration(func))
if func.name.primary_name == "main" =>
{
mains.push(func)
}
TypedAstNodeContent::Declaration(TypedDeclaration::ImplTrait {
methods,
type_implementing_for: TypeInfo::Contract,
..
}) => abi_entries.append(&mut methods.clone()),
TypedAstNodeContent::Declaration(decl) => declarations.push(decl),
_ => (),
};
}
if *tree_type != TreeType::Contract {
errors.append(&mut disallow_impure_functions(&declarations, &mains));
}
let typed_parse_tree = match tree_type {
TreeType::Predicate => {
if mains.is_empty() {
errors.push(CompileError::NoPredicateMainFunction(span));
return err(warnings, errors);
}
if mains.len() > 1 {
errors.push(CompileError::MultiplePredicateMainFunctions(
mains.last().unwrap().span.clone(),
));
}
let main_func = &mains[0];
match look_up_type_id(main_func.return_type) {
TypeInfo::Boolean => (),
_ => errors.push(CompileError::PredicateMainDoesNotReturnBool(
main_func.span.clone(),
)),
}
TypedParseTree::Predicate {
main_function: main_func.clone(),
all_nodes,
namespace,
declarations,
}
}
TreeType::Script => {
if mains.is_empty() {
errors.push(CompileError::NoScriptMainFunction(span));
return err(warnings, errors);
}
if mains.len() > 1 {
errors.push(CompileError::MultipleScriptMainFunctions(
mains.last().unwrap().span.clone(),
));
}
TypedParseTree::Script {
main_function: mains[0].clone(),
all_nodes,
namespace,
declarations,
}
}
TreeType::Library { .. } => TypedParseTree::Library {
all_nodes,
namespace,
},
TreeType::Contract => TypedParseTree::Contract {
abi_entries,
namespace,
declarations,
all_nodes,
},
};
ok(typed_parse_tree, warnings, errors)
}
}
fn disallow_impure_functions<'sc>(
declarations: &[TypedDeclaration<'sc>],
mains: &[TypedFunctionDeclaration<'sc>],
) -> Vec<CompileError<'sc>> {
let fn_decls = declarations
.iter()
.filter_map(|decl| match decl {
TypedDeclaration::FunctionDeclaration(decl) => Some(decl),
_ => None,
})
.chain(mains);
fn_decls
.filter_map(|TypedFunctionDeclaration { purity, name, .. }| {
if *purity == Purity::Impure {
Some(CompileError::ImpureInNonContract {
span: name.span.clone(),
})
} else {
None
}
})
.collect()
}