Skip to content

Commit

Permalink
impl trait for "const generics" (#6946)
Browse files Browse the repository at this point in the history
## Description

This PR is part of #6860.
It implements "impl traits" for "const generics", which means syntax
such as:

```sway
trait A {
    fn my_len(self) -> u64;
}

impl<T, const N: u64> A for [T; N] {
    fn my_len(self) -> u64 {
        N
    }
}
```

This also opens the space for simplifying our current implementation of
`AbiEncode` for arrays. Today we implement for each size, which limits
us to a specific size. With this one can have just one "impl item", and
support arrays of arbitrary size.

Both implementations will coexist in `codec.sw` until we stabilize
"const generics".

```sway
#[cfg(experimental_const_generics = true)]
impl<T, const N: u64> AbiEncode for [T; N] 
where
    T: AbiEncode,
{
    fn abi_encode(self, buffer: Buffer) -> Buffer {
        ....
    }
}

#[cfg(experimental_const_generics = false)]
impl<T> AbiEncode for [T; 0]
where
    T: AbiEncode,
{
    fn abi_encode(self, buffer: Buffer) -> Buffer {
        ...
    }
}
```


## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [ ] 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.
  • Loading branch information
xunilrj authored Mar 7, 2025
1 parent 8a3d8df commit 13542d2
Show file tree
Hide file tree
Showing 64 changed files with 1,125 additions and 159 deletions.
3 changes: 3 additions & 0 deletions sway-core/src/control_flow_analysis/analyze_return_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ fn connect_declaration<'eng: 'cfg, 'cfg>(
}
Ok(Some(entry_node))
}
ty::TyDecl::ConstGenericDecl(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
ty::TyDecl::FunctionDecl(ty::FunctionDecl { decl_id, .. }) => {
let fn_decl = decl_engine.get_function(decl_id);
let entry_node = graph.add_node(ControlFlowGraphNode::from_node(node));
Expand Down
17 changes: 17 additions & 0 deletions sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,9 @@ fn connect_declaration<'eng: 'cfg, 'cfg>(
Ok(leaves.to_vec())
}
}
ty::TyDecl::ConstGenericDecl(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860");
}
ty::TyDecl::FunctionDecl(ty::FunctionDecl { decl_id, .. }) => {
let fn_decl = decl_engine.get_function(decl_id);
connect_typed_fn_decl(
Expand Down Expand Up @@ -1514,6 +1517,17 @@ fn connect_expression<'eng: 'cfg, 'cfg>(

Ok(vec![node])
}
ConstGenericExpression { decl, .. } => {
let Some(node) = graph.namespace.get_const_generic(decl).cloned() else {
return Ok(leaves.to_vec());
};

for leaf in leaves {
graph.add_edge(*leaf, node, "".into());
}

Ok(vec![node])
}
EnumInstantiation {
enum_ref,
variant_name,
Expand Down Expand Up @@ -2552,6 +2566,9 @@ fn allow_dead_code_ast_node(decl_engine: &DeclEngine, node: &ty::TyAstNode) -> b
ty::TyDecl::ConfigurableDecl(ty::ConfigurableDecl { decl_id, .. }) => {
allow_dead_code(decl_engine.get_configurable(decl_id).attributes.clone())
}
ty::TyDecl::ConstGenericDecl(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
ty::TyDecl::TraitTypeDecl(ty::TraitTypeDecl { decl_id, .. }) => {
allow_dead_code(decl_engine.get_type(decl_id).attributes.clone())
}
Expand Down
10 changes: 9 additions & 1 deletion sway-core/src/control_flow_analysis/flow_graph/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use super::{EntryPoint, ExitPoint};
use crate::{
language::{
parsed::TreeType,
ty::{self, TyConfigurableDecl, TyConstantDecl, TyFunctionDecl, TyFunctionSig},
ty::{
self, TyConfigurableDecl, TyConstGenericDecl, TyConstantDecl, TyFunctionDecl,
TyFunctionSig,
},
CallPath,
},
type_system::TypeInfo,
Expand Down Expand Up @@ -58,6 +61,7 @@ pub struct ControlFlowNamespace {
pub(crate) struct_namespace: HashMap<String, StructNamespaceEntry>,
pub(crate) const_namespace: HashMap<Ident, NodeIndex>,
pub(crate) configurable_namespace: HashMap<Ident, NodeIndex>,
pub(crate) const_generic_namespace: HashMap<Ident, NodeIndex>,
pub(crate) storage: HashMap<Ident, NodeIndex>,
pub(crate) code_blocks: Vec<ControlFlowCodeBlock>,
pub(crate) alias: HashMap<IdentUnique, NodeIndex>,
Expand Down Expand Up @@ -93,6 +97,10 @@ impl ControlFlowNamespace {
self.configurable_namespace.get(&decl.name().clone())
}

pub(crate) fn get_const_generic(&self, decl: &TyConstGenericDecl) -> Option<&NodeIndex> {
self.const_generic_namespace.get(&decl.name().clone())
}

#[allow(dead_code)]
pub(crate) fn insert_constant(
&mut self,
Expand Down
35 changes: 30 additions & 5 deletions sway-core/src/decl_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use crate::{
engine_threading::*,
language::{
parsed::{
AbiDeclaration, ConfigurableDeclaration, ConstantDeclaration, Declaration,
EnumDeclaration, FunctionDeclaration, ImplSelfOrTrait, StorageDeclaration,
AbiDeclaration, ConfigurableDeclaration, ConstGenericDeclaration, ConstantDeclaration,
Declaration, EnumDeclaration, FunctionDeclaration, ImplSelfOrTrait, StorageDeclaration,
StructDeclaration, TraitDeclaration, TraitFn, TraitTypeDeclaration,
TypeAliasDeclaration,
},
ty::{
self, TyAbiDecl, TyConfigurableDecl, TyConstantDecl, TyDeclParsedType, TyEnumDecl,
TyFunctionDecl, TyImplSelfOrTrait, TyStorageDecl, TyStructDecl, TyTraitDecl, TyTraitFn,
TyTraitType, TyTypeAliasDecl,
self, TyAbiDecl, TyConfigurableDecl, TyConstGenericDecl, TyConstantDecl,
TyDeclParsedType, TyEnumDecl, TyFunctionDecl, TyImplSelfOrTrait, TyStorageDecl,
TyStructDecl, TyTraitDecl, TyTraitFn, TyTraitType, TyTypeAliasDecl,
},
},
};
Expand All @@ -39,6 +39,7 @@ pub struct DeclEngine {
abi_slab: ConcurrentSlab<TyAbiDecl>,
constant_slab: ConcurrentSlab<TyConstantDecl>,
configurable_slab: ConcurrentSlab<TyConfigurableDecl>,
const_generics_slab: ConcurrentSlab<TyConstGenericDecl>,
enum_slab: ConcurrentSlab<TyEnumDecl>,
type_alias_slab: ConcurrentSlab<TyTypeAliasDecl>,

Expand All @@ -59,6 +60,8 @@ pub struct DeclEngine {
RwLock<HashMap<DeclId<TyConstantDecl>, ParsedDeclId<ConstantDeclaration>>>,
configurable_parsed_decl_id_map:
RwLock<HashMap<DeclId<TyConfigurableDecl>, ParsedDeclId<ConfigurableDeclaration>>>,
const_generics_parsed_decl_id_map:
RwLock<HashMap<DeclId<TyConstGenericDecl>, ParsedDeclId<ConstGenericDeclaration>>>,
enum_parsed_decl_id_map: RwLock<HashMap<DeclId<TyEnumDecl>, ParsedDeclId<EnumDeclaration>>>,
type_alias_parsed_decl_id_map:
RwLock<HashMap<DeclId<TyTypeAliasDecl>, ParsedDeclId<TypeAliasDeclaration>>>,
Expand All @@ -79,6 +82,7 @@ impl Clone for DeclEngine {
abi_slab: self.abi_slab.clone(),
constant_slab: self.constant_slab.clone(),
configurable_slab: self.configurable_slab.clone(),
const_generics_slab: self.const_generics_slab.clone(),
enum_slab: self.enum_slab.clone(),
type_alias_slab: self.type_alias_slab.clone(),
function_parsed_decl_id_map: RwLock::new(
Expand All @@ -103,6 +107,9 @@ impl Clone for DeclEngine {
configurable_parsed_decl_id_map: RwLock::new(
self.configurable_parsed_decl_id_map.read().clone(),
),
const_generics_parsed_decl_id_map: RwLock::new(
self.const_generics_parsed_decl_id_map.read().clone(),
),
enum_parsed_decl_id_map: RwLock::new(self.enum_parsed_decl_id_map.read().clone()),
type_alias_parsed_decl_id_map: RwLock::new(
self.type_alias_parsed_decl_id_map.read().clone(),
Expand Down Expand Up @@ -187,6 +194,7 @@ decl_engine_get!(storage_slab, ty::TyStorageDecl);
decl_engine_get!(abi_slab, ty::TyAbiDecl);
decl_engine_get!(constant_slab, ty::TyConstantDecl);
decl_engine_get!(configurable_slab, ty::TyConfigurableDecl);
decl_engine_get!(const_generics_slab, ty::TyConstGenericDecl);
decl_engine_get!(enum_slab, ty::TyEnumDecl);
decl_engine_get!(type_alias_slab, ty::TyTypeAliasDecl);

Expand Down Expand Up @@ -258,6 +266,11 @@ decl_engine_insert!(
configurable_parsed_decl_id_map,
ty::TyConfigurableDecl
);
decl_engine_insert!(
const_generics_slab,
const_generics_parsed_decl_id_map,
ty::TyConstGenericDecl
);
decl_engine_insert!(enum_slab, enum_parsed_decl_id_map, ty::TyEnumDecl);
decl_engine_insert!(
type_alias_slab,
Expand Down Expand Up @@ -695,6 +708,18 @@ impl DeclEngine {
self.get(index)
}

/// Friendly helper method for calling the `get` method from the
/// implementation of [DeclEngineGet] for [DeclEngine]
///
/// Calling [DeclEngine][get] directly is equivalent to this method, but
/// this method adds additional syntax that some users may find helpful.
pub fn get_const_generic<I>(&self, index: &I) -> Arc<ty::TyConstGenericDecl>
where
DeclEngine: DeclEngineGet<I, ty::TyConstGenericDecl>,
{
self.get(index)
}

/// Friendly helper method for calling the `get` method from the
/// implementation of [DeclEngineGet] for [DeclEngine]
///
Expand Down
12 changes: 12 additions & 0 deletions sway-core/src/decl_engine/parsed_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@ impl ParsedDeclEngine {
self.get(index)
}

/// Friendly helper method for calling the `get` method from the
/// implementation of [ParsedDeclEngineGet] for [ParsedDeclEngine]
///
/// Calling [ParsedDeclEngine][get] directly is equivalent to this method, but
/// this method adds additional syntax that some users may find helpful.
pub fn get_const_generic<I>(&self, index: &I) -> Arc<ConstGenericDeclaration>
where
ParsedDeclEngine: ParsedDeclEngineGet<I, ConstGenericDeclaration>,
{
self.get(index)
}

/// Friendly helper method for calling the `get` method from the
/// implementation of [ParsedDeclEngineGet] for [ParsedDeclEngine]
///
Expand Down
14 changes: 13 additions & 1 deletion sway-core/src/decl_engine/replace_decls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use sway_error::handler::{ErrorEmitted, Handler};

use crate::{
engine_threading::Engines,
language::ty::{self, TyDecl},
language::ty::{self, TyDecl, TyExpression},
semantic_analysis::TypeCheckContext,
};

Expand Down Expand Up @@ -37,3 +37,15 @@ pub(crate) trait ReplaceFunctionImplementingType {
pub(crate) trait UpdateConstantExpression {
fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl);
}

// Iterate the tree searching for references to a const generic,
// and initialize its value with the passed value
pub(crate) trait MaterializeConstGenerics {
fn materialize_const_generics(
&mut self,
engines: &Engines,
handler: &Handler,
name: &str,
value: &TyExpression,
) -> Result<(), ErrorEmitted>;
}
3 changes: 3 additions & 0 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ fn const_eval_typed_expr(
}

Ok(match &expr.expression {
ty::TyExpressionVariant::ConstGenericExpression { .. } => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
ty::TyExpressionVariant::Literal(Literal::Numeric(n)) => {
let implied_lit = match &*lookup.engines.te().get(expr.return_type) {
TypeInfo::UnsignedInteger(IntegerBits::Eight) => Literal::U8(*n as u8),
Expand Down
7 changes: 7 additions & 0 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ impl<'eng> FnCompiler<'eng> {
ty::TyDecl::ConfigurableDecl(ty::ConfigurableDecl { .. }) => {
unreachable!()
}
ty::TyDecl::ConstGenericDecl(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
ty::TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. }) => {
let ted = self.engines.de().get_enum(decl_id);
create_tagged_union_type(
Expand Down Expand Up @@ -546,6 +549,10 @@ impl<'eng> FnCompiler<'eng> {
ty::TyExpressionVariant::ConfigurableExpression {
decl: const_decl, ..
} => self.compile_config_expr(context, const_decl, span_md_idx),
ty::TyExpressionVariant::ConstGenericExpression { decl, .. } => {
let value = decl.value.as_ref().unwrap();
self.compile_expression(context, md_mgr, value)
}
ty::TyExpressionVariant::VariableExpression {
name, call_path, ..
} => self.compile_var_expr(context, call_path, name, span_md_idx),
Expand Down
8 changes: 8 additions & 0 deletions sway-core/src/language/parsed/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub enum Declaration {
TypeAliasDeclaration(ParsedDeclId<TypeAliasDeclaration>),
TraitTypeDeclaration(ParsedDeclId<TraitTypeDeclaration>),
TraitFnDeclaration(ParsedDeclId<TraitFn>),
ConstGenericDeclaration(ParsedDeclId<ConstGenericDeclaration>),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -100,6 +101,7 @@ impl Declaration {
AbiDeclaration(_) => "abi",
StorageDeclaration(_) => "contract storage",
TypeAliasDeclaration(_) => "type alias",
ConstGenericDeclaration(_) => "const generic",
}
}

Expand All @@ -121,6 +123,9 @@ impl Declaration {
TypeAliasDeclaration(decl_id) => pe.get_type_alias(decl_id).span(),
TraitTypeDeclaration(decl_id) => pe.get_trait_type(decl_id).span(),
TraitFnDeclaration(decl_id) => pe.get_trait_fn(decl_id).span(),
ConstGenericDeclaration(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
}
}

Expand Down Expand Up @@ -195,6 +200,9 @@ impl Declaration {
| Declaration::AbiDeclaration(_)
| Declaration::TraitTypeDeclaration(_)
| Declaration::TraitFnDeclaration(_) => Visibility::Public,
Declaration::ConstGenericDeclaration(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/language/parsed/declaration/function.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
ast_elements::type_parameter::ConstGenericParameter,
engine_threading::*,
language::{parsed::*, *},
transform::{self, AttributeKind},
Expand All @@ -25,6 +26,7 @@ pub struct FunctionDeclaration {
pub span: Span,
pub return_type: TypeArgument,
pub type_parameters: Vec<TypeParameter>,
pub const_generic_parameters: Vec<ConstGenericParameter>,
pub where_clause: Vec<(Ident, Vec<TraitConstraint>)>,
pub kind: FunctionDeclarationKind,
pub implementing_type: Option<Declaration>,
Expand Down
25 changes: 25 additions & 0 deletions sway-core/src/language/ty/ast_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,25 @@ impl GetDeclIdent for TyAstNode {
}
}

impl MaterializeConstGenerics for TyAstNode {
fn materialize_const_generics(
&mut self,
engines: &Engines,
handler: &Handler,
name: &str,
value: &TyExpression,
) -> Result<(), ErrorEmitted> {
match self.content {
TyAstNodeContent::Declaration(_) => Ok(()),
TyAstNodeContent::Expression(ref mut expr) => {
expr.materialize_const_generics(engines, handler, name, value)
}
TyAstNodeContent::SideEffect(_) => Ok(()),
TyAstNodeContent::Error(_, _) => Ok(()),
}
}
}

impl TyAstNode {
/// Returns `true` if this AST node will be exported in a library, i.e. it is a public declaration.
pub(crate) fn is_public(&self, decl_engine: &DeclEngine) -> bool {
Expand Down Expand Up @@ -226,6 +245,9 @@ impl TyAstNode {
value.check_deprecated(engines, handler, allow_deprecated);
}
}
TyDecl::ConstGenericDecl(_) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
TyDecl::TraitTypeDecl(_) => {}
TyDecl::FunctionDecl(decl) => {
let decl = engines.de().get(&decl.decl_id);
Expand Down Expand Up @@ -285,6 +307,9 @@ impl TyAstNode {
TyDecl::VariableDecl(_decl) => {}
TyDecl::ConstantDecl(_decl) => {}
TyDecl::ConfigurableDecl(_decl) => {}
TyDecl::ConstGenericDecl(_decl) => {
todo!("Will be implemented by https://github.com/FuelLabs/sway/issues/6860")
}
TyDecl::TraitTypeDecl(_) => {}
TyDecl::FunctionDecl(decl) => {
let fn_decl_id = decl.decl_id;
Expand Down
14 changes: 14 additions & 0 deletions sway-core/src/language/ty/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,17 @@ impl UpdateConstantExpression for TyCodeBlock {
.for_each(|x| x.update_constant_expression(engines, implementing_type));
}
}

impl MaterializeConstGenerics for TyCodeBlock {
fn materialize_const_generics(
&mut self,
engines: &Engines,
handler: &Handler,
name: &str,
value: &TyExpression,
) -> Result<(), ErrorEmitted> {
self.contents
.iter_mut()
.try_for_each(|x| x.materialize_const_generics(engines, handler, name, value))
}
}
Loading

0 comments on commit 13542d2

Please sign in to comment.