Skip to content

Delete rules for vector<vector<T>> and for nested containers - #530

Merged
nunoplopes merged 7 commits into
Cpp2Rust:masterfrom
lucic71:nested-containers
Oct 13, 2026
Merged

nunoplopes merged 7 commits into
Cpp2Rust:masterfrom
lucic71:nested-containers

Conversation

@lucic71

@lucic71 lucic71 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Closes #15

Translates vector<vector<T>> as Vec<Vec<T>> instead of Vec<Value<Vec<T>>>. Nested arrays also lose the extra Value. This allows deleting the rules for vector<vector<T>>.

This adds a new PtrKind:

pub trait SliceParent<T> {
    fn root(&self) -> Rc<dyn Any>;
    fn borrow_slice<'a>(&self, root: &'a dyn Any) -> Ref<'a, [T]>;
    fn borrow_slice_mut<'a>(&self, root: &'a dyn Any) -> RefMut<'a, [T]>;
    fn address(&self) -> usize;
}

PtrKind {
  Nested(Rc<dyn SliceParent<T>>)
  ...
}

root accesses the parent, borrow_slice/borrow_slice_mut are used for reading/writing into the nested row and address is sued for identity.

commit faa6d928c58b144648a61c1c3c2847397c45bfbc
Merge: 943b49750 72bb583d7
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 15:43:19 2026 +0100

    Merge branch 'master' into issue-15

commit 943b49750111c5053cde8e92866386960c158654
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 15:39:03 2026 +0100

    Don't clone entire row on global access

commit 434c7bbf506a7fa95f529c67ef1870556b3532dc
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:56:15 2026 +0100

    Add UB tests

commit 03d04950cd299eabd790fb7db25cc496bcb59e27
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:50:20 2026 +0100

    Update docs

commit 1240976194f7676d7870e607492aab8c973997d3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:45:05 2026 +0100

    Add reinterpret for nested pointers

commit 97cfa97bbf59db6f73e7b424fc6bbe25344a12a2
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:44:20 2026 +0100

    Update tests

commit b0f2819648b1583f42c216250acb9ac3a2b38e6f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:36:14 2026 +0100

    Fix pointer subscript

commit 8d675d13945efee622952b052e02b27ae5d3877c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:15:56 2026 +0100

    Fix imbricated access for array

commit ce937c1eb124111e194abd5f200a3cefc28de1ab
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:08:56 2026 +0100

    Update tests

commit 3a0358fa56d2db56c5579a95a0cfbb568ab9f8b3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:03:34 2026 +0100

    Drop boxing for multi dimensional arrays

commit 9118872d0ca88012d31204091d9b43e049474a79
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 14:00:09 2026 +0100

    Add library support for multi dimensional containers

commit c157d41ba52103a960a018c1f340bac20528c05c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 13:59:53 2026 +0100

    Drop Value from imbricated containers

commit 6b083f6235f153f2cd8eb0948b783d9f14a30239
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 13 13:53:58 2026 +0100

    Delete vector<vector> rules

commit 78d6570714b4eacb601f613e98c89306d17447ab
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 21:35:16 2026 +0100

    Fix list initializers for unions (#514)

commit e7d20118c6ddf771c25a9a5b90c0c82e1138de02
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 15:15:52 2026 +0100

    Format rules (#528)

commit ba095717c5fee13684d9e865095e892b15565361
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 10:39:46 2026 +0100

    Name anonymous records (#521)

commit cd5b552d100b47110bdba4db8d8368a24b62dfd7
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 10:21:23 2026 +0100

    Delete abstract_structs_ (#526)

    We don't need extra state to translate abstract structs.

commit eacf79e9fa09adcfaba29698aba496dc8fe9458a
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 10:20:27 2026 +0100

    Escape Rust keywords (#519)

commit 6627b2f75b2f6e9620130ba8fe6d0d29f4e87387
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Oct 12 10:19:57 2026 +0100

    Call printf rule when the format string is not static (#508)

commit d343b2777a8d0c56c398a533f9c8a3412bb2024c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 21:11:14 2026 +0100

    Skip elidable constructors  (#525)

commit 6e47d5b61b3a027b84f5c48926e4520ff12c1f22
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 15:59:50 2026 +0100

    Fix woff2 build after #523 (#524)

    I realized that #523 fully works only with the new AST matcher because
    of const types: string matcher matches strings which is broken for const
    pointers in general. Until the AST matcher lands I fixed the string
    matcher.

commit 80c214f82b8620fece91c7842b76118cce30d0a8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 13:51:19 2026 +0100

    Remove converter hack around std::map::iterator (#523)

    The old code special cased `std::map::iterator` (which is just a sugar
    for `std::pair<const K, V>`) to generate `i.first()` and `i.second()`
    instead of `i.0` and `i.1`.

    This PR removes the hack by saving a pair in `libcc2rs::MapIter` which
    can be accessed with `.0` and `.1`. It also fixes a translation bug:

    ```cpp
     for (auto i : m) {
        i.second = -1;
        assert(i.second == -1);
      }
      for (const auto &i : m) {
        assert(i.second != -1);
      }
    ```

commit a447c09c3d45100afa8ddeaeb38f2ac5ed144807
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 08:15:51 2026 +0100

    [NFC] Pass Converter to Mapper (#450)

    The current string matcher must save string rules for user defined
    types. For example `std::vector<UserDefinedRecord>` works by first
    applying the string rule for vector then the string rule for
    UserDefinedRecord.

    The new AST matcher (#447) won't need string rules for user defined
    types. After applying the rule for vector it will ask Converter to print
    UserDefinedRecord.

    I also added a test that fails with a current matcher and will pass with
    the AST matcher: `std::vector<S *const *>`. There is no string rule for
    `S * const *` and the current matcher panics.

commit 79fd877916655b1f46140a98aa85a2a31d1fb1e9
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 08:14:21 2026 +0100

    Translate va_list references and pointers (#522)

commit 2411760b3e1e819b20d341b5eaf545129314594c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 11 08:11:22 2026 +0100

    Write mutable fields through const objects (#520)

commit 86861510dfea6a3807cb0c5d86d8555c6fecbfb0
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sat Oct 10 12:41:58 2026 +0100

    Fix clang 24 build

commit 69fdca5d94c4b078d0d7f2e466baa3cc8ed04227
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 12:24:22 2026 +0100

    Selectively load rules in the same TU as the translated code (#451)

    Part of #447. This only loads the rules into cpp2rust, AST matching
    comes in a future PR:

    > To implement this, rules are included in the translated TU. Only the
    rules that parse are kept. If the TU never included <map> then there are
    no rules for map. This is faster than the string matching approach.

    cpp_rule_indexer runs at build time to build an index of rules. This
    index is used by rule_loader to lazily load the rules into the converter
    for doing AST matching.

    There are 2 index files: one for C code and one for C++ code. Both live
    in build/rules_index. C translated code only loads the C index, C++
    translated code loads both indexes.

    Example of index:

    ```
    key:      std/vector/push_back
    requires: std/vector
    dir:      vector
    text:
            template <typename T1> void f14(std::vector<T1> &o, T1 &&value) {
              return o.push_back(std::move(value));
            }

    key:      std/basic_ifstream/rdbuf
    requires: std/basic_ifstream, std/basic_filebuf
    dir:      ifstream
    text:
            std::filebuf *f4(const std::ifstream &o) { return o.rdbuf(); }
    ```

    `key` is generated using the dependent name of the rules for templates
    and using the concrete name for non-templates. `text` is extracted from
    the file and appended into the translated TU if it needs the rule.
    `requires` only loads the rule in the TU if the translated code uses all
    the dependencies, basic_ifstream and basic_fielbuf in the example.

    This makes the translation fast. With 100K dummy rules, it takes on
    avergage 20ms per invocation to selectively load the rules. With full
    loading of all the 100K dummy rules, it takes 5 seconds on average.

commit ebb7f176804416fd5996346e93e7e91e97b3d70d
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sat Oct 10 11:54:35 2026 +0100

    fix tests

commit 4701d294776ee363faa76e9df50b394e16926ad3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 11:37:07 2026 +0100

    Skip explicit specializations of template functions (#517)

commit ce92121d0f63fe70f543474626fba7f9059b1d17
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 11:35:34 2026 +0100

    Set computed expression type for default args and init lists (#516)

commit b0cddeb77da7b1d55619dace8006ec7488a2f15a
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 11:34:51 2026 +0100

    Fix lambda calls, parameters, captures and namespace-scope lambdas (#501)

    In VisitCallExpr convert the arguments of the operator call in Unboxed
    context to avoid unnecessary Value boxes
    Use ConvertVarDeclSkipInit to convert the params of the operator call so
    that it can add mut where needed
    PushConstInitializer(false) before converting the body of lambda to fix
    translation of global lambdas
    Call the move constructor when the lambda is moved
    Fix generic lambdas with no specialisations in libcc2rs-macros
    Fix lambda in lambda syntax in libcc2rs-macros

commit b7476ab50a67316c81eea3c0753166afb3597629
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 08:16:03 2026 +0100

    Fix signed printing of integers (#511)

commit 99206c57f4947c6a3ba3646f8a7bac00fd4033e0
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 08:12:55 2026 +0100

    Translate unsafe Default in unsafe context (#509)

commit 43d63624f52cb770996cc3c3e9062032416657a5
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 08:09:33 2026 +0100

    Fix delete for const pointers (#513)

commit 5c0309a5b38288016ccd9cdc7cead0d11f13010f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 08:09:16 2026 +0100

    Push init type in materialized temporaries (#512)

commit 32c5b1f830f258a3245e819e278340e663960601
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 08:03:30 2026 +0100

    Make const variables that need destruction mut in unsafe (#515)

commit 2ae262e13dd8669ef90320279d5654ca54ffab42
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Oct 10 07:59:33 2026 +0100

    Accept the j format specifier and escape braces in printf (#518)

commit e6c335eadc1f30dd56a6970871d6030f31efa8f8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 19:05:28 2026 +0100

    Fix variables named _ (#502)

commit e986cc86236699bb95923a94f4e33a14484d84f5
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 18:57:15 2026 +0100

    Fix Default for floating types and floating to boolean casts (#505)

commit 1757106b8a31eb0d60bca8ad340f7b7a8dda91d2
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 18:56:24 2026 +0100

    Fix converter crashes around freshness (#503)

commit 29ff9cd90b3aaad2772cd0e11193254fa49bf9fc
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 18:55:43 2026 +0100

    Fix Default for enums with bool underlying type (#504)

commit c5fbc922ed8901a79c280acddd4e796366e2919e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 18:55:00 2026 +0100

    Extend arity of function pointer arguments (#507)

commit ab5265de2b8f07e6867c2d9befb89c737ff07cd5
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 18:54:42 2026 +0100

    Fix const_cast for references (#506)

commit 0834e8884eff3fd872620efc008d631424b560d0
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 13:56:05 2026 +0100

    Translate self referencing initialiser  (#498)

    Fixes the original issue reported in #248. Self-refrencing in
    constructors is not handled here.

    It translates:

    ```cpp
    struct list_head list = { &list };
    ```

    to:

    ```rs
       let list: Value<list_head> = <Value<list_head>>::default();
        *list.borrow_mut() = list_head {
            next: (list.as_pointer()),
        };
    ```

    That is, first create a default value, then assign it the
    self-referencing initialiser. Types that have defaults with side-effects
    are not supported.

commit c4276ba0126554ed86e08248a47bbf27a584cfaa
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 11:00:49 2026 +0100

    Fix lambdas that receive pack captures (#500)

commit fcee2ce0b70eef2eade0900e850ef8adfb3c0f6c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 9 10:59:56 2026 +0100

    Translate generic lambdas (#499)

commit dd4b7eb7df84c4a1e59b7ce3ea6c1ceb0f489c99
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Oct 8 20:50:25 2026 +0100

    Simplify lambda translation by using macros to infer copy/move/destruct automatically

commit e8961cf10534a76a3b6fd14d5eb71268b3f28d83
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 19:40:33 2026 +0100

    Allow move-only types to implement FnPtrArg (#476)

commit 5741a8d62b82990d6ce658277eaebaea01f54d33
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 15:53:55 2026 +0100

    Translate moveable/copyable/destroyable lambdas (#458)

commit fd1f8c804a2384290e92468a143699e09ff1f09e
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Oct 8 15:51:20 2026 +0100

    Fix bug in printf %c

commit f4f82ae467383dafd7eb1f8ec51d026609823736
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 13:59:09 2026 +0100

    Translate C99 flexible array member (#473)

commit 478bd1823ff5e886104917e643fe138a76087641
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 13:50:31 2026 +0100

    Translate comparison operators that don't return bool (#496)

commit cf6b6a01824f9fbb38c94a15b6c4f434619bf79e
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Thu Oct 8 12:16:57 2026 +0100

    Remove unnecessary `unsafe` from time refcount rules (#495)

    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 8dfc4d2cd7d645e98e193e585d00be3046b8c122
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 12:07:44 2026 +0100

    Add standard-version ifdefs in rules (#448)

    Part of #447

commit abb9792bb41c863de58df7f63ec01c3743812855
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 12:03:32 2026 +0100

    Hoist static locals to module scope (#491)

commit c700ce65458ceb0c8c928426567c549a67ca6355
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 12:02:04 2026 +0100

    Fix array pointer dereference and temporary array materialisation (#487)

commit 5d79b189c7815c3614e92d0999fbb152e9743eb8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 11:59:19 2026 +0100

    Fix mismatched usize/u64 array element type (#494)

    Closes #320

commit 5a49110f7e24f3de5fbf8ace789546b1fe4fb9bb
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 10:38:26 2026 +0100

    Print float literals at full precision (#483)

commit 0d92094f6e16a2347d6b195a066dc32bb3eb3565
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 09:39:27 2026 +0100

    Emit clang-computed sizeof values in refcount (#490)

commit 5eb6fd10f26d5e76373f9fd52ecdb3ef634901b3
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Thu Oct 8 09:12:51 2026 +0100

    Fix `std::find` refcount rule when the first iterator has a non-zero offset (#493)

    The refcount `std::find` rule compared a relative `enumerate` index
    against the absolute `a1.get_offset()`. If `a0` didn't start at offset
    0, the bound was too large, so the search could run past `last`.

    - Fixes #456

    ---------

    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 6d6f83cd35c5405e810f87d9f6f57b42645145c5
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:31:38 2026 +0100

    Skip explicit template instantiation declarations (#481)

commit 9e57ef73a63a12907ceb82dc3a43e269900f0ef5
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:31:23 2026 +0100

    Distinguish declarations from one macro expansion in decl IDs (#485)

commit cb82dad9d013188ae6b804fbe002b10a236f0bcb
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:31:08 2026 +0100

    Translate references to functions as function pointers (#486)

commit 585798adfccdc40f5bf6b3e6f0bfb614935469ab
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:22:17 2026 +0100

    Translate only the taken branch of constant if conditions (#488)

    ```cpp
    template <typename T> unsigned long long widen(T v) {
      if (sizeof(T) > 4) {
        T hi = v;
        hi <<= 32;
        return hi;
      }
      return v;
    }
    ```

    The code inside if is not compilable. `hi <<= 32` is a compilation error
    in rust when T = int.

commit f68ce854f8ed9dda46a0a10426fd73f194be6411
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:18:40 2026 +0100

    Translate out-of-line copy constructors (#489)

commit 2e92ab3ca0851db6665cffbade334cf21ce1e71a
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Thu Oct 8 08:17:10 2026 +0100

    Preserve first-element semantics in max_element translations (#479)

    `std::max_element` returns the first iterator when multiple elements
    share the maximum, but the Rust translations selected the last. This
    affected vector rules f42/f94 and algorithm rule f8.

    - Fixes #454

    ---------

    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 9190c3c3ac9d3790bd29142b85776da1a9df8baa
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:16:06 2026 +0100

    Treat u8 literals by their element type (#492)

    Pre C++-20 u8 literals were `const char*`, post C++-20 they are `const
    char8_t*`

commit cca70e234b8521adcd8318155985ebe8bdd9a69d
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:13:28 2026 +0100

    Translate transparent init lists (#484)

commit 6661085aa791ee81f84a0ac61d2a1cf863918fde
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Oct 8 08:05:41 2026 +0100

    Return 0 when main falls off its end (#482)

commit 4e953e4c75abd7d07e11d23aa11ed0e92c997199
Author: Copilot <198982749+Copilot@users.noreply.github.com>
Date:   Wed Oct 7 20:53:57 2026 +0100

    Initialize empty `std::string` values with a null terminator (#478)

    The `std::string` rules model strings as null-terminated vectors, but
    default initialization produced an empty vector. Operations such as
    `length()` then incorrectly assume a terminator exists.

    - **Type rules:** Initialize default strings with `vec![0]` in both
    unsafe and refcount models.
    - **Regression coverage:** Add a default-constructed string case that
    checks `length()`.

    ```cpp
    std::string empty;
    assert(empty.length() == 0);
    ```

    <!-- START COPILOT CODING AGENT SUFFIX -->

    - Fixes #455

    ---------

    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 488bee6f7003caaebabe7991c79dcf521f68a079
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 19:38:24 2026 +0100

    Don't use uninstantiated copy constructor (#477)

commit 5911673b6df4c1453e81b1515c40c699df93cffe
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 14:59:13 2026 +0100

    Translate SourceLocExpr (#468)

commit 9263f5d4b685fc1350123db700b985c6f876cbac
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 14:58:23 2026 +0100

    Move reinterpret cast OOB tests in tests/ub (#475)

commit 8c3cee8e90107ec713f1c4dc6dcb5e169a147552
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 12:30:11 2026 +0100

    Access unions through `Ptr<Self>` (#474)

    Fixes #311

commit d546c8cd3e8f0c815ca3f3032c52ff2b78d6645f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 12:27:14 2026 +0100

    Visit linkage spec decls and skip class template specializations (#467)

commit 4efff884ff53c32c606f3c0bf3d564adfe44cd08
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 12:26:15 2026 +0100

    Add i128/u128 inc/dec and i64 Ptr add/sub (#466)

commit 485d6bbf90187d202876d34e234ea90bbc41cdea
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 12:16:38 2026 +0100

    Evaluate static_assert conditions in constant context (#469)

commit 6f6d359e163f95473000f925c0fcc5c63ca09493
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 11:09:23 2026 +0100

    Emit bool values for enums with bool underlying type (#470)

commit eb92b55fd0581f03f11ae226c4eef7292de6fac2
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:54:40 2026 +0100

    Update derives for unions and opaque records (#471)

commit 8e6b297b2acc7e35415c8dc4f5a6e6d3960ba50f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:51:56 2026 +0100

    Fix lvalue and address of conditional operator (#472)

commit 9f80cb2f9928940a190563563738403e1727cce8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:48:42 2026 +0100

    Translate noexcept expressions (#462)

    Rust has no equivalent for noexcept, let clang do the computation.

commit a16b44fed42761a06ab2737431f12932e3b8e085
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:47:16 2026 +0100

    Use fresh rvalues for comma operands and materialized temporaries (#461)

    Fixes miscompilations, rvalue comma operands should be cloned, not
    moved.

commit 405fdc480adb337295e739c0b97b6728c933b8e1
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:44:48 2026 +0100

    Name user-defined literal operators (#463)

commit 55e814ab60200a3a3668661210517159e20eb540
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Oct 7 10:44:00 2026 +0100

    Skip methods with undeduced return type (#465)

commit e3fd97157eb0012b7c7de209d475e372f647d2da
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 6 22:07:09 2026 +0100

    Evaluate default arguments lazily and support reference defaults (#459)

    The old `arg.unwrap(default_value)` pattern unconditionally evaluated
    default_value. This is wrong if default_value has side effects and the
    default value is never used.

    Also fix default arguments bound to reference arguments.

commit d8aa9c42b23f6e743cebad0c583eeaace2cad44b
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Oct 6 21:15:12 2026 +0100

    Translate delete nullptr as no-op (#457)

    delete nullptr is no-op in C++. It's wrong to translate it to
    `drop(Box::from_raw(null()))` because it triggers unsafe precondition
    violation.

    Skip the drop call when the deleted pointer is nullptr.

commit 004a9050788593c01ef9158bf2978e9e100788d3
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Tue Oct 6 11:09:59 2026 +0100

    Unbox local vectors and issue straight [] accesses instead of going through Ptr

commit d6ced87e1e5a2dae4501b43ab0e85793ee4e1081
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Tue Oct 6 09:06:59 2026 +0100

    Unbox holders of std::unique_ptr

commit 8067fe934468f0aada39460d7c87e029149bc285
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 22:28:08 2026 +0100

    Move instead of clone for returned vars

commit cfdccf1d04b04d564b2c4536d7d9290f96c3342e
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 21:58:38 2026 +0100

    Address taken analysis: dig into initializer lists

commit 373a36f4747f9276a4e0653efa799613a84fb59b
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 18:00:04 2026 +0100

    Unbox struct locals and optimize compound ptr assignments to not copy the pointer
    Around 40% speedup on Brunsli

commit f0f836e29ac3552eefa7c484fcd6f128f21caf8d
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 15:13:15 2026 +0100

    Replace `ptr.offset(x).op` with `offset!(ptr, x).op` in the rules
    The latter is more efficient

commit d7fec13190e6c322faa3bcc006960d456b18c2e8
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 15:01:59 2026 +0100

    Fail format CI if clippy issues a warning

commit ccf088359ccfb06daecd65cf11ba49ea29b63b52
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 14:59:14 2026 +0100

    fix clippy warnings

commit 3ba181cc374684c8d1cd3f1566843e63b4fdf4a1
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 14:56:05 2026 +0100

    Remove unneeded Value<T> boxing from constructors

commit 0753f651c0752d56b964b2a3117b08d89472d229
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 14:33:37 2026 +0100

    Add lightweight analysis that gathers non-address taken variables
    Remove Value<T> boxing in those cases

commit b41990ed6f5baca2570285e769faa444e5777f06
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Oct 5 13:39:33 2026 +0100

    Switch char to i8 (#452)

    Fixes #246

commit 9e032ae9ca050c099ecd6b2ee154d24874102086
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Oct 4 19:48:49 2026 +0100

    Fix testsuite compilation

commit 67fbc74348849bf4d42df74016754b6549e393cd
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Oct 4 18:28:25 2026 +0100

    fix tests

commit 89e83a88f7f2a08687790f5e48db4259a3b7f237
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 4 18:26:41 2026 +0100

    Translate lambdas using FnPtr (#439)

    Fixes #314.

commit 8be9b75ef58d40f460b1d50080f7cdbb19f86de8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 4 18:09:09 2026 +0100

    Dedup conversion operator names (#445)

    char8_t and char are both translated to u8. This breaks conversion
    operators: to_u8 (char8_t) vs to_u8 (char). Dedup the names to avoid
    name collisions.

commit 91f76f55aa14af1c26b656acff0c71013d4e8966
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Oct 4 11:08:03 2026 +0100

    Move C specific rules in src.c (#449)

    Part of #447.

commit 42533cb9860779d8bbd2693a2214eec6f2a3e807
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 2 11:59:37 2026 +0100

    Respect signedness for integer literals  (#446)

commit 5e045731bd7ae9e8588e36899295e75c80a516ef
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 2 10:47:50 2026 +0100

    Fix mismatched u64/usize pointers and references (#444)

commit 4f7fe3b64e40d1d11f69efaf03920deacd215aa4
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Oct 2 10:11:46 2026 +0100

    fix test

commit 93ca13844e100e935bd1a73ea29b1348db3debd1
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Oct 2 09:55:31 2026 +0100

    Use the `Default` default implementation more often

commit f6154ccc7163a4c069d31742d4db7b420ee71f5e
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Oct 2 08:56:03 2026 +0100

    Add a DeepClone macro to remove manual Clone implementations
    FIxes #216

commit f3700559de071679c614dcad16404f5b3461eab1
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Oct 2 08:40:00 2026 +0100

    Add `offset!(ptr, idx)` macro to create temporary pointers without ref counting overhead

commit 251c0677fbc781e4ecd2e4aa69ad10ee2dc5fd7c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 2 08:20:44 2026 +0100

    Translate static member templates (#440)

commit 6d59ab88f299a453d77f9f6e6630d9e8688d4659
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Oct 2 08:15:40 2026 +0100

    Fix u64/usize mismatch in array init and ctor call (#442)

commit 0383cd5b894c1545fdea048d01327d69c378ce5b
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Oct 1 22:07:57 2026 +0100

    Update to Rust 1.99 (#443)

commit 63dd17e631ed9144c7755d01778ee3630acba83f
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Wed Sep 30 19:34:10 2026 +0100

    Reduce the number of temporary variabkes created by using braces
    Rust 2024 treates the last expr as temporary, allowing us to write
    *x.borrow_mut() = { *x.borrow() +1 }
    without causing borrowing errors

    We must careful only with side effects and evaluation orders

commit ce687c40e84bb232b13583066962e1b6cc55ab47
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Wed Sep 30 18:59:47 2026 +0100

    Add a macro to derive ByteRepr automatically
    Shrinks the translated code
    Part of #216

commit 619be59e0df1de36bcd316841f93403aacb7632f
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Wed Sep 30 18:06:48 2026 +0100

    Remove Value<> boxing from struct fields except arrays/vectors (#437)

commit aa9e5746c20cca6f0eabd481dd9adb38a4ecd908
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 30 14:46:08 2026 +0100

    Add extra tests for struct fields, PR #437 (#438)

commit 6a1926705ab5146cea7050f5203b3bce33e7b5eb
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 30 09:13:52 2026 +0100

    [NFC] Split responsibilities of Mapper (#434)

    Mapper grew too big. It has 4 responsibilities at the same time:
    1. Map expressions or types to a rust string
    2. Match expressions or types against translation rules
    3. Answer queries about the translation rules (ParamIsPointer,
    IsLibcPassthrough, etc)
    4. Print string representation of expressions or types

    Only the first should be in Mapper, the rest should be split.

    For (2) I plan to add a new tree matcher instead of the current string
    matcher. To make the transition smooth I extracted the matching logic
    form Mapper into `cpp2rust/converter/rules/matcher.h`.

    For (3) I added `cpp2rust/converter/rules/registry.h` which keeps the
    `exprs_` and `rules_` multimaps + handles queries about the translation
    rules.

    For (4) I added `cpp2rust/converter/printer.h` which should BTW
    disappear after we switch to tree matching instead of string matching.

    This is a building block for fixing #435

commit edd15e8cbf509beeb3c58ce23a26532137f3fe23
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Wed Sep 30 09:02:15 2026 +0100

    fix test output

commit eaac2eb6fdeb726709461a85346e8a2179c40231
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Wed Sep 30 08:55:59 2026 +0100

    fix build with clang 24

commit 44deda0d72b763a030abe9fe2bfc109c9d6b6591
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 29 22:30:20 2026 +0100

    Avoid extremely long names in overloaded methods (#431)

    ```diff
    -   pub unsafe fn apply_Optionunsafefnpmuti32_i32__i32_const(
    -        &self,
    -        mut f: Option<unsafe fn(*mut i32, i32)>,
    -        mut x: i32,
    -    ) -> i32 {
    +    pub unsafe fn apply_11(&self, mut f: Option<unsafe fn(*mut i32, i32)>, mut x: i32) -> i32 {
    ```

    This generates more readable names and avoid special-casing characters
    in the mangled name.
    Also it fixes a problem in the codegen where a template is visited
    before the type it's instantiated with.

commit ee38fdab975a2b224c712d08c49476bc0728377e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 29 15:21:54 2026 +0100

    Derive VaArg and FnPtrArg for cloneable records (#432)

commit 90ddea537e3265ad7c7a252b799bf95271b34471
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 29 15:14:54 2026 +0100

    Avoid collision between user-defined record and libcc2rs (#433)

    The other option is to use fully qualified names in the output, for
    example libcc2rs::Ptr, but that's too verbose.

commit c8329a61aea05cc9d9ce96638f77f1fa856731ff
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 28 21:44:26 2026 +0100

    Handle multi-dimensional arrays in codegen (#428)

commit 3a9064ff4f7c63b826945d68aac1639a158298ea
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 28 21:34:59 2026 +0100

    Translate access of static member through object (#430)

    ```cpp
    struct S {
      static int static_member = 0;
    }

    assert(S{}.static_member == 0);
    assert(S::static_member == 0);
    ```

    Both are valid C++. Same for static methods.

commit 9ba0d6974c5bff4952f69a7bd768ca3a02eb50ee
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 28 21:33:40 2026 +0100

    Move builtin types in rules/ (#429)

    Removes the awkward handling of builtin types from addBuiltinTypes.
    Remove unneeded casts between builtin pointer types.
    Also useful for a future PR where I plan to replace string rule matching
    with structured tree matching.

commit e3c65269c67a403f1a8f839f355027c41c28305e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 28 21:30:58 2026 +0100

    Drop helper structs in algorithm rules (#427)

commit 047cb7c66d99149cb20e642b6214e52f6ddef176
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 28 18:45:22 2026 +0100

    Don't clone whole global arrays on a read

commit 1f5e1f5d6cbaca09f82ef0982cbc21dc21d7bafc
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 28 08:51:09 2026 +0100

    libcc2rs: various optimizations that yield about 10% speedup on Brunsli
    Inline a few hot functions
    Rearrange PtrKind to simplify the generated code

commit 825b71c16665cdb4a199e102d2baaf4a328814a5
Author: João Gouveia <joaotalonegouveia@tecnico.ulisboa.pt>
Date:   Mon Sep 28 08:34:44 2026 +0100

    Use fully-qualified syntax for decay calls (#426)

    Addresses the second issue mentioned in #425

commit 83af9de94d1eb37bcd77775ffb66dcac6438cb49
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 27 10:43:05 2026 +0100

    Fix build of testsuite

commit 2b2612f305810ec01de1cd4f2ba5bc52cef0193b
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 27 10:20:25 2026 +0100

    Fix compilation after `ninja format` has been run

commit 4a4238166a79a018ac6a778efe23ef0fd6d1b812
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 27 09:59:32 2026 +0100

    Remove a few spurious clones before Ptr::to_any

commit 58a2a29b43ac13b747f031f392e12cdbe932a9f4
Author: João Gouveia <joaotalonegouveia@tecnico.ulisboa.pt>
Date:   Sun Sep 27 09:45:47 2026 +0100

     Fix void pointer comparisons (#424)

    `AnyPtr` comparisons rely on the equals method of the `ErasedPtr` trait.
    For `Ptr`, this method is as follows:
    ```rust
    fn equals(&self, other: &dyn ErasedPtr) -> bool {
        other.as_any().downcast_ref::<Ptr<T>>() == Some(self)
    }
    ```
    This implementation causes `AnyPtr` equality checks to succeed only when
    the underlying `ErasedPtr`s have the same pointee type, since
    `.as_any().downcast_ref::<Ptr<T>>()` returns `None` otherwise.

    I do not believe this behavior is correct. Two pointers cast to `void *`
    compare equal if they point to the same address, regardless of their
    original types.

    This patch adds two unit tests covering this bug and addresses it by
    comparing the two `ErasedPtrs` through their byte representations.

commit ba061bfe0afb8aeb214bd7387cd9ad889979340f
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Sep 25 18:00:59 2026 +0100

    [unsage] Fix missing transmute in function pointer cast
    Fixes test fn_ptr_typedef_cast.c

commit 3086a02129a93771649fa720d12fae855a57435a
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Sep 25 17:56:16 2026 +0100

    Fix string.h translation with glibc >= 2.43

commit da79fe9efc8c77b094b76b54aa8d7abd07740ba4
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Sep 25 17:27:11 2026 +0100

    Build all of Rust in the build directory and avoid double build on format

commit bce8f54006312d4642382983fc0581e5738657ca
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Sep 25 16:41:52 2026 +0100

    Move shims from libcc2rs into rules (#421)

    We used `libcc2rs/src/libc_shims` to define various shims for
    interacting with libc from refcount: CFile, CFdSet, networking structs,
    etc. This will soon get very big with the addition of new rules.

    I moved each shim in the corresponding rule dir, for example
    `rules/stdio/shim.rs`, `rules/select/shim.rs`, etc. This way, shims stay
    small and separated.

    libcc2rs gathers all shim.rs files form rules/ at build time and
    compiles them.

commit 2a8276e92a0392c1dd6583fd10c8fb3d5cb0efd7
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Fri Sep 25 16:21:59 2026 +0100

    Translate concepts and requires expressions (#347)

    They are evaluated by clang. Only the result of the evaluation is
    translated.

    Furthermore, static_assert is no-op because it's evaluated at compile
    time. Same for concept declaration.

commit 483abe62b6ff4eaa9fc506d9fb4f8f19de146c00
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Sep 24 21:34:12 2026 +0100

    Add lambda testsuite (#423)

commit 1b519b63b8784121c0ff8697ea73e97b74029e67
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 22:34:34 2026 +0100

    Add support for rules that receive argument packs (#416)

    Add support for rules like `std::vector<T1>::emplace_back(Args&&....)`
    and `std::unique_ptr<T>::make_unique(Args&&...)`. This also deletes the
    emplace back plugin.

    This sets the stage for adding other argument pack rules like
    `std::construct_at` or `std::map::try_emplace`.

    Rust does not support forwarding argument packs. So emplace_back is
    translated as `v.push(T::new(arg1, arg2, ...))`, i.e. it builds the
    object at the call site. For things that might not build the object
    using the provided arguments, like try_emplace, we will use the
    following translation that conditionally builds the object:
    `m.entry(key).or_insert_with(|| T::new(arg1, arg2, ...))`.

    I used a new node in Rules IR, called init, which rule-preprocessor
    creates when encountering a pack that builds an object. cpp2rust reads
    the init node and creates a construct expression which is then
    translated in order to create `T::new(arg1, arg2, ...)`.

commit 6fbeb0cf6aa3ed1c26c691b45437d36f39d67741
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:55:12 2026 +0100

    Translate in-class initializers in Default trait (#332)

commit 578694b8b2b09a30a71480d8b533ca2bd21fc150
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:39:10 2026 +0100

    Use copy_from/move_from to translate copy/move ctor (#418)

    If possible, use the `copy_from`/`move_from` idiomatic name instead of
    `S_pmutS_rv`. This is only possible when the copy/move constructor is
    unique (no other copy/move constructor exists) and when no other method
    in the class is called copy_from/move_from.

    Do the same for copy/move assignment: copy_assign and move_assign.

commit aa7c10462a7d9623830933934499d0faa509bb88
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:13:37 2026 +0100

    Translate if constexpr (#345)

commit 98cc1b796f9db0913c4b90fdfc1bf5f76710065e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:04:04 2026 +0100

    Use idiomatic S::new for ctor instead of S::S (#420)

commit aa071d8f43fe7e7f42a1bff9662d1bb39262a925
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:03:09 2026 +0100

    Generate to_bool instead of operator__Bool (#419)

commit 43df4b9738d30aaa9b79c9b395cfff54bf724660
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 23 21:02:16 2026 +0100

    Translate std::launder (#417)

    Initially I wanted to special case this in the converter like std::move
    and std::forward. However since this is just a passthrough rule that
    receives a pointer and returns a pointer + does not change the value
    category of the argument, I put this in rules.

commit ed10067a1b04e549f299cd8483cb427c9e23c443
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Tue Sep 22 17:22:34 2026 +0100

    Refactor how function pointer casts work by creating a trampoline automatically
    Simplifies the generated code at the expense of more runtime library code

commit 8173b259fc1fcf4c690374629aaf7179f8a5e870
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 22 14:18:19 2026 +0100

    Add rules for __builtin_abort/nanf/inff/clzl (#415)

commit 511e5b752e2bb09d6dc2993e8fd2cee2d8580799
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 22 10:27:27 2026 +0100

    Handle transparent std calls (#414)

    Similar to move and forward, handle move_if_noexcept and as_const in
    codegen. They have no effect at runtime.

commit d9b33e5eb95565ad3785594266126926778e0065
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 22 08:31:23 2026 +0100

    Translate std::forward (#413)

    Similar to `std::move`, `std::forward` performs a cast and helps the AST
    choose the right overload. It has no effect at runtime.

commit aabc367196e0c46d80737bb2dc04c141675a2efa
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 21 21:10:47 2026 +0100

    Translate friend operators (#412)

commit b398a1b7d01cdb3242292cd9e497417a94d1319b
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 21 13:48:04 2026 +0100

    Fix late instantiated virtual methods (#411)

    #394 translates records that contain declared but not defined methods.
    That did not cover virtual methods in derived records. This PR solves
    that.

    cpp2rust generated an empty `impl Base for Derived {}` when no virtual
    method had a definition. Then, when an out-of-line definition was
    encountered, cpp2rust refused it because multiple `impl Base for Derived
    {}` are not allowed by rustc. This resulted in usages of undefiend
    methods because neither the record visitor nor the out-of-line method
    visitor generated the methods.

    The fix is to defer the `impl Base for Derived {}` block at the end of
    the file, similar to how methods on Ptr are deferred. At the end of the
    file, where every TU was visited, we know all the methods that have
    definitions.

commit 174ebb6f2a32c6ebccfa1924d19f8ce664b13dd9
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 21 11:13:43 2026 +0100

    optimize reinterpret casts to avoid heap allocation on dereferences
    Also add fast paths for common operations (aligned access, deref u8)

commit 9934ac78bb2dc4ad2b6f6a57873d1b35ab583e50
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 21 10:31:49 2026 +0100

    optimize function pointers to avoid heap allocation

commit 6a43cf98cc90fa7f95938ce7ef8595c896018bb9
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 21 09:42:56 2026 +0100

    optimize vaarg handling

commit 8f54ac90cb04e550c22b9a86c16a4b84b82e196e
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 21 09:28:25 2026 +0100

    fix test

commit a40ebe7df024940b95f842f7b941a87b76c28dff
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Mon Sep 21 09:26:37 2026 +0100

    optimizations to libcc2rs related with strings
    Reduces heap allocations in the common case

commit 7a801325da28170bcf72dc69e10e4dabf36c9f84
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 20 22:06:45 2026 +0100

    Replace Ptr::delete_array with Ptr::delete
    There's no need to catch all those UB cases; we can refine into working code.

commit b5aa8518d98a4f68607f191c0cc71c91be3acf20
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 20 21:43:53 2026 +0100

    Remove Ptr::to_strong() and add Ptr::decay() (#410)

    To decay a pointer, we would previously translate first to a strong reference and then call `as_pointer`.
    This patch avoids that by 1) decaying pointers whenever possible and by using `decay` as a more efficient
    way to decay pointers (does only a weak clone).

commit 50d844d21bbcef8d98c09a0b866f670f4d83134f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 18:59:25 2026 +0100

    Translate operators that receive by value arguments (#408)

commit 4edecf6d25d9c32c3a083503fc162e7a5422d793
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 18:42:16 2026 +0100

    Fix address of static method (#409)

commit 325c11ff0744d7cc6027415e095967e6c5b11d8a
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 20 16:55:18 2026 +0100

    fix compilation of assert with C++11

commit 352ac8a41af4c090bc542b3279ad4a459a33970c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:50:54 2026 +0100

    Don't add default impl for declared but not defined default ctor (#405)

commit 4d5177b768b934f679d908145b51f908a9535794
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:50:17 2026 +0100

    Don't translate partial specializations (#406)

    Only the full specialization is translated.

commit 2a8929229fdb8ac4dc06768cba72345a771a1fe3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:49:53 2026 +0100

    Convert operand as lvalue in array-to-pointer decay (#404)

commit 74c683000708a4c6480b93cb49a6830331c8d093
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:43:51 2026 +0100

    Define constexpr cpp2rust_assert_fail (#401)

    Calling assert in constexpr context requires cpp2rust_assert_fail to be
    constexpr defined.

commit 444164c4982971f47c4bee31b94046e9586c31e8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:42:01 2026 +0100

    Translate inner templates (#400)

commit 6c870265d3794e8794ed0e76455688a4015cc572
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:41:16 2026 +0100

    Fix address of conditional operand (#403)

commit 582e0f6d2ce5124757be2e58e110b0f42aab0dfa
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:40:03 2026 +0100

    Translate empty unions in unsafe (#402)

    Unions cannot be empty in unsafe, add a dummy u8 member.

commit d9a10be01b671557ef3cc1a38a774b0a184fb834
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 15:39:04 2026 +0100

    Translate unary plus (#399)

commit 3b4667707b49c5c9fe19f4071a061498ae447c68
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 13:59:24 2026 +0100

    Don't use uninstantiated default argument (#398)

commit e348a4fbe5b7c6ef0ca984c9b3bd43fa41fc496a
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 13:57:55 2026 +0100

    Fix temporary receivers (#397)

commit 28b0d414ac2b524e7a4756a6e96da2884a3f77b3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 13:56:51 2026 +0100

    Translate nameless copy constructor agrument as _a0 instead of self (#396)

    `self` is used only on defaulted copy constructor, i.e. inside the Clone
    implementation.

commit e345b15913b921aa8718a6ef8a08e2e823c79155
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 13:00:36 2026 +0100

    Translate functional casts (#395)

commit 0fe86a0b58bcbfd2fa323db361d19e8fe54d9549
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 12:58:00 2026 +0100

    Translate records that contain declared but not defined methods (#394)

    ```cpp
    struct S {
      int a;
      void declared_but_never_defined();
    ];

    int main() {
      S s{1};
      assert(s.a == 1);
    }
    ```

    S is a valid record that can be translated to Rust. This PR translates
    it like this:

    ```rs
    struct S {
      a: i32
    }

    trait SImpl {
      fn declared_but_never_defined(&self) { unimplemented!() }
    }
    ```

    The function gets a default `unimplemented` implementation. Other TU
    might offer a definition for the declared-only method.

commit 053a8306ef69e4d1f1018c7f3ea14b85056f91b8
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 12:55:02 2026 +0100

    Convert delegating constructors (#386)

commit 06dc24e92b154a307fb692920ce314e70a90c24c
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 11:34:56 2026 +0100

    Translate classes with mutable fields (#387)

    Classes with mutable fields can modify the mutable fields even in const
    methods. For this reason, the receiver of const methods in classes that
    contain mutable fields should be `&mut` instead of `&`.

commit 3943e3ec711c30507ea088a2a52c7da3192fad78
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 11:03:35 2026 +0100

    Translate wide strings (#381)

commit cadf6a850bc3a96c79e592930889045179e93eff
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 10:56:16 2026 +0100

    Fix translation of out-of-line template static methods (#393)

commit de1698661c793e9ecad732a2dc49a4e8598e3bd3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 10:50:39 2026 +0100

    Unbox initializer list in refcount (#392)

commit 4dcdf6181cbbfc49123ef0980735160291928df3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 10:50:29 2026 +0100

    Default initialize reamaining fields in small initializer list (#391)

commit d8a53d495392fdab9b0027350fe3bbebd84f4265
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 10:29:54 2026 +0100

    Default initialize object allocated with new (#390)

commit 62e28c9ce8ef3a09d5a39a08a40c3ddc7d35fb70
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 20 10:21:21 2026 +0100

    fix #377: modify global variables without cloning the Rc

commit cc7dcb0fe15603053faef87c751709a765ca82d3
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 09:24:06 2026 +0100

    Translate variable template declarations (#389)

commit deac5b5bfa5311954a36158d7976517e254acd83
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:58:39 2026 +0100

    Add rules for operator new and operator delete (#380)

commit e7fb91fbafd54a12bb764ce1847861f4838b0d2d
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:57:12 2026 +0100

    Ignore explicit casts to reference types (#382)

commit f65bef426f25fc50abf5ff99a4ce6db545f3284a
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:56:09 2026 +0100

    Handle more patterns in mangler (#384)

commit f9fc65d8de0870e14b44b83bfbb34dcf98c3bb24
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:54:32 2026 +0100

    Derive Clone when all fields are references (#388)

    Also when there are no fields.

commit 21842412bfa0cb40dea1ad50437427c99075bb3e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:53:06 2026 +0100

    Don't call destructor for trivially destructible types (#383)

commit deba8923b7fbb7885bd859234811c1aa18ddad4e
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 20 08:51:20 2026 +0100

    Implement Ord for Ptr (#385)

commit 754f8181a72a417954a0a53f62695533a9f15aaa
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Sep 18 09:29:51 2026 +0100

    fix #193: remove Brotli headers from repo

commit 78fe7c436e26161147214a6b9f1589c11e38d24d
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Fri Sep 18 09:00:40 2026 +0100

    cmake fixes

commit d8cc325ba5874fdea9c4905b09d88fe3f1778434
Author: João Gouveia <joaotalonegouveia@tecnico.ulisboa.pt>
Date:   Fri Sep 18 08:18:14 2026 +0100

    Make rule-preprocessor process a single directory at a time (#372)

    Changes `rule-preprocessor` to process a single directory per
    invocation, making it possible to parallelize. This matches what is
    already the case for `cpp-rule-preprocessor`. Additionally, this drops
    `modules.rs` from source control.

commit 00d90a0dc79cf2cbce4e6a99c27b438e301738a6
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Sep 17 15:31:51 2026 +0100

    Read global variables without clonning the Rc
    Part of #377. Needs to fix the writing as well.

commit e54bd9ba2640d3874095e9868530adb7ed1b425c
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Sep 17 14:56:58 2026 +0100

    Fix #114: Rust 2024 compatibility

commit 79e0bd2ac802e6c499c69b4f0f2a97e144589780
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Sep 17 14:42:03 2026 +0100

    [unsafe] Fix non-const global initializers and [unsafe, refcount] order of global initialization (#366)

    Rust requires initializers of global variables to be const. C++ does not
    require that.

    To fix this tension, wrap global variables in `std::cell::LazyCell` so
    that initialization does not depend on the constness of the initializer.

    Another problem that this PR fixes is the moment when global variables
    are initialized in both refcount and unsafe. `thread_local` in refcount
    and the new `LazyCell` in unsafe initialize the variable on the first
    usage. This is wrong if the constructors of the global variables have
    side effects that must be visible in the program, for example:

    ```cpp
    #include <cassert>

    static int total = 0;

    struct S {
      S(int x) { total += x; }
    };

    // there is no explicit usage of a and b inside the program other than the initialization
    // however the constructor has side effects that are visible on the total variable
    // so the initialization has to happen before entering main, not lazily as thread_local
    // and LazyCell normally do
    static S a(1);
    static S b(10);

    int main() {
      assert(total == 11);
      return 0;
    }
    ```

    To fix this, I added a new function that runs before main and forces the
    initialization of global variables: `__cpp2rust_init_globals()`.

    ---------

    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 9e771ccf8ea1d2eccd4f3f95e25d5508d06038a9
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Sep 17 13:34:31 2026 +0100

    format

commit 69ebb2c47922d9eecdb568d31cc4f2cb85d853a9
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Thu Sep 17 12:31:53 2026 +0100

    Don't generate empty thread_locals

    Co-authored-by: Francisco Gouveia <francisco.t.gouveia@tecnico.ulisboa.pt>

commit c99fe02d40c1cecf0657d5b688e98b001cc94bc7
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Thu Sep 17 09:56:44 2026 +0100

    Translate user defined moves (#369)

    This PR translates move constructors as ordinary constructors: `fn
    S_pmutS(_a0: *mut S) -> Self`.

    The rules for translating and using the move constructors are:

    |No | Move Constructor (MC) | Copy Constructor | Result |
    | --- | --- | --- | --- |
    | 1 | user-written | implicit/defaulted/deleted | translate MC and
    always use it |
    | 2 | user-written | user-written | translate MC and always use it |
    | 3 | implicit/defaulted | implicit/defaulted | don't translate MC and
    use `.clone()` on move |
    | 4 | implicit/defautled | user-written/deleted | translate MC and
    always use it |

    For 1 and 2, we always use the translated move constructor because it
    might contain custom logic that needs to run on every move. For 3 we use
    `.clone()` because it has the same effect as a move would have. For 4
    `.clone()` is not available (deleted) or might contain custom logic
    (user-written) so we synthesize an implicit/defaulted move constructor
    and use it.

    `std::move` becomes a transparent construct, i.e. no move decision is
    taken at the time of the cast. It's only used by clang to choose the
    right constructor. Moves happen on CXXConstructExpr.

    Each STL type has move construction rules. Rules that move using
    `std::mem::take` have their bodies rewritten as follows:
    * record has move constructor => rewrite `std::mem::take` as a call to
    the move constructor
    * record does not have move constructor and is copyable => convert as
    rvalue (without clone)
    * record does not have move constructor and is not copyable => convert
    as fresh rvalue (with clone)
    * record is STL type => use the rule for STL move constructor
    * temporary value => convert as rvalue

commit 9138d71748d2826cfe700b361e5d5e75375d3a87
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 16 21:11:15 2026 +0100

    Remove duplicated lamda/function pointer rules (#373)

    `stable_sort` rules had 2 version of rules: one that takes a lambda and
    one that takes a function pointer. This PR removes the duplication by
    adding a new trait in libcc2rs: Callable.

    Callable is implemented by `Fn` (closures, safe function items, safe
    function pointers) and `unsafe fn` pointers. Unsafe functoin items reach
    the `Callable` trait to an explicit cast to `unsafe fn` pointers because
    they don't implement `Fn`.

commit 20563817f8ff43e353d525d14b52b5daa3d8e11f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 16 21:10:30 2026 +0100

    Move function-local records in global scope (#375)

    ```cpp
    template <typename T> int get(T t) { return t.x; }

    int main() {
      struct Local {
        int x;
      };
      Local l{7};
      assert(get(l) == 7);
      return 0;
    }
    ```

    The `get` function template lives in the global scope while `Local`
    (used to instantiate `get`) lives at function scope. In the AST
    instantiations live in the global scope as well, so it gets translated
    as:

    ```rs
    // Wrong: the global scope has no Local_1. It only exists inside main
    pub unsafe fn get_0(mut t: Local_1) -> i32 {
        return (t.x as i32);
    }

    unsafe fn main_0() -> i32 {
        pub struct Local_1 {
          pub x: i32,
        };
        let mut l: Local_1 = Local_1 { x: 7 };
        assert!(((unsafe { get_0(l,) }) == (7)));
        return 0;
    }
    ```

    This PR fixes this by moving the definition of `Local_1` at a global
    scope:

    ```rs
    // Correct: now Local_1 is visible for the instantiation
    pub unsafe fn get_0(mut t: Local_1) -> i32 {
        return (t.x as i32);
    }

    unsafe fn main_0() -> i32 {
        let mut l: Local_1 = Local_1 { x: 7 };
        assert!(((unsafe { get_0(l,) }) == (7)));
        return 0;
    }

    pub struct Local_1 {
        pub x: i32,
    };
    ```

commit 07be1084b140f2ed31a15febfce8895eb9cbee91
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Wed Sep 16 15:38:24 2026 +0100

    Delete test banners and the unused address_taken.cpp (#374)

commit ae8355b1fd267abcdd718217fbd940c0570dd8b9
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 15 22:17:38 2026 +0100

    Add codegen/types docs (#370)

    This doesn't have all the info updated. For example enums are no more
    transalted as enum in Rust, I plan to update that in a future PR. For
    the moment I would like to get the core of codegen/types merged, then I
    will make the appropiate edits.

commit 1d6980f06b38889c69298f09b9c39fb8f21556ee
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Tue Sep 15 15:26:22 2026 +0100

    Remove unneeded clones in converter and rules (#371)

    This PR removes unneeded clones **in converter** by calling
    `SetValueFreshness`/`computed_expr_type_`/`SetFreshType` on every
    fressh-value-producing path. It asserts that `computed_expr_type_` is
    not Unknown after a conversion to make sure that we did not miss any
    path.

    PendingDeref carries freshness separately. Producers of PendingDeref set
    freshness and consumers act based on that. I also added
    `ComputedExprType::Pending` because freshness is unknown until the
    consumer of PendingDeref sets it.

    This PR also removes unneeded clones **in rules** using the `Access` IR
    attribute. Rules no longer clone their arguments. The preprocessor
    infers how each argument is used: `borrow, borrow_mut, move, take`. A
    `move` (`let s = a0`) consumes the argument, and it's cloned if it's not
    already fresh. A `borrow` usage (`a0.len()`) is never cloned.

    Besides removing unneeded clones, on the converter side, this fixes
    https://github.com/Cpp2Rust/cpp2rust/issues/363. On the rules side,
    `rule-preprocessor` infers the access type of each argument
    automatically instead on relying on the rule author to encode it
    properly. This caught a bug in `std::vector::push_back(T&&)`: the rule
    argument took the argument by value and cloned it, instead of moving it.

commit 04f96ff48129eacbeb376f25adf3eb5d7deef55b
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 14 22:13:54 2026 +0100

    Skip cast when reference coerces to pointer (#368)

commit d40c5c8b75aec28d784a1b1b44412a864422897f
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 14 15:57:26 2026 +0100

    Add more rules for move assignment and constructor (#362)

commit d96ed5a414b2f1214e8af73459bcbead112ebed0
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 14 10:36:21 2026 +0100

    Translate nameless C++ params as _a1, _a2, etc (#361)

commit 4290c68af058382c57bf33ce6548091822c95ce9
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Mon Sep 14 10:04:05 2026 +0100

    Add move rules for STL containers (#360)

    Don't special case the move constructor of unique_ptr inside the
    converter. Instead add a rule for it.

    Also added move constructs and assignment rules for array, vector, map.

commit c2bf86b6a727262b4c5f316073c2b64b8ad38b66
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 13 18:30:38 2026 +0100

    Bump to LLVM 23 (#359)

    Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>

commit 8ffb93b50e99c4eb9f9cdbe0b13e03ca2aae4de6
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 13 13:51:31 2026 +0100

    Fix clang 23 build

commit bf1b2d49004b24387c3af4ddeeb0b50e4109f03c
Author: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
Date:   Sun Sep 13 09:14:57 2026 +0100

    fix build with clang 24

commit 8275e7eb20d9bce411316e752523f0223bfec711
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 13 08:58:14 2026 +0100

    Translate defaulted and implicit comparison operators (#356)

    This PR translates defaulted comparison operator such as `==` or `<=>`
    and the implicit comparison operators derived from `<=>`.

    It asks Sema to define the defaulted/implicit operators and then
    cpp2rust translates them as normal methods.

commit 345588146b165049905f33c907a074e81a393e75
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sun Sep 13 08:56:27 2026 +0100

    Translate reference to array (#357)

    Reference to array is translated as `Ptr<Box<[T]>>` in refcount and
    `*const/mut [T; N]` in unsafe.

    I added a new ConversionKind called Pointee to translate reference to
    array in refcount. The semantics are:
    * `FullRefCount`: `Value<Pointee>`
    * `Pointee`: the object itself
    * `Unboxed`: same as `Pointee`, except arrays which are `[T; N]` intead
    `Box<[T]>`

    I also added `Ptr::from_string_literal_array` which is similar to
    `Ptr::from_string_literal`, the only difference is that that the new
    function returns `Ptr<Box<[u8]>>` instead of `Ptr<u8>`.

commit 329a61aace8b1c7707b44abcdb430028365c12f1
Author: Lucian Popescu <lucian.popescu@tecnico.ulisboa.pt>
Date:   Sat Sep 12 13:59:07 2026 +0100

    Add traits on field-less structs (#354)

    Instantiated templates can be generated as structs with zero fields, aka
    unit struct.

    We need unit structs to be copyable, clonable, defaultable and byterepr
    becau…
Comment thread libcc2rs/src/rc.rs
HeapArray(Weak<RefCell<Box<[T]>>>),
StackVec(Weak<RefCell<Vec<T>>>),
HeapVec(Weak<RefCell<Vec<T>>>),
Nested(Rc<dyn SliceParent<T>>),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this dyn doubles the size of PtrKind. Needs benchmarking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size is the same. It's wrapped in an Rc, same as ReinterpretedView

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check if micro already contains benchmarks for arrays, otherwise I will add some

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nop. size of Rc dyn is 2x size of Rc without dyn.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant that it does not increase the size of PtrKind because we already have a Weak<dyn> which is Field

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I benchmarked brunsli and woff2 and I saw a slowdown between 1.2% and 1.4%. I will also benchmark micro

Comment thread libcc2rs/src/rc.rs
}

fn address(&self) -> usize {
self.kind.address().wrapping_add(self.byte_offset())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a few tests that subtract pointers to ensure addresses are being computed correctly across multiple nested levels.
e.g.:

int v[3][4];
assert(&v[0][3] - &v[0][1] == 2);
assert(v[1] - (&v[0][0] + 3)  == 1);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second assert is UB. It's rejected by clang in constexpr mode: https://godbolt.org/z/T43K6M3v1. The pointers are not from the same array object

@nunoplopes

Copy link
Copy Markdown
Contributor

Is this change expected?

--- a/woff2/out/refcount/src/bin/woff2_compress.rs
+++ b/woff2/out/refcount/src/bin/woff2_compress.rs
@@ -640,7 +640,7 @@ pub struct woff2_Font {
     pub num_tables: u16,
     #[offset(8)]
     #[byte_size(48)]
-    pub tables: BTreeMap<u32, Value<woff2_Font_Table>>,
+    pub tables: BTreeMap<u32, Value<(Value<u32>, Value<woff2_Font_Table>)>>,
 }
 #[derive(DeepClone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
 #[byte_size(80)]
@@ -651,7 +651,7 @@ pub struct woff2_FontCollection {
     pub header_version: u32,
     #[offset(8)]
     #[byte_size(48)]
-    pub tables: BTreeMap<u32, Value<Ptr<woff2_Font_Table>>>,
+    pub tables: BTreeMap<u32, Value<(Value<u32>, Value<Ptr<woff2_Font_Table>>)>>,
     #[offset(56)]
     #[byte_size(24)]
     pub fonts: Value<Vec<woff2_Font>>,
@@ -704,7 +704,8 @@ pub fn ReadTrueTypeFont_33(
     {
         return false;
     }
-    let intervals: Value<BTreeMap<u32, Value<u32>>> = Rc::new(RefCell::new(BTreeMap::new()));
+    let intervals: Value<BTreeMap<u32, Value<(Value<u32>, Value<u32>)>>> =
+        Rc::new(RefCell::new(BTreeMap::new()));
     let mut i: u16 = 0_u16;
     'loop_: while ({ (i as i32) } < { (font.with(|__s| __s.num_tables) as i32) }) {
         let table: Value<woff2_Font_Table> = Rc::new(RefCell::new(<woff2_Font_Table>::default()));

@lucic71

lucic71 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Is this change expected?

--- a/woff2/out/refcount/src/bin/woff2_compress.rs
+++ b/woff2/out/refcount/src/bin/woff2_compress.rs
@@ -640,7 +640,7 @@ pub struct woff2_Font {
     pub num_tables: u16,
     #[offset(8)]
     #[byte_size(48)]
-    pub tables: BTreeMap<u32, Value<woff2_Font_Table>>,
+    pub tables: BTreeMap<u32, Value<(Value<u32>, Value<woff2_Font_Table>)>>,
 }
 #[derive(DeepClone, Record, ByteRepr, VaArg, FnPtrArg, Default)]
 #[byte_size(80)]
@@ -651,7 +651,7 @@ pub struct woff2_FontCollection {
     pub header_version: u32,
     #[offset(8)]
     #[byte_size(48)]
-    pub tables: BTreeMap<u32, Value<Ptr<woff2_Font_Table>>>,
+    pub tables: BTreeMap<u32, Value<(Value<u32>, Value<Ptr<woff2_Font_Table>>)>>,
     #[offset(56)]
     #[byte_size(24)]
     pub fonts: Value<Vec<woff2_Font>>,
@@ -704,7 +704,8 @@ pub fn ReadTrueTypeFont_33(
     {
         return false;
     }
-    let intervals: Value<BTreeMap<u32, Value<u32>>> = Rc::new(RefCell::new(BTreeMap::new()));
+    let intervals: Value<BTreeMap<u32, Value<(Value<u32>, Value<u32>)>>> =
+        Rc::new(RefCell::new(BTreeMap::new()));
     let mut i: u16 = 0_u16;
     'loop_: while ({ (i as i32) } < { (font.with(|__s| __s.num_tables) as i32) }) {
         let table: Value<woff2_Font_Table> = Rc::new(RefCell::new(<woff2_Font_Table>::default()));

Yes, see #523

@lucic71

lucic71 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

I benchmarked the tests I added in Cpp2Rust/cpp2rust-testsuite#8, numbers only for refcount:

test master (ms) master vs cpp nested-containers vs master
matmul_vector 139 164x 1.78x
matmul_array 104 59x 5.62x
row_ptr_vector 192 6x 2x
row_ptr_array 181 6.7x 1.87x

@nunoplopes
nunoplopes merged commit 240cd0f into Cpp2Rust:master Oct 13, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rules do not apply recursively

2 participants