Skip to content

Commit 5e7e4bc

Browse files
authored
const generics for standalone fns (#7175)
## Description This PR is part of #6860 and allows standalone functions to have "const generics" like the code below: ```sway fn return_n<const NNN: u64>() -> u64 { NNN } ``` ## 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.
1 parent ee565f9 commit 5e7e4bc

File tree

13 files changed

+420
-172
lines changed

13 files changed

+420
-172
lines changed

sway-core/src/ir_generation/function.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ impl<'eng> FnCompiler<'eng> {
530530
)
531531
} else {
532532
let function_decl = self.engines.de().get_function(fn_ref);
533+
533534
self.compile_fn_call(
534535
context,
535536
md_mgr,

sway-core/src/language/ty/declaration/function.rs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
type_system::*,
1313
types::*,
1414
};
15+
use ast_elements::type_parameter::ConstGenericExpr;
1516
use monomorphization::MonomorphizeHelper;
1617
use serde::{Deserialize, Serialize};
1718
use sha2::{Digest, Sha256};
@@ -74,14 +75,18 @@ impl DebugWithEngines for TyFunctionDecl {
7475
self.type_parameters
7576
.iter()
7677
.map(|p| {
77-
let p = p
78-
.as_type_parameter()
79-
.expect("only works for type parameters");
80-
format!(
81-
"{:?} -> {:?}",
82-
engines.help_out(p.initial_type_id),
83-
engines.help_out(p.type_id)
84-
)
78+
match p {
79+
TypeParameter::Type(p) => {
80+
format!(
81+
"{:?} -> {:?}",
82+
engines.help_out(p.initial_type_id),
83+
engines.help_out(p.type_id)
84+
)
85+
}
86+
TypeParameter::Const(p) => {
87+
format!("{} -> {:?}", p.name, p.expr)
88+
}
89+
}
8590
})
8691
.collect::<Vec<_>>()
8792
.join(", ")
@@ -150,6 +155,19 @@ impl MaterializeConstGenerics for TyFunctionDecl {
150155
name: &str,
151156
value: &TyExpression,
152157
) -> Result<(), ErrorEmitted> {
158+
for tp in self.type_parameters.iter_mut() {
159+
match tp {
160+
TypeParameter::Type(p) => p
161+
.type_id
162+
.materialize_const_generics(engines, handler, name, value)?,
163+
TypeParameter::Const(p) if p.name.as_str() == name => {
164+
assert!(p.expr.is_none());
165+
p.expr = Some(ConstGenericExpr::from_ty_expression(handler, value)?);
166+
}
167+
_ => {}
168+
}
169+
}
170+
153171
for param in self.parameters.iter_mut() {
154172
param
155173
.type_argument
@@ -666,11 +684,17 @@ impl TyFunctionParameter {
666684
}
667685
}
668686

687+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
688+
pub enum TyFunctionSigTypeParameter {
689+
Type(TypeId),
690+
Const(ConstGenericExpr),
691+
}
692+
669693
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
670694
pub struct TyFunctionSig {
671695
pub return_type: TypeId,
672696
pub parameters: Vec<TypeId>,
673-
pub type_parameters: Vec<TypeId>,
697+
pub type_parameters: Vec<TyFunctionSigTypeParameter>,
674698
}
675699

676700
impl DisplayWithEngines for TyFunctionSig {
@@ -688,7 +712,11 @@ impl DebugWithEngines for TyFunctionSig {
688712
"<{}>",
689713
self.type_parameters
690714
.iter()
691-
.map(|p| format!("{}", engines.help_out(p)))
715+
.map(|p| match p {
716+
TyFunctionSigTypeParameter::Type(t) => format!("{:?}", engines.help_out(t)),
717+
TyFunctionSigTypeParameter::Const(expr) =>
718+
format!("{:?}", engines.help_out(expr)),
719+
})
692720
.collect::<Vec<_>>()
693721
.join(", "),
694722
)
@@ -719,9 +747,16 @@ impl TyFunctionSig {
719747
type_parameters: fn_decl
720748
.type_parameters
721749
.iter()
722-
.filter_map(|x| x.as_type_parameter())
723-
.map(|p| p.type_id)
724-
.collect::<Vec<_>>(),
750+
.map(|x| match x {
751+
TypeParameter::Type(p) => TyFunctionSigTypeParameter::Type(p.type_id),
752+
TypeParameter::Const(p) => {
753+
let expr = ConstGenericExpr::AmbiguousVariableExpression {
754+
ident: p.name.clone(),
755+
};
756+
TyFunctionSigTypeParameter::Const(p.expr.clone().unwrap_or(expr))
757+
}
758+
})
759+
.collect(),
725760
}
726761
}
727762

@@ -735,7 +770,11 @@ impl TyFunctionSig {
735770
&& self
736771
.type_parameters
737772
.iter()
738-
.all(|p| p.is_concrete(engines, TreatNumericAs::Concrete))
773+
.filter_map(|x| match x {
774+
TyFunctionSigTypeParameter::Type(type_id) => Some(type_id),
775+
TyFunctionSigTypeParameter::Const(_) => None,
776+
})
777+
.all(|type_id| type_id.is_concrete(engines, TreatNumericAs::Concrete))
739778
}
740779

741780
/// Returns a String representing the function.
@@ -749,7 +788,15 @@ impl TyFunctionSig {
749788
"<{}>",
750789
self.type_parameters
751790
.iter()
752-
.map(|p| p.get_type_str(engines))
791+
.map(|x| match x {
792+
TyFunctionSigTypeParameter::Type(type_id) => type_id.get_type_str(engines),
793+
TyFunctionSigTypeParameter::Const(p) => {
794+
match p {
795+
ConstGenericExpr::Literal { val, .. } => val.to_string(),
796+
ConstGenericExpr::AmbiguousVariableExpression { .. } => todo!(),
797+
}
798+
}
799+
})
753800
.collect::<Vec<_>>()
754801
.join(", "),
755802
)

sway-core/src/semantic_analysis/ast_node/declaration/function.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ use sway_error::{
99
use symbol_collection_context::SymbolCollectionContext;
1010

1111
use crate::{
12-
decl_engine::{parsed_id::ParsedDeclId, DeclId, DeclRefFunction},
12+
decl_engine::{
13+
parsed_id::ParsedDeclId, DeclEngineInsert as _, DeclId, DeclRefFunction,
14+
ParsedDeclEngineGet as _,
15+
},
1316
language::{
1417
parsed::*,
15-
ty::{self, TyCodeBlock, TyFunctionDecl},
16-
CallPath, Visibility,
18+
ty::{self, ConstGenericDecl, TyCodeBlock, TyConstGenericDecl, TyDecl, TyFunctionDecl},
19+
CallPath, CallPathType, Visibility,
1720
},
1821
semantic_analysis::*,
1922
type_system::*,
@@ -34,6 +37,22 @@ impl ty::TyFunctionDecl {
3437

3538
// create a namespace for the function
3639
let _ = ctx.scoped(engines, fn_decl.span.clone(), Some(decl), |scoped_ctx| {
40+
let const_generic_parameters = fn_decl
41+
.type_parameters
42+
.iter()
43+
.filter_map(|x| x.as_const_parameter())
44+
.filter_map(|x| x.id.as_ref());
45+
46+
for const_generic_parameter in const_generic_parameters {
47+
let const_generic_decl = engines.pe().get(const_generic_parameter);
48+
scoped_ctx.insert_parsed_symbol(
49+
handler,
50+
engines,
51+
const_generic_decl.name.clone(),
52+
Declaration::ConstGenericDeclaration(*const_generic_parameter),
53+
)?;
54+
}
55+
3756
TyCodeBlock::collect(handler, engines, scoped_ctx, &fn_decl.body)
3857
});
3958
Ok(())
@@ -114,6 +133,36 @@ impl ty::TyFunctionDecl {
114133
None,
115134
)?;
116135

136+
// const generic parameters
137+
let const_generic_parameters = type_parameters
138+
.iter()
139+
.filter_map(|x| x.as_const_parameter())
140+
.filter_map(|x| x.id.as_ref());
141+
for const_generic_decl_id in const_generic_parameters {
142+
let const_generic_decl = ctx.engines.pe().get(const_generic_decl_id);
143+
let decl_ref = ctx.engines.de().insert(
144+
TyConstGenericDecl {
145+
call_path: CallPath {
146+
prefixes: vec![],
147+
suffix: const_generic_decl.name.clone(),
148+
callpath_type: CallPathType::Ambiguous,
149+
},
150+
span: const_generic_decl.span.clone(),
151+
return_type: const_generic_decl.ty,
152+
value: None,
153+
},
154+
Some(const_generic_decl_id),
155+
);
156+
157+
ctx.insert_symbol(
158+
handler,
159+
const_generic_decl.name.clone(),
160+
TyDecl::ConstGenericDecl(ConstGenericDecl {
161+
decl_id: *decl_ref.id(),
162+
}),
163+
)?;
164+
}
165+
117166
// type check the function parameters, which will also insert them into the namespace
118167
let mut new_parameters = vec![];
119168
handler.scope(|handler| {
@@ -212,10 +261,10 @@ impl ty::TyFunctionDecl {
212261

213262
// Insert the previously type checked type parameters into the current namespace.
214263
// We insert all type parameter before the constraints because some constraints may depend on the parameters.
215-
for p in type_parameters.iter().filter_map(|x| x.as_type_parameter()) {
264+
for p in type_parameters.iter() {
216265
p.insert_into_namespace_self(handler, ctx.by_ref())?;
217266
}
218-
for p in type_parameters.iter().filter_map(|x| x.as_type_parameter()) {
267+
for p in type_parameters.iter() {
219268
p.insert_into_namespace_constraints(handler, ctx.by_ref())?;
220269
}
221270

sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ pub(crate) fn instantiate_function_application(
9696
function_decl.name.as_str(),
9797
&call_path_binding.span(),
9898
)?;
99-
10099
function_decl.replace_decls(&decl_mapping, handler, &mut ctx)?;
101100
}
102101

sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,7 @@ pub(crate) fn struct_instantiation(
300300
.scoped(handler, None, |scoped_ctx| {
301301
// Insert struct type parameter into namespace.
302302
// This is required so check_type_parameter_bounds can resolve generic trait type parameters.
303-
for p in struct_decl
304-
.generic_parameters
305-
.iter()
306-
.filter_map(|x| x.as_type_parameter())
307-
{
303+
for p in struct_decl.generic_parameters.iter() {
308304
p.insert_into_namespace_self(handler, scoped_ctx.by_ref())?;
309305
}
310306

sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ pub fn item_fn_to_function_declaration(
561561
let kind = override_kind.unwrap_or(kind);
562562
let implementing_type = context.implementing_type.clone();
563563

564-
let generic_parameters = generic_params_opt_to_type_parameters_with_parent(
564+
let mut generic_parameters = generic_params_opt_to_type_parameters_with_parent(
565565
context,
566566
handler,
567567
engines,
@@ -571,6 +571,19 @@ pub fn item_fn_to_function_declaration(
571571
parent_where_clause_opt,
572572
)?;
573573

574+
for p in generic_parameters.iter_mut() {
575+
match p {
576+
TypeParameter::Type(_) => {}
577+
TypeParameter::Const(p) => {
578+
p.id = Some(engines.pe().insert(ConstGenericDeclaration {
579+
name: p.name.clone(),
580+
ty: p.ty,
581+
span: p.span.clone(),
582+
}));
583+
}
584+
}
585+
}
586+
574587
let fn_decl = FunctionDeclaration {
575588
purity: attributes.purity(),
576589
attributes,

sway-core/src/type_system/ast_elements/binding.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ impl TypeCheckTypeBinding<ty::TyFunctionDecl> for TypeBinding<CallPath> {
319319
let fn_ref = unknown_decl.to_fn_ref(handler, ctx.engines())?;
320320
// Get a new copy from the declaration engine.
321321
let mut new_copy = (*decl_engine.get_function(fn_ref.id())).clone();
322+
322323
match self.type_arguments {
323324
// Monomorphize the copy, in place.
324325
TypeArgs::Regular(_) => {
@@ -352,6 +353,7 @@ impl TypeCheckTypeBinding<ty::TyFunctionDecl> for TypeBinding<CallPath> {
352353
decl_engine.get_parsed_decl_id(fn_ref.id()).as_ref(),
353354
)
354355
.with_parent(ctx.engines.de(), fn_ref.id().into());
356+
355357
Ok((new_fn_ref, None, None))
356358
}
357359
}

0 commit comments

Comments
 (0)