diff --git a/cc/arch/aarch64/codegen.rs b/cc/arch/aarch64/codegen.rs index 33f468f5..2f5f4de4 100644 --- a/cc/arch/aarch64/codegen.rs +++ b/cc/arch/aarch64/codegen.rs @@ -168,8 +168,11 @@ impl Aarch64CodeGen { // Data section self.push_lir(Aarch64Inst::Directive(Directive::Data)); - // Global visibility (if not static) - if !is_static { + // Check if this is a local symbol (starts with '.') + let is_local = name.starts_with('.'); + + // Global visibility (if not static and not local) + if !is_static && !is_local { self.push_lir(Aarch64Inst::Directive(Directive::global(name))); } @@ -184,8 +187,12 @@ impl Aarch64CodeGen { ))); } - // Label - self.push_lir(Aarch64Inst::Directive(Directive::global_label(name))); + // Label - use local_label for names starting with '.' + if is_local { + self.push_lir(Aarch64Inst::Directive(Directive::local_label(name))); + } else { + self.push_lir(Aarch64Inst::Directive(Directive::global_label(name))); + } // Emit initializer self.emit_initializer_data(init, size as usize); diff --git a/cc/arch/x86_64/codegen.rs b/cc/arch/x86_64/codegen.rs index f7b36fc6..37c7a156 100644 --- a/cc/arch/x86_64/codegen.rs +++ b/cc/arch/x86_64/codegen.rs @@ -168,8 +168,11 @@ impl X86_64CodeGen { // Data section self.push_lir(X86Inst::Directive(Directive::Data)); - // Global visibility (if not static) - if !is_static { + // Check if this is a local symbol (starts with '.') + let is_local = name.starts_with('.'); + + // Global visibility (if not static and not local) + if !is_static && !is_local { self.push_lir(X86Inst::Directive(Directive::global(name))); } @@ -182,8 +185,12 @@ impl X86_64CodeGen { self.push_lir(X86Inst::Directive(Directive::Align(align.trailing_zeros()))); } - // Label - self.push_lir(X86Inst::Directive(Directive::global_label(name))); + // Label - use local_label for names starting with '.' + if is_local { + self.push_lir(X86Inst::Directive(Directive::local_label(name))); + } else { + self.push_lir(X86Inst::Directive(Directive::global_label(name))); + } // Emit initializer self.emit_initializer_data(init, size as usize); diff --git a/cc/doc/BUGS.md b/cc/doc/BUGS.md index 2d9f20a9..a82120e2 100644 --- a/cc/doc/BUGS.md +++ b/cc/doc/BUGS.md @@ -4,25 +4,6 @@ This file documents known bugs where valid C99 code fails to compile. ## Parse Errors on Valid C99 Code -### Compound Literals - -**Status**: Parse error - -**Valid C99 Code**: -```c -struct Point { int x, y; }; -struct Point p = (struct Point){1, 2}; // Compound literal -``` - -**Error**: -``` -parse error: unexpected token in expression -``` - -**C99 Reference**: 6.5.2.5 - ---- - ### Array Parameter with `static` **Status**: Parse error diff --git a/cc/ir/linearize.rs b/cc/ir/linearize.rs index a6e7e368..e42add9b 100644 --- a/cc/ir/linearize.rs +++ b/cc/ir/linearize.rs @@ -94,6 +94,8 @@ pub struct Linearizer<'a> { current_func_name: String, /// Counter for generating unique static local names static_local_counter: u32, + /// Counter for generating unique compound literal names (for file-scope compound literals) + compound_literal_counter: u32, /// Static local variables (local name -> static local info) /// This is persistent across function calls (not cleared per function) static_locals: HashMap, @@ -135,6 +137,7 @@ impl<'a> Linearizer<'a> { struct_return_size: 0, current_func_name: String::new(), static_local_counter: 0, + compound_literal_counter: 0, static_locals: HashMap::new(), current_pos: None, target, @@ -518,7 +521,8 @@ impl<'a> Linearizer<'a> { /// - Struct initializers with designated and positional fields /// - Address-of expressions (&symbol) /// - Nested initializers - fn ast_init_to_ir(&self, expr: &Expr, typ: TypeId) -> Initializer { + /// - Compound literals (C99 6.5.2.5) + fn ast_init_to_ir(&mut self, expr: &Expr, typ: TypeId) -> Initializer { match &expr.kind { ExprKind::IntLit(v) => Initializer::Int(*v), ExprKind::FloatLit(v) => Initializer::Float(*v), @@ -573,6 +577,33 @@ impl<'a> Linearizer<'a> { // Initializer list for arrays/structs ExprKind::InitList { elements } => self.ast_init_list_to_ir(elements, typ), + // Compound literal in initializer context (C99 6.5.2.5) + ExprKind::CompoundLiteral { + typ: cl_type, + elements, + } => { + // Check if compound literal type matches target type + if *cl_type == typ { + // Direct value - treat like InitList + self.ast_init_list_to_ir(elements, typ) + } else if self.types.kind(typ) == TypeKind::Pointer { + // Pointer initialization - create anonymous static global + // and return its address + let anon_name = format!(".CL{}", self.compound_literal_counter); + self.compound_literal_counter += 1; + + // Create the anonymous global + let init = self.ast_init_list_to_ir(elements, *cl_type); + self.module.add_global(&anon_name, *cl_type, init); + + // Return address of the anonymous global + Initializer::SymAddr(anon_name) + } else { + // Type mismatch - try to use the initializer list directly + self.ast_init_list_to_ir(elements, typ) + } + } + // Identifier - for constant addresses (function pointers, array decay, etc.) // or enum constants ExprKind::Ident { name } => { @@ -603,7 +634,7 @@ impl<'a> Linearizer<'a> { } /// Convert an AST initializer list to an IR Initializer - fn ast_init_list_to_ir(&self, elements: &[InitElement], typ: TypeId) -> Initializer { + fn ast_init_list_to_ir(&mut self, elements: &[InitElement], typ: TypeId) -> Initializer { let type_kind = self.types.kind(typ); let total_size = (self.types.size_bits(typ) / 8) as usize; @@ -2147,6 +2178,28 @@ impl<'a> Linearizer<'a> { )); addr } + ExprKind::CompoundLiteral { typ, elements } => { + // Compound literal as lvalue: create it and return its address + // This is used for &(struct S){...} + let sym_id = self.alloc_pseudo(); + let unique_name = format!(".compound_literal#{}", sym_id.0); + let sym = Pseudo::sym(sym_id, unique_name.clone()); + if let Some(func) = &mut self.current_func { + func.add_pseudo(sym); + func.add_local(&unique_name, sym_id, *typ, false, self.current_bb); + } + self.linearize_init_list(sym_id, *typ, elements); + + // Return address of the compound literal + let result = self.alloc_pseudo(); + let pseudo = Pseudo::reg(result, result.0); + if let Some(func) = &mut self.current_func { + func.add_pseudo(pseudo); + } + let ptr_type = self.types.pointer_to(*typ); + self.emit(Instruction::sym_addr(result, sym_id, ptr_type)); + result + } _ => { // Fallback: just evaluate the expression (shouldn't happen for valid lvalues) self.linearize_expr(expr) @@ -3249,6 +3302,44 @@ impl<'a> Linearizer<'a> { panic!("InitList should be handled in declaration context, not as standalone expression") } + ExprKind::CompoundLiteral { typ, elements } => { + // Compound literals have automatic storage at block scope + // Create an anonymous local variable, similar to how local variables work + + // Create a symbol pseudo for the compound literal (its address) + let sym_id = self.alloc_pseudo(); + let unique_name = format!(".compound_literal#{}", sym_id.0); + let sym = Pseudo::sym(sym_id, unique_name.clone()); + if let Some(func) = &mut self.current_func { + func.add_pseudo(sym); + // Register as local for proper stack allocation + func.add_local(&unique_name, sym_id, *typ, false, self.current_bb); + } + + // Initialize using existing init list machinery + self.linearize_init_list(sym_id, *typ, elements); + + // For arrays: return pointer (array-to-pointer decay) + // For structs/scalars: load and return the value + let result = self.alloc_pseudo(); + let pseudo = Pseudo::reg(result, result.0); + if let Some(func) = &mut self.current_func { + func.add_pseudo(pseudo); + } + + if self.types.kind(*typ) == TypeKind::Array { + // Array compound literal - decay to pointer to first element + let elem_type = self.types.base_type(*typ).unwrap_or(self.types.int_id); + let ptr_type = self.types.pointer_to(elem_type); + self.emit(Instruction::sym_addr(result, sym_id, ptr_type)); + } else { + // Struct/scalar compound literal - load the value + let size = self.types.size_bits(*typ); + self.emit(Instruction::load(result, sym_id, 0, *typ, size)); + } + result + } + // ================================================================ // Variadic function support (va_* builtins) // ================================================================ diff --git a/cc/parse/ast.rs b/cc/parse/ast.rs index d2a5e1f4..b1ba92ee 100644 --- a/cc/parse/ast.rs +++ b/cc/parse/ast.rs @@ -244,6 +244,13 @@ pub enum ExprKind { expr: Box, }, + /// Compound literal: (type){ init-list } + /// C99 6.5.2.5: Creates unnamed object with automatic/static storage + CompoundLiteral { + typ: TypeId, + elements: Vec, + }, + /// sizeof type: sizeof(int) SizeofType(TypeId), diff --git a/cc/parse/parser.rs b/cc/parse/parser.rs index f0b09366..6c757951 100644 --- a/cc/parse/parser.rs +++ b/cc/parse/parser.rs @@ -1511,12 +1511,19 @@ impl<'a> Parser<'a> { result_id = self.types.intern(Type::pointer(result_id)); } - // Handle array declarators: int[10], char[20], etc. + // Handle array declarators: int[10], char[20], int[], etc. while self.is_special(b'[') { self.advance(); - if let Ok(size_expr) = self.parse_conditional_expr() { + // Check for empty brackets [] (incomplete array type for compound literals) + if self.is_special(b']') { + // Empty brackets - create array with size 0 (size to be determined from initializer) + result_id = self.types.intern(Type::array(result_id, 0)); + } else if let Ok(size_expr) = self.parse_conditional_expr() { if let Some(size) = self.eval_const_expr(&size_expr) { result_id = self.types.intern(Type::array(result_id, size as usize)); + } else { + // Non-constant size (VLA in type name) - create array with size 0 + result_id = self.types.intern(Type::array(result_id, 0)); } } if !self.is_special(b']') { @@ -2214,11 +2221,42 @@ impl<'a> Parser<'a> { let paren_pos = self.current_pos(); self.advance(); - // Try to detect cast (type) - simplified check + // Try to detect cast (type) or compound literal (type){...} if let Some(typ) = self.try_parse_type_name() { self.expect_special(b')')?; + + // Check for compound literal: (type){ ... } + if self.is_special(b'{') { + let init_list = self.parse_initializer_list()?; + let elements = match init_list.kind { + ExprKind::InitList { elements } => elements, + _ => unreachable!(), + }; + + // For incomplete array types (size 0), determine size from initializer + let final_typ = if self.types.kind(typ) == TypeKind::Array + && self.types.get(typ).array_size == Some(0) + { + // Array size should be determined from initializer element count + let elem_type = + self.types.base_type(typ).unwrap_or(self.types.int_id); + self.types.intern(Type::array(elem_type, elements.len())) + } else { + typ + }; + + return Ok(Self::typed_expr( + ExprKind::CompoundLiteral { + typ: final_typ, + elements, + }, + final_typ, + paren_pos, + )); + } + + // Regular cast expression let expr = self.parse_unary_expr()?; - // Cast expression has the cast type return Ok(Self::typed_expr( ExprKind::Cast { cast_type: typ, diff --git a/cc/tests/datatypes/struct_type.rs b/cc/tests/datatypes/struct_type.rs index 139170ec..4c02b008 100644 --- a/cc/tests/datatypes/struct_type.rs +++ b/cc/tests/datatypes/struct_type.rs @@ -310,3 +310,126 @@ int main(void) { "#; assert_eq!(compile_and_run("struct_return_large", code), 0); } + +// ============================================================================ +// Compound Literals (C99 6.5.2.5) +// ============================================================================ + +#[test] +fn compound_literal_struct_basic() { + let code = r#" +struct Point { int x; int y; }; + +int main(void) { + struct Point p = (struct Point){10, 20}; + if (p.x != 10) return 1; + if (p.y != 20) return 2; + return 0; +} +"#; + assert_eq!(compile_and_run("compound_literal_struct_basic", code), 0); +} + +#[test] +fn compound_literal_struct_designated() { + let code = r#" +struct Point { int x; int y; }; + +int main(void) { + struct Point p = (struct Point){.y = 30, .x = 40}; + if (p.x != 40) return 1; + if (p.y != 30) return 2; + return 0; +} +"#; + assert_eq!( + compile_and_run("compound_literal_struct_designated", code), + 0 + ); +} + +#[test] +fn compound_literal_array() { + let code = r#" +int main(void) { + int *p = (int[]){1, 2, 3, 4, 5}; + if (p[0] != 1) return 1; + if (p[2] != 3) return 2; + if (p[4] != 5) return 3; + return 0; +} +"#; + assert_eq!(compile_and_run("compound_literal_array", code), 0); +} + +#[test] +fn compound_literal_as_argument() { + let code = r#" +struct Point { int x; int y; }; + +int sum_point(struct Point p) { + return p.x + p.y; +} + +int main(void) { + int result = sum_point((struct Point){5, 7}); + if (result != 12) return 1; + return 0; +} +"#; + assert_eq!(compile_and_run("compound_literal_as_argument", code), 0); +} + +#[test] +fn compound_literal_address_of() { + let code = r#" +struct Point { int x; int y; }; + +int main(void) { + struct Point *ptr = &(struct Point){100, 200}; + if (ptr->x != 100) return 1; + if (ptr->y != 200) return 2; + return 0; +} +"#; + assert_eq!(compile_and_run("compound_literal_address_of", code), 0); +} + +#[test] +fn compound_literal_file_scope_struct() { + let code = r#" +struct Point { int x; int y; }; + +// File-scope compound literal - static storage +struct Point origin = (struct Point){0, 0}; + +int main(void) { + if (origin.x != 0) return 1; + if (origin.y != 0) return 2; + return 0; +} +"#; + assert_eq!( + compile_and_run("compound_literal_file_scope_struct", code), + 0 + ); +} + +#[test] +fn compound_literal_file_scope_array_ptr() { + let code = r#" +// File-scope pointer to compound literal array +int *primes = (int[]){2, 3, 5, 7, 11}; + +int main(void) { + if (primes[0] != 2) return 1; + if (primes[2] != 5) return 2; + if (primes[4] != 11) return 3; + return 0; +} +"#; + assert_eq!( + compile_and_run("compound_literal_file_scope_array_ptr", code), + 0 + ); +}