Skip to content

Commit 64f8faa

Browse files
committed
[cc] support C99 compound literals
1 parent 59389b7 commit 64f8faa

File tree

7 files changed

+287
-33
lines changed

7 files changed

+287
-33
lines changed

cc/arch/aarch64/codegen.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,11 @@ impl Aarch64CodeGen {
168168
// Data section
169169
self.push_lir(Aarch64Inst::Directive(Directive::Data));
170170

171-
// Global visibility (if not static)
172-
if !is_static {
171+
// Check if this is a local symbol (starts with '.')
172+
let is_local = name.starts_with('.');
173+
174+
// Global visibility (if not static and not local)
175+
if !is_static && !is_local {
173176
self.push_lir(Aarch64Inst::Directive(Directive::global(name)));
174177
}
175178

@@ -184,8 +187,12 @@ impl Aarch64CodeGen {
184187
)));
185188
}
186189

187-
// Label
188-
self.push_lir(Aarch64Inst::Directive(Directive::global_label(name)));
190+
// Label - use local_label for names starting with '.'
191+
if is_local {
192+
self.push_lir(Aarch64Inst::Directive(Directive::local_label(name)));
193+
} else {
194+
self.push_lir(Aarch64Inst::Directive(Directive::global_label(name)));
195+
}
189196

190197
// Emit initializer
191198
self.emit_initializer_data(init, size as usize);

cc/arch/x86_64/codegen.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,11 @@ impl X86_64CodeGen {
168168
// Data section
169169
self.push_lir(X86Inst::Directive(Directive::Data));
170170

171-
// Global visibility (if not static)
172-
if !is_static {
171+
// Check if this is a local symbol (starts with '.')
172+
let is_local = name.starts_with('.');
173+
174+
// Global visibility (if not static and not local)
175+
if !is_static && !is_local {
173176
self.push_lir(X86Inst::Directive(Directive::global(name)));
174177
}
175178

@@ -182,8 +185,12 @@ impl X86_64CodeGen {
182185
self.push_lir(X86Inst::Directive(Directive::Align(align.trailing_zeros())));
183186
}
184187

185-
// Label
186-
self.push_lir(X86Inst::Directive(Directive::global_label(name)));
188+
// Label - use local_label for names starting with '.'
189+
if is_local {
190+
self.push_lir(X86Inst::Directive(Directive::local_label(name)));
191+
} else {
192+
self.push_lir(X86Inst::Directive(Directive::global_label(name)));
193+
}
187194

188195
// Emit initializer
189196
self.emit_initializer_data(init, size as usize);

cc/doc/BUGS.md

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,6 @@ This file documents known bugs where valid C99 code fails to compile.
44

55
## Parse Errors on Valid C99 Code
66

7-
### Compound Literals
8-
9-
**Status**: Parse error
10-
11-
**Valid C99 Code**:
12-
```c
13-
struct Point { int x, y; };
14-
struct Point p = (struct Point){1, 2}; // Compound literal
15-
```
16-
17-
**Error**:
18-
```
19-
parse error: unexpected token in expression
20-
```
21-
22-
**C99 Reference**: 6.5.2.5
23-
24-
---
25-
267
### Array Parameter with `static`
278

289
**Status**: Parse error

cc/ir/linearize.rs

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pub struct Linearizer<'a> {
9494
current_func_name: String,
9595
/// Counter for generating unique static local names
9696
static_local_counter: u32,
97+
/// Counter for generating unique compound literal names (for file-scope compound literals)
98+
compound_literal_counter: u32,
9799
/// Static local variables (local name -> static local info)
98100
/// This is persistent across function calls (not cleared per function)
99101
static_locals: HashMap<String, StaticLocalInfo>,
@@ -135,6 +137,7 @@ impl<'a> Linearizer<'a> {
135137
struct_return_size: 0,
136138
current_func_name: String::new(),
137139
static_local_counter: 0,
140+
compound_literal_counter: 0,
138141
static_locals: HashMap::new(),
139142
current_pos: None,
140143
target,
@@ -518,7 +521,8 @@ impl<'a> Linearizer<'a> {
518521
/// - Struct initializers with designated and positional fields
519522
/// - Address-of expressions (&symbol)
520523
/// - Nested initializers
521-
fn ast_init_to_ir(&self, expr: &Expr, typ: TypeId) -> Initializer {
524+
/// - Compound literals (C99 6.5.2.5)
525+
fn ast_init_to_ir(&mut self, expr: &Expr, typ: TypeId) -> Initializer {
522526
match &expr.kind {
523527
ExprKind::IntLit(v) => Initializer::Int(*v),
524528
ExprKind::FloatLit(v) => Initializer::Float(*v),
@@ -573,6 +577,33 @@ impl<'a> Linearizer<'a> {
573577
// Initializer list for arrays/structs
574578
ExprKind::InitList { elements } => self.ast_init_list_to_ir(elements, typ),
575579

580+
// Compound literal in initializer context (C99 6.5.2.5)
581+
ExprKind::CompoundLiteral {
582+
typ: cl_type,
583+
elements,
584+
} => {
585+
// Check if compound literal type matches target type
586+
if *cl_type == typ {
587+
// Direct value - treat like InitList
588+
self.ast_init_list_to_ir(elements, typ)
589+
} else if self.types.kind(typ) == TypeKind::Pointer {
590+
// Pointer initialization - create anonymous static global
591+
// and return its address
592+
let anon_name = format!(".CL{}", self.compound_literal_counter);
593+
self.compound_literal_counter += 1;
594+
595+
// Create the anonymous global
596+
let init = self.ast_init_list_to_ir(elements, *cl_type);
597+
self.module.add_global(&anon_name, *cl_type, init);
598+
599+
// Return address of the anonymous global
600+
Initializer::SymAddr(anon_name)
601+
} else {
602+
// Type mismatch - try to use the initializer list directly
603+
self.ast_init_list_to_ir(elements, typ)
604+
}
605+
}
606+
576607
// Identifier - for constant addresses (function pointers, array decay, etc.)
577608
// or enum constants
578609
ExprKind::Ident { name } => {
@@ -603,7 +634,7 @@ impl<'a> Linearizer<'a> {
603634
}
604635

605636
/// Convert an AST initializer list to an IR Initializer
606-
fn ast_init_list_to_ir(&self, elements: &[InitElement], typ: TypeId) -> Initializer {
637+
fn ast_init_list_to_ir(&mut self, elements: &[InitElement], typ: TypeId) -> Initializer {
607638
let type_kind = self.types.kind(typ);
608639
let total_size = (self.types.size_bits(typ) / 8) as usize;
609640

@@ -2147,6 +2178,28 @@ impl<'a> Linearizer<'a> {
21472178
));
21482179
addr
21492180
}
2181+
ExprKind::CompoundLiteral { typ, elements } => {
2182+
// Compound literal as lvalue: create it and return its address
2183+
// This is used for &(struct S){...}
2184+
let sym_id = self.alloc_pseudo();
2185+
let unique_name = format!(".compound_literal#{}", sym_id.0);
2186+
let sym = Pseudo::sym(sym_id, unique_name.clone());
2187+
if let Some(func) = &mut self.current_func {
2188+
func.add_pseudo(sym);
2189+
func.add_local(&unique_name, sym_id, *typ, false, self.current_bb);
2190+
}
2191+
self.linearize_init_list(sym_id, *typ, elements);
2192+
2193+
// Return address of the compound literal
2194+
let result = self.alloc_pseudo();
2195+
let pseudo = Pseudo::reg(result, result.0);
2196+
if let Some(func) = &mut self.current_func {
2197+
func.add_pseudo(pseudo);
2198+
}
2199+
let ptr_type = self.types.pointer_to(*typ);
2200+
self.emit(Instruction::sym_addr(result, sym_id, ptr_type));
2201+
result
2202+
}
21502203
_ => {
21512204
// Fallback: just evaluate the expression (shouldn't happen for valid lvalues)
21522205
self.linearize_expr(expr)
@@ -3249,6 +3302,44 @@ impl<'a> Linearizer<'a> {
32493302
panic!("InitList should be handled in declaration context, not as standalone expression")
32503303
}
32513304

3305+
ExprKind::CompoundLiteral { typ, elements } => {
3306+
// Compound literals have automatic storage at block scope
3307+
// Create an anonymous local variable, similar to how local variables work
3308+
3309+
// Create a symbol pseudo for the compound literal (its address)
3310+
let sym_id = self.alloc_pseudo();
3311+
let unique_name = format!(".compound_literal#{}", sym_id.0);
3312+
let sym = Pseudo::sym(sym_id, unique_name.clone());
3313+
if let Some(func) = &mut self.current_func {
3314+
func.add_pseudo(sym);
3315+
// Register as local for proper stack allocation
3316+
func.add_local(&unique_name, sym_id, *typ, false, self.current_bb);
3317+
}
3318+
3319+
// Initialize using existing init list machinery
3320+
self.linearize_init_list(sym_id, *typ, elements);
3321+
3322+
// For arrays: return pointer (array-to-pointer decay)
3323+
// For structs/scalars: load and return the value
3324+
let result = self.alloc_pseudo();
3325+
let pseudo = Pseudo::reg(result, result.0);
3326+
if let Some(func) = &mut self.current_func {
3327+
func.add_pseudo(pseudo);
3328+
}
3329+
3330+
if self.types.kind(*typ) == TypeKind::Array {
3331+
// Array compound literal - decay to pointer to first element
3332+
let elem_type = self.types.base_type(*typ).unwrap_or(self.types.int_id);
3333+
let ptr_type = self.types.pointer_to(elem_type);
3334+
self.emit(Instruction::sym_addr(result, sym_id, ptr_type));
3335+
} else {
3336+
// Struct/scalar compound literal - load the value
3337+
let size = self.types.size_bits(*typ);
3338+
self.emit(Instruction::load(result, sym_id, 0, *typ, size));
3339+
}
3340+
result
3341+
}
3342+
32523343
// ================================================================
32533344
// Variadic function support (va_* builtins)
32543345
// ================================================================

cc/parse/ast.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,13 @@ pub enum ExprKind {
244244
expr: Box<Expr>,
245245
},
246246

247+
/// Compound literal: (type){ init-list }
248+
/// C99 6.5.2.5: Creates unnamed object with automatic/static storage
249+
CompoundLiteral {
250+
typ: TypeId,
251+
elements: Vec<InitElement>,
252+
},
253+
247254
/// sizeof type: sizeof(int)
248255
SizeofType(TypeId),
249256

cc/parse/parser.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,12 +1511,19 @@ impl<'a> Parser<'a> {
15111511
result_id = self.types.intern(Type::pointer(result_id));
15121512
}
15131513

1514-
// Handle array declarators: int[10], char[20], etc.
1514+
// Handle array declarators: int[10], char[20], int[], etc.
15151515
while self.is_special(b'[') {
15161516
self.advance();
1517-
if let Ok(size_expr) = self.parse_conditional_expr() {
1517+
// Check for empty brackets [] (incomplete array type for compound literals)
1518+
if self.is_special(b']') {
1519+
// Empty brackets - create array with size 0 (size to be determined from initializer)
1520+
result_id = self.types.intern(Type::array(result_id, 0));
1521+
} else if let Ok(size_expr) = self.parse_conditional_expr() {
15181522
if let Some(size) = self.eval_const_expr(&size_expr) {
15191523
result_id = self.types.intern(Type::array(result_id, size as usize));
1524+
} else {
1525+
// Non-constant size (VLA in type name) - create array with size 0
1526+
result_id = self.types.intern(Type::array(result_id, 0));
15201527
}
15211528
}
15221529
if !self.is_special(b']') {
@@ -2214,11 +2221,42 @@ impl<'a> Parser<'a> {
22142221
let paren_pos = self.current_pos();
22152222
self.advance();
22162223

2217-
// Try to detect cast (type) - simplified check
2224+
// Try to detect cast (type) or compound literal (type){...}
22182225
if let Some(typ) = self.try_parse_type_name() {
22192226
self.expect_special(b')')?;
2227+
2228+
// Check for compound literal: (type){ ... }
2229+
if self.is_special(b'{') {
2230+
let init_list = self.parse_initializer_list()?;
2231+
let elements = match init_list.kind {
2232+
ExprKind::InitList { elements } => elements,
2233+
_ => unreachable!(),
2234+
};
2235+
2236+
// For incomplete array types (size 0), determine size from initializer
2237+
let final_typ = if self.types.kind(typ) == TypeKind::Array
2238+
&& self.types.get(typ).array_size == Some(0)
2239+
{
2240+
// Array size should be determined from initializer element count
2241+
let elem_type =
2242+
self.types.base_type(typ).unwrap_or(self.types.int_id);
2243+
self.types.intern(Type::array(elem_type, elements.len()))
2244+
} else {
2245+
typ
2246+
};
2247+
2248+
return Ok(Self::typed_expr(
2249+
ExprKind::CompoundLiteral {
2250+
typ: final_typ,
2251+
elements,
2252+
},
2253+
final_typ,
2254+
paren_pos,
2255+
));
2256+
}
2257+
2258+
// Regular cast expression
22202259
let expr = self.parse_unary_expr()?;
2221-
// Cast expression has the cast type
22222260
return Ok(Self::typed_expr(
22232261
ExprKind::Cast {
22242262
cast_type: typ,

0 commit comments

Comments
 (0)