diff --git a/CMakeLists.txt b/CMakeLists.txt index 933b9a235..5500eabc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,7 +231,7 @@ set(rules_index_parts_dir ${CMAKE_BINARY_DIR}/rules_index/parts) set(RULES_IR_DIR "${CMAKE_BINARY_DIR}/rules") set(rust_rules_ir_outputs) foreach(_rule_dir IN LISTS rule_subdirs) - file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp ${_rule_dir}/src_cxx*.cpp) + file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp ${_rule_dir}/src_*.cpp) if(NOT _cpp_srcs) continue() endif() diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index 73f5c1bfe..7f12147d8 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -320,6 +320,9 @@ std::string BuildRulesEpilogue(const std::unordered_set &keys, if (auto since = rule->getInteger("since"); since && *since > standard) { continue; } + if (auto until = rule->getInteger("until"); until && *until <= standard) { + continue; + } if (auto required = rule->getArray("requires"); required && !llvm::all_of(*required, [&](const llvm::json::Value &key) { diff --git a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp index 2478d6f2c..240431c17 100644 --- a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp +++ b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp @@ -335,15 +335,27 @@ llvm::json::Object ReadJson(const fs::path &path) { return std::move(*parsed->getAsObject()); } -unsigned SinceStandard(const fs::path &src_path) { +struct StandardRange { + unsigned since = 11; + unsigned until = 0; +}; + +StandardRange RuleFileStandards(const fs::path &src_path) { auto stem_string = src_path.stem().string(); llvm::StringRef stem = stem_string; - unsigned standard = 11; - if (stem.consume_front("src_cxx") && stem.getAsInteger(10, standard)) { + StandardRange range; + bool valid = stem.consume_front("src"); + if (valid && stem.consume_front("_cxx")) { + valid = !stem.consumeInteger(10, range.since); + } + if (valid && stem.consume_front("_until_cxx")) { + valid = !stem.consumeInteger(10, range.until); + } + if (!valid || !stem.empty()) { llvm::errs() << "ERROR: unexpected rule file " << src_path.string() << '\n'; std::exit(EXIT_FAILURE); } - return standard; + return range; } void MergeRules(llvm::json::Object &into, llvm::json::Object &&from) { @@ -364,20 +376,24 @@ void IndexDir(const fs::path &rule_dir, const fs::path &out) { for (const auto &entry : fs::directory_iterator(rule_dir)) { auto name = entry.path().filename().string(); if (name == "src.c" || name == "src.cpp" || - (name.starts_with("src_cxx") && name.ends_with(".cpp"))) { + (name.starts_with("src_") && name.ends_with(".cpp"))) { cxx_srcs.push_back(entry.path()); } } std::ranges::sort(cxx_srcs); llvm::json::Object cxx_rules; for (const auto &src_path : cxx_srcs) { - auto standard = SinceStandard(src_path); + auto range = RuleFileStandards(src_path); llvm::json::Object rules; Index(src_path, /*is_c=*/false, dir_name, - {std::format("-std=c++{}", standard)}, rules); + {std::format("-std=c++{}", range.since)}, rules); for (auto &[key, entries] : rules) { for (auto &entry : *entries.getAsArray()) { - (*entry.getAsObject())["since"] = standard; + auto &rule = *entry.getAsObject(); + rule["since"] = range.since; + if (range.until) { + rule["until"] = range.until; + } } } MergeRules(cxx_rules, std::move(rules)); diff --git a/rules/algorithm/src_until_cxx17.cpp b/rules/algorithm/src_until_cxx17.cpp new file mode 100644 index 000000000..09eb27554 --- /dev/null +++ b/rules/algorithm/src_until_cxx17.cpp @@ -0,0 +1,8 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template void f18(T1 first, T1 last) { + return std::random_shuffle(first, last); +} diff --git a/rules/algorithm/tgt_refcount.rs b/rules/algorithm/tgt_refcount.rs index 52a96393e..fb5d275e3 100644 --- a/rules/algorithm/tgt_refcount.rs +++ b/rules/algorithm/tgt_refcount.rs @@ -117,3 +117,16 @@ fn f16(a0: Ptr, a1: Ptr) -> Ptr { fn f17(a0: Ptr, a1: Ptr) -> Ptr { if a0.read() >= a1.read() { a0 } else { a1 } } + +fn f18(a0: Ptr, a1: Ptr) { + let count = a1.get_offset() - a0.get_offset(); + let random = std::hash::RandomState::new(); + for i in (1..count).rev() { + let j = std::hash::BuildHasher::hash_one(&random, i) as usize % (i + 1); + let x = a0.offset(i); + let y = a0.offset(j); + let tmp = x.read(); + x.write(y.read()); + y.write(tmp); + } +} diff --git a/rules/algorithm/tgt_unsafe.rs b/rules/algorithm/tgt_unsafe.rs index 6257589a5..ffec814fc 100644 --- a/rules/algorithm/tgt_unsafe.rs +++ b/rules/algorithm/tgt_unsafe.rs @@ -133,3 +133,11 @@ unsafe fn f17(a0: *const T1, a1: *const T1) -> *const T1 { (a1) as *const _ } } + +unsafe fn f18(a0: *mut T1, a1: *mut T1) { + let len = a1.offset_from(a0) as usize; + let slice = ::std::slice::from_raw_parts_mut(a0, len); + for i in (1..len).rev() { + slice.swap(i, libc::rand() as usize % (i + 1)); + } +} diff --git a/rules/string/src_until_cxx20.cpp b/rules/string/src_until_cxx20.cpp new file mode 100644 index 000000000..1e763b1fe --- /dev/null +++ b/rules/string/src_until_cxx20.cpp @@ -0,0 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +bool f31(const std::string &a, const char *b) { return a != b; } diff --git a/rules/string/tgt_refcount.rs b/rules/string/tgt_refcount.rs index 96b9cb6ed..4d96960d2 100644 --- a/rules/string/tgt_refcount.rs +++ b/rules/string/tgt_refcount.rs @@ -190,3 +190,7 @@ fn f29(a0: Ptr>, a1: Vec) { fn f30(a0: Ptr>, a1: &mut Vec) { a0.write(std::mem::take(&mut *a1)) } + +fn f31(a0: Vec, a1: Ptr) -> bool { + a1.with_c_str(|__s| a0[..a0.len().saturating_sub(1)] != *__s) +} diff --git a/rules/string/tgt_unsafe.rs b/rules/string/tgt_unsafe.rs index a31efb208..a7b7966de 100644 --- a/rules/string/tgt_unsafe.rs +++ b/rules/string/tgt_unsafe.rs @@ -184,3 +184,10 @@ unsafe fn f29(a0: &mut Vec, a1: Vec) { unsafe fn f30(a0: &mut Vec, a1: &mut Vec) { *a0 = std::mem::take(&mut *a1) } + +unsafe fn f31(a0: Vec, a1: *const libc::c_char) -> bool { + a0 != { + let s = a1; + std::slice::from_raw_parts(s, (0..).take_while(|&i| *s.add(i) != 0).count() + 1).to_vec() + } +} diff --git a/tests/unit/elidable_iterator_copy_cxx11.cpp b/tests/unit/elidable_iterator_copy_cxx11.cpp index 6b8fad741..fbb30febd 100644 --- a/tests/unit/elidable_iterator_copy_cxx11.cpp +++ b/tests/unit/elidable_iterator_copy_cxx11.cpp @@ -10,5 +10,8 @@ int main() { std::vector::iterator it = std::find(v.begin(), v.end(), 7); assert(it != v.end()); assert(*it == 7); + std::random_shuffle(v.begin(), v.end()); + std::sort(v.begin(), v.end()); + assert(v[0] == 4 && v[1] == 7); return 0; } diff --git a/tests/unit/out/refcount/elidable_iterator_copy_cxx11.rs b/tests/unit/out/refcount/elidable_iterator_copy_cxx11.rs index cda73dcf7..adc8e2314 100644 --- a/tests/unit/out/refcount/elidable_iterator_copy_cxx11.rs +++ b/tests/unit/out/refcount/elidable_iterator_copy_cxx11.rs @@ -34,6 +34,21 @@ fn main_0() -> i32 { }; assert!(it != (v.as_pointer() as Ptr).to_end()); assert!(((it.read()) == 7)); + { + let count = (v.as_pointer() as Ptr).to_end().get_offset() + - (v.as_pointer() as Ptr).get_offset(); + let random = std::hash::RandomState::new(); + for i in (1..count).rev() { + let j = std::hash::BuildHasher::hash_one(&random, i) as usize % (i + 1); + let x = (v.as_pointer() as Ptr).offset(i); + let y = (v.as_pointer() as Ptr).offset(j); + let tmp = x.read(); + x.write(y.read()); + y.write(tmp); + } + }; + (v.as_pointer() as Ptr).sort((v.as_pointer() as Ptr).to_end().get_offset()); + assert!(({ (*v.borrow())[0_usize] } == 4) && ({ (*v.borrow())[1_usize] } == 7)); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/string.rs b/tests/unit/out/refcount/string.rs index 1f4e13a22..69da8da79 100644 --- a/tests/unit/out/refcount/string.rs +++ b/tests/unit/out/refcount/string.rs @@ -29,6 +29,14 @@ fn main_0() -> i32 { Ptr::::from_string_literal(b"hello") .with_c_str(|__s| (*s1.borrow())[..(*s1.borrow()).len().saturating_sub(1)] == *__s) ); + assert!( + Ptr::::from_string_literal(b"world") + .with_c_str(|__s| (*s1.borrow())[..(*s1.borrow()).len().saturating_sub(1)] != *__s) + ); + assert!( + !(Ptr::::from_string_literal(b"hello") + .with_c_str(|__s| (*s1.borrow())[..(*s1.borrow()).len().saturating_sub(1)] != *__s)) + ); let mut p1: Ptr = (s1.as_pointer() as Ptr); assert!((((elem!(p1, 0).read()) as i32) == (('h' as i8) as i32))); assert!((((elem!(p1, 1).read()) as i32) == (('e' as i8) as i32))); diff --git a/tests/unit/out/unsafe/elidable_iterator_copy_cxx11.rs b/tests/unit/out/unsafe/elidable_iterator_copy_cxx11.rs index ddbf28195..e53770a75 100644 --- a/tests/unit/out/unsafe/elidable_iterator_copy_cxx11.rs +++ b/tests/unit/out/unsafe/elidable_iterator_copy_cxx11.rs @@ -31,6 +31,18 @@ unsafe fn main_0() -> i32 { }; assert!(it != v.as_mut_ptr().add(v.len())); assert!(((*it) == (7))); + { + let len = v.as_mut_ptr().add(v.len()).offset_from(v.as_mut_ptr()) as usize; + let slice = ::std::slice::from_raw_parts_mut(v.as_mut_ptr(), len); + for i in (1..len).rev() { + slice.swap(i, libc::rand() as usize % (i + 1)); + } + }; + { + let len = v.as_mut_ptr().add(v.len()).offset_from(v.as_mut_ptr()) as usize; + ::std::slice::from_raw_parts_mut(v.as_mut_ptr(), len).sort() + }; + assert!(((v[(0_usize)]) == (4)) && ((v[(1_usize)]) == (7))); return 0; } pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/string.rs b/tests/unit/out/unsafe/string.rs index 71432816b..eb2fe255e 100644 --- a/tests/unit/out/unsafe/string.rs +++ b/tests/unit/out/unsafe/string.rs @@ -33,6 +33,20 @@ unsafe fn main_0() -> i32 { .to_vec() } ); + assert!( + s1 != { + let s = c"world".as_ptr(); + std::slice::from_raw_parts(s, (0..).take_while(|&i| *s.add(i) != 0).count() + 1) + .to_vec() + } + ); + assert!( + !(s1 != { + let s = c"hello".as_ptr(); + std::slice::from_raw_parts(s, (0..).take_while(|&i| *s.add(i) != 0).count() + 1) + .to_vec() + }) + ); let mut p1: *const libc::c_char = (s1.as_mut_ptr()).cast_const(); assert!((((*p1.offset((0) as isize)) as i32) == (('h' as libc::c_char) as i32))); assert!((((*p1.offset((1) as isize)) as i32) == (('e' as libc::c_char) as i32))); diff --git a/tests/unit/string.cpp b/tests/unit/string.cpp index 560531bce..41ba4d0ee 100644 --- a/tests/unit/string.cpp +++ b/tests/unit/string.cpp @@ -1,3 +1,4 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++17 #include #include #include @@ -14,6 +15,8 @@ int main() { assert(s1[3] == 'l'); assert(s1[4] == 'o'); assert(s1 == "hello"); + assert(s1 != "world"); + assert(!(s1 != "hello")); const char *p1 = s1.data(); assert(p1[0] == 'h'); assert(p1[1] == 'e');