diff --git a/sway-ast/src/assignable.rs b/sway-ast/src/assignable.rs index a6a07eb8a9b..0d22d4427ea 100644 --- a/sway-ast/src/assignable.rs +++ b/sway-ast/src/assignable.rs @@ -42,6 +42,12 @@ pub enum ElementAccess { field: BigUint, field_span: Span, }, + Deref { + target: Box, + star_token: StarToken, + /// Multiple Derefs can be nested, this is true for the outer most Deref. + is_root_element: bool, + }, } impl Spanned for Assignable { @@ -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()), } } } diff --git a/sway-ast/src/expr/mod.rs b/sway-ast/src/expr/mod.rs index 52999a63dcf..206355ab689 100644 --- a/sway-ast/src/expr/mod.rs +++ b/sway-ast/src/expr/mod.rs @@ -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 { - match self { + fn try_into_element_access( + self, + accept_deref_without_parens: bool, + ) -> Result { + 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, @@ -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, @@ -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, @@ -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), } } diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index b24645a28fd..046dca88d2b 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -2059,7 +2059,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>( } } } - ty::TyReassignmentTarget::Deref(exp) => { + ty::TyReassignmentTarget::DerefAccess { exp, indices } => { connect_expression( engines, &exp.expression, @@ -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, + )?; + } + } } }; diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 97748b500f8..98cc0693376 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -731,7 +731,7 @@ fn const_eval_typed_expr( }); } } - ty::TyReassignmentTarget::Deref(_) => { + ty::TyReassignmentTarget::DerefAccess { .. } => { return Err(ConstEvalError::CannotBeEvaluatedToConst { span: expr.span.clone(), }); diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index d6b7963568f..a5e5fe122c8 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -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::::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 @@ -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.", @@ -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) + } } }; @@ -3649,6 +3628,76 @@ 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: &Vec, + ) -> Result<(Option, Vec), 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::::new(); + let mut cur_type_id = base_type; + // TODO-IG: Add support for projections being references themselves. + for idx_kind in indices.iter() { + loop { + if let TypeInfo::Ref { + referenced_type, .. + } = &*self.engines.te().get_unaliased(cur_type_id) + { + cur_type_id = referenced_type.type_id; + } else { + break; + } + } + 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, diff --git a/sway-core/src/language/ty/expression/expression_variant.rs b/sway-core/src/language/ty/expression/expression_variant.rs index 8513cc0065f..d48c8310ab7 100644 --- a/sway-core/src/language/ty/expression/expression_variant.rs +++ b/sway-core/src/language/ty/expression/expression_variant.rs @@ -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: _, diff --git a/sway-core/src/language/ty/expression/reassignment.rs b/sway-core/src/language/ty/expression/reassignment.rs index 698af692dda..456d67443d7 100644 --- a/sway-core/src/language/ty/expression/reassignment.rs +++ b/sway-core/src/language/ty/expression/reassignment.rs @@ -42,13 +42,22 @@ pub enum TyReassignmentTarget { indices: Vec, }, /// An dereferencing [TyExpression] representing dereferencing - /// of an arbitrary reference expression. + /// of an arbitrary reference expression with optional indices. /// E.g.: /// - *my_ref /// - **if x > 0 { &mut &mut a } else { &mut &mut b } + /// - (*my_ref)\[0\] + /// - (**my_ref)\[0\] /// /// The [TyExpression] is guaranteed to be of [TyExpressionVariant::Deref]. - Deref(Box), + DerefAccess { + /// [TyExpression] of one or multiple nested [TyExpressionVariant::Deref]. + exp: Box, + /// Indices representing the path from the `base_name` to the + /// final part of an aggregate. + /// Empty if the LHS of the reassignment is a single variable. + indices: Vec, + }, } impl EqWithEngines for TyReassignmentTarget {} @@ -56,7 +65,16 @@ impl PartialEqWithEngines for TyReassignmentTarget { fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool { let type_engine = ctx.engines().te(); match (self, other) { - (TyReassignmentTarget::Deref(l), TyReassignmentTarget::Deref(r)) => (*l).eq(r, ctx), + ( + TyReassignmentTarget::DerefAccess { + exp: l, + indices: l_indices, + }, + TyReassignmentTarget::DerefAccess { + exp: r, + indices: r_indices, + }, + ) => (*l).eq(r, ctx) && l_indices.eq(r_indices, ctx), ( TyReassignmentTarget::ElementAccess { base_name: l_name, @@ -90,7 +108,10 @@ impl HashWithEngines for TyReassignmentTarget { fn hash(&self, state: &mut H, engines: &Engines) { let type_engine = engines.te(); match self { - TyReassignmentTarget::Deref(exp) => exp.hash(state, engines), + TyReassignmentTarget::DerefAccess { exp, indices } => { + exp.hash(state, engines); + indices.hash(state, engines); + } TyReassignmentTarget::ElementAccess { base_name, base_type, @@ -117,7 +138,12 @@ impl SubstTypes for TyReassignmentTarget { fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges { has_changes! { match self { - TyReassignmentTarget::Deref(exp) => exp.subst(ctx), + TyReassignmentTarget::DerefAccess{exp, indices} => { + has_changes! { + exp.subst(ctx); + indices.subst(ctx); + } + }, TyReassignmentTarget::ElementAccess { base_type, indices, .. } => { has_changes! { base_type.subst(ctx); @@ -146,7 +172,16 @@ impl ReplaceDecls for TyReassignmentTarget { ctx: &mut TypeCheckContext, ) -> Result { Ok(match self { - TyReassignmentTarget::Deref(exp) => exp.replace_decls(decl_mapping, handler, ctx)?, + TyReassignmentTarget::DerefAccess { exp, indices } => { + let mut changed = exp.replace_decls(decl_mapping, handler, ctx)?; + changed |= indices + .iter_mut() + .map(|i| i.replace_decls(decl_mapping, handler, ctx)) + .collect::, _>>()? + .iter() + .any(|is_changed| *is_changed); + changed + } TyReassignmentTarget::ElementAccess { indices, .. } => indices .iter_mut() .map(|i| i.replace_decls(decl_mapping, handler, ctx)) @@ -178,7 +213,14 @@ impl TypeCheckAnalysis for TyReassignmentTarget { ctx: &mut TypeCheckAnalysisContext, ) -> Result<(), ErrorEmitted> { match self { - TyReassignmentTarget::Deref(exp) => exp.type_check_analyze(handler, ctx)?, + TyReassignmentTarget::DerefAccess { exp, indices } => { + exp.type_check_analyze(handler, ctx)?; + indices + .iter() + .map(|i| i.type_check_analyze(handler, ctx)) + .collect::, _>>() + .map(|_| ())? + } TyReassignmentTarget::ElementAccess { indices, .. } => indices .iter() .map(|i| i.type_check_analyze(handler, ctx)) @@ -209,7 +251,14 @@ impl TypeCheckFinalization for TyReassignmentTarget { ctx: &mut TypeCheckFinalizationContext, ) -> Result<(), ErrorEmitted> { match self { - TyReassignmentTarget::Deref(exp) => exp.type_check_finalize(handler, ctx)?, + TyReassignmentTarget::DerefAccess { exp, indices } => { + exp.type_check_finalize(handler, ctx)?; + indices + .iter_mut() + .map(|i| i.type_check_finalize(handler, ctx)) + .collect::, _>>() + .map(|_| ())?; + } TyReassignmentTarget::ElementAccess { indices, .. } => indices .iter_mut() .map(|i| i.type_check_finalize(handler, ctx)) @@ -236,8 +285,11 @@ impl TypeCheckFinalization for TyReassignment { impl UpdateConstantExpression for TyReassignmentTarget { fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) { match self { - TyReassignmentTarget::Deref(exp) => { - exp.update_constant_expression(engines, implementing_type) + TyReassignmentTarget::DerefAccess { exp, indices } => { + exp.update_constant_expression(engines, implementing_type); + indices + .iter_mut() + .for_each(|i| i.update_constant_expression(engines, implementing_type)); } TyReassignmentTarget::ElementAccess { indices, .. } => { indices diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index c912d7d2b62..87cb7d9112d 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -48,7 +48,7 @@ use sway_error::{ handler::{ErrorEmitted, Handler}, warning::{CompileWarning, Warning}, }; -use sway_types::{integer_bits::IntegerBits, u256::U256, Ident, Named, Span, Spanned}; +use sway_types::{integer_bits::IntegerBits, u256::U256, BaseIdent, Ident, Named, Span, Spanned}; use symbol_collection_context::SymbolCollectionContext; use type_resolve::{resolve_call_path, VisibilityCheck}; @@ -2399,7 +2399,10 @@ impl ty::TyExpression { let expected_rhs_type = deref_exp.return_type; ( - TyReassignmentTarget::Deref(Box::new(deref_exp)), + TyReassignmentTarget::DerefAccess { + exp: Box::new(deref_exp), + indices: vec![], + }, expected_rhs_type, ) } @@ -2407,10 +2410,15 @@ impl ty::TyExpression { let lhs_span = path.span.clone(); let mut expr = path; let mut indices = Vec::new(); + + // This variaable is used after the loop. + #[allow(unused_assignments)] + let mut base_deref_expr = None; // Loop through the LHS "backwards" starting from the outermost expression // (the whole LHS) and moving towards the first identifier that must // be a mutable variable. let (base_name, base_type) = loop { + base_deref_expr = None; match expr.kind { ExpressionKind::Variable(name) => { // check that the reassigned name exists @@ -2497,6 +2505,18 @@ impl ty::TyExpression { }); expr = prefix; } + ExpressionKind::Deref(reference_exp) => { + let reference_exp_span = reference_exp.span(); + let deref_expr = Self::type_check_deref( + handler, + ctx.by_ref(), + &reference_exp, + reference_exp_span.clone(), + )?; + base_deref_expr = Some(deref_expr.clone()); + + break (BaseIdent::new(deref_expr.span), deref_expr.return_type); + } _ => { return Err( handler.emit_err(CompileError::InvalidExpressionOnLhs { span }) @@ -2514,18 +2534,29 @@ impl ty::TyExpression { ctx.engines(), ctx.namespace(), &base_name, + &base_deref_expr, &indices, ) })?; - ( - TyReassignmentTarget::ElementAccess { - base_name, - base_type, - indices, - }, - ty_of_field, - ) + if let Some(base_deref_expr) = base_deref_expr { + ( + TyReassignmentTarget::DerefAccess { + exp: Box::new(base_deref_expr), + indices, + }, + ty_of_field, + ) + } else { + ( + TyReassignmentTarget::ElementAccess { + base_name, + base_type, + indices, + }, + ty_of_field, + ) + } } }; @@ -2552,6 +2583,7 @@ impl ty::TyExpression { engines: &Engines, namespace: &Namespace, base_name: &Ident, + base_deref_expr: &Option, projections: &[ty::ProjectionKind], ) -> Result<(TypeId, TypeId), ErrorEmitted> { let ret = module.walk_scope_chain(|lexical_scope| { @@ -2561,6 +2593,7 @@ impl ty::TyExpression { engines, namespace, base_name, + base_deref_expr, projections, ) })?; @@ -2584,21 +2617,27 @@ impl ty::TyExpression { engines: &Engines, namespace: &Namespace, base_name: &Ident, + base_deref_expr: &Option, projections: &[ty::ProjectionKind], ) -> Result, ErrorEmitted> { let type_engine = engines.te(); let decl_engine = engines.de(); - let symbol = match lexical_scope.items.symbols.get(base_name).cloned() { - Some(s) => s, - None => { - return Ok(None); + let mut symbol = if let Some(ref base_deref_expr) = base_deref_expr { + base_deref_expr.return_type + } else { + let symbol = match lexical_scope.items.symbols.get(base_name).cloned() { + Some(s) => s, + None => { + return Ok(None); + } + }; + match symbol { + ResolvedDeclaration::Parsed(_) => unreachable!(), + ResolvedDeclaration::Typed(ty_decl) => ty_decl.return_type(handler, engines)?, } }; - let mut symbol = match symbol { - ResolvedDeclaration::Parsed(_) => unreachable!(), - ResolvedDeclaration::Typed(ty_decl) => ty_decl.return_type(handler, engines)?, - }; + let mut symbol_span = base_name.span(); let mut parent_rover = symbol; let mut full_span_for_error = base_name.span(); @@ -2673,41 +2712,56 @@ impl ty::TyExpression { symbol_span = index_span.clone(); full_span_for_error = Span::join(full_span_for_error, index_span); } - ( - TypeInfo::Array(elem_ty, array_length), - ty::ProjectionKind::ArrayIndex { index, index_span }, - ) if array_length.as_literal_val().is_some() => { - parent_rover = symbol; - symbol = elem_ty.type_id; - symbol_span = index_span.clone(); - - if let Some(index_literal) = index - .expression - .as_literal() - .and_then(|x| x.cast_value_to_u64()) + (mut actually, ty::ProjectionKind::ArrayIndex { index, index_span }) => { + if let TypeInfo::Ref { + referenced_type, .. + } = actually { - // SAFETY: safe by the guard above - let array_length = array_length - .as_literal_val() - .expect("unexpected non literal array length") - as u64; - if index_literal >= array_length { - return Err(handler.emit_err(CompileError::ArrayOutOfBounds { - index: index_literal, - count: array_length, - span: index.span.clone(), + actually = (*engines.te().get(referenced_type.type_id)).clone(); + } + match actually { + TypeInfo::Array(elem_ty, array_length) + if array_length.as_literal_val().is_some() => + { + parent_rover = symbol; + symbol = elem_ty.type_id; + symbol_span = index_span.clone(); + + if let Some(index_literal) = index + .expression + .as_literal() + .and_then(|x| x.cast_value_to_u64()) + { + // SAFETY: safe by the guard above + let array_length = array_length + .as_literal_val() + .expect("unexpected non literal array length") + as u64; + if index_literal >= array_length { + return Err(handler.emit_err(CompileError::ArrayOutOfBounds { + index: index_literal, + count: array_length, + span: index.span.clone(), + })); + } + } + + // `index_span` does not contain the enclosing square brackets. + // Which means, if this array index access is the last one before the + // erroneous expression, the `full_span_for_error` will be missing the + // closing `]`. We can live with this small glitch so far. To fix it, + // we would need to bring the full span of the index all the way from + // the parsing stage. An effort that doesn't pay off at the moment. + // TODO: Include the closing square bracket into the error span. + full_span_for_error = Span::join(full_span_for_error, index_span); + } + _ => { + return Err(handler.emit_err(CompileError::NotIndexable { + actually: engines.help_out(actually).to_string(), + span: full_span_for_error, })); } } - - // `index_span` does not contain the enclosing square brackets. - // Which means, if this array index access is the last one before the - // erroneous expression, the `full_span_for_error` will be missing the - // closing `]`. We can live with this small glitch so far. To fix it, - // we would need to bring the full span of the index all the way from - // the parsing stage. An effort that doesn't pay off at the moment. - // TODO: Include the closing square bracket into the error span. - full_span_for_error = Span::join(full_span_for_error, index_span); } (actually, ty::ProjectionKind::StructField { name }) => { return Err(handler.emit_err(CompileError::FieldAccessOnNonStruct { @@ -2732,12 +2786,6 @@ impl ty::TyExpression { }), ); } - (actually, ty::ProjectionKind::ArrayIndex { .. }) => { - return Err(handler.emit_err(CompileError::NotIndexable { - actually: engines.help_out(actually).to_string(), - span: full_span_for_error, - })); - } } } Ok(Some((symbol, parent_rover))) diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index 9a679c37873..8a5ee71cf38 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -4612,6 +4612,16 @@ fn assignable_to_expression( span, } } + ElementAccess::Deref { target, .. } => Expression { + kind: ExpressionKind::Deref(Box::new(element_access_to_expression( + context, + handler, + engines, + *target, + span.clone(), + )?)), + span, + }, }; Ok(expression) diff --git a/sway-lsp/src/traverse/lexed_tree.rs b/sway-lsp/src/traverse/lexed_tree.rs index 0c2e2b94c75..5500c6200f8 100644 --- a/sway-lsp/src/traverse/lexed_tree.rs +++ b/sway-lsp/src/traverse/lexed_tree.rs @@ -769,7 +769,8 @@ impl Parse for ElementAccess { | ElementAccess::TupleFieldProjection { target, .. } => { target.parse(ctx); } - _ => {} + ElementAccess::Deref { target, .. } => target.parse(ctx), + ElementAccess::Var(_) => {} } } } diff --git a/sway-lsp/src/traverse/typed_tree.rs b/sway-lsp/src/traverse/typed_tree.rs index fc32303b126..1b640e020db 100644 --- a/sway-lsp/src/traverse/typed_tree.rs +++ b/sway-lsp/src/traverse/typed_tree.rs @@ -1133,7 +1133,29 @@ impl Parse for ty::TyReassignment { fn parse(&self, ctx: &ParseContext) { self.rhs.parse(ctx); match &self.lhs { - TyReassignmentTarget::Deref(exp) => exp.parse(ctx), + TyReassignmentTarget::DerefAccess { exp, indices } => { + exp.parse(ctx); + adaptive_iter(indices, |proj_kind| { + if let ty::ProjectionKind::StructField { name } = proj_kind { + if let Some(mut token) = ctx.tokens.try_get_mut_with_retry(&ctx.ident(name)) + { + token.ast_node = + TokenAstNode::Typed(TypedAstToken::TypedReassignment(self.clone())); + if let Some(struct_decl) = &ctx + .tokens + .struct_declaration_of_type_id(ctx.engines, &exp.return_type) + { + struct_decl.fields.iter().for_each(|decl_field| { + if &decl_field.name == name { + token.type_def = + Some(TypeDefinition::Ident(decl_field.name.clone())); + } + }); + } + } + } + }); + } TyReassignmentTarget::ElementAccess { base_name, base_type, diff --git a/swayfmt/src/utils/language/expr/assignable.rs b/swayfmt/src/utils/language/expr/assignable.rs index 1df99ad70cf..34c7b8cf751 100644 --- a/swayfmt/src/utils/language/expr/assignable.rs +++ b/swayfmt/src/utils/language/expr/assignable.rs @@ -48,6 +48,20 @@ impl Format for ElementAccess { target.format(formatted_code, formatter)?; write!(formatted_code, "{}{}", DotToken::AS_STR, field)?; } + ElementAccess::Deref { + target, + star_token: _, + is_root_element: root_element, + } => { + if *root_element { + write!(formatted_code, "(")?; + } + write!(formatted_code, "{}", StarToken::AS_STR)?; + target.format(formatted_code, formatter)?; + if *root_element { + write!(formatted_code, ")")?; + } + } } Ok(()) } @@ -114,6 +128,14 @@ impl LeafSpans for ElementAccess { collected_spans.push(ByteSpan::from(dot_token.span())); collected_spans.push(ByteSpan::from(field_span.clone())); } + ElementAccess::Deref { + target, + star_token, + is_root_element: _, + } => { + collected_spans.push(ByteSpan::from(star_token.span())); + collected_spans.append(&mut target.leaf_spans()); + } }; collected_spans }