Skip to content
Merged
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
15 changes: 10 additions & 5 deletions src/analyser/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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).
Expand All @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion src/common/ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub enum Type {
Float,
Double,
Void,
Array(Box<Type>),
Array(Box<Type>, Option<usize>),
Pointer(Box<Type>),
Struct(String),
Enum(String),
Expand Down
5 changes: 4 additions & 1 deletion src/common/ast/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
162 changes: 145 additions & 17 deletions src/ir/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, i64> {
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
Expand All @@ -102,16 +110,17 @@ 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"),
)),
},
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(
Expand Down Expand Up @@ -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<Operand> {
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 {
Expand Down Expand Up @@ -806,6 +833,28 @@ fn resolve_alias(ty: &Type, typedefs: &HashMap<String, Type>) -> 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(&param.ty, typedefs),
is_const: param.is_const,
is_unsigned: param.is_unsigned,
})
.collect();
return Type::Function(Box::new(ret), params);
}
other => return other,
}
}
Expand Down Expand Up @@ -923,12 +972,18 @@ fn type_size(ty: &Type) -> LowerResult<i64> {
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"),
)),
}
}

Expand Down Expand Up @@ -964,6 +1019,14 @@ mod tests {
}
}

fn array_ty(inner: Type, size: Option<usize>) -> 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())
}
Expand Down Expand Up @@ -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(_),
..
}
)));
}
}
25 changes: 20 additions & 5 deletions src/parser/rules/declarations/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<QualifierType, CompilerError> {
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)
}

Expand Down
10 changes: 5 additions & 5 deletions src/tests/parser_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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 ────────────────────────────────────────────────────────────────

Expand Down
2 changes: 1 addition & 1 deletion src/tests/semantic_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Loading
Loading