Skip to content

Commit 70c84ca

Browse files
xunilrjJoshuaBatty
andauthored
Implement array repeat without repeating item at the AST/CST trees (#6901)
## Description This PR is part of #6860. In future PRs, we will need a way to declare array lengths that are not literals. This PR enables this by postponing the length resolution to the `type-check` phase. ## 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. --------- Co-authored-by: Joshua Batty <joshpbatty@gmail.com>
1 parent eafd99e commit 70c84ca

File tree

21 files changed

+529
-179
lines changed

21 files changed

+529
-179
lines changed

sway-core/src/control_flow_analysis/dead_code_analysis.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
17911791
address.span.clone(),
17921792
options,
17931793
),
1794-
Array {
1794+
ArrayExplicit {
17951795
elem_type: _,
17961796
contents,
17971797
} => {
@@ -1818,6 +1818,35 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
18181818

18191819
Ok(last)
18201820
}
1821+
ArrayRepeat {
1822+
elem_type: _,
1823+
value,
1824+
length,
1825+
} => {
1826+
let value_idx = connect_expression(
1827+
engines,
1828+
&value.expression,
1829+
graph,
1830+
leaves,
1831+
exit_node,
1832+
"",
1833+
tree_type,
1834+
value.span.clone(),
1835+
options,
1836+
)?;
1837+
let length_idx = connect_expression(
1838+
engines,
1839+
&length.expression,
1840+
graph,
1841+
leaves,
1842+
exit_node,
1843+
"",
1844+
tree_type,
1845+
length.span.clone(),
1846+
options,
1847+
)?;
1848+
Ok([value_idx, length_idx].concat())
1849+
}
18211850
ArrayIndex { prefix, index } => {
18221851
let prefix_idx = connect_expression(
18231852
engines,

sway-core/src/ir_generation/const_eval.rs

+52-25
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,38 @@ pub(crate) fn compile_constant_expression_to_constant(
247247
}
248248
}
249249

250+
fn create_array_from_vec(
251+
lookup: &mut LookupEnv,
252+
elem_type: crate::TypeId,
253+
element_typs: Vec<crate::TypeId>,
254+
element_vals: Vec<Constant>,
255+
) -> Option<Constant> {
256+
let te = lookup.engines.te();
257+
assert!({
258+
let unify_check = UnifyCheck::coercion(lookup.engines);
259+
element_typs
260+
.iter()
261+
.all(|tid| unify_check.check(*tid, elem_type))
262+
});
263+
264+
let arr = create_array_aggregate(
265+
te,
266+
lookup.engines.de(),
267+
lookup.context,
268+
elem_type,
269+
element_typs.len().try_into().unwrap(),
270+
)
271+
.map_or(None, |array_ty| {
272+
Some(Constant::new_array(
273+
lookup.context,
274+
array_ty.get_array_elem_type(lookup.context).unwrap(),
275+
element_vals,
276+
))
277+
});
278+
279+
arr
280+
}
281+
250282
/// Given an environment mapping names to constants,
251283
/// attempt to evaluate a typed expression to a constant.
252284
fn const_eval_typed_expr(
@@ -413,7 +445,7 @@ fn const_eval_typed_expr(
413445
))
414446
})
415447
}
416-
ty::TyExpressionVariant::Array {
448+
ty::TyExpressionVariant::ArrayExplicit {
417449
elem_type,
418450
contents,
419451
} => {
@@ -434,30 +466,25 @@ fn const_eval_typed_expr(
434466
assert!(element_typs.len() == contents.len());
435467
assert!(element_vals.len() == contents.len());
436468

437-
let te = lookup.engines.te();
438-
assert!({
439-
let unify_check = UnifyCheck::coercion(lookup.engines);
440-
element_typs
441-
.iter()
442-
.all(|tid| unify_check.check(*tid, *elem_type))
443-
});
444-
445-
let arr = create_array_aggregate(
446-
te,
447-
lookup.engines.de(),
448-
lookup.context,
449-
*elem_type,
450-
element_typs.len().try_into().unwrap(),
451-
)
452-
.map_or(None, |array_ty| {
453-
Some(Constant::new_array(
454-
lookup.context,
455-
array_ty.get_array_elem_type(lookup.context).unwrap(),
456-
element_vals,
457-
))
458-
});
459-
460-
arr
469+
create_array_from_vec(lookup, *elem_type, element_typs, element_vals)
470+
}
471+
ty::TyExpressionVariant::ArrayRepeat {
472+
elem_type,
473+
value,
474+
length,
475+
} => {
476+
let constant = const_eval_typed_expr(lookup, known_consts, value)?.unwrap();
477+
let length = const_eval_typed_expr(lookup, known_consts, length)?
478+
.unwrap()
479+
.as_uint()
480+
.unwrap() as usize;
481+
let element_vals = (0..length).map(|_| constant.clone()).collect::<Vec<_>>();
482+
let element_typs = (0..length).map(|_| value.return_type).collect::<Vec<_>>();
483+
484+
assert!(element_typs.len() == length);
485+
assert!(element_vals.len() == length);
486+
487+
create_array_from_vec(lookup, *elem_type, element_typs, element_vals)
461488
}
462489
ty::TyExpressionVariant::EnumInstantiation {
463490
enum_ref,

sway-core/src/ir_generation/function.rs

+147-53
Original file line numberDiff line numberDiff line change
@@ -545,10 +545,24 @@ impl<'eng> FnCompiler<'eng> {
545545
ty::TyExpressionVariant::VariableExpression {
546546
name, call_path, ..
547547
} => self.compile_var_expr(context, call_path, name, span_md_idx),
548-
ty::TyExpressionVariant::Array {
548+
ty::TyExpressionVariant::ArrayExplicit {
549549
elem_type,
550550
contents,
551-
} => self.compile_array_expr(context, md_mgr, *elem_type, contents, span_md_idx),
551+
} => {
552+
self.compile_array_explicit_expr(context, md_mgr, *elem_type, contents, span_md_idx)
553+
}
554+
ty::TyExpressionVariant::ArrayRepeat {
555+
elem_type,
556+
value,
557+
length,
558+
} => self.compile_array_repeat_expr(
559+
context,
560+
md_mgr,
561+
*elem_type,
562+
value,
563+
length,
564+
span_md_idx,
565+
),
552566
ty::TyExpressionVariant::ArrayIndex { prefix, index } => {
553567
self.compile_array_index(context, md_mgr, prefix, index, span_md_idx)
554568
}
@@ -3635,7 +3649,67 @@ impl<'eng> FnCompiler<'eng> {
36353649
Ok(TerminatorValue::new(val, context))
36363650
}
36373651

3638-
fn compile_array_expr(
3652+
fn compile_array_repeat_expr(
3653+
&mut self,
3654+
context: &mut Context,
3655+
md_mgr: &mut MetadataManager,
3656+
elem_type: TypeId,
3657+
value: &ty::TyExpression,
3658+
length: &ty::TyExpression,
3659+
span_md_idx: Option<MetadataIndex>,
3660+
) -> Result<TerminatorValue, CompileError> {
3661+
let elem_type = convert_resolved_typeid_no_span(
3662+
self.engines.te(),
3663+
self.engines.de(),
3664+
context,
3665+
elem_type,
3666+
)?;
3667+
3668+
let length_as_u64 = length.as_literal_u64().unwrap();
3669+
let array_type = Type::new_array(context, elem_type, length_as_u64);
3670+
3671+
let temp_name = self.lexical_map.insert_anon();
3672+
let array_local_var = self
3673+
.function
3674+
.new_local_var(context, temp_name, array_type, None, false)
3675+
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
3676+
let array_value = self
3677+
.current_block
3678+
.append(context)
3679+
.get_local(array_local_var)
3680+
.add_metadatum(context, span_md_idx);
3681+
3682+
let value_value = return_on_termination_or_extract!(
3683+
self.compile_expression_to_value(context, md_mgr, value)?
3684+
);
3685+
3686+
if length_as_u64 > 5 {
3687+
self.compile_array_init_loop(
3688+
context,
3689+
array_value,
3690+
elem_type,
3691+
value_value,
3692+
length_as_u64,
3693+
span_md_idx,
3694+
);
3695+
} else {
3696+
for i in 0..length_as_u64 {
3697+
let gep_val = self.current_block.append(context).get_elem_ptr_with_idx(
3698+
array_value,
3699+
elem_type,
3700+
i,
3701+
);
3702+
self.current_block
3703+
.append(context)
3704+
.store(gep_val, value_value)
3705+
.add_metadatum(context, span_md_idx);
3706+
}
3707+
}
3708+
3709+
Ok(TerminatorValue::new(array_value, context))
3710+
}
3711+
3712+
fn compile_array_explicit_expr(
36393713
&mut self,
36403714
context: &mut Context,
36413715
md_mgr: &mut MetadataManager,
@@ -3700,59 +3774,14 @@ impl<'eng> FnCompiler<'eng> {
37003774
})
37013775
});
37023776
if let Some(const_initializer) = const_initialiser_opt {
3703-
// Create a loop to insert const_initializer to all array elements.
3704-
let loop_block = self
3705-
.function
3706-
.create_block(context, Some("array_init_loop".into()));
3707-
// The loop begins with 0.
3708-
let zero = Constant::new_uint(context, 64, 0);
3709-
let zero = Value::new_constant(context, zero);
3710-
// Branch to the loop block, passing the initial iteration value.
3711-
self.current_block
3712-
.append(context)
3713-
.branch(loop_block, vec![zero]);
3714-
// Add a block argument (for the IV) to the loop block.
3715-
let index_var_index = loop_block.new_arg(context, Type::get_uint64(context));
3716-
let index = loop_block.get_arg(context, index_var_index).unwrap();
3717-
// Create an exit block.
3718-
let exit_block = self
3719-
.function
3720-
.create_block(context, Some("array_init_exit".into()));
3721-
// Start building the loop block.
3722-
self.current_block = loop_block;
3723-
let gep_val = self.current_block.append(context).get_elem_ptr(
3777+
self.compile_array_init_loop(
3778+
context,
37243779
array_value,
37253780
elem_type,
3726-
vec![index],
3727-
);
3728-
self.current_block
3729-
.append(context)
3730-
.store(gep_val, *const_initializer)
3731-
.add_metadatum(context, span_md_idx);
3732-
// Increment index by one.
3733-
let one = Constant::new_uint(context, 64, 1);
3734-
let one = Value::new_constant(context, one);
3735-
let index_inc =
3736-
self.current_block
3737-
.append(context)
3738-
.binary_op(BinaryOpKind::Add, index, one);
3739-
// continue = index_inc < contents.len()
3740-
let len = Constant::new_uint(context, 64, contents.len() as u64);
3741-
let len = Value::new_constant(context, len);
3742-
let r#continue =
3743-
self.current_block
3744-
.append(context)
3745-
.cmp(Predicate::LessThan, index_inc, len);
3746-
// if continue then loop_block else exit_block.
3747-
self.current_block.append(context).conditional_branch(
3748-
r#continue,
3749-
loop_block,
3750-
exit_block,
3751-
vec![index_inc],
3752-
vec![],
3781+
*const_initializer,
3782+
contents.len() as u64,
3783+
span_md_idx,
37533784
);
3754-
// Continue compilation in the exit block.
3755-
self.current_block = exit_block;
37563785
} else {
37573786
// Insert each element separately.
37583787
for (idx, elem_value) in compiled_elems.iter().enumerate() {
@@ -3788,6 +3817,71 @@ impl<'eng> FnCompiler<'eng> {
37883817
Ok(TerminatorValue::new(array_value, context))
37893818
}
37903819

3820+
// initialize an array with all elements equals to "init_value",
3821+
// which should be "Copy", concept that sway still don´t have.
3822+
fn compile_array_init_loop(
3823+
&mut self,
3824+
context: &mut Context,
3825+
array_value: Value,
3826+
elem_type: Type,
3827+
init_value: Value,
3828+
length: u64,
3829+
span_md_idx: Option<MetadataIndex>,
3830+
) {
3831+
// Create a loop to insert const_initializer to all array elements.
3832+
let loop_block = self
3833+
.function
3834+
.create_block(context, Some("array_init_loop".into()));
3835+
// The loop begins with 0.
3836+
let zero = Constant::new_uint(context, 64, 0);
3837+
let zero = Value::new_constant(context, zero);
3838+
// Branch to the loop block, passing the initial iteration value.
3839+
self.current_block
3840+
.append(context)
3841+
.branch(loop_block, vec![zero]);
3842+
// Add a block argument (for the IV) to the loop block.
3843+
let index_var_index = loop_block.new_arg(context, Type::get_uint64(context));
3844+
let index = loop_block.get_arg(context, index_var_index).unwrap();
3845+
// Create an exit block.
3846+
let exit_block = self
3847+
.function
3848+
.create_block(context, Some("array_init_exit".into()));
3849+
// Start building the loop block.
3850+
self.current_block = loop_block;
3851+
let gep_val =
3852+
self.current_block
3853+
.append(context)
3854+
.get_elem_ptr(array_value, elem_type, vec![index]);
3855+
self.current_block
3856+
.append(context)
3857+
.store(gep_val, init_value)
3858+
.add_metadatum(context, span_md_idx);
3859+
// Increment index by one.
3860+
let one = Constant::new_uint(context, 64, 1);
3861+
let one = Value::new_constant(context, one);
3862+
let index_inc = self
3863+
.current_block
3864+
.append(context)
3865+
.binary_op(BinaryOpKind::Add, index, one);
3866+
// continue = index_inc < contents.len()
3867+
let len = Constant::new_uint(context, 64, length);
3868+
let len = Value::new_constant(context, len);
3869+
let r#continue =
3870+
self.current_block
3871+
.append(context)
3872+
.cmp(Predicate::LessThan, index_inc, len);
3873+
// if continue then loop_block else exit_block.
3874+
self.current_block.append(context).conditional_branch(
3875+
r#continue,
3876+
loop_block,
3877+
exit_block,
3878+
vec![index_inc],
3879+
vec![],
3880+
);
3881+
// Continue compilation in the exit block.
3882+
self.current_block = exit_block;
3883+
}
3884+
37913885
fn compile_array_index(
37923886
&mut self,
37933887
context: &mut Context,

0 commit comments

Comments
 (0)