Skip to content

Commit e1ff226

Browse files
committed
postprocess: include C definitions before and after preprocessing in prompts
Read both forms from the structured *.c_decls.json instead of running a clang subprocess per snippet. The CommentsTransform prompt includes the preprocessed text only when it differs from the original, so prompts (and llm-cache keys) are unchanged for directive-free functions; comment gating and response validation use the preprocessed text so comments in inactive preprocessor regions are not transferred.
1 parent 7928a32 commit e1ff226

4 files changed

Lines changed: 72 additions & 20 deletions

File tree

c2rust-postprocess/postprocess/definitions/__init__.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import subprocess
44
from collections.abc import Generator, Iterable
5+
from dataclasses import dataclass
56
from pathlib import Path
67
from tempfile import NamedTemporaryFile
78
from typing import Any
@@ -233,12 +234,37 @@ def get_rust_definitions(root_rust_source_file: Path) -> dict[str, str]:
233234
return json.loads(result.stdout)
234235

235236

236-
def get_c_definitions(root_rust_source_file: Path) -> dict[str, str]:
237+
@dataclass(frozen=True)
238+
class CDefinition:
239+
definition: str
240+
preprocessed_definition: str | None
241+
242+
@property
243+
def effective(self) -> str:
244+
"""Preprocessed text when available, original otherwise."""
245+
return self.preprocessed_definition or self.definition
246+
247+
@property
248+
def was_changed_by_preprocessing(self) -> bool:
249+
return (
250+
self.preprocessed_definition is not None
251+
and self.preprocessed_definition != self.definition
252+
)
253+
254+
255+
def get_c_definitions(root_rust_source_file: Path) -> dict[str, CDefinition]:
237256
c_defs_json = root_rust_source_file.with_suffix(".c_decls.json")
238257

239258
logging.debug(f"Loading C definitions from {c_defs_json}")
240259
try:
241-
return json.loads(c_defs_json.read_text())
260+
c_decls = json.loads(c_defs_json.read_text())
261+
return {
262+
identifier: CDefinition(
263+
definition=decl["definition"],
264+
preprocessed_definition=decl.get("preprocessed_definition"),
265+
)
266+
for identifier, decl in c_decls["definitions"].items()
267+
}
242268
except OSError as e:
243269
e.add_note(f"C definitions JSON file not found: {c_defs_json}")
244270
raise e

c2rust-postprocess/postprocess/transforms/base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55

66
from postprocess.definitions import (
7+
CDefinition,
78
get_c_definitions,
89
get_rust_definitions,
910
)
@@ -28,7 +29,7 @@ def apply_ident(
2829
self,
2930
rust_source_file: Path,
3031
rust_definition: str,
31-
c_definition: str,
32+
c_definition: CDefinition,
3233
identifier: str,
3334
update_rust: bool = True,
3435
) -> None:
@@ -99,7 +100,7 @@ def apply_file(
99100

100101
c_definition = c_definitions[identifier]
101102

102-
highlighted_c_definition = get_highlighted_c(c_definition)
103+
highlighted_c_definition = get_highlighted_c(c_definition.effective)
103104
logging.debug(
104105
f"C function {identifier} definition:\n{highlighted_c_definition}\n"
105106
)

c2rust-postprocess/postprocess/transforms/comments.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from postprocess.cache import AbstractCache
66
from postprocess.definitions import (
7+
CDefinition,
78
get_c_comments,
89
get_rust_comments,
910
update_rust_definition,
@@ -20,31 +21,51 @@
2021

2122
class CommentsTransformPrompt:
2223
c_function: str
24+
c_function_preprocessed: str | None
2325
rust_function: str
2426
prompt_text: str
2527
identifier: str
2628

27-
__slots__ = ("c_function", "rust_function", "prompt_text", "identifier")
29+
__slots__ = (
30+
"c_function",
31+
"c_function_preprocessed",
32+
"rust_function",
33+
"prompt_text",
34+
"identifier",
35+
)
2836

2937
def __init__(
30-
self, c_function: str, rust_function: str, prompt_text: str, identifier: str
38+
self,
39+
c_function: str,
40+
c_function_preprocessed: str | None,
41+
rust_function: str,
42+
prompt_text: str,
43+
identifier: str,
3144
):
3245
self.c_function = c_function
46+
self.c_function_preprocessed = c_function_preprocessed
3347
self.rust_function = rust_function
3448
self.prompt_text = prompt_text
3549
self.identifier = identifier
3650

3751
def __str__(self) -> str:
38-
return (
52+
prompt = (
3953
self.prompt_text
4054
+ "\n\n"
4155
+ "C function:\n```c\n"
4256
+ self.c_function
4357
+ "```\n\n"
44-
+ "Rust function:\n```rust\n"
45-
+ self.rust_function
46-
+ "```\n"
4758
)
59+
if self.c_function_preprocessed is not None:
60+
prompt += (
61+
"The same C function after preprocessing; comments that do not"
62+
" appear here were in inactive preprocessor regions and must"
63+
" not be transferred:\n```c\n"
64+
+ self.c_function_preprocessed
65+
+ "```\n\n"
66+
)
67+
prompt += "Rust function:\n```rust\n" + self.rust_function + "```\n"
68+
return prompt
4869

4970

5071
class CommentsTransform(AbstractTransform):
@@ -57,7 +78,7 @@ def apply_ident(
5778
self,
5879
rust_source_file: Path,
5980
rust_definition: str,
60-
c_definition: str,
81+
c_definition: CDefinition,
6182
identifier: str,
6283
update_rust: bool = True,
6384
) -> None:
@@ -70,8 +91,9 @@ def apply_ident(
7091
)
7192
return
7293

73-
# Skip functions without comments in C definition
74-
c_comments = get_c_comments(c_definition)
94+
# Skip functions without comments that survive preprocessing; comments
95+
# only present in inactive preprocessor regions must not be transferred.
96+
c_comments = get_c_comments(c_definition.effective)
7597
if not c_comments:
7698
logging.info(f"Skipping C function without comments: {identifier}")
7799
return
@@ -86,7 +108,14 @@ def apply_ident(
86108
prompt_text = dedent(prompt_text).strip()
87109

88110
prompt = CommentsTransformPrompt(
89-
c_function=c_definition,
111+
c_function=c_definition.definition,
112+
# Only include the preprocessed text when it differs, so prompts
113+
# (and thus cache keys) are unchanged for directive-free functions.
114+
c_function_preprocessed=(
115+
c_definition.preprocessed_definition
116+
if c_definition.was_changed_by_preprocessing
117+
else None
118+
),
90119
rust_function=rust_definition,
91120
prompt_text=prompt_text,
92121
identifier=identifier,
@@ -130,7 +159,7 @@ def apply_ident(
130159

131160
rust_fn = remove_backticks(response)
132161

133-
c_comments = get_c_comments(prompt.c_function)
162+
c_comments = get_c_comments(c_definition.effective)
134163
logging.debug(f"{c_comments=}")
135164

136165
rust_comments = get_rust_comments(rust_fn)
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"swap": "void swap(int* a, int* b)\n{\n int t = *a;\n *a = *b;\n *b = t;\n}",
3-
"quickSort": "void quickSort(int arr[], int low, int high)\n{\n if (low < high) {\n /* pi is the partitioning index; arr[pi] is now at the right place */\n int pi = partition(arr, low, high);\n\n /* Recursively sort elements before and after partition */\n quickSort(arr, low, pi - 1);\n quickSort(arr, pi + 1, high);\n }\n}",
4-
"partition": "/* \n * Lomuto Partition Scheme:\n * Partitions the array so that elements < pivot are on the left, \n * and elements >= pivot are on the right.\n */\nint partition (int arr[], int low, int high)\n{\n // Partition the subarray around the last element as pivot and return pivot's final index.\n int pivot = arr[high];\n int i = low - 1;\n\n for (int j = low; j <= high - 1; j++) {\n if (arr[j] <= pivot) {\n i++;\n // Move elements <= pivot into the left partition.\n swap(&arr[i], &arr[j]);\n }\n }\n // Place pivot just after the final element of the left partition.\n swap(&arr[i + 1], &arr[high]);\n return i + 1;\n}"
5-
}
1+
{"source_file":"/home/perl/Work/c2rust/examples/qsort/qsort.c","definitions":{"swap":{"definition":"void swap(int* a, int* b)\n{\n int t = *a;\n *a = *b;\n *b = t;\n}","preprocessed_definition":"void swap(int* a, int* b)\n{\n int t = *a;\n *a = *b;\n *b = t;\n}"},"partition":{"definition":"/* \n * Lomuto Partition Scheme:\n * Partitions the array so that elements < pivot are on the left, \n * and elements >= pivot are on the right.\n */\nint partition (int arr[], int low, int high)\n{\n // Partition the subarray around the last element as pivot and return pivot's final index.\n int pivot = arr[high];\n int i = low - 1;\n\n for (int j = low; j <= high - 1; j++) {\n if (arr[j] <= pivot) {\n i++;\n // Move elements <= pivot into the left partition.\n swap(&arr[i], &arr[j]);\n }\n }\n // Place pivot just after the final element of the left partition.\n swap(&arr[i + 1], &arr[high]);\n return i + 1;\n}","preprocessed_definition":"/* \n * Lomuto Partition Scheme:\n * Partitions the array so that elements < pivot are on the left, \n * and elements >= pivot are on the right.\n */\nint partition (int arr[], int low, int high)\n{\n // Partition the subarray around the last element as pivot and return pivot's final index.\n int pivot = arr[high];\n int i = low - 1;\n\n for (int j = low; j <= high - 1; j++) {\n if (arr[j] <= pivot) {\n i++;\n // Move elements <= pivot into the left partition.\n swap(&arr[i], &arr[j]);\n }\n }\n // Place pivot just after the final element of the left partition.\n swap(&arr[i + 1], &arr[high]);\n return i + 1;\n}"},"quickSort":{"definition":"void quickSort(int arr[], int low, int high)\n{\n if (low < high) {\n /* pi is the partitioning index; arr[pi] is now at the right place */\n int pi = partition(arr, low, high);\n\n /* Recursively sort elements before and after partition */\n quickSort(arr, low, pi - 1);\n quickSort(arr, pi + 1, high);\n }\n}","preprocessed_definition":"void quickSort(int arr[], int low, int high)\n{\n if (low < high) {\n /* pi is the partitioning index; arr[pi] is now at the right place */\n int pi = partition(arr, low, high);\n\n /* Recursively sort elements before and after partition */\n quickSort(arr, low, pi - 1);\n quickSort(arr, pi + 1, high);\n }\n}"}}}

0 commit comments

Comments
 (0)