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
12 changes: 12 additions & 0 deletions bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19143,6 +19143,18 @@ impl CCodegen {
format!("{}*", base)
};
}
// Prefix pointer: t27 spells it `*T`, C spells it `T*`. There was no
// arm for this, so the text passed through verbatim and the header read
//
// uint64_t set(*Bitmap bitmap);
//
// where C parses `Bitmap` as the declared NAME with an implicit-int
// type, then finds a second identifier: "type specifier missing" and
// "expected ')'". 52 generated files carried it.
if let Some(inner) = ty.strip_prefix('*') {
let base = Self::param_type_to_c(inner.trim());
return format!("{}*", base);
}
// Slice types: []Type → Type*, including the `[]const T` spelling --
// without stripping `const` the inner type was the literal string
// "const u8" and C received `[]const u8* positional;`.
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
82e020cf95b211d048f71ee03840e1c5b3acbdc4192ebabeab8d951f4a26eeda
f54e190f098bb7663bfdea7fdbde6f2450fd38174bf407073fc6f6b17acb4275
66 changes: 66 additions & 0 deletions bootstrap/tests/pointer_param_c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! A t27 `*T` parameter must reach C as `T*`.
//!
//! `param_type_to_c` has arms for `?T`, `[]T`, `[T; N]` and `[N]T`, and had none
//! for the prefix star, so the text passed through verbatim:
//!
//! uint64_t set(*Bitmap bitmap);
//!
//! C parses `Bitmap` there as the declared NAME carrying an implicit-int type,
//! then finds a second identifier -- "type specifier missing, defaults to 'int'"
//! followed by "expected ')'". Fifty-two generated headers carried it.
//!
//! Removing the family moved `cc accepts` by zero, which is the measurement that
//! matters about this backlog rather than a disappointment: of 404 rejected
//! files only 20 are blocked by a single family, so no one family is a lever.
//! What it does move is what comes NEXT -- with pointers typed correctly, the
//! member-access defect (`.` where C needs `->`) becomes visible as its own
//! single-family file.

use std::io::Write;
use std::process::Command;

fn gen_c(src: &str) -> String {
let dir = std::env::temp_dir().join(format!("t27-ptrparam-{}-{}", std::process::id(), src.len()));
std::fs::create_dir_all(&dir).expect("temp dir");
let path = dir.join("m.t27");
let mut f = std::fs::File::create(&path).expect("write spec");
f.write_all(src.as_bytes()).expect("write spec");
let out = Command::new(env!("CARGO_BIN_EXE_t27c"))
.arg("gen-c")
.arg(&path)
.output()
.expect("run t27c");
let _ = std::fs::remove_dir_all(&dir);
String::from_utf8_lossy(&out.stdout).to_string()
}

const SPEC: &str = "module ptr\n\nstruct Bitmap {\n bits: u64,\n}\n\nfn set(bitmap: *Bitmap) -> u64 {\n return 1\n}\n";

#[test]
fn a_prefix_star_parameter_becomes_a_trailing_star_in_c() {
let c = gen_c(SPEC);
assert!(
c.contains("uint64_t set(Bitmap* bitmap)"),
"expected `Bitmap* bitmap`, got:\n{}",
c.lines().filter(|l| l.contains("set(")).collect::<Vec<_>>().join("\n")
);
assert!(
!c.contains("(*Bitmap bitmap"),
"the prefix star must not survive into C"
);
}

/// A pointer to a slice keeps both, and in the right order.
#[test]
fn the_star_arm_recurses_rather_than_prepending_to_raw_text() {
let c = gen_c("module p2\n\nfn f(xs: *[]u8) -> u64 {\n return 1\n}\n");
let line = c
.lines()
.find(|l| l.contains("f(") && l.contains("xs"))
.unwrap_or_default()
.to_string();
assert!(
line.contains("uint8_t** xs"),
"a pointer to a slice is `uint8_t**`, got: {line}"
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# NOW -- A family removed and the headline unmoved (2026-08-30)

## A family removed and the headline unmoved (Closes #2929)

- `param_type_to_c` had arms for `?T`, `[]T`, `[T; N]` and `[N]T` and none for `*T`, so 52 headers carried `uint64_t set(*Bitmap bitmap);`
- in C the star belongs to the declarator: written first, `Bitmap` parses as the NAME with an implicit-int type and the next identifier is unexpected
- files with the family **52 -> 0**; `cc accepts` **174 -> 174**
- that is the measurement, not a disappointment: only 20 of 404 rejected files are blocked by a single family, so no one family is a lever
- what it moves is what comes NEXT -- `member reference type 'Bitmap *' is a pointer; did you mean to use '->'?` is now its own single-family file, invisible while the parameter never parsed
- the arm recurses: `*[]u8` is `uint8_t**`, and prepending the star to raw text gives `[]u8*`
Loading