Skip to content

Commit

Permalink
Implement associated consts for ABIs (FuelLabs#4425)
Browse files Browse the repository at this point in the history
## Description

This PR adds support for associated consts to ABIs.

It extends ABIs code to be able to insert its interface surface in the
trait map, which is later used to support looking up consts.

It then changes `impl trait` semantic analysis to insert the interface
surface of its implemented `trait`/ `abi` so the interface surface
constant/methods can be self-referenced.

Additionally the `Self` type of `Contract` inside a `Contract` impl is
now the actual `ABI` type instead of just being hardcoded to `Contract`.

Finally it fixes IR gen to support non-global consts so that we are able
to generate correct code for consts.

Closes FuelLabs#4290.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

Co-authored-by: Joshua Batty <[email protected]>
Co-authored-by: Sophie Dankel <[email protected]>
  • Loading branch information
3 people authored Apr 20, 2023
1 parent 3ffd038 commit e77749e
Show file tree
Hide file tree
Showing 42 changed files with 442 additions and 54 deletions.
2 changes: 1 addition & 1 deletion sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ fn connect_abi_declaration(
}
}
}
ty::TyTraitInterfaceItem::Constant(_) => todo!(),
ty::TyTraitInterfaceItem::Constant(_const_decl) => {}
}
}

Expand Down
8 changes: 6 additions & 2 deletions sway-core/src/ir_generation/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ pub(crate) fn compile_constants(
if let Some(ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. })) =
module_ns.symbols.get(decl_name)
{
let ty::TyConstantDecl { call_path, .. } = engines.de().get_constant(decl_id);
let const_decl = engines.de().get_constant(decl_id);
let call_path = const_decl.call_path.clone();
compile_const_decl(
&mut LookupEnv {
type_engine,
Expand All @@ -230,6 +231,7 @@ pub(crate) fn compile_constants(
lookup: compile_const_decl,
},
&call_path,
&Some(const_decl),
)?;
}
}
Expand Down Expand Up @@ -263,6 +265,7 @@ fn compile_declarations(
match declaration {
ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. }) => {
let decl = decl_engine.get_constant(decl_id);
let call_path = decl.call_path.clone();
compile_const_decl(
&mut LookupEnv {
type_engine,
Expand All @@ -274,7 +277,8 @@ fn compile_declarations(
function_compiler: None,
lookup: compile_const_decl,
},
&decl.call_path,
&call_path,
&Some(decl),
)?;
}

Expand Down
94 changes: 56 additions & 38 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
decl_engine::DeclEngine,
engine_threading::*,
language::{
ty::{self, TyIntrinsicFunctionKind},
ty::{self, TyConstantDecl, TyIntrinsicFunctionKind},
CallPath,
},
metadata::MetadataManager,
Expand Down Expand Up @@ -39,12 +39,18 @@ pub(crate) struct LookupEnv<'a> {
pub(crate) module: Module,
pub(crate) module_ns: Option<&'a namespace::Module>,
pub(crate) function_compiler: Option<&'a FnCompiler<'a>>,
pub(crate) lookup: fn(&mut LookupEnv, &CallPath) -> Result<Option<Value>, CompileError>,
#[allow(clippy::type_complexity)]
pub(crate) lookup: fn(
&mut LookupEnv,
&CallPath,
&Option<TyConstantDecl>,
) -> Result<Option<Value>, CompileError>,
}

pub(crate) fn compile_const_decl(
env: &mut LookupEnv,
call_path: &CallPath,
const_decl: &Option<TyConstantDecl>,
) -> Result<Option<Value>, CompileError> {
// Check if it's a processed local constant.
if let Some(fn_compiler) = env.function_compiler {
Expand Down Expand Up @@ -103,47 +109,59 @@ pub(crate) fn compile_const_decl(
(_, Some(config_val), _) => Ok(Some(config_val)),
(None, None, Some(module_ns)) => {
// See if we it's a global const and whether we can compile it *now*.
let decl = module_ns.check_symbol(&call_path.suffix)?;
let decl_name_value = match decl {
ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. }) => {
let decl = module_ns.check_symbol(&call_path.suffix);
let const_decl = match const_decl {
Some(decl) => Some(decl),
None => None,
};
let const_decl = match decl {
Ok(decl) => match decl {
ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. }) => {
Some(env.decl_engine.get_constant(decl_id))
}
_otherwise => const_decl.cloned(),
},
Err(_) => const_decl.cloned(),
};
match const_decl {
Some(const_decl) => {
let ty::TyConstantDecl {
call_path,
value,
is_configurable,
..
} = env.decl_engine.get_constant(decl_id);
Some((call_path, value, is_configurable))
}
_otherwise => None,
};
if let Some((call_path, Some(value), is_configurable)) = decl_name_value {
let const_val = compile_constant_expression(
Engines::new(env.type_engine, env.decl_engine),
env.context,
env.md_mgr,
env.module,
env.module_ns,
env.function_compiler,
&call_path,
&value,
is_configurable,
)?;
if !is_configurable {
env.module.add_global_constant(
env.context,
call_path.as_vec_string().to_vec(),
const_val,
);
} else {
env.module.add_global_configurable(
} = const_decl;
if value.is_none() {
return Ok(None);
}

let const_val = compile_constant_expression(
Engines::new(env.type_engine, env.decl_engine),
env.context,
call_path.as_vec_string().to_vec(),
const_val,
);
env.md_mgr,
env.module,
env.module_ns,
env.function_compiler,
&call_path,
&value.unwrap(),
is_configurable,
)?;
if !is_configurable {
env.module.add_global_constant(
env.context,
call_path.as_vec_string().to_vec(),
const_val,
);
} else {
env.module.add_global_configurable(
env.context,
call_path.as_vec_string().to_vec(),
const_val,
);
}
Ok(Some(const_val))
}
Ok(Some(const_val))
} else {
Ok(None)
None => Ok(None),
}
}
_ => Ok(None),
Expand Down Expand Up @@ -277,7 +295,7 @@ fn const_eval_typed_expr(
Some(cvs) => Some(cvs.clone()),
None => {
// 2. Check if name is a global constant.
(lookup.lookup)(lookup, call_path)
(lookup.lookup)(lookup, call_path, &Some(*const_decl.clone()))
.ok()
.flatten()
.and_then(|v| v.get_constant_or_configurable(lookup.context).cloned())
Expand All @@ -295,7 +313,7 @@ fn const_eval_typed_expr(
None => CallPath::from(name.clone()),
};
// 2. Check if name is a global constant.
(lookup.lookup)(lookup, &call_path)
(lookup.lookup)(lookup, &call_path, &None)
.ok()
.flatten()
.and_then(|v| v.get_constant(lookup.context).cloned())
Expand Down
126 changes: 114 additions & 12 deletions sway-core/src/semantic_analysis/ast_node/declaration/abi.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use std::collections::HashSet;

use sway_error::error::CompileError;
use sway_types::Spanned;
use sway_types::{Ident, Spanned};

use crate::{
decl_engine::DeclEngineInsert,
error::*,
language::{
parsed::*,
ty::{self, TyTraitItem},
ty::{self, TyImplItem, TyTraitItem},
},
semantic_analysis::{declaration::insert_supertraits_into_namespace, Mode, TypeCheckContext},
CompileResult,
CompileResult, ReplaceSelfType, TypeId, TypeInfo,
};

impl ty::TyAbiDecl {
Expand Down Expand Up @@ -37,23 +39,29 @@ impl ty::TyAbiDecl {
// A temporary namespace for checking within this scope.
let type_engine = ctx.type_engine;
let decl_engine = ctx.decl_engine;
let contract_type = type_engine.insert(decl_engine, crate::TypeInfo::Contract);
let mut abi_namespace = ctx.namespace.clone();
let mut ctx = ctx.scoped(&mut abi_namespace).with_mode(Mode::ImplAbiFn);
let self_type = type_engine.insert(decl_engine, TypeInfo::SelfType);
let mut ctx = ctx
.scoped(&mut abi_namespace)
.with_mode(Mode::ImplAbiFn)
.with_self_type(self_type);

// Recursively make the interface surfaces and methods of the
// supertraits available to this abi.
check!(
insert_supertraits_into_namespace(ctx.by_ref(), contract_type, &supertraits),
insert_supertraits_into_namespace(ctx.by_ref(), self_type, &supertraits),
return err(warnings, errors),
warnings,
errors
);

// Type check the interface surface.
let mut new_interface_surface = vec![];

let mut ids: HashSet<Ident> = HashSet::default();

for item in interface_surface.into_iter() {
match item {
let decl_name = match item {
TraitItem::TraitFn(method) => {
let method = check!(
ty::TyTraitFn::type_check(ctx.by_ref(), method),
Expand All @@ -70,17 +78,49 @@ impl ty::TyAbiDecl {
}
}
new_interface_surface.push(ty::TyTraitInterfaceItem::TraitFn(
ctx.decl_engine.insert(method),
ctx.decl_engine.insert(method.clone()),
));
method.name.clone()
}
TraitItem::Constant(_) => {
// not implemented for now since abis and traits will be unified soon
todo!();
TraitItem::Constant(const_decl) => {
let const_decl = check!(
ty::TyConstantDecl::type_check(ctx.by_ref(), const_decl.clone(),),
return err(warnings, errors),
warnings,
errors
);
let decl_ref = ctx.decl_engine.insert(const_decl.clone());
new_interface_surface
.push(ty::TyTraitInterfaceItem::Constant(decl_ref.clone()));

let const_name = const_decl.call_path.suffix.clone();
check!(
ctx.namespace.insert_symbol(
const_name.clone(),
ty::TyDecl::ConstantDecl(ty::ConstantDecl {
name: const_name.clone(),
decl_id: *decl_ref.id(),
decl_span: const_decl.span.clone()
})
),
return err(warnings, errors),
warnings,
errors
);

const_name
}
};

if !ids.insert(decl_name.clone()) {
errors.push(CompileError::MultipleDefinitionsOfName {
name: decl_name.clone(),
span: decl_name.span(),
})
}
}

// Type check the methods.
// Type check the items.
let mut new_items = vec![];
for method in methods.into_iter() {
let method = check!(
Expand Down Expand Up @@ -113,4 +153,66 @@ impl ty::TyAbiDecl {
};
ok(abi_decl, warnings, errors)
}

pub(crate) fn insert_interface_surface_and_items_into_namespace(
&self,
ctx: TypeCheckContext,
type_id: TypeId,
) {
let decl_engine = ctx.decl_engine;
let engines = ctx.engines();

let ty::TyAbiDecl {
interface_surface,
items,
..
} = self;

let mut all_items = vec![];

for item in interface_surface.iter() {
match item {
ty::TyTraitInterfaceItem::TraitFn(decl_ref) => {
let mut method = decl_engine.get_trait_fn(decl_ref);
method.replace_self_type(engines, type_id);
all_items.push(TyImplItem::Fn(
ctx.decl_engine
.insert(method.to_dummy_func(Mode::ImplAbiFn))
.with_parent(ctx.decl_engine, (*decl_ref.id()).into()),
));
}
ty::TyTraitInterfaceItem::Constant(decl_ref) => {
let const_decl = decl_engine.get_constant(decl_ref);
let const_name = const_decl.call_path.suffix.clone();
all_items.push(TyImplItem::Constant(decl_ref.clone()));
ctx.namespace.insert_symbol(
const_name.clone(),
ty::TyDecl::ConstantDecl(ty::ConstantDecl {
name: const_name,
decl_id: *decl_ref.id(),
decl_span: const_decl.span.clone(),
}),
);
}
}
}
for item in items.iter() {
match item {
ty::TyTraitItem::Fn(decl_ref) => {
let mut method = decl_engine.get_function(decl_ref);
method.replace_self_type(engines, type_id);
all_items.push(TyImplItem::Fn(
ctx.decl_engine
.insert(method)
.with_parent(ctx.decl_engine, (*decl_ref.id()).into()),
));
}
ty::TyTraitItem::Constant(decl_ref) => {
let mut const_decl = decl_engine.get_constant(decl_ref);
const_decl.replace_self_type(engines, type_id);
all_items.push(TyImplItem::Constant(ctx.decl_engine.insert(const_decl)));
}
}
}
}
}
16 changes: 16 additions & 0 deletions sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ impl ty::TyImplTrait {
errors
);

// Insert the interface surface and methods from this trait into
// the namespace.
trait_decl.insert_interface_surface_and_items_into_namespace(
ctx.by_ref(),
&trait_name,
&trait_type_arguments,
implementing_for.type_id,
);

let new_items = check!(
type_check_trait_implementation(
ctx.by_ref(),
Expand Down Expand Up @@ -173,6 +182,13 @@ impl ty::TyImplTrait {

let mut ctx = ctx.with_mode(Mode::ImplAbiFn);

// Insert the interface surface and methods from this trait into
// the namespace.
abi.insert_interface_surface_and_items_into_namespace(
ctx.by_ref(),
implementing_for.type_id,
);

let new_items = check!(
type_check_trait_implementation(
ctx.by_ref(),
Expand Down
Loading

0 comments on commit e77749e

Please sign in to comment.