Skip to content

Commit

Permalink
Merge 1b48780 into 20b8ff0
Browse files Browse the repository at this point in the history
  • Loading branch information
esdrubal committed May 6, 2024
2 parents 20b8ff0 + 1b48780 commit 137c576
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 58 deletions.
41 changes: 41 additions & 0 deletions sway-core/src/language/ty/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,27 @@ pub struct TyFunctionSig {
pub parameters: Vec<TypeId>,
}

impl DisplayWithEngines for TyFunctionSig {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
write!(f, "{:?}", engines.help_out(self))
}
}

impl DebugWithEngines for TyFunctionSig {
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
write!(
f,
"fn({}) -> {}",
self.parameters
.iter()
.map(|p| format!("{}", engines.help_out(p)))
.collect::<Vec<_>>()
.join(", "),
engines.help_out(self.return_type),
)
}
}

impl TyFunctionSig {
pub fn from_fn_decl(fn_decl: &TyFunctionDecl) -> Self {
Self {
Expand All @@ -550,4 +571,24 @@ impl TyFunctionSig {
.collect::<Vec<_>>(),
}
}

pub fn is_concrete(&self, engines: &Engines) -> bool {
self.return_type.is_concrete(engines)
&& self.parameters.iter().all(|p| p.is_concrete(engines))
}

/// Returns a String representing the function.
/// When the function is monomorphized the returned String is unique.
/// Two monomorphized functions that generate the same String can be assumed to be the same.
pub fn get_type_str(&self, engines: &Engines) -> String {
format!(
"fn({}) -> {}",
self.parameters
.iter()
.map(|p| p.get_type_str(engines))
.collect::<Vec<_>>()
.join(", "),
self.return_type.get_type_str(engines),
)
}
}
39 changes: 38 additions & 1 deletion sway-core/src/query_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ use std::{collections::HashMap, sync::Arc};

use sway_error::error::CompileError;
use sway_error::warning::CompileWarning;
use sway_types::IdentUnique;

use crate::Programs;
use crate::decl_engine::{DeclId, DeclRef};
use crate::language::ty::{TyFunctionDecl, TyFunctionSig};
use crate::{Engines, Programs};

pub type ModulePath = Arc<PathBuf>;

Expand Down Expand Up @@ -46,11 +49,19 @@ pub struct ProgramsCacheEntry {

pub type ProgramsCacheMap = HashMap<ModulePath, ProgramsCacheEntry>;

#[derive(Clone, Debug)]
pub struct MethodCacheEntry {
pub fn_decl: DeclRef<DeclId<TyFunctionDecl>>,
}

pub type MethodsCacheMap = HashMap<(IdentUnique, String), MethodCacheEntry>;

#[derive(Debug, Default, Clone)]
pub struct QueryEngine {
// We want the below types wrapped in Arcs to optimize cloning from LSP.
parse_module_cache: Arc<RwLock<ModuleCacheMap>>,
programs_cache: Arc<RwLock<ProgramsCacheMap>>,
method_cache: Arc<RwLock<MethodsCacheMap>>,
}

impl QueryEngine {
Expand Down Expand Up @@ -79,4 +90,30 @@ impl QueryEngine {
let mut cache = self.programs_cache.write().unwrap();
cache.insert(entry.path.clone(), entry);
}

pub fn get_function(
&self,
engines: &Engines,
ident: IdentUnique,
sig: TyFunctionSig,
) -> Option<DeclRef<DeclId<TyFunctionDecl>>> {
let cache = self.method_cache.read().unwrap();
cache
.get(&(ident, sig.get_type_str(engines)))
.map(|s| s.fn_decl.clone())
}

pub fn insert_function(
&self,
engines: &Engines,
ident: IdentUnique,
sig: TyFunctionSig,
fn_decl: DeclRef<DeclId<TyFunctionDecl>>,
) {
let mut cache = self.method_cache.write().unwrap();
cache.insert(
(ident, sig.get_type_str(engines)),
MethodCacheEntry { fn_decl },
);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::{
decl_engine::{DeclEngineInsert, DeclRefFunction, ReplaceDecls},
language::{ty, *},
language::{
ty::{self, TyFunctionSig},
*,
},
semantic_analysis::{ast_node::*, TypeCheckContext},
};
use indexmap::IndexMap;
use sway_error::error::CompileError;
use sway_types::Spanned;
use sway_types::{IdentUnique, Spanned};

const UNIFY_ARGS_HELP_TEXT: &str =
"The argument that has been provided to this function's type does \
Expand All @@ -21,7 +24,8 @@ pub(crate) fn instantiate_function_application(
arguments: Option<&[Expression]>,
span: Span,
) -> Result<ty::TyExpression, ErrorEmitted> {
let decl_engine = ctx.engines.de();
let engines = ctx.engines();
let decl_engine = engines.de();

let mut function_decl = (*decl_engine.get_function(&function_decl_ref)).clone();

Expand Down Expand Up @@ -62,22 +66,53 @@ pub(crate) fn instantiate_function_application(
&function_decl.parameters,
)?;

// Handle the trait constraints. This includes checking to see if the trait
// constraints are satisfied and replacing old decl ids based on the
// constraint with new decl ids based on the new type.
let decl_mapping = TypeParameter::gather_decl_mapping_from_trait_constraints(
handler,
ctx.by_ref(),
&function_decl.type_parameters,
function_decl.name.as_str(),
&call_path_binding.span(),
)?;
let mut function_return_type_id = function_decl.return_type.type_id;

let function_ident: IdentUnique = function_decl.name.clone().into();
let function_sig = TyFunctionSig::from_fn_decl(&function_decl);

function_decl.replace_decls(&decl_mapping, handler, &mut ctx)?;
let return_type = function_decl.return_type.clone();
let new_decl_ref = decl_engine
.insert(function_decl)
.with_parent(decl_engine, (*function_decl_ref.id()).into());
let new_decl_ref = if let Some(cached_fn_ref) =
ctx.engines()
.qe()
.get_function(engines, function_ident.clone(), function_sig.clone())
{
cached_fn_ref
} else {
// Handle the trait constraints. This includes checking to see if the trait
// constraints are satisfied and replacing old decl ids based on the
// constraint with new decl ids based on the new type.
let decl_mapping = TypeParameter::gather_decl_mapping_from_trait_constraints(
handler,
ctx.by_ref(),
&function_decl.type_parameters,
function_decl.name.as_str(),
&call_path_binding.span(),
)?;

function_decl.replace_decls(&decl_mapping, handler, &mut ctx)?;

let are_type_parameters_concrete = function_decl
.type_parameters
.iter()
.all(|p| p.type_id.is_concrete(engines));
let method_sig = TyFunctionSig::from_fn_decl(&function_decl);

function_return_type_id = function_decl.return_type.type_id;
let new_decl_ref = decl_engine
.insert(function_decl)
.with_parent(decl_engine, (*function_decl_ref.id()).into());

if method_sig.is_concrete(engines) && are_type_parameters_concrete {
ctx.engines().qe().insert_function(
engines,
function_ident,
method_sig,
new_decl_ref.clone(),
);
}

new_decl_ref
};

let exp = ty::TyExpression {
expression: ty::TyExpressionVariant::FunctionApplication {
Expand All @@ -91,7 +126,7 @@ pub(crate) fn instantiate_function_application(
contract_call_params: IndexMap::new(),
contract_caller: None,
},
return_type: return_type.type_id,
return_type: function_return_type_id,
span,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
},
language::{
parsed::*,
ty::{self, TyDecl, TyExpression},
ty::{self, TyDecl, TyExpression, TyFunctionSig},
*,
},
namespace::TryInsertingTraitImplOnFailure,
Expand All @@ -20,7 +20,7 @@ use sway_error::{
error::CompileError,
handler::{ErrorEmitted, Handler},
};
use sway_types::{constants, integer_bits::IntegerBits, BaseIdent};
use sway_types::{constants, integer_bits::IntegerBits, BaseIdent, IdentUnique};
use sway_types::{constants::CONTRACT_CALL_COINS_PARAMETER_NAME, Spanned};
use sway_types::{Ident, Span};

Expand Down Expand Up @@ -83,12 +83,13 @@ pub(crate) fn type_check_method_application(
.collect(),
)?;

let fn_ref = monomorphize_method(
let mut fn_ref = monomorphize_method(
handler,
ctx.by_ref(),
original_decl_ref.clone(),
method_name_binding.type_arguments.to_vec_mut(),
)?;

let mut method = (*decl_engine.get_function(&fn_ref)).clone();

// unify method return type with current ctx.type_annotation().
Expand Down Expand Up @@ -620,47 +621,73 @@ pub(crate) fn type_check_method_application(
let arguments =
unify_arguments_and_parameters(handler, ctx.by_ref(), &arguments, &method.parameters)?;

// This handles the case of substituting the generic blanket type by call_path_typeid.
if let Some(TyDecl::ImplTrait(t)) = method.clone().implementing_type {
let t = &engines.de().get(&t.decl_id).implementing_for;
if let TypeInfo::Custom {
qualified_call_path,
type_arguments: _,
root_type_id: _,
} = &*type_engine.get(t.initial_type_id)
{
for p in method.type_parameters.clone() {
if p.name_ident.as_str() == qualified_call_path.call_path.suffix.as_str() {
let type_subst = TypeSubstMap::from_type_parameters_and_type_arguments(
vec![t.initial_type_id],
vec![call_path_typeid],
);
method.subst(&type_subst, engines);
let mut method_return_type_id = method.return_type.type_id;

let method_ident: IdentUnique = method.name.clone().into();
let method_sig = TyFunctionSig::from_fn_decl(&method);

if let Some(cached_fn_ref) =
ctx.engines()
.qe()
.get_function(engines, method_ident.clone(), method_sig.clone())
{
fn_ref = cached_fn_ref;
} else {
// This handles the case of substituting the generic blanket type by call_path_typeid.
if let Some(TyDecl::ImplTrait(t)) = method.clone().implementing_type {
let t = &engines.de().get(&t.decl_id).implementing_for;
if let TypeInfo::Custom {
qualified_call_path,
type_arguments: _,
root_type_id: _,
} = &*type_engine.get(t.initial_type_id)
{
for p in method.type_parameters.clone() {
if p.name_ident.as_str() == qualified_call_path.call_path.suffix.as_str() {
let type_subst = TypeSubstMap::from_type_parameters_and_type_arguments(
vec![t.initial_type_id],
vec![call_path_typeid],
);
method.subst(&type_subst, engines);
}
}
}
}
}

// Handle the trait constraints. This includes checking to see if the trait
// constraints are satisfied and replacing old decl ids based on the
// constraint with new decl ids based on the new type.
let decl_mapping = TypeParameter::gather_decl_mapping_from_trait_constraints(
handler,
ctx.by_ref(),
&method.type_parameters,
method.name.as_str(),
&call_path.span(),
)
.ok();

if let Some(decl_mapping) = decl_mapping {
if !ctx.defer_monomorphization() {
method.replace_decls(&decl_mapping, handler, &mut ctx)?;
// Handle the trait constraints. This includes checking to see if the trait
// constraints are satisfied and replacing old decl ids based on the
// constraint with new decl ids based on the new type.
let decl_mapping = TypeParameter::gather_decl_mapping_from_trait_constraints(
handler,
ctx.by_ref(),
&method.type_parameters,
method.name.as_str(),
&call_path.span(),
)
.ok();

if let Some(decl_mapping) = decl_mapping {
if !ctx.defer_monomorphization() {
method.replace_decls(&decl_mapping, handler, &mut ctx)?;
}
}
}

let method_return_type_id = method.return_type.type_id;
decl_engine.replace(*fn_ref.id(), method);
let are_type_parameters_concrete = method
.type_parameters
.iter()
.all(|p| p.type_id.is_concrete(engines));

let method_sig = TyFunctionSig::from_fn_decl(&method);

method_return_type_id = method.return_type.type_id;
decl_engine.replace(*fn_ref.id(), method.clone());

if method_sig.is_concrete(engines) && are_type_parameters_concrete {
ctx.engines()
.qe()
.insert_function(engines, method_ident, method_sig, fn_ref.clone());
}
}

let fn_app = ty::TyExpressionVariant::FunctionApplication {
call_path: call_path.clone(),
Expand Down
18 changes: 18 additions & 0 deletions sway-core/src/type_system/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,20 @@ impl TypeId {
}))
}

pub(crate) fn is_concrete(&self, engines: &Engines) -> bool {
let nested_types = (*self).extract_nested_types(engines);
!nested_types.into_iter().any(|x| {
matches!(
x,
TypeInfo::UnknownGeneric { .. }
| TypeInfo::Custom { .. }
| TypeInfo::Placeholder(..)
| TypeInfo::TraitType { .. }
| TypeInfo::TypeParam(..)
)
})
}

/// `check_type_parameter_bounds` does two types of checks. Lets use the example below for demonstrating the two checks:
/// ```ignore
/// enum MyEnum<T> where T: MyAdd {
Expand Down Expand Up @@ -655,4 +669,8 @@ impl TypeId {
}
found_error
}

pub fn get_type_str(&self, engines: &Engines) -> String {
engines.te().get(*self).get_type_str(engines)
}
}
Loading

0 comments on commit 137c576

Please sign in to comment.