diff --git a/src/analyser/semantic.rs b/src/analyser/semantic.rs index 1c95af1..7cb8158 100644 --- a/src/analyser/semantic.rs +++ b/src/analyser/semantic.rs @@ -513,7 +513,7 @@ impl SemanticAnalyser { is_unsigned: false, }, crate::common::ast::expr::UnOp::Deref => match inner_ty.ty { - Type::Pointer(base) | Type::Array(base) => QualifierType { + Type::Pointer(base) | Type::Array(base, _) => QualifierType { ty: *base, is_const: inner_ty.is_const, is_unsigned: inner_ty.is_unsigned, @@ -655,7 +655,7 @@ impl SemanticAnalyser { } match arr_ty.ty { - Type::Array(inner) | Type::Pointer(inner) => QualifierType { + Type::Array(inner, _) | Type::Pointer(inner) => QualifierType { ty: *inner, is_const: arr_ty.is_const, is_unsigned: arr_ty.is_unsigned, @@ -793,7 +793,9 @@ impl SemanticAnalyser { fn resolve_type_inner(&self, ty: &Type) -> Type { match ty { - Type::Array(inner) => Type::Array(Box::new(self.resolve_type_inner(inner))), + Type::Array(inner, size) => { + Type::Array(Box::new(self.resolve_type_inner(inner)), *size) + } Type::Pointer(inner) => Type::Pointer(Box::new(self.resolve_type_inner(inner))), Type::Alias(name) => self .sym @@ -1029,7 +1031,7 @@ fn is_numeric(ty: &Type) -> bool { /// Retorna `true` se o tipo é ponteiro ou array (array decai para ponteiro em C). fn is_pointer(ty: &Type) -> bool { - matches!(ty, Type::Pointer(_) | Type::Array(_)) + matches!(ty, Type::Pointer(_) | Type::Array(_, _)) } /// Retorna `true` se o tipo é escalar (numérico, ponteiro ou enum). @@ -1049,7 +1051,10 @@ fn type_name(ty: &Type) -> String { Type::Double => "double".into(), Type::Void => "void".into(), Type::Pointer(inner) => format!("{}*", type_name(inner)), - Type::Array(inner) => format!("{}[]", type_name(inner)), + Type::Array(inner, size) => match size { + Some(size) => format!("{}[{size}]", type_name(inner)), + None => format!("{}[]", type_name(inner)), + }, Type::Struct(n) => format!("struct {}", n), Type::Enum(n) => format!("enum {}", n), Type::Alias(n) => n.clone(), diff --git a/src/common/ast/ast.rs b/src/common/ast/ast.rs index d54e8d7..0baa81d 100644 --- a/src/common/ast/ast.rs +++ b/src/common/ast/ast.rs @@ -10,7 +10,7 @@ pub enum Type { Float, Double, Void, - Array(Box), + Array(Box, Option), Pointer(Box), Struct(String), Enum(String), diff --git a/src/common/ast/pretty.rs b/src/common/ast/pretty.rs index a413374..fe73cd4 100644 --- a/src/common/ast/pretty.rs +++ b/src/common/ast/pretty.rs @@ -357,7 +357,10 @@ fn fmt_type(ty: &Type) -> String { Type::Double => "double".into(), Type::Void => "void".into(), Type::Pointer(inner) => format!("{}*", fmt_type(inner)), - Type::Array(inner) => format!("{}[]", fmt_type(inner)), + Type::Array(inner, size) => match size { + Some(size) => format!("{}[{size}]", fmt_type(inner)), + None => format!("{}[]", fmt_type(inner)), + }, Type::Struct(n) => format!("struct {}", n), Type::Enum(n) => format!("enum {}", n), Type::Alias(n) => n.clone(), diff --git a/src/ir/lower.rs b/src/ir/lower.rs index 5fe7b70..9179775 100644 --- a/src/ir/lower.rs +++ b/src/ir/lower.rs @@ -74,15 +74,23 @@ impl Lowerer { } /// Tamanho (em bytes) de cada variavel cujo valor nao cabe num slot - /// escalar de 8 bytes — hoje, apenas structs locais. Chamado ao final do + /// escalar de 8 bytes — structs locais e arrays fixos. Chamado ao final do /// lowering de uma funcao, para popular `TacFunction::var_sizes`. fn compute_var_sizes(&self) -> HashMap { let mut sizes = HashMap::new(); for (name, ty) in &self.var_types { - if let Type::Struct(struct_name) = resolve_alias(ty, &self.typedefs) { - if let Some(layout) = self.struct_layouts.get(&struct_name) { - sizes.insert(name.clone(), layout.size); + match resolve_alias(ty, &self.typedefs) { + Type::Struct(struct_name) => { + if let Some(layout) = self.struct_layouts.get(&struct_name) { + sizes.insert(name.clone(), layout.size); + } + } + array_ty @ Type::Array(_, _) => { + if let Ok(size) = type_size(&array_ty) { + sizes.insert(name.clone(), size); + } } + _ => {} } } sizes @@ -102,7 +110,7 @@ impl Lowerer { .map(|ty| resolve_alias(ty, &self.typedefs)) .ok_or_else(|| codegen_error("tipo de variavel desconhecido no lowering", Some("type"))), Expr::Unary(UnOp::Deref, inner, _) => match self.infer_type(inner)? { - Type::Pointer(t) | Type::Array(t) => Ok(resolve_alias(&t, &self.typedefs)), + Type::Pointer(t) | Type::Array(t, _) => Ok(resolve_alias(&t, &self.typedefs)), _ => Err(codegen_error( "deref de valor que nao e ponteiro/array", Some("type"), @@ -110,8 +118,9 @@ impl Lowerer { }, Expr::Index(arr, _, _) => match self.infer_type(arr)? { Type::Pointer(t) => Ok(resolve_alias(&t, &self.typedefs)), - Type::Array(_) => Err(codegen_error( - "indexacao de array fixo ainda nao suportada (tamanho do array nao e rastreado pelo lowering); indexacao via ponteiro funciona normalmente", + Type::Array(t, Some(_)) => Ok(resolve_alias(&t, &self.typedefs)), + Type::Array(_, None) => Err(codegen_error( + "indexacao de array com tamanho desconhecido nao suportada no lowering", Some("index"), )), _ => Err(codegen_error( @@ -182,13 +191,31 @@ impl Lowerer { }) } - /// Calcula o endereco (em bytes) de `arr[idx]`, assumindo que `arr` e um - /// ponteiro: `endereco = lower_expr(arr) + idx * sizeof(elemento)`. + /// Calcula o endereco (em bytes) de `arr[idx]`. + /// + /// Para ponteiros, a base e o valor do ponteiro. Para arrays fixos, a + /// base e o endereco do bloco contiguo reservado para a variavel. fn lower_index_address(&mut self, arr: &Expr, idx: &Expr) -> LowerResult { - let elem_ty = self.infer_type(arr)?; + let (elem_ty, base_ptr) = match self.infer_type(arr)? { + Type::Pointer(inner) => (resolve_alias(&inner, &self.typedefs), self.lower_expr(arr)?), + Type::Array(inner, Some(_)) => ( + resolve_alias(&inner, &self.typedefs), + self.lower_address_of(arr)?, + ), + Type::Array(_, None) => { + return Err(codegen_error( + "indexacao de array com tamanho desconhecido nao suportada no lowering", + Some("index"), + )) + } + _ => { + return Err(codegen_error( + "indexacao de valor que nao e ponteiro/array", + Some("index"), + )) + } + }; let elem_size = type_size(&elem_ty)?; - - let base_ptr = self.lower_expr(arr)?; let idx_op = self.lower_expr(idx)?; let offset = if elem_size == 1 { @@ -806,6 +833,28 @@ fn resolve_alias(ty: &Type, typedefs: &HashMap) -> Type { Some(next) => current = next.clone(), None => return Type::Alias(name), }, + Type::Pointer(inner) => { + return Type::Pointer(Box::new(resolve_alias(&inner, typedefs))) + } + Type::Array(inner, size) => { + return Type::Array(Box::new(resolve_alias(&inner, typedefs)), size) + } + Type::Function(ret, params) => { + let ret = QualifierType { + ty: resolve_alias(&ret.ty, typedefs), + is_const: ret.is_const, + is_unsigned: ret.is_unsigned, + }; + let params = params + .iter() + .map(|param| QualifierType { + ty: resolve_alias(¶m.ty, typedefs), + is_const: param.is_const, + is_unsigned: param.is_unsigned, + }) + .collect(); + return Type::Function(Box::new(ret), params); + } other => return other, } } @@ -923,12 +972,18 @@ fn type_size(ty: &Type) -> LowerResult { Type::Short => Ok(2), Type::Int | Type::Float | Type::Enum(_) => Ok(4), Type::Long | Type::Double | Type::Pointer(_) => Ok(8), - Type::Array(_) | Type::Void | Type::Struct(_) | Type::Alias(_) | Type::Function(_, _) => { - Err(codegen_error( - "lowering de sizeof(type) requer layout/tamanho completo", - Some("sizeof"), - )) + Type::Array(inner, Some(len)) => { + let elem_size = type_size(inner)?; + Ok(elem_size * (*len as i64)) } + Type::Array(_, None) => Err(codegen_error( + "lowering de sizeof(array) requer tamanho de array conhecido", + Some("sizeof"), + )), + Type::Void | Type::Struct(_) | Type::Alias(_) | Type::Function(_, _) => Err(codegen_error( + "lowering de sizeof(type) requer layout/tamanho completo", + Some("sizeof"), + )), } } @@ -964,6 +1019,14 @@ mod tests { } } + fn array_ty(inner: Type, size: Option) -> QualifierType { + QualifierType { + ty: Type::Array(Box::new(inner), size), + is_const: false, + is_unsigned: false, + } + } + fn int(value: i64) -> Expr { Expr::Literal(Literal::Int(value), span()) } @@ -1142,4 +1205,69 @@ mod tests { }] ); } + + #[test] + fn lower_fixed_array_local_populates_var_sizes() { + let decl = Decl::Function( + int_ty(), + "main".to_string(), + vec![], + vec![ + Stmt::VarDecl( + array_ty(Type::Int, Some(3)), + "arr".to_string(), + None, + span(), + ), + Stmt::Return(Some(int(0)), span()), + ], + span(), + ); + + let func = lower_function(&decl).unwrap(); + + assert_eq!(func.var_sizes.get("arr"), Some(&12)); + } + + #[test] + fn lower_fixed_array_index_uses_address_of_base() { + let mut lowerer = Lowerer::new(); + lowerer.declare_var_type("arr", &Type::Array(Box::new(Type::Int), Some(3))); + let expr = Expr::Assign( + Box::new(Expr::Index( + Box::new(ident("arr")), + Box::new(int(1)), + span(), + )), + Box::new(int(7)), + span(), + ); + + lowerer.lower_expr(&expr).unwrap(); + let instrs = lowerer.finish(); + + assert!(instrs.iter().any(|instr| matches!( + instr, + TacInstr::UnOp { + op: UnOp::AddrOf, + src: Operand::Var(name), + .. + } if name == "arr" + ))); + assert!(instrs.iter().any(|instr| matches!( + instr, + TacInstr::BinOp { + op: BinOp::Mul, + rhs: Operand::Const(ConstValue::Int(4)), + .. + } + ))); + assert!(instrs.iter().any(|instr| matches!( + instr, + TacInstr::Copy { + dst: Operand::Deref(_), + .. + } + ))); + } } diff --git a/src/parser/rules/declarations/types.rs b/src/parser/rules/declarations/types.rs index b63f867..8820b1f 100644 --- a/src/parser/rules/declarations/types.rs +++ b/src/parser/rules/declarations/types.rs @@ -4,20 +4,35 @@ use crate::lexer::tokens::token_kind::TokenKind; use crate::parser::parser::Parser; /// Consome sufixos `[expr?]` após o nome de uma variável e envolve o tipo em `Type::Array`. -/// Suporta múltiplas dimensões: `int arr[3][4]` → `Array(Array(Int))`. -/// O tamanho é consumido mas não armazenado (AST atual não possui campo de tamanho). +/// Suporta múltiplas dimensões: `int arr[3][4]` → `Array(Array(Int, Some(4)), Some(3))`. pub fn parse_array_suffix( parser: &mut Parser, mut qty: QualifierType, ) -> Result { + let mut dimensions = Vec::new(); + while parser.check(&TokenKind::LeftBracket) { parser.advance(); - if !parser.check(&TokenKind::RightBracket) { + + let size = if parser.check(&TokenKind::RightBracket) { + None + } else if let TokenKind::IntLiteral(value) = parser.peek_kind() { + let value = *value; parser.parse_expr(0)?; - } + usize::try_from(value).ok() + } else { + parser.parse_expr(0)?; + None + }; + parser.expect(&TokenKind::RightBracket, "']' ao fim do tamanho do array")?; - qty.ty = Type::Array(Box::new(qty.ty)); + dimensions.push(size); + } + + for size in dimensions.into_iter().rev() { + qty.ty = Type::Array(Box::new(qty.ty), size); } + Ok(qty) } diff --git a/src/tests/parser_test.rs b/src/tests/parser_test.rs index 3e4d824..b4285ef 100644 --- a/src/tests/parser_test.rs +++ b/src/tests/parser_test.rs @@ -1542,7 +1542,7 @@ mod tests { panic!("esperava GlobalVar"); }; assert_eq!(name, "arr"); - assert!(matches!(qty.ty, Type::Array(_))); + assert!(matches!(qty.ty, Type::Array(_, Some(10)))); } #[test] @@ -1572,7 +1572,7 @@ mod tests { panic!("esperava VarDecl"); }; assert_eq!(name, "arr"); - assert!(matches!(qty.ty, Type::Array(_))); + assert!(matches!(qty.ty, Type::Array(_, Some(5)))); } #[test] @@ -1595,10 +1595,10 @@ mod tests { let Decl::GlobalVar(qty, _, None, _) = &prog.decls[0] else { panic!("esperava GlobalVar"); }; - let Type::Array(inner) = &qty.ty else { + let Type::Array(inner, Some(3)) = &qty.ty else { panic!("esperava Array externo"); }; - assert!(matches!(**inner, Type::Array(_))); + assert!(matches!(**inner, Type::Array(_, Some(4)))); } #[test] @@ -1619,7 +1619,7 @@ mod tests { let Decl::GlobalVar(qty, _, _, _) = &prog.decls[0] else { panic!("esperava GlobalVar"); }; - assert!(matches!(qty.ty, Type::Array(_))); + assert!(matches!(qty.ty, Type::Array(_, None))); } // ── struct ──────────────────────────────────────────────────────────────── diff --git a/src/tests/semantic_test.rs b/src/tests/semantic_test.rs index 08b7412..40c71f8 100644 --- a/src/tests/semantic_test.rs +++ b/src/tests/semantic_test.rs @@ -767,7 +767,7 @@ mod tests { .sym .declare(crate::analyser::symbol_table::Symbol { name: name.into(), - ty: qty(Type::Array(Box::new(inner))), + ty: qty(Type::Array(Box::new(inner), Some(4))), mutable: true, params: None, decl_span: span(), diff --git a/tests/exe_smoke_test.rs b/tests/exe_smoke_test.rs index e75c6ad..296b47a 100644 --- a/tests/exe_smoke_test.rs +++ b/tests/exe_smoke_test.rs @@ -259,6 +259,25 @@ fn smoke_pointer_index_read_and_write_runs() { assert_eq!(status.code(), Some(15)); } +#[test] +fn smoke_fixed_array_index_read_and_write_runs() { + require_gcc!(); + + let status = compile_and_run( + "fixed_array_index", + "int main() { \ + int arr[3]; \ + arr[0] = 1; \ + arr[1] = 2; \ + arr[2] = 3; \ + return arr[0] + arr[1] + arr[2]; \ + }", + ); + + #[cfg(unix)] + assert_eq!(status.code(), Some(6)); +} + #[test] fn smoke_struct_member_read_and_write_runs() { require_gcc!();