Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements deref in reassignment for projections. #6941

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions sway-ast/src/assignable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub enum ElementAccess {
field: BigUint,
field_span: Span,
},
Deref {
target: Box<ElementAccess>,
star_token: StarToken,
/// Multiple Derefs can be nested, this is true for the outer most Deref.
is_root_element: bool,
},
}

impl Spanned for Assignable {
Expand All @@ -64,6 +70,11 @@ impl Spanned for ElementAccess {
ElementAccess::TupleFieldProjection {
target, field_span, ..
} => Span::join(target.span(), field_span),
ElementAccess::Deref {
target,
star_token,
is_root_element: _,
} => Span::join(target.span(), &star_token.span()),
}
}
}
41 changes: 35 additions & 6 deletions sway-ast/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,17 +473,22 @@ impl Expr {
if let Expr::Deref { star_token, expr } = self {
Ok(Assignable::Deref { star_token, expr })
} else {
Ok(Assignable::ElementAccess(self.try_into_element_access()?))
Ok(Assignable::ElementAccess(
self.try_into_element_access(false)?,
))
}
}

fn try_into_element_access(self) -> Result<ElementAccess, Expr> {
match self {
fn try_into_element_access(
self,
accept_deref_without_parens: bool,
) -> Result<ElementAccess, Expr> {
match self.clone() {
Expr::Path(path_expr) => match path_expr.try_into_ident() {
Ok(name) => Ok(ElementAccess::Var(name)),
Err(path_expr) => Err(Expr::Path(path_expr)),
},
Expr::Index { target, arg } => match target.try_into_element_access() {
Expr::Index { target, arg } => match target.try_into_element_access(false) {
Ok(target) => Ok(ElementAccess::Index {
target: Box::new(target),
arg,
Expand All @@ -494,7 +499,7 @@ impl Expr {
target,
dot_token,
name,
} => match target.try_into_element_access() {
} => match target.try_into_element_access(false) {
Ok(target) => Ok(ElementAccess::FieldProjection {
target: Box::new(target),
dot_token,
Expand All @@ -507,7 +512,7 @@ impl Expr {
dot_token,
field,
field_span,
} => match target.try_into_element_access() {
} => match target.try_into_element_access(false) {
Ok(target) => Ok(ElementAccess::TupleFieldProjection {
target: Box::new(target),
dot_token,
Expand All @@ -516,6 +521,30 @@ impl Expr {
}),
error => error,
},
Expr::Parens(Parens { inner, .. }) => {
if let Expr::Deref { expr, star_token } = *inner {
match expr.try_into_element_access(true) {
Ok(target) => Ok(ElementAccess::Deref {
target: Box::new(target),
star_token,
is_root_element: true,
}),
error => error,
}
} else {
Err(self)
}
}
Expr::Deref { expr, star_token } if accept_deref_without_parens => {
match expr.try_into_element_access(true) {
Ok(target) => Ok(ElementAccess::Deref {
target: Box::new(target),
star_token,
is_root_element: false,
}),
error => error,
}
}
expr => Err(expr),
}
}
Expand Down
18 changes: 17 additions & 1 deletion sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2059,7 +2059,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
}
}
}
ty::TyReassignmentTarget::Deref(exp) => {
ty::TyReassignmentTarget::DerefAccess { exp, indices } => {
connect_expression(
engines,
&exp.expression,
Expand All @@ -2071,6 +2071,22 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
exp.span.clone(),
options,
)?;

for projection in indices {
if let ProjectionKind::ArrayIndex { index, index_span } = projection {
connect_expression(
engines,
&index.expression,
graph,
leaves,
exit_node,
"variable reassignment LHS array index",
tree_type,
index_span.clone(),
options,
)?;
}
}
}
};

Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ fn const_eval_typed_expr(
});
}
}
ty::TyReassignmentTarget::Deref(_) => {
ty::TyReassignmentTarget::DerefAccess { .. } => {
return Err(ConstEvalError::CannotBeEvaluatedToConst {
span: expr.span.clone(),
});
Expand Down
162 changes: 103 additions & 59 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3549,63 +3549,10 @@ impl<'eng> FnCompiler<'eng> {
// A non-aggregate; use a direct `store`.
lhs_val
} else {
// Create a GEP by following the chain of LHS indices. We use a scan which is
// essentially a map with context, which is the parent type id for the current field.
let mut gep_indices = Vec::<Value>::new();
let mut cur_type_id = *base_type;
// TODO-IG: Add support for projections being references themselves.
for idx_kind in indices.iter() {
let cur_type_info_arc = self.engines.te().get_unaliased(cur_type_id);
let cur_type_info = &*cur_type_info_arc;
match (idx_kind, cur_type_info) {
(
ProjectionKind::StructField { name: idx_name },
TypeInfo::Struct(decl_ref),
) => {
let struct_decl = self.engines.de().get_struct(decl_ref);

match struct_decl.get_field_index_and_type(idx_name) {
None => {
return Err(CompileError::InternalOwned(
format!(
"Unknown field name \"{idx_name}\" for struct \"{}\" \
in reassignment.",
struct_decl.call_path.suffix.as_str(),
),
idx_name.span(),
))
}
Some((field_idx, field_type_id)) => {
cur_type_id = field_type_id;
gep_indices
.push(Constant::get_uint(context, 64, field_idx));
}
}
}
(
ProjectionKind::TupleField { index, .. },
TypeInfo::Tuple(field_tys),
) => {
cur_type_id = field_tys[*index].type_id;
gep_indices.push(Constant::get_uint(context, 64, *index as u64));
}
(
ProjectionKind::ArrayIndex { index, .. },
TypeInfo::Array(elem_ty, _),
) => {
cur_type_id = elem_ty.type_id;
let val = return_on_termination_or_extract!(
self.compile_expression_to_value(context, md_mgr, index)?
);
gep_indices.push(val);
}
_ => {
return Err(CompileError::Internal(
"Unknown field in reassignment.",
idx_kind.span(),
))
}
}
let (terminator, gep_indices) =
self.compile_indices(context, md_mgr, *base_type, indices)?;
if let Some(terminator) = terminator {
return Ok(terminator);
}

// Using the type of the RHS for the GEP, rather than the final inner type of the
Expand All @@ -3625,7 +3572,10 @@ impl<'eng> FnCompiler<'eng> {
.add_metadatum(context, span_md_idx)
}
}
ty::TyReassignmentTarget::Deref(dereference_exp) => {
ty::TyReassignmentTarget::DerefAccess {
exp: dereference_exp,
indices,
} => {
let TyExpressionVariant::Deref(reference_exp) = &dereference_exp.expression else {
return Err(CompileError::Internal(
"Left-hand side of the reassignment must be dereferencing.",
Expand All @@ -3636,7 +3586,36 @@ impl<'eng> FnCompiler<'eng> {
let (ptr, _) =
self.compile_deref_up_to_ptr(context, md_mgr, reference_exp, span_md_idx)?;

return_on_termination_or_extract!(ptr)
if indices.is_empty() {
// A non-aggregate;
return_on_termination_or_extract!(ptr)
} else {
let (terminator, gep_indices) = self.compile_indices(
context,
md_mgr,
dereference_exp.return_type,
indices,
)?;
if let Some(terminator) = terminator {
return Ok(terminator);
}

// Using the type of the RHS for the GEP, rather than the final inner type of the
// aggregate, but getting the latter is a bit of a pain, though the `scan` above knew it.
// The program is type checked and the IR types on the LHS and RHS are the same.
let field_type = rhs.get_type(context).ok_or_else(|| {
CompileError::Internal(
"Failed to determine type of reassignment.",
dereference_exp.span.clone(),
)
})?;

// Create the GEP.
self.current_block
.append(context)
.get_elem_ptr(ptr.value, field_type, gep_indices)
.add_metadatum(context, span_md_idx)
}
}
};

Expand All @@ -3649,6 +3628,71 @@ impl<'eng> FnCompiler<'eng> {
Ok(TerminatorValue::new(val, context))
}

fn compile_indices(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
base_type: TypeId,
indices: &[ProjectionKind],
) -> Result<(Option<TerminatorValue>, Vec<Value>), CompileError> {
// Create a GEP by following the chain of LHS indices. We use a scan which is
// essentially a map with context, which is the parent type id for the current field.
let mut gep_indices = Vec::<Value>::new();
let mut cur_type_id = base_type;
for idx_kind in indices.iter() {
while let TypeInfo::Ref {
referenced_type, ..
} = &*self.engines.te().get_unaliased(cur_type_id)
{
cur_type_id = referenced_type.type_id;
}
let cur_type_info_arc = self.engines.te().get_unaliased(cur_type_id);
let cur_type_info = &*cur_type_info_arc;
match (idx_kind, cur_type_info) {
(ProjectionKind::StructField { name: idx_name }, TypeInfo::Struct(decl_ref)) => {
let struct_decl = self.engines.de().get_struct(decl_ref);

match struct_decl.get_field_index_and_type(idx_name) {
None => {
return Err(CompileError::InternalOwned(
format!(
"Unknown field name \"{idx_name}\" for struct \"{}\" \
in reassignment.",
struct_decl.call_path.suffix.as_str(),
),
idx_name.span(),
))
}
Some((field_idx, field_type_id)) => {
cur_type_id = field_type_id;
gep_indices.push(Constant::get_uint(context, 64, field_idx));
}
}
}
(ProjectionKind::TupleField { index, .. }, TypeInfo::Tuple(field_tys)) => {
cur_type_id = field_tys[*index].type_id;
gep_indices.push(Constant::get_uint(context, 64, *index as u64));
}
(ProjectionKind::ArrayIndex { index, .. }, TypeInfo::Array(elem_ty, _)) => {
cur_type_id = elem_ty.type_id;
let val = self.compile_expression_to_value(context, md_mgr, index)?;
if val.is_terminator {
return Ok((Some(val), vec![]));
}
gep_indices.push(val.value);
}
_ => {
return Err(CompileError::Internal(
"Unknown field in reassignment.",
idx_kind.span(),
))
}
}
}

Ok((None, gep_indices))
}

fn compile_array_repeat_expr(
&mut self,
context: &mut Context,
Expand Down
20 changes: 19 additions & 1 deletion sway-core/src/language/ty/expression/expression_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1562,7 +1562,25 @@ impl DebugWithEngines for TyExpressionVariant {
TyExpressionVariant::Continue => "continue".to_string(),
TyExpressionVariant::Reassignment(reassignment) => {
let target = match &reassignment.lhs {
TyReassignmentTarget::Deref(exp) => format!("{:?}", engines.help_out(exp)),
TyReassignmentTarget::DerefAccess { exp, indices } => {
let mut target = format!("{:?}", engines.help_out(exp));
for index in indices {
match index {
ProjectionKind::StructField { name } => {
target.push('.');
target.push_str(name.as_str());
}
ProjectionKind::TupleField { index, .. } => {
target.push('.');
target.push_str(index.to_string().as_str());
}
ProjectionKind::ArrayIndex { index, .. } => {
write!(&mut target, "[{:?}]", engines.help_out(index)).unwrap();
}
}
}
target
}
TyReassignmentTarget::ElementAccess {
base_name,
base_type: _,
Expand Down
Loading
Loading