use crate::{
error::CompileError,
metadata::MetadataManager,
parse_tree::Visibility,
semantic_analysis::{ast_node::*, namespace},
type_system::look_up_type_id,
};
use super::{
const_eval::{compile_const_decl, LookupEnv},
convert::convert_resolved_typeid,
function::FnCompiler,
};
use sway_ir::{metadata::combine as md_combine, *};
use sway_types::{span::Span, Spanned};
pub(super) fn compile_script(
context: &mut Context,
main_function: TypedFunctionDeclaration,
namespace: &namespace::Module,
declarations: Vec<TypedDeclaration>,
) -> Result<Module, CompileError> {
let module = Module::new(context, Kind::Script);
let mut md_mgr = MetadataManager::default();
compile_constants(context, &mut md_mgr, module, namespace)?;
compile_declarations(context, &mut md_mgr, module, namespace, declarations)?;
compile_function(context, &mut md_mgr, module, main_function)?;
Ok(module)
}
pub(super) fn compile_contract(
context: &mut Context,
abi_entries: Vec<TypedFunctionDeclaration>,
namespace: &namespace::Module,
declarations: Vec<TypedDeclaration>,
) -> Result<Module, CompileError> {
let module = Module::new(context, Kind::Contract);
let mut md_mgr = MetadataManager::default();
compile_constants(context, &mut md_mgr, module, namespace)?;
compile_declarations(context, &mut md_mgr, module, namespace, declarations)?;
for decl in abi_entries {
compile_abi_method(context, &mut md_mgr, module, decl)?;
}
Ok(module)
}
pub(crate) fn compile_constants(
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
module_ns: &namespace::Module,
) -> Result<(), CompileError> {
for decl_name in module_ns.get_all_declared_symbols() {
compile_const_decl(
&mut LookupEnv {
context,
md_mgr,
module,
module_ns: Some(module_ns),
lookup: compile_const_decl,
},
decl_name,
)?;
}
for submodule_ns in module_ns.submodules().values() {
compile_constants(context, md_mgr, module, submodule_ns)?;
}
Ok(())
}
fn compile_declarations(
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
namespace: &namespace::Module,
declarations: Vec<TypedDeclaration>,
) -> Result<(), CompileError> {
for declaration in declarations {
match declaration {
TypedDeclaration::ConstantDeclaration(decl) => {
compile_const_decl(
&mut LookupEnv {
context,
md_mgr,
module,
module_ns: Some(namespace),
lookup: compile_const_decl,
},
&decl.name,
)?;
}
TypedDeclaration::FunctionDeclaration(_decl) => {
}
TypedDeclaration::ImplTrait(_) => {
}
TypedDeclaration::StructDeclaration(_)
| TypedDeclaration::EnumDeclaration(_)
| TypedDeclaration::TraitDeclaration(_)
| TypedDeclaration::VariableDeclaration(_)
| TypedDeclaration::AbiDeclaration(_)
| TypedDeclaration::GenericTypeForFunctionScope { .. }
| TypedDeclaration::StorageDeclaration(_)
| TypedDeclaration::ErrorRecovery => (),
}
}
Ok(())
}
pub(super) fn compile_function(
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
ast_fn_decl: TypedFunctionDeclaration,
) -> Result<Option<Function>, CompileError> {
if !ast_fn_decl.type_parameters.is_empty() {
Ok(None)
} else {
let args = ast_fn_decl
.parameters
.iter()
.map(|param| convert_fn_param(context, param))
.collect::<Result<Vec<(String, Type, Span)>, CompileError>>()?;
compile_fn_with_args(context, md_mgr, module, ast_fn_decl, args, None).map(&Some)
}
}
fn convert_fn_param(
context: &mut Context,
param: &TypedFunctionParameter,
) -> Result<(String, Type, Span), CompileError> {
convert_resolved_typeid(context, ¶m.type_id, ¶m.type_span).map(|ty| {
(
param.name.as_str().into(),
if param.is_reference && look_up_type_id(param.type_id).is_copy_type() {
Type::Pointer(Pointer::new(context, ty, param.is_mutable, None))
} else {
ty
},
param.name.span(),
)
})
}
fn compile_fn_with_args(
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
ast_fn_decl: TypedFunctionDeclaration,
args: Vec<(String, Type, Span)>,
selector: Option<[u8; 4]>,
) -> Result<Function, CompileError> {
let TypedFunctionDeclaration {
name,
body,
return_type,
return_type_span,
visibility,
purity,
span,
..
} = ast_fn_decl;
let args = args
.into_iter()
.map(|(name, ty, span)| (name, ty, md_mgr.span_to_md(context, &span)))
.collect();
let ret_type = convert_resolved_typeid(context, &return_type, &return_type_span)?;
let span_md_idx = md_mgr.span_to_md(context, &span);
let storage_md_idx = md_mgr.purity_to_md(context, purity);
let metadata = md_combine(context, &span_md_idx, &storage_md_idx);
let func = Function::new(
context,
module,
name.as_str().to_owned(),
args,
ret_type,
selector,
visibility == Visibility::Public,
metadata,
);
let mut compiler = FnCompiler::new(context, module, func);
let mut ret_val = compiler.compile_code_block(context, md_mgr, body)?;
if ret_type.eq(context, &Type::Unit) && !matches!(ret_val.get_type(context), Some(Type::Unit)) {
ret_val = Constant::get_unit(context);
}
let already_returns = compiler.current_block.is_terminated_by_ret(context);
if !already_returns
&& (compiler.current_block.num_instructions(context) > 0
|| compiler.current_block == compiler.function.get_entry_block(context)
|| compiler.current_block.num_predecessors(context) > 0)
{
if ret_type.eq(context, &Type::Unit) {
ret_val = Constant::get_unit(context);
}
compiler.current_block.ins(context).ret(ret_val, ret_type);
}
Ok(func)
}
fn compile_abi_method(
context: &mut Context,
md_mgr: &mut MetadataManager,
module: Module,
ast_fn_decl: TypedFunctionDeclaration,
) -> Result<Function, CompileError> {
let get_selector_result = ast_fn_decl.to_fn_selector_value();
let mut warnings = Vec::new();
let mut errors = Vec::new();
let selector = match get_selector_result.ok(&mut warnings, &mut errors) {
Some(selector) => selector,
None => {
return if !errors.is_empty() {
Err(errors[0].clone())
} else {
Err(CompileError::InternalOwned(
format!(
"Cannot generate selector for ABI method: {}",
ast_fn_decl.name.as_str()
),
ast_fn_decl.name.span(),
))
};
}
};
let args = ast_fn_decl
.parameters
.iter()
.map(|param| {
convert_resolved_typeid(context, ¶m.type_id, ¶m.type_span)
.map(|ty| (param.name.as_str().into(), ty, param.name.span()))
})
.collect::<Result<Vec<(String, Type, Span)>, CompileError>>()?;
compile_fn_with_args(context, md_mgr, module, ast_fn_decl, args, Some(selector))
}