fix: C backend string comparison, underscore patterns, and list memory
1. String == comparison now uses strcmp instead of pointer comparison - Added check in emit_expr() for BinaryOp::Eq/Ne on strings - Also fixed in emit_expr_with_env() for closures 2. Support `let _ = expr` pattern to discard values - Parser now accepts underscore in let bindings (both blocks and expressions) - C backend emits (void)expr; for underscore patterns 3. Fix list head/tail/get memory management - Added lux_incref() when extracting elements from lists - Prevents use-after-free when original list is freed 4. String.startsWith was already implemented (verified working) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -318,6 +318,20 @@ impl CBackend {
|
|||||||
Expr::BinaryOp { op, left, right, .. } => {
|
Expr::BinaryOp { op, left, right, .. } => {
|
||||||
let l = self.emit_expr_with_env(left, captured)?;
|
let l = self.emit_expr_with_env(left, captured)?;
|
||||||
let r = self.emit_expr_with_env(right, captured)?;
|
let r = self.emit_expr_with_env(right, captured)?;
|
||||||
|
|
||||||
|
// Check for string comparison - use strcmp instead of pointer comparison
|
||||||
|
if matches!(op, BinaryOp::Eq | BinaryOp::Ne) {
|
||||||
|
let left_is_string = self.is_string_expr(left);
|
||||||
|
let right_is_string = self.is_string_expr(right);
|
||||||
|
if left_is_string || right_is_string {
|
||||||
|
if matches!(op, BinaryOp::Eq) {
|
||||||
|
return Ok(format!("(strcmp({}, {}) == 0)", l, r));
|
||||||
|
} else {
|
||||||
|
return Ok(format!("(strcmp({}, {}) != 0)", l, r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let op_str = match op {
|
let op_str = match op {
|
||||||
BinaryOp::Add => "+",
|
BinaryOp::Add => "+",
|
||||||
BinaryOp::Sub => "-",
|
BinaryOp::Sub => "-",
|
||||||
@@ -2229,6 +2243,19 @@ impl CBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for string comparison - use strcmp instead of pointer comparison
|
||||||
|
if matches!(op, BinaryOp::Eq | BinaryOp::Ne) {
|
||||||
|
let left_is_string = self.is_string_expr(left);
|
||||||
|
let right_is_string = self.is_string_expr(right);
|
||||||
|
if left_is_string || right_is_string {
|
||||||
|
if matches!(op, BinaryOp::Eq) {
|
||||||
|
return Ok(format!("(strcmp({}, {}) == 0)", l, r));
|
||||||
|
} else {
|
||||||
|
return Ok(format!("(strcmp({}, {}) != 0)", l, r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let op_str = match op {
|
let op_str = match op {
|
||||||
BinaryOp::Add => "+",
|
BinaryOp::Add => "+",
|
||||||
BinaryOp::Sub => "-",
|
BinaryOp::Sub => "-",
|
||||||
@@ -2341,6 +2368,16 @@ impl CBackend {
|
|||||||
|
|
||||||
Expr::Let { name, value, body, .. } => {
|
Expr::Let { name, value, body, .. } => {
|
||||||
let val = self.emit_expr(value)?;
|
let val = self.emit_expr(value)?;
|
||||||
|
|
||||||
|
// Handle underscore pattern - just evaluate the expression, discard result
|
||||||
|
if name.name == "_" {
|
||||||
|
// Just emit the expression for its side effects
|
||||||
|
self.writeln(&format!("(void){};", val));
|
||||||
|
// Continue with body
|
||||||
|
let body_result = self.emit_expr(body)?;
|
||||||
|
return Ok(body_result);
|
||||||
|
}
|
||||||
|
|
||||||
let var_name = format!("{}_{}", name.name, self.fresh_name());
|
let var_name = format!("{}_{}", name.name, self.fresh_name());
|
||||||
|
|
||||||
// Infer the type from the value expression
|
// Infer the type from the value expression
|
||||||
@@ -2541,6 +2578,16 @@ impl CBackend {
|
|||||||
for stmt in statements {
|
for stmt in statements {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Let { name, value, .. } => {
|
Statement::Let { name, value, .. } => {
|
||||||
|
// Handle underscore pattern - just evaluate the expression, discard result
|
||||||
|
if name.name == "_" {
|
||||||
|
let val = self.emit_expr(value)?;
|
||||||
|
// Just evaluate for side effects, don't store
|
||||||
|
if !val.is_empty() && !val.starts_with("(void)") {
|
||||||
|
self.writeln(&format!("(void){};", val));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// First, infer type from value expression (before emitting)
|
// First, infer type from value expression (before emitting)
|
||||||
let inferred_type = self.infer_expr_type(value);
|
let inferred_type = self.infer_expr_type(value);
|
||||||
|
|
||||||
@@ -3196,7 +3243,18 @@ impl CBackend {
|
|||||||
}
|
}
|
||||||
let list = self.emit_expr(&args[0])?;
|
let list = self.emit_expr(&args[0])?;
|
||||||
let result_var = format!("_head_{}", self.fresh_name());
|
let result_var = format!("_head_{}", self.fresh_name());
|
||||||
self.writeln(&format!("Option {} = ({}->length > 0) ? lux_option_some({}->elements[0]) : lux_option_none();", result_var, list, list));
|
// Need to incref the element since it's being extracted from the list
|
||||||
|
self.writeln(&format!("Option {};", result_var));
|
||||||
|
self.writeln(&format!("if ({0}->length > 0) {{", list));
|
||||||
|
self.indent += 1;
|
||||||
|
self.writeln(&format!("lux_incref({0}->elements[0]);", list));
|
||||||
|
self.writeln(&format!("{0} = lux_option_some({1}->elements[0]);", result_var, list));
|
||||||
|
self.indent -= 1;
|
||||||
|
self.writeln("} else {");
|
||||||
|
self.indent += 1;
|
||||||
|
self.writeln(&format!("{} = lux_option_none();", result_var));
|
||||||
|
self.indent -= 1;
|
||||||
|
self.writeln("}");
|
||||||
Ok(result_var)
|
Ok(result_var)
|
||||||
}
|
}
|
||||||
"tail" => {
|
"tail" => {
|
||||||
@@ -3211,6 +3269,8 @@ impl CBackend {
|
|||||||
self.writeln(&format!("LuxList* _tail_list = lux_list_new({0}->length - 1);", list));
|
self.writeln(&format!("LuxList* _tail_list = lux_list_new({0}->length - 1);", list));
|
||||||
self.writeln(&format!("for (int64_t i = 1; i < {0}->length; i++) {{", list));
|
self.writeln(&format!("for (int64_t i = 1; i < {0}->length; i++) {{", list));
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
|
// Incref each element being copied to the new list
|
||||||
|
self.writeln(&format!("lux_incref({0}->elements[i]);", list));
|
||||||
self.writeln(&format!("_tail_list->elements[i-1] = {0}->elements[i];", list));
|
self.writeln(&format!("_tail_list->elements[i-1] = {0}->elements[i];", list));
|
||||||
self.indent -= 1;
|
self.indent -= 1;
|
||||||
self.writeln("}");
|
self.writeln("}");
|
||||||
@@ -3231,7 +3291,18 @@ impl CBackend {
|
|||||||
let list = self.emit_expr(&args[0])?;
|
let list = self.emit_expr(&args[0])?;
|
||||||
let idx = self.emit_expr(&args[1])?;
|
let idx = self.emit_expr(&args[1])?;
|
||||||
let result_var = format!("_get_{}", self.fresh_name());
|
let result_var = format!("_get_{}", self.fresh_name());
|
||||||
self.writeln(&format!("Option {} = ({} >= 0 && {} < {}->length) ? lux_option_some({}->elements[{}]) : lux_option_none();", result_var, idx, idx, list, list, idx));
|
// Need to incref the element since it's being extracted from the list
|
||||||
|
self.writeln(&format!("Option {};", result_var));
|
||||||
|
self.writeln(&format!("if ({0} >= 0 && {0} < {1}->length) {{", idx, list));
|
||||||
|
self.indent += 1;
|
||||||
|
self.writeln(&format!("lux_incref({0}->elements[{1}]);", list, idx));
|
||||||
|
self.writeln(&format!("{0} = lux_option_some({1}->elements[{2}]);", result_var, list, idx));
|
||||||
|
self.indent -= 1;
|
||||||
|
self.writeln("} else {");
|
||||||
|
self.indent += 1;
|
||||||
|
self.writeln(&format!("{} = lux_option_none();", result_var));
|
||||||
|
self.indent -= 1;
|
||||||
|
self.writeln("}");
|
||||||
Ok(result_var)
|
Ok(result_var)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -532,7 +532,14 @@ impl Parser {
|
|||||||
let start = self.current_span();
|
let start = self.current_span();
|
||||||
self.expect(TokenKind::Let)?;
|
self.expect(TokenKind::Let)?;
|
||||||
|
|
||||||
let name = self.parse_ident()?;
|
// Allow underscore as wildcard pattern (discards the value)
|
||||||
|
let name = if self.check(TokenKind::Underscore) {
|
||||||
|
let span = self.current_span();
|
||||||
|
self.advance();
|
||||||
|
Ident::new("_".to_string(), span)
|
||||||
|
} else {
|
||||||
|
self.parse_ident()?
|
||||||
|
};
|
||||||
|
|
||||||
let typ = if self.check(TokenKind::Colon) {
|
let typ = if self.check(TokenKind::Colon) {
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -1962,7 +1969,14 @@ impl Parser {
|
|||||||
let start = self.current_span();
|
let start = self.current_span();
|
||||||
self.expect(TokenKind::Let)?;
|
self.expect(TokenKind::Let)?;
|
||||||
|
|
||||||
let name = self.parse_ident()?;
|
// Allow underscore as wildcard pattern (discards the value)
|
||||||
|
let name = if self.check(TokenKind::Underscore) {
|
||||||
|
let span = self.current_span();
|
||||||
|
self.advance();
|
||||||
|
Ident::new("_".to_string(), span)
|
||||||
|
} else {
|
||||||
|
self.parse_ident()?
|
||||||
|
};
|
||||||
|
|
||||||
let typ = if self.check(TokenKind::Colon) {
|
let typ = if self.check(TokenKind::Colon) {
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -2207,7 +2221,15 @@ impl Parser {
|
|||||||
// Let statement
|
// Let statement
|
||||||
let let_start = self.current_span();
|
let let_start = self.current_span();
|
||||||
self.advance();
|
self.advance();
|
||||||
let name = self.parse_ident()?;
|
|
||||||
|
// Allow underscore as wildcard pattern (discards the value)
|
||||||
|
let name = if self.check(TokenKind::Underscore) {
|
||||||
|
let span = self.current_span();
|
||||||
|
self.advance();
|
||||||
|
Ident::new("_".to_string(), span)
|
||||||
|
} else {
|
||||||
|
self.parse_ident()?
|
||||||
|
};
|
||||||
|
|
||||||
let typ = if self.check(TokenKind::Colon) {
|
let typ = if self.check(TokenKind::Colon) {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|||||||
Reference in New Issue
Block a user