use super::{
storage_only_types, TypedAstNode, TypedAstNodeContent, TypedDeclaration,
TypedFunctionDeclaration, TypedImplTrait, TypedStorageDeclaration,
};
use crate::{
error::*,
metadata::MetadataManager,
parse_tree::{ParseProgram, Purity, TreeType},
semantic_analysis::{
namespace::{self, Namespace},
TypeCheckContext, TypedModule,
},
type_system::*,
};
use fuel_tx::StorageSlot;
use sway_ir::{Context, Module};
use sway_types::{span::Span, Ident, JsonABIProgram, JsonTypeDeclaration, Spanned};
#[derive(Debug)]
pub struct TypedProgram {
pub kind: TypedProgramKind,
pub root: TypedModule,
pub storage_slots: Vec<StorageSlot>,
}
impl TypedProgram {
pub fn type_check(
parsed: &ParseProgram,
initial_namespace: namespace::Module,
) -> CompileResult<Self> {
let mut namespace = Namespace::init_root(initial_namespace);
let ctx = TypeCheckContext::from_root(&mut namespace);
let ParseProgram { root, kind } = parsed;
let mod_span = root.tree.span.clone();
let mod_res = TypedModule::type_check(ctx, root);
mod_res.flat_map(|root| {
let kind_res = Self::validate_root(&root, kind.clone(), mod_span);
kind_res.map(|kind| Self {
kind,
root,
storage_slots: vec![],
})
})
}
pub fn validate_root(
root: &TypedModule,
kind: TreeType,
module_span: Span,
) -> CompileResult<TypedProgramKind> {
let mut errors = vec![];
let mut warnings = vec![];
for (_, submodule) in &root.submodules {
check!(
Self::validate_root(
&submodule.module,
TreeType::Library {
name: submodule.library_name.clone(),
},
submodule.library_name.span().clone(),
),
continue,
warnings,
errors
);
}
let mut mains = Vec::new();
let mut declarations = Vec::new();
let mut abi_entries = Vec::new();
let mut fn_declarations = std::collections::HashSet::new();
for node in &root.all_nodes {
match &node.content {
TypedAstNodeContent::Declaration(TypedDeclaration::FunctionDeclaration(func))
if func.name.as_str() == "main" =>
{
mains.push(func.clone())
}
TypedAstNodeContent::Declaration(TypedDeclaration::ImplTrait(TypedImplTrait {
methods,
implementing_for_type_id,
..
})) if matches!(
look_up_type_id(*implementing_for_type_id),
TypeInfo::Contract
) =>
{
abi_entries.extend(methods.clone())
}
TypedAstNodeContent::Declaration(decl) => {
if let TypedDeclaration::FunctionDeclaration(func) = &decl {
let name = func.name.clone();
if !fn_declarations.insert(name.clone()) {
errors.push(CompileError::MultipleDefinitionsOfFunction { name });
}
}
declarations.push(decl.clone())
}
_ => (),
};
}
for ast_n in &root.all_nodes {
check!(
storage_only_types::validate_decls_for_storage_only_types_in_ast(&ast_n.content),
continue,
warnings,
errors
);
}
if kind != TreeType::Contract {
if !matches!(kind, TreeType::Library { .. }) {
errors.extend(disallow_impure_functions(&declarations, &mains));
}
let storage_decl = declarations
.iter()
.find(|decl| matches!(decl, TypedDeclaration::StorageDeclaration(_)));
if let Some(TypedDeclaration::StorageDeclaration(TypedStorageDeclaration {
span,
..
})) = storage_decl
{
errors.push(CompileError::StorageDeclarationInNonContract {
program_kind: format!("{kind}"),
span: span.clone(),
});
}
}
let typed_program_kind = match kind {
TreeType::Contract => TypedProgramKind::Contract {
abi_entries,
declarations,
},
TreeType::Library { name } => TypedProgramKind::Library { name },
TreeType::Predicate => {
if mains.is_empty() {
errors.push(CompileError::NoPredicateMainFunction(module_span));
return err(vec![], errors);
}
if mains.len() > 1 {
errors.push(CompileError::MultipleDefinitionsOfFunction {
name: mains.last().unwrap().name.clone(),
});
}
let main_func = mains.remove(0);
match look_up_type_id(main_func.return_type) {
TypeInfo::Boolean => (),
_ => errors.push(CompileError::PredicateMainDoesNotReturnBool(
main_func.span.clone(),
)),
}
TypedProgramKind::Predicate {
main_function: main_func,
declarations,
}
}
TreeType::Script => {
if mains.is_empty() {
errors.push(CompileError::NoScriptMainFunction(module_span));
return err(vec![], errors);
}
if mains.len() > 1 {
errors.push(CompileError::MultipleDefinitionsOfFunction {
name: mains.last().unwrap().name.clone(),
});
}
TypedProgramKind::Script {
main_function: mains.remove(0),
declarations,
}
}
};
match &typed_program_kind {
TypedProgramKind::Script { main_function, .. }
| TypedProgramKind::Predicate { main_function, .. } => {
if !main_function.parameters.is_empty() {
errors.push(CompileError::MainArgsNotYetSupported {
span: main_function.span.clone(),
})
}
}
_ => (),
}
ok(typed_program_kind, warnings, errors)
}
pub(crate) fn finalize_types(&self) -> CompileResult<()> {
let errors: Vec<_> = match &self.kind {
TypedProgramKind::Library { .. } => self
.root
.all_nodes
.iter()
.filter(|x| x.is_public())
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
TypedProgramKind::Script { .. } => self
.root
.all_nodes
.iter()
.filter(|x| x.is_main_function(TreeType::Script))
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
TypedProgramKind::Predicate { .. } => self
.root
.all_nodes
.iter()
.filter(|x| x.is_main_function(TreeType::Predicate))
.flat_map(UnresolvedTypeCheck::check_for_unresolved_types)
.collect(),
TypedProgramKind::Contract { abi_entries, .. } => abi_entries
.iter()
.map(TypedAstNode::from)
.flat_map(|x| x.check_for_unresolved_types())
.collect(),
};
if errors.is_empty() {
ok((), vec![], errors)
} else {
err(vec![], errors)
}
}
pub(crate) fn get_typed_program_with_initialized_storage_slots(
&self,
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
) -> CompileResult<Self> {
let mut warnings = vec![];
let mut errors = vec![];
match &self.kind {
TypedProgramKind::Contract { declarations, .. } => {
let storage_decl = declarations
.iter()
.find(|decl| matches!(decl, TypedDeclaration::StorageDeclaration(_)));
match storage_decl {
Some(TypedDeclaration::StorageDeclaration(decl)) => {
let mut storage_slots = check!(
decl.get_initialized_storage_slots(context, md_mgr, module),
return err(warnings, errors),
warnings,
errors,
);
storage_slots.sort();
ok(
Self {
kind: self.kind.clone(),
root: self.root.clone(),
storage_slots,
},
warnings,
errors,
)
}
_ => ok(
Self {
kind: self.kind.clone(),
root: self.root.clone(),
storage_slots: vec![],
},
warnings,
errors,
),
}
}
_ => ok(
Self {
kind: self.kind.clone(),
root: self.root.clone(),
storage_slots: vec![],
},
warnings,
errors,
),
}
}
}
#[derive(Clone, Debug)]
pub enum TypedProgramKind {
Contract {
abi_entries: Vec<TypedFunctionDeclaration>,
declarations: Vec<TypedDeclaration>,
},
Library {
name: Ident,
},
Predicate {
main_function: TypedFunctionDeclaration,
declarations: Vec<TypedDeclaration>,
},
Script {
main_function: TypedFunctionDeclaration,
declarations: Vec<TypedDeclaration>,
},
}
impl TypedProgramKind {
pub fn tree_type(&self) -> TreeType {
match self {
TypedProgramKind::Contract { .. } => TreeType::Contract,
TypedProgramKind::Library { name } => TreeType::Library { name: name.clone() },
TypedProgramKind::Predicate { .. } => TreeType::Predicate,
TypedProgramKind::Script { .. } => TreeType::Script,
}
}
pub fn generate_json_abi_program(
&self,
types: &mut Vec<JsonTypeDeclaration>,
) -> JsonABIProgram {
match self {
TypedProgramKind::Contract { abi_entries, .. } => {
let result = abi_entries
.iter()
.map(|x| x.generate_json_abi_function(types))
.collect();
JsonABIProgram {
types: types.to_vec(),
functions: result,
}
}
TypedProgramKind::Script { main_function, .. } => {
let result = vec![main_function.generate_json_abi_function(types)];
JsonABIProgram {
types: types.to_vec(),
functions: result,
}
}
_ => JsonABIProgram {
types: vec![],
functions: vec![],
},
}
}
}
fn disallow_impure_functions(
declarations: &[TypedDeclaration],
mains: &[TypedFunctionDeclaration],
) -> Vec<CompileError> {
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::Pure {
Some(CompileError::ImpureInNonContract { span: name.span() })
} else {
None
}
})
.collect()
}