Skip to content

Commit 380692f

Browse files
committed
Implements deref in reassignement of projections.
Implements dereferencing as LHS in reassignments for projections. With this PR we will support: - `(*array)[0] = 1` - `(*tuple).0 = 1` - `(*struct).a = 1` This allow includes support for nested references. Fixes #6397
1 parent 49d0eb4 commit 380692f

File tree

17 files changed

+495
-142
lines changed

17 files changed

+495
-142
lines changed

sway-ast/src/assignable.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ pub enum ElementAccess {
4242
field: BigUint,
4343
field_span: Span,
4444
},
45+
Deref {
46+
target: Box<ElementAccess>,
47+
star_token: StarToken,
48+
/// Multiple Derefs can be nested, this is true for the outer most Deref.
49+
is_root_element: bool,
50+
},
4551
}
4652

4753
impl Spanned for Assignable {
@@ -64,6 +70,11 @@ impl Spanned for ElementAccess {
6470
ElementAccess::TupleFieldProjection {
6571
target, field_span, ..
6672
} => Span::join(target.span(), field_span),
73+
ElementAccess::Deref {
74+
target,
75+
star_token,
76+
is_root_element: _,
77+
} => Span::join(target.span(), &star_token.span()),
6778
}
6879
}
6980
}

sway-ast/src/expr/mod.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -473,17 +473,22 @@ impl Expr {
473473
if let Expr::Deref { star_token, expr } = self {
474474
Ok(Assignable::Deref { star_token, expr })
475475
} else {
476-
Ok(Assignable::ElementAccess(self.try_into_element_access()?))
476+
Ok(Assignable::ElementAccess(
477+
self.try_into_element_access(false)?,
478+
))
477479
}
478480
}
479481

480-
fn try_into_element_access(self) -> Result<ElementAccess, Expr> {
481-
match self {
482+
fn try_into_element_access(
483+
self,
484+
accept_deref_without_parens: bool,
485+
) -> Result<ElementAccess, Expr> {
486+
match self.clone() {
482487
Expr::Path(path_expr) => match path_expr.try_into_ident() {
483488
Ok(name) => Ok(ElementAccess::Var(name)),
484489
Err(path_expr) => Err(Expr::Path(path_expr)),
485490
},
486-
Expr::Index { target, arg } => match target.try_into_element_access() {
491+
Expr::Index { target, arg } => match target.try_into_element_access(false) {
487492
Ok(target) => Ok(ElementAccess::Index {
488493
target: Box::new(target),
489494
arg,
@@ -494,7 +499,7 @@ impl Expr {
494499
target,
495500
dot_token,
496501
name,
497-
} => match target.try_into_element_access() {
502+
} => match target.try_into_element_access(false) {
498503
Ok(target) => Ok(ElementAccess::FieldProjection {
499504
target: Box::new(target),
500505
dot_token,
@@ -507,7 +512,7 @@ impl Expr {
507512
dot_token,
508513
field,
509514
field_span,
510-
} => match target.try_into_element_access() {
515+
} => match target.try_into_element_access(false) {
511516
Ok(target) => Ok(ElementAccess::TupleFieldProjection {
512517
target: Box::new(target),
513518
dot_token,
@@ -516,6 +521,30 @@ impl Expr {
516521
}),
517522
error => error,
518523
},
524+
Expr::Parens(Parens { inner, .. }) => {
525+
if let Expr::Deref { expr, star_token } = *inner {
526+
match expr.try_into_element_access(true) {
527+
Ok(target) => Ok(ElementAccess::Deref {
528+
target: Box::new(target),
529+
star_token,
530+
is_root_element: true,
531+
}),
532+
error => error,
533+
}
534+
} else {
535+
Err(self)
536+
}
537+
}
538+
Expr::Deref { expr, star_token } if accept_deref_without_parens => {
539+
match expr.try_into_element_access(true) {
540+
Ok(target) => Ok(ElementAccess::Deref {
541+
target: Box::new(target),
542+
star_token,
543+
is_root_element: false,
544+
}),
545+
error => error,
546+
}
547+
}
519548
expr => Err(expr),
520549
}
521550
}

sway-core/src/control_flow_analysis/dead_code_analysis.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2059,7 +2059,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
20592059
}
20602060
}
20612061
}
2062-
ty::TyReassignmentTarget::Deref(exp) => {
2062+
ty::TyReassignmentTarget::DerefAccess { exp, indices } => {
20632063
connect_expression(
20642064
engines,
20652065
&exp.expression,
@@ -2071,6 +2071,22 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
20712071
exp.span.clone(),
20722072
options,
20732073
)?;
2074+
2075+
for projection in indices {
2076+
if let ProjectionKind::ArrayIndex { index, index_span } = projection {
2077+
connect_expression(
2078+
engines,
2079+
&index.expression,
2080+
graph,
2081+
leaves,
2082+
exit_node,
2083+
"variable reassignment LHS array index",
2084+
tree_type,
2085+
index_span.clone(),
2086+
options,
2087+
)?;
2088+
}
2089+
}
20742090
}
20752091
};
20762092

sway-core/src/ir_generation/const_eval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ fn const_eval_typed_expr(
770770
});
771771
}
772772
}
773-
ty::TyReassignmentTarget::Deref(_) => {
773+
ty::TyReassignmentTarget::DerefAccess { .. } => {
774774
return Err(ConstEvalError::CannotBeEvaluatedToConst {
775775
span: expr.span.clone(),
776776
});

sway-core/src/ir_generation/function.rs

Lines changed: 103 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3557,68 +3557,10 @@ impl<'eng> FnCompiler<'eng> {
35573557
// A non-aggregate; use a direct `store`.
35583558
lhs_val
35593559
} else {
3560-
// Create a GEP by following the chain of LHS indices. We use a scan which is
3561-
// essentially a map with context, which is the parent type id for the current field.
3562-
let mut gep_indices = Vec::<Value>::new();
3563-
let mut cur_type_id = *base_type;
3564-
// TODO-IG: Add support for projections being references themselves.
3565-
for idx_kind in indices.iter() {
3566-
let cur_type_info_arc = self.engines.te().get_unaliased(cur_type_id);
3567-
let cur_type_info = &*cur_type_info_arc;
3568-
match (idx_kind, cur_type_info) {
3569-
(
3570-
ProjectionKind::StructField { name: idx_name },
3571-
TypeInfo::Struct(decl_ref),
3572-
) => {
3573-
let struct_decl = self.engines.de().get_struct(decl_ref);
3574-
3575-
match struct_decl.get_field_index_and_type(idx_name) {
3576-
None => {
3577-
return Err(CompileError::InternalOwned(
3578-
format!(
3579-
"Unknown field name \"{idx_name}\" for struct \"{}\" \
3580-
in reassignment.",
3581-
struct_decl.call_path.suffix.as_str(),
3582-
),
3583-
idx_name.span(),
3584-
))
3585-
}
3586-
Some((field_idx, field_type_id)) => {
3587-
cur_type_id = field_type_id;
3588-
gep_indices.push(ConstantContent::get_uint(
3589-
context, 64, field_idx,
3590-
));
3591-
}
3592-
}
3593-
}
3594-
(
3595-
ProjectionKind::TupleField { index, .. },
3596-
TypeInfo::Tuple(field_tys),
3597-
) => {
3598-
cur_type_id = field_tys[*index].type_id;
3599-
gep_indices.push(ConstantContent::get_uint(
3600-
context,
3601-
64,
3602-
*index as u64,
3603-
));
3604-
}
3605-
(
3606-
ProjectionKind::ArrayIndex { index, .. },
3607-
TypeInfo::Array(elem_ty, _),
3608-
) => {
3609-
cur_type_id = elem_ty.type_id;
3610-
let val = return_on_termination_or_extract!(
3611-
self.compile_expression_to_value(context, md_mgr, index)?
3612-
);
3613-
gep_indices.push(val);
3614-
}
3615-
_ => {
3616-
return Err(CompileError::Internal(
3617-
"Unknown field in reassignment.",
3618-
idx_kind.span(),
3619-
))
3620-
}
3621-
}
3560+
let (terminator, gep_indices) =
3561+
self.compile_indices(context, md_mgr, *base_type, indices)?;
3562+
if let Some(terminator) = terminator {
3563+
return Ok(terminator);
36223564
}
36233565

36243566
// Using the type of the RHS for the GEP, rather than the final inner type of the
@@ -3638,7 +3580,10 @@ impl<'eng> FnCompiler<'eng> {
36383580
.add_metadatum(context, span_md_idx)
36393581
}
36403582
}
3641-
ty::TyReassignmentTarget::Deref(dereference_exp) => {
3583+
ty::TyReassignmentTarget::DerefAccess {
3584+
exp: dereference_exp,
3585+
indices,
3586+
} => {
36423587
let TyExpressionVariant::Deref(reference_exp) = &dereference_exp.expression else {
36433588
return Err(CompileError::Internal(
36443589
"Left-hand side of the reassignment must be dereferencing.",
@@ -3649,7 +3594,36 @@ impl<'eng> FnCompiler<'eng> {
36493594
let (ptr, _) =
36503595
self.compile_deref_up_to_ptr(context, md_mgr, reference_exp, span_md_idx)?;
36513596

3652-
return_on_termination_or_extract!(ptr)
3597+
if indices.is_empty() {
3598+
// A non-aggregate;
3599+
return_on_termination_or_extract!(ptr)
3600+
} else {
3601+
let (terminator, gep_indices) = self.compile_indices(
3602+
context,
3603+
md_mgr,
3604+
dereference_exp.return_type,
3605+
indices,
3606+
)?;
3607+
if let Some(terminator) = terminator {
3608+
return Ok(terminator);
3609+
}
3610+
3611+
// Using the type of the RHS for the GEP, rather than the final inner type of the
3612+
// aggregate, but getting the latter is a bit of a pain, though the `scan` above knew it.
3613+
// The program is type checked and the IR types on the LHS and RHS are the same.
3614+
let field_type = rhs.get_type(context).ok_or_else(|| {
3615+
CompileError::Internal(
3616+
"Failed to determine type of reassignment.",
3617+
dereference_exp.span.clone(),
3618+
)
3619+
})?;
3620+
3621+
// Create the GEP.
3622+
self.current_block
3623+
.append(context)
3624+
.get_elem_ptr(ptr.value, field_type, gep_indices)
3625+
.add_metadatum(context, span_md_idx)
3626+
}
36533627
}
36543628
};
36553629

@@ -3662,6 +3636,71 @@ impl<'eng> FnCompiler<'eng> {
36623636
Ok(TerminatorValue::new(val, context))
36633637
}
36643638

3639+
fn compile_indices(
3640+
&mut self,
3641+
context: &mut Context,
3642+
md_mgr: &mut MetadataManager,
3643+
base_type: TypeId,
3644+
indices: &[ProjectionKind],
3645+
) -> Result<(Option<TerminatorValue>, Vec<Value>), CompileError> {
3646+
// Create a GEP by following the chain of LHS indices. We use a scan which is
3647+
// essentially a map with context, which is the parent type id for the current field.
3648+
let mut gep_indices = Vec::<Value>::new();
3649+
let mut cur_type_id = base_type;
3650+
for idx_kind in indices.iter() {
3651+
while let TypeInfo::Ref {
3652+
referenced_type, ..
3653+
} = &*self.engines.te().get_unaliased(cur_type_id)
3654+
{
3655+
cur_type_id = referenced_type.type_id;
3656+
}
3657+
let cur_type_info_arc = self.engines.te().get_unaliased(cur_type_id);
3658+
let cur_type_info = &*cur_type_info_arc;
3659+
match (idx_kind, cur_type_info) {
3660+
(ProjectionKind::StructField { name: idx_name }, TypeInfo::Struct(decl_ref)) => {
3661+
let struct_decl = self.engines.de().get_struct(decl_ref);
3662+
3663+
match struct_decl.get_field_index_and_type(idx_name) {
3664+
None => {
3665+
return Err(CompileError::InternalOwned(
3666+
format!(
3667+
"Unknown field name \"{idx_name}\" for struct \"{}\" \
3668+
in reassignment.",
3669+
struct_decl.call_path.suffix.as_str(),
3670+
),
3671+
idx_name.span(),
3672+
))
3673+
}
3674+
Some((field_idx, field_type_id)) => {
3675+
cur_type_id = field_type_id;
3676+
gep_indices.push(ConstantContent::get_uint(context, 64, field_idx));
3677+
}
3678+
}
3679+
}
3680+
(ProjectionKind::TupleField { index, .. }, TypeInfo::Tuple(field_tys)) => {
3681+
cur_type_id = field_tys[*index].type_id;
3682+
gep_indices.push(ConstantContent::get_uint(context, 64, *index as u64));
3683+
}
3684+
(ProjectionKind::ArrayIndex { index, .. }, TypeInfo::Array(elem_ty, _)) => {
3685+
cur_type_id = elem_ty.type_id;
3686+
let val = self.compile_expression_to_value(context, md_mgr, index)?;
3687+
if val.is_terminator {
3688+
return Ok((Some(val), vec![]));
3689+
}
3690+
gep_indices.push(val.value);
3691+
}
3692+
_ => {
3693+
return Err(CompileError::Internal(
3694+
"Unknown field in reassignment.",
3695+
idx_kind.span(),
3696+
))
3697+
}
3698+
}
3699+
}
3700+
3701+
Ok((None, gep_indices))
3702+
}
3703+
36653704
fn compile_array_repeat_expr(
36663705
&mut self,
36673706
context: &mut Context,

sway-core/src/language/ty/expression/expression.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,21 @@ impl TyExpression {
633633
TyExpressionVariant::Break => {}
634634
TyExpressionVariant::Continue => {}
635635
TyExpressionVariant::Reassignment(reass) => {
636-
if let TyReassignmentTarget::Deref(expr) = &reass.lhs {
637-
expr.check_deprecated(engines, handler, allow_deprecated);
636+
if let TyReassignmentTarget::DerefAccess { exp, indices } = &reass.lhs {
637+
exp.check_deprecated(engines, handler, allow_deprecated);
638+
for indice in indices {
639+
match indice {
640+
ProjectionKind::StructField { name: _ } => {}
641+
ProjectionKind::TupleField {
642+
index: _,
643+
index_span: _,
644+
} => {}
645+
ProjectionKind::ArrayIndex {
646+
index,
647+
index_span: _,
648+
} => index.check_deprecated(engines, handler, allow_deprecated),
649+
}
650+
}
638651
}
639652
reass
640653
.rhs

sway-core/src/language/ty/expression/expression_variant.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1562,7 +1562,25 @@ impl DebugWithEngines for TyExpressionVariant {
15621562
TyExpressionVariant::Continue => "continue".to_string(),
15631563
TyExpressionVariant::Reassignment(reassignment) => {
15641564
let target = match &reassignment.lhs {
1565-
TyReassignmentTarget::Deref(exp) => format!("{:?}", engines.help_out(exp)),
1565+
TyReassignmentTarget::DerefAccess { exp, indices } => {
1566+
let mut target = format!("{:?}", engines.help_out(exp));
1567+
for index in indices {
1568+
match index {
1569+
ProjectionKind::StructField { name } => {
1570+
target.push('.');
1571+
target.push_str(name.as_str());
1572+
}
1573+
ProjectionKind::TupleField { index, .. } => {
1574+
target.push('.');
1575+
target.push_str(index.to_string().as_str());
1576+
}
1577+
ProjectionKind::ArrayIndex { index, .. } => {
1578+
write!(&mut target, "[{:?}]", engines.help_out(index)).unwrap();
1579+
}
1580+
}
1581+
}
1582+
target
1583+
}
15661584
TyReassignmentTarget::ElementAccess {
15671585
base_name,
15681586
base_type: _,

0 commit comments

Comments
 (0)