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, .. } => {
|
||||
let l = self.emit_expr_with_env(left, 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 {
|
||||
BinaryOp::Add => "+",
|
||||
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 {
|
||||
BinaryOp::Add => "+",
|
||||
BinaryOp::Sub => "-",
|
||||
@@ -2341,6 +2368,16 @@ impl CBackend {
|
||||
|
||||
Expr::Let { name, value, body, .. } => {
|
||||
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());
|
||||
|
||||
// Infer the type from the value expression
|
||||
@@ -2541,6 +2578,16 @@ impl CBackend {
|
||||
for stmt in statements {
|
||||
match stmt {
|
||||
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)
|
||||
let inferred_type = self.infer_expr_type(value);
|
||||
|
||||
@@ -3196,7 +3243,18 @@ impl CBackend {
|
||||
}
|
||||
let list = self.emit_expr(&args[0])?;
|
||||
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)
|
||||
}
|
||||
"tail" => {
|
||||
@@ -3211,6 +3269,8 @@ impl CBackend {
|
||||
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.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.indent -= 1;
|
||||
self.writeln("}");
|
||||
@@ -3231,7 +3291,18 @@ impl CBackend {
|
||||
let list = self.emit_expr(&args[0])?;
|
||||
let idx = self.emit_expr(&args[1])?;
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -532,7 +532,14 @@ impl Parser {
|
||||
let start = self.current_span();
|
||||
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) {
|
||||
self.advance();
|
||||
@@ -1962,7 +1969,14 @@ impl Parser {
|
||||
let start = self.current_span();
|
||||
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) {
|
||||
self.advance();
|
||||
@@ -2207,7 +2221,15 @@ impl Parser {
|
||||
// Let statement
|
||||
let let_start = self.current_span();
|
||||
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) {
|
||||
self.advance();
|
||||
|
||||
Reference in New Issue
Block a user