Skip to content

Commit 8802f62

Browse files
authored
Support src.cpp and src.c in the same rules dir (#92)
For C string functions, C++ defines const overloads. For example, in C, strchr is defined as: ```c char *strchr(const char *s, int c); ``` In C++, it's overloaded as: ```cpp const char *strchr(const char *s, int c); char *strchr(char *s, int c); ``` Writing `char *f5(const char *a0, int a1) { return strchr(a0, a1); }` would result in the following error: ``` error: cannot initialize return object of type 'char *' with an rvalue of type 'const char *' 6 | char *f6(const char *a0, int a1) { return strchr(a0, a1); } ``` For this reason, parse both `src.cpp` and `src.c` in a rules dir. `src.c` keeps C specific rules and `src.cpp` keeps C++ specific rules. The function names in the 2 functions cannot collide, cpp-rule-preprocessor checks against colliding names.
1 parent 7e9a423 commit 8802f62

11 files changed

Lines changed: 199 additions & 22 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,20 @@ add_custom_target("check"
146146
DEPENDS check-libcc2rs check-unit
147147
)
148148

149-
file(GLOB rule_src_files ${PROJECT_SOURCE_DIR}/rules/*/src.cpp)
149+
file(GLOB rule_subdirs ${PROJECT_SOURCE_DIR}/rules/*)
150150
set(cpp_rules_ir_outputs)
151151
set(rust_rules_ir_outputs)
152152
set(rust_rules_inputs)
153-
foreach(_src IN LISTS rule_src_files)
154-
get_filename_component(_rule_dir ${_src} DIRECTORY)
153+
foreach(_rule_dir IN LISTS rule_subdirs)
154+
file(GLOB _srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp)
155+
if(NOT _srcs)
156+
continue()
157+
endif()
155158
set(_out ${_rule_dir}/ir_src.json)
156159
add_custom_command(
157160
OUTPUT ${_out}
158-
COMMAND $<TARGET_FILE:cpp-rule-preprocessor> --file ${_src}
159-
DEPENDS ${_src} ${PROJECT_SOURCE_DIR}/cpp2rust/cpp_rule_preprocessor.cpp
161+
COMMAND $<TARGET_FILE:cpp-rule-preprocessor> --dir ${_rule_dir}
162+
DEPENDS ${_srcs} ${PROJECT_SOURCE_DIR}/cpp2rust/cpp_rule_preprocessor.cpp
160163
VERBATIM
161164
)
162165
list(APPEND cpp_rules_ir_outputs ${_out})

cpp2rust/converter/mapper.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ TranslationRule::TypeRule *search(clang::QualType qual_type) {
405405
void addRulesFromDirectory(const std::filesystem::path &dir, Model model) {
406406
for (const auto &entry : std::filesystem::recursive_directory_iterator(dir)) {
407407
auto &path = entry.path();
408-
if (entry.is_regular_file() && path.extension() == ".cpp") {
408+
if (entry.is_regular_file() &&
409+
(path.extension() == ".cpp" || path.extension() == ".c")) {
409410
auto [expr_rules, type_rules] = TranslationRule::Load(path, model);
410411
if (expr_rules.empty() && type_rules.empty()) {
411412
log() << "No rules found in " << path << '\n';

cpp2rust/cpp_rule_preprocessor.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -777,24 +777,39 @@ namespace {
777777
llvm::cl::OptionCategory cat("cpp-rule-preprocessor options");
778778

779779
llvm::cl::opt<std::string>
780-
SrcFile("file",
781-
llvm::cl::desc("Path to a rule's src.cpp. ir_src.json is written "
782-
"next to it"),
783-
llvm::cl::value_desc("src.cpp"), llvm::cl::Required,
784-
llvm::cl::cat(cat));
780+
SrcDir("dir",
781+
llvm::cl::desc("Path to a rule directory containing src.c and/or "
782+
"src.cpp. ir_src.json is written into this dir."),
783+
llvm::cl::value_desc("rule-dir"), llvm::cl::Required,
784+
llvm::cl::cat(cat));
785785

786786
} // namespace
787787

788788
int main(int argc, char *argv[]) {
789789
llvm::cl::HideUnrelatedOptions(cat);
790790
llvm::cl::ParseCommandLineOptions(argc, argv);
791791

792-
fs::path src = SrcFile.getValue();
793-
llvm::errs() << "Preprocessing " << src.string() << '\n';
792+
fs::path dir = SrcDir.getValue();
794793
llvm::json::Object root;
795-
cpp2rust::Extract(src, root);
794+
for (const char *name : {"src.c", "src.cpp"}) {
795+
auto path = dir / name;
796+
if (!fs::exists(path)) {
797+
continue;
798+
}
799+
llvm::errs() << "Preprocessing " << path.string() << '\n';
800+
llvm::json::Object file_root;
801+
cpp2rust::Extract(path, file_root);
802+
for (auto &[k, v] : file_root) {
803+
if (!root.try_emplace(k, std::move(v)).second) {
804+
llvm::errs() << "ERROR: rule name " << k.str()
805+
<< " defined in multiple files in " << dir.string()
806+
<< '\n';
807+
return EXIT_FAILURE;
808+
}
809+
}
810+
}
796811

797-
auto out_path = src.parent_path() / "ir_src.json";
812+
auto out_path = dir / "ir_src.json";
798813
std::error_code ec;
799814
llvm::raw_fd_ostream out(out_path.string(), ec);
800815
if (ec) {

rules/cstring/ir_unsafe.json

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,5 +312,81 @@
312312
"type": "*mut u8",
313313
"is_unsafe_pointer": true
314314
}
315+
},
316+
"f5": {
317+
"body": [
318+
{
319+
"text": "libc::strchr("
320+
},
321+
{
322+
"placeholder": {
323+
"arg": 0,
324+
"access": "read"
325+
}
326+
},
327+
{
328+
"text": " as *const i8, "
329+
},
330+
{
331+
"placeholder": {
332+
"arg": 1,
333+
"access": "read"
334+
}
335+
},
336+
{
337+
"text": ") as *mut u8"
338+
}
339+
],
340+
"params": {
341+
"a0": {
342+
"type": "*const u8",
343+
"is_unsafe_pointer": true
344+
},
345+
"a1": {
346+
"type": "i32"
347+
}
348+
},
349+
"return_type": {
350+
"type": "*mut u8",
351+
"is_unsafe_pointer": true
352+
}
353+
},
354+
"f6": {
355+
"body": [
356+
{
357+
"text": "libc::strchr("
358+
},
359+
{
360+
"placeholder": {
361+
"arg": 0,
362+
"access": "read"
363+
}
364+
},
365+
{
366+
"text": " as *const i8, "
367+
},
368+
{
369+
"placeholder": {
370+
"arg": 1,
371+
"access": "read"
372+
}
373+
},
374+
{
375+
"text": ") as *const u8"
376+
}
377+
],
378+
"params": {
379+
"a0": {
380+
"type": "*const u8",
381+
"is_unsafe_pointer": true
382+
},
383+
"a1": {
384+
"type": "i32"
385+
}
386+
},
387+
"return_type": {
388+
"type": "*const u8",
389+
"is_unsafe_pointer": true
390+
}
315391
}
316392
}

rules/cstring/src.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
#include <string.h>
5+
6+
void *f1(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); }
7+
8+
void *f2(void *dst, int c, size_t n) { return memset(dst, c, n); }
9+
10+
int f3(const void *s1, const void *s2, size_t n) { return memcmp(s1, s2, n); }
11+
12+
void *f4(void *dst, const void *src, size_t n) { return memmove(dst, src, n); }
13+
14+
char *f5(const char *a0, int a1) { return strchr(a0, a1); }

rules/cstring/src.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,4 @@
33

44
#include <string.h>
55

6-
void *f1(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); }
7-
8-
void *f2(void *dst, int c, size_t n) { return memset(dst, c, n); }
9-
10-
int f3(const void *s1, const void *s2, size_t n) { return memcmp(s1, s2, n); }
11-
12-
void *f4(void *dst, const void *src, size_t n) { return memmove(dst, src, n); }
6+
const char *f6(const char *a0, int a1) { return strchr(a0, a1); }

rules/cstring/tgt_unsafe.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,11 @@ unsafe fn f4(a0: *mut u8, a1: *const u8, a2: usize) -> *mut u8 {
3535
}
3636
a0
3737
}
38+
39+
unsafe fn f5(a0: *const u8, a1: i32) -> *mut u8 {
40+
libc::strchr(a0 as *const i8, a1) as *mut u8
41+
}
42+
43+
unsafe fn f6(a0: *const u8, a1: i32) -> *const u8 {
44+
libc::strchr(a0 as *const i8, a1) as *const u8
45+
}

tests/unit/out/unsafe/strchr_c.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub fn main() {
10+
unsafe {
11+
std::process::exit(main_0() as i32);
12+
}
13+
}
14+
unsafe fn main_0() -> i32 {
15+
let mut s: *const u8 = b"hello world\0".as_ptr().cast_mut().cast_const();
16+
let mut r: *mut u8 = libc::strchr(s as *const i8, ('w' as i32)) as *mut u8;
17+
assert!((((!((r).is_null())) as i32) != 0));
18+
assert!((((((*r) as i32) == ('w' as i32)) as i32) != 0));
19+
assert!(((((libc::strchr(s as *const i8, ('z' as i32)) as *mut u8).is_null()) as i32) != 0));
20+
return 0;
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub fn main() {
10+
unsafe {
11+
std::process::exit(main_0() as i32);
12+
}
13+
}
14+
unsafe fn main_0() -> i32 {
15+
let mut s: *const u8 = b"hello world\0".as_ptr();
16+
let mut r: *const u8 = libc::strchr(s as *const i8, (('w' as u8) as i32)) as *const u8;
17+
assert!(!((r).is_null()));
18+
assert!((((*r) as i32) == (('w' as u8) as i32)));
19+
assert!((libc::strchr(s as *const i8, (('z' as u8) as i32)) as *const u8).is_null());
20+
return 0;
21+
}

tests/unit/strchr_c.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// no-compile: refcount
2+
#include <assert.h>
3+
#include <string.h>
4+
5+
int main() {
6+
const char *s = "hello world";
7+
char *r = strchr(s, 'w');
8+
assert(r != NULL);
9+
assert(*r == 'w');
10+
assert(strchr(s, 'z') == NULL);
11+
return 0;
12+
}

0 commit comments

Comments
 (0)